├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── MongooseConfig.cmake.in ├── README.md ├── docs ├── API.md ├── AndroidBuild.md ├── Embed.md ├── FAQ.md ├── Internals.md ├── LuaSqlite.md ├── Options.md ├── SSL.md └── Usage.md ├── examples ├── Makefile ├── c# │ ├── example.cs │ └── mongoose.cs ├── chat.c ├── hello.c ├── helloworld.cpp ├── html │ ├── favicon.ico │ ├── index.html │ ├── jquery.js │ ├── login.html │ ├── logo.png │ ├── main.js │ └── style.css ├── json.cpp ├── lua │ ├── dirscan.lp │ └── prime_numbers.lp ├── lua_dll.c ├── main.cpp ├── post.c ├── qcomm.c ├── ssl_cert.pem ├── upload.c ├── websocket.c ├── websocket.cpp └── websocket_html_root │ ├── index.html │ └── ws.html ├── mongoose.c ├── mongoose.h └── mongoose ├── Controller.cpp ├── Controller.h ├── JsonController.cpp ├── JsonController.h ├── JsonResponse.cpp ├── JsonResponse.h ├── LICENSE ├── Mutex.cpp ├── Mutex.h ├── Request.cpp ├── Request.h ├── RequestHandler.h ├── Response.cpp ├── Response.h ├── Server.cpp ├── Server.h ├── Session.cpp ├── Session.h ├── Sessions.cpp ├── Sessions.h ├── StreamResponse.cpp ├── StreamResponse.h ├── UploadFile.cpp ├── UploadFile.h ├── Utils.cpp ├── Utils.h ├── WebController.cpp ├── WebController.h ├── WebSocket.cpp ├── WebSocket.h ├── WebSockets.cpp └── WebSockets.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (mongoose) 3 | find_package (Threads) 4 | 5 | option (MAIN 6 | "Compile the main" OFF) 7 | 8 | option (EXAMPLES 9 | "Compile examples" OFF) 10 | 11 | option (WEBSOCKET 12 | "Enables websocket" OFF) 13 | 14 | option (CPP_BINDING 15 | "Enables C++ binding" ON) 16 | 17 | option (HAS_JSONCPP 18 | "Enables JsonCpp" OFF) 19 | 20 | option (ENABLE_STATS 21 | "Enable server statistics" ON) 22 | 23 | option (ENABLE_REGEX_URL 24 | "Enable url regex matching dispatcher" OFF) 25 | 26 | set (JSONCPP_DIR "${PROJECT_SOURCE_DIR}/../jsoncpp" 27 | CACHE STRING "Json C++ directory") 28 | 29 | set (SOURCES 30 | mongoose.c 31 | ) 32 | 33 | set (MONGOOSE_CPP "${PROJECT_SOURCE_DIR}/mongoose") 34 | 35 | include_directories ("${PROJECT_SOURCE_DIR}") 36 | 37 | if (ENABLE_STATS) 38 | add_definitions("-DENABLE_STATS") 39 | endif (ENABLE_STATS) 40 | 41 | if (ENABLE_REGEX_URL) 42 | add_definitions("-DENABLE_REGEX_URL") 43 | SET (CMAKE_CXX_FLAGS "-std=c++11") 44 | endif (ENABLE_REGEX_URL) 45 | 46 | if (CPP_BINDING) 47 | set (SOURCES 48 | ${SOURCES} 49 | ${MONGOOSE_CPP}/Utils.cpp 50 | ${MONGOOSE_CPP}/Controller.cpp 51 | ${MONGOOSE_CPP}/Mutex.cpp 52 | ${MONGOOSE_CPP}/Request.cpp 53 | ${MONGOOSE_CPP}/Response.cpp 54 | ${MONGOOSE_CPP}/Server.cpp 55 | ${MONGOOSE_CPP}/Session.cpp 56 | ${MONGOOSE_CPP}/Sessions.cpp 57 | ${MONGOOSE_CPP}/StreamResponse.cpp 58 | ${MONGOOSE_CPP}/UploadFile.cpp 59 | ${MONGOOSE_CPP}/WebController.cpp 60 | ) 61 | 62 | if (HAS_JSONCPP) 63 | set (SOURCES 64 | ${SOURCES} 65 | ${MONGOOSE_CPP}/JsonResponse.cpp 66 | ${MONGOOSE_CPP}/JsonController.cpp 67 | ) 68 | 69 | include_directories ("${JSONCPP_DIR}/include/") 70 | endif (HAS_JSONCPP) 71 | 72 | if (WEBSOCKET) 73 | set (SOURCES 74 | ${SOURCES} 75 | ${MONGOOSE_CPP}/WebSocket.cpp 76 | ${MONGOOSE_CPP}/WebSockets.cpp 77 | ) 78 | endif (WEBSOCKET) 79 | 80 | include_directories ("${MONGOOSE_CPP}") 81 | endif (CPP_BINDING) 82 | 83 | if (NOT WEBSOCKET) 84 | add_definitions("-DNO_WEBSOCKET") 85 | endif (NOT WEBSOCKET) 86 | 87 | # Adding dl 88 | if (NOT WIN32) 89 | set (EXTRA_LIBS ${EXTRA_LIBS} dl) 90 | endif (NOT WIN32) 91 | 92 | # Adding sockets for Win32 93 | if (WIN32) 94 | set (EXTRA_LIBS ${EXTRA_LIBS} ws2_32) 95 | endif (WIN32) 96 | 97 | # Compiling library 98 | add_library (mongoose ${SOURCES}) 99 | target_link_libraries (mongoose ${EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT}) 100 | 101 | if (HAS_JSONCPP) 102 | target_link_libraries (mongoose json) 103 | endif (HAS_JSONCPP) 104 | 105 | if (EXAMPLES OR MAIN) 106 | if (HAS_JSONCPP) 107 | add_subdirectory("${JSONCPP_DIR}" jsoncpp) 108 | endif (HAS_JSONCPP) 109 | endif () 110 | 111 | # Compiling executable 112 | if (MAIN) 113 | add_executable (main main.c) 114 | target_link_libraries (main mongoose) 115 | endif (MAIN) 116 | 117 | # Compiling tests 118 | if (EXAMPLES) 119 | add_executable (post examples/post.c) 120 | target_link_libraries (post mongoose) 121 | 122 | if (NOT WIN32) 123 | add_executable (hello examples/hello.c) 124 | target_link_libraries (hello mongoose) 125 | endif (NOT WIN32) 126 | 127 | if (CPP_BINDING) 128 | add_executable (helloworld examples/helloworld.cpp) 129 | target_link_libraries (helloworld mongoose) 130 | 131 | add_executable (cpp examples/main.cpp) 132 | target_link_libraries (cpp mongoose) 133 | 134 | if (HAS_JSONCPP) 135 | add_executable (json_api examples/json.cpp) 136 | target_link_libraries (json_api mongoose) 137 | endif (HAS_JSONCPP) 138 | 139 | if (WEBSOCKET) 140 | add_executable (cpp_websocket examples/websocket.cpp) 141 | target_link_libraries (cpp_websocket mongoose) 142 | endif (WEBSOCKET) 143 | endif (CPP_BINDING) 144 | endif (EXAMPLES) 145 | 146 | # install 147 | set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") 148 | configure_file(MongooseConfig.cmake.in MongooseConfig.cmake @ONLY) 149 | 150 | install (FILES mongoose.h DESTINATION "${INCLUDE_INSTALL_DIR}") 151 | install (DIRECTORY mongoose DESTINATION "${INCLUDE_INSTALL_DIR}" PATTERN "*.cpp" EXCLUDE) 152 | install (TARGETS mongoose DESTINATION lib EXPORT mongoose-targets) 153 | install (EXPORT mongoose-targets DESTINATION "lib/cmake/mongoose" FILE MongooseTargets.cmake) 154 | install (FILES ${CMAKE_CURRENT_BINARY_DIR}/MongooseConfig.cmake DESTINATION "lib/cmake/mongoose") 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2013 Sergey Lyubka 2 | Copyright (c) 2013 Cesanta Software Limited 3 | All rights reserved 4 | 5 | This code is dual-licensed: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 2 as 7 | published by the Free Software Foundation. For the terms of this 8 | license, see http://www.gnu.org/licenses. 9 | 10 | You are free to use this code under the terms of the GNU General 11 | Public License, but WITHOUT ANY WARRANTY; without even the implied 12 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | See the GNU General Public License for more details. 14 | 15 | Alternatively, you can license this code under a commercial 16 | license, as set out in http://cesanta.com/products.html. 17 | -------------------------------------------------------------------------------- /MongooseConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component (myDir ${CMAKE_CURRENT_LIST_FILE} PATH) 2 | 3 | set (MONGOOSE_LIBRARIES mongoose) 4 | set (MONGOOSE_INCLUDE_DIR "@INCLUDE_INSTALL_DIR@") 5 | 6 | include(${myDir}/MongooseTargets.cmake) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mongoose-C++ 2 | 3 | Mongoose-C++ is a fork of the popular [mongoose](https://github.com/valenok/mongoose) 4 | lightweight web server which aims to add C++ bindings and a easy-to-use 5 | API. 6 | 7 | # Features 8 | 9 | - Object-Oriented high level API, keeping the lightweight mongoose implementation 10 | as a backend 11 | - Easy-to-use controllers sytem to build an application with modules 12 | - Possibility of enabling JsonCPP to create a json compliant web application 13 | - URL dispatcher using regex matches (C++11) 14 | - Session system to store data about an user using cookies and garbage collect cleaning 15 | - Simple access to GET & POST requests 16 | - Websockets support 17 | 18 | # Hello world 19 | 20 | Here is an example, this will serve the static files from `www/` directory (which 21 | is the default setting) and the `/hello` page will be answered by a controller which 22 | will display the GET `name` variable, for instance `/hello?name=bob` will display 23 | the string "Hello bob". Default parameter value, if not provided, will be 24 | "... waht's your name ?". This is the `helloworld` program build in the examples: 25 | 26 | ```c++ 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | using namespace Mongoose; 34 | 35 | class MyController : public WebController 36 | { 37 | public: 38 | void hello(Request &request, StreamResponse &response) 39 | { 40 | response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; 41 | } 42 | 43 | void setup() 44 | { 45 | addRoute("GET", "/hello", MyController, hello); 46 | } 47 | }; 48 | 49 | 50 | int main() 51 | { 52 | MyController myController; 53 | Server server(8080); 54 | server.registerController(&myController); 55 | 56 | server.start(); 57 | 58 | while (1) { 59 | sleep(10); 60 | } 61 | } 62 | ``` 63 | 64 | # Building examples 65 | 66 | You can build examples using CMake: 67 | 68 | ``` 69 | mkdir build 70 | cd build 71 | cmake -DEXAMPLES=ON .. 72 | make 73 | ``` 74 | 75 | This will build you the `cpp` program with examples of GET, POST (form), session and 76 | HTTP response code 77 | 78 | You can also enable Json example using the `-DHAS_JSONCPP=ON` option when cmake'ing, 79 | this will build the `json` executable. You also have to specify the `JSONCPP_DIR` that is the [JsonCpp](http://jsoncpp.sourceforge.net/) installation directory. 80 | 81 | Websockets are also supported and will be compiled if the `-DWEBSOCKET=ON` option is 82 | set with cmake (which is the default). `websocket.cpp` will be compiled to the 83 | `cpp_websocket` executable which let you see an example. Note that references to the 84 | `WebSocket*` clients can be keeped to dispatch data to them, which can be really 85 | useful to push data to some clients. 86 | 87 | To enable url regex matching dispatcher use `-DENABLE_REGEX_URL=ON` option. 88 | Note that this depends on C++11. 89 | 90 | # Development 91 | 92 | The code writing take places in the `mongoose/` directory and the whole repository 93 | will be merged as often as possible from the original mongoose project. 94 | 95 | # License 96 | 97 | The mongoose binding (`mongoose/` folder) is under MIT license 98 | 99 | However, the original mongoose project license is different, have a look to the 100 | `LICENSE` file for more information. 101 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | # Mongoose API Reference 2 | 3 | struct mg_server *mg_create_server(void *server_param); 4 | 5 | Creates web server instance. Returns opaque instance pointer, or NULL if 6 | there is not enough memory. Note that this function doesn't make the 7 | server instance to serve. Serving is done by `mg_poll_server()` function. 8 | Web server instance has a list of active connections, initially empty, 9 | and a list of URI handlers, initially empty, a list of configuration 10 | options that can be modified by `mg_set_option()`. 11 | 12 | `user_param`: Could be any pointer, or NULL. This pointer will be passed 13 | to callback functions as `struct mg_connection::server_param` field. 14 | A common use case is to pass `this` pointer of the C++ wrapper class 15 | as `user_param`, to let the callback get the pointer to the C++ 16 | object. 17 | 18 | Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom 19 | processing is required SIGPIPE, signal handler must be set up after 20 | calling `mg_create_server()`. 21 | 22 | Important: Mongoose does not install `SIGCHLD` handler. If CGI is used, 23 | `SIGCHLD` handler must be set up to reap CGI zombie processes. 24 | 25 | 26 | void mg_destroy_server(struct mg_server **server); 27 | 28 | Deallocates web server instance, closes all pending connections, and makes 29 | server pointer a NULL pointer. 30 | 31 | const char mg_set_option(struct mg_server *server, const char *name, 32 | const char *value); 33 | 34 | Sets a particular server option. Please refer to a separate documentation page 35 | that lists all available option names. Note that at least one option, 36 | `listening_port`, must be specified. To serve static files, `document_root` 37 | must be specified too. If `document_root` option is left unset, Mongoose 38 | will not access filesystem at all. This function returns NULL if option was 39 | set successfully, otherwise it returns human-readable error string. It is 40 | allowed to call `mg_set_option()` by the same thread that does 41 | `mg_poll_server()` (an IO thread) and change server configuration while it 42 | is serving, in between `mg_poll_server()` calls. 43 | 44 | void mg_poll_server(struct mg_server *server, int milliseconds); 45 | 46 | This function performs one iteration of IO loop by iterating over all 47 | active connections, performing `select()` syscall on all sockets, and sleeping 48 | for `milliseconds` number of milliseconds. When `select()` returns, Mongoose 49 | does an IO for each socket that has data to be sent or received. Application 50 | code must call `mg_poll_server()` in a loop. It is an error to have more then 51 | one thread calling `mg_poll_server()`, `mg_set_option()` or any other function 52 | that take `struct mg_server *` parameter. Mongoose does not 53 | mutex-protect `struct mg_server *`, therefore the best practice is 54 | to call server management functions from the same thread (an IO thread). 55 | 56 | void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t); 57 | 58 | Adds an URI handler. If Mongoose gets a request and request's URI starts 59 | with `uri`, then specified handler is called to serve the request. Thus, an 60 | `uri` is a match prefix. For example, if `uri` is "/", then all requests will 61 | be routed to the handler, because all URIs start with `/` character. 62 | 63 | void mg_set_http_error_handler(struct mg_server *, mg_handler_t); 64 | 65 | Adds HTTP error handler. An actual HTTP error is passed as 66 | `struct mg_connection::status_code` parameter. If handler returns 0, it 67 | means a handler has not processed the connection, and mongoose proceeds 68 | with sending HTTP error to the client. Otherwise, mongoose does nothing. 69 | 70 | const char **mg_get_valid_option_names(void); 71 | 72 | Returns a NULL-terminated array of option names and their default values. 73 | There are two entries per option in an array: an option name followed by a 74 | default value. A default value could be NULL. A NULL name indicates an end 75 | of the array. 76 | 77 | const char *mg_get_option(const struct mg_server *server, const char *name); 78 | 79 | Returns the value of particular configuration parameter. If 80 | given parameter name is not valid, NULL is returned. For valid names, return 81 | value is guaranteed to be non-NULL. If parameter is not set, zero-length string 82 | is returned. 83 | 84 | 85 | int mg_iterate_over_connections(struct mg_server *, 86 | void (*func)(struct mg_connection *, void *), 87 | void *param); 88 | 89 | This is an interface primarily designed to push arbitrary data to websocket 90 | connections at any time. This function could be called from any thread. When 91 | it returns, an IO thread called `func()` on each active websocket connection, 92 | passing `param` as an extra parameter. It is allowed to call `mg_write()` or 93 | `mg_websocket_write()` within a callback, cause these write functions are 94 | thread-safe. 95 | 96 | int mg_write(struct mg_connection *, const void *buf, int len); 97 | 98 | Send data to the client. This function is thread-safe. This function appends 99 | given buffer to a send queue of a given connection. It may return 0 if 100 | there is not enough memory, in which case the data will not be sent. 101 | 102 | int mg_websocket_write(struct mg_connection* conn, int opcode, 103 | const char *data, size_t data_len); 104 | 105 | Similar to `mg_write()`, but wraps the data into a websocket frame with a 106 | given websocket `opcode`. This function is available when mongoose is 107 | compiled with `-DUSE_WEBSOCKET`. 108 | 109 | const char *mg_get_header(const struct mg_connection *, const char *name); 110 | 111 | Get the value of particular HTTP header. This is a helper function. 112 | It traverses http_headers array, and if the header is present in the array, 113 | returns its value. If it is not present, NULL is returned. 114 | 115 | 116 | int mg_get_var(const struct mg_connection *conn, const char *var_name, 117 | char *buf, size_t buf_len); 118 | 119 | Gets HTTP form variable. Both POST buffer and query string are inspected. 120 | Form variable is url-decoded and written to the buffer. On success, this 121 | function returns the length of decoded variable. On error, -1 is returned if 122 | variable not found, and -2 is returned if destination buffer is too small 123 | to hold the variable. Destination buffer is guaranteed to be 124 | '\0' - terminated if it is not NULL or zero length. 125 | 126 | int mg_parse_header(const char *hdr, const char *var_name, char *buf, 127 | size_t buf_size); 128 | 129 | This function parses HTTP header and fetches given variable's value in a buffer. 130 | A header should be like `x=123, y=345, z="other value"`. This function is 131 | designed to parse Cookie headers, Authorization headers, and similar. Returns 132 | the length of the fetched value, or 0 if variable not found. 133 | 134 | int mg_modify_passwords_file(const char *passwords_file_name, 135 | const char *domain, 136 | const char *user, 137 | const char *password); 138 | 139 | Add, edit or delete the entry in the passwords file. 140 | This function allows an application to manipulate .htpasswd files on the 141 | fly by adding, deleting and changing user records. This is one of the 142 | several ways of implementing authentication on the server side. For another, 143 | cookie-based way please refer to the examples/chat.c in the source tree. 144 | If password is not NULL, entry is added (or modified if already exists). 145 | If password is NULL, entry is deleted. 146 | Return: 1 on success, 0 on error. 147 | -------------------------------------------------------------------------------- /docs/AndroidBuild.md: -------------------------------------------------------------------------------- 1 | # Mongoose Build on Android 2 | 3 | This is a small guide to help you run mongoose on Android. Currently it is 4 | tested on the HTC Wildfire. If you have managed to run it on other devices 5 | as well, please comment or drop an email in the mailing list. 6 | Note : You dont need root access to run mongoose on Android. 7 | 8 | - Clone Mongoose Git repo 9 | - Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html) 10 | - Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build` 11 | That should generate mongoose/lib/armeabi/mongoose 12 | - Using the adb tool (you need to have Android SDK installed for that), 13 | push the generated mongoose binary to `/data/local` folder on device. 14 | - From adb shell, navigate to `/data/local` and execute `./mongoose`. 15 | - To test if the server is running fine, visit your web-browser and 16 | navigate to `http://127.0.0.1:8080` You should see the `Index of /` page. 17 | 18 | ![screenshot](https://a248.e.akamai.net/camo.github.com/b88428bf009a2b6141000937ab684e04cc8586af/687474703a2f2f692e696d6775722e636f6d2f62676f6b702e706e67) 19 | 20 | 21 | Notes: 22 | 23 | - `jni` stands for Java Native Interface. Read up on Android NDK if you want 24 | to know how to interact with the native C functions of mongoose in Android 25 | Java applications. 26 | - TODO: A Java application that interacts with the native binary or a 27 | shared library. 28 | -------------------------------------------------------------------------------- /docs/Embed.md: -------------------------------------------------------------------------------- 1 | # Mongoose Embedding Guide 2 | 3 | Embedding Mongoose is done in two steps: 4 | 5 | 1. Copy 6 | [mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and 7 | [mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h) 8 | to your application's source tree and include these two files in the build. 9 | 2. Somewhere in the application code, call `mg_create_server()` to create 10 | a server, configure it with `mg_set_option()` and loop with 11 | `mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup. 12 | 13 | Here's a minimal application `app.c` that embeds mongoose: 14 | 15 | #include "mongoose.h" 16 | int main(void) { 17 | struct mg_server *server = mg_create_server(NULL); 18 | mg_set_option(server, "document_root", "."); 19 | mg_set_option(server, "listening_port", "8080"); 20 | for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop 21 | mg_destroy_server(&server); 22 | return 0; 23 | } 24 | 25 | To compile it, put `mongoose.c`, `mongoose.h` and `minimal.c` into one 26 | folder, then run the following UNIX command: 27 | 28 | cc app.c mongoose.c -o app 29 | 30 | If you're on Windows, run this in a Visual Studio shell: 31 | 32 | cl app.c mongoose.c /TC /MD 33 | 34 | When run, this simple application opens port 8080 and serves static files, 35 | CGI files and lists directory content in the current working directory. 36 | 37 | Mongoose can call user-defined functions when certain URIs are requested. 38 | These functions are _called uri handlers_. `mg_add_uri_handler()` registers 39 | an URI handler, and there is no restriction exist on the number of URI handlers. 40 | Also, mongoose can call a user-defined function when it is about to send 41 | HTTP error back to client. That function is called _http error handler_ and 42 | can be registered by `mg_set_http_error_handler()`. Handlers are called 43 | by Mongoose with `struct mg_connection *` pointer as a parameter, which 44 | has all information about the request: HTTP headers, POST or websocket 45 | data buffer, etcetera. 46 | 47 | Let's extend our minimal application example and 48 | create an URI that will be served by user's C code. The app will handle 49 | `/hello` URI by showing a hello message. So, when app is run, 50 | http://127.0.0.1:8080/hello will say hello, and here's the code: 51 | 52 | #include 53 | #include "mongoose.h" 54 | 55 | static int handle_hello(struct mg_connection *conn) { 56 | static const char *reply = "HTTP/1.0 200 OK\r\n\r\nHello world!\n"; 57 | mg_write(conn, reply, strlen(reply)); 58 | return 1; 59 | } 60 | 61 | int main(void) { 62 | struct mg_server *server = mg_create_server(NULL); 63 | mg_set_option(server, "document_root", "."); 64 | mg_set_option(server, "listening_port", "8080"); 65 | mg_add_uri_handler(server, "/hello", &handle_hello); 66 | for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop 67 | mg_destroy_server(&server); 68 | return 0; 69 | } 70 | 71 | Note that URI handler must output valid HTTP response, which includes 72 | the reply line with status code `HTTP/1.0 200 OK`, HTTP headers which are 73 | empty in our example, and message body `Hello world!\n`. Note that reply 74 | line is ended with `\r\n`, and HTTP headers are also ended with `\r\n`. 75 | 76 | Mongoose source code contains a well-commented example code, listed below: 77 | 78 | * [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c) 79 | shows how to handle form input, file upload, websocket communication, get 80 | cookie values. 81 | 82 | * [chat](https://github.com/cesanta/mongoose/blob/master/examples/chat) 83 | implements basic online chat functionality using Lua scripting capabilities 84 | of Mongoose. Not a single line of C is written for that example. 85 | Demostrates usage of database, cookie-based authentication, session 86 | support, RESTful interface. No additional software is required to run it 87 | on any platform. 88 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | # Mongoose FAQ 2 | 3 | ### PHP doesn't work: getting empty page, or 'File not found' error 4 | 5 | The reason for that is wrong paths to the interpreter. Remember that with PHP, 6 | correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify 7 | full path to the PHP interpreter, e.g.: 8 | 9 | mongoose -cgi_interpreter /full/path/to/php-cgi 10 | 11 | ### Mongoose fails to start 12 | 13 | If Mongoose exits immediately when run, this 14 | usually indicates a syntax error in the configuration file 15 | (named `mongoose.conf` by default) or the command-line arguments. 16 | Syntax checking is omitted from Mongoose to keep its size low. However, 17 | the Manual should be of help. Note: the syntax changes from time to time, 18 | so updating the config file might be necessary after executable update. 19 | 20 | ### Embedding with OpenSSL on Windows might fail because of calling convention 21 | 22 | To force Mongoose to use `__stdcall` convention, add `/Gz` compilation 23 | flag to the Visual Studio project settings. 24 | -------------------------------------------------------------------------------- /docs/Internals.md: -------------------------------------------------------------------------------- 1 | # Mongoose Internals 2 | 3 | 60 | -------------------------------------------------------------------------------- /docs/LuaSqlite.md: -------------------------------------------------------------------------------- 1 | # Mongoose Lua Server Pages 2 | 3 | Pre-built Windows and Mac mongoose binaries have built-in Lua Server Pages 4 | support. That means it is possible to write PHP-like scripts with mongoose, 5 | using Lua programming language instead of PHP. Lua is known 6 | for it's speed and small size. Mongoose uses Lua version 5.2.1, the 7 | documentation for it can be found at 8 | [Lua 5.2 reference manual](http://www.lua.org/manual/5.2/). 9 | 10 | To create a Lua Page, make sure a file has `.lp` extension. For example, 11 | let's say it is going to be `my_page.lp`. The contents of the file, just like 12 | with PHP, is HTML with embedded Lua code. Lua code must be enclosed in 13 | `` blocks, and can appear anywhere on the page. For example, to 14 | print current weekday name, one can write: 15 | 16 |

17 | Today is: 18 | 19 |

20 | 21 | Note that this example uses function `mg.write()`, which prints data to the 22 | web page. Using function `mg.write()` is the way to generate web content from 23 | inside Lua code. In addition to `mg.write()`, all standard library functions 24 | are accessible from the Lua code (please check reference manual for details), 25 | and also information about the request is available in `mg.request_info` object, 26 | like request method, all headers, etcetera. Please refer to 27 | `struct mg_request_info` definition in 28 | [mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h) 29 | to see what kind of information is present in `mg.request_info` object. Also, 30 | [page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp) and 31 | [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) 32 | contains some example code that uses `request_info` and other functions(form submitting for example). 33 | 34 | Mongoose exports the following to the Lua server page: 35 | 36 | mg.read() -- reads a chunk from POST data, returns it as a string 37 | mg.write(str) -- writes string to the client 38 | mg.include(path) -- sources another Lua file 39 | mg.redirect(uri) -- internal redirect to a given URI 40 | mg.onerror(msg) -- error handler, can be overridden 41 | mg.version -- a string that holds Mongoose version 42 | mg.request_info -- a table with request information 43 | 44 | -- Connect to the remote TCP server. This function is an implementation 45 | -- of simple socket interface. It returns a socket object with three 46 | -- methods: send, recv, close, which are synchronous (blocking). 47 | -- connect() throws an exception on connection error. 48 | connect(host, port, use_ssl) 49 | 50 | -- Example of using connect() interface: 51 | local host = 'code.google.com' -- IP address or domain name 52 | local ok, sock = pcall(connect, host, 80, 1) 53 | if ok then 54 | sock:send('GET /p/mongoose/ HTTP/1.0\r\n' .. 55 | 'Host: ' .. host .. '\r\n\r\n') 56 | local reply = sock:recv() 57 | sock:close() 58 | -- reply now contains the web page https://code.google.com/p/mongoose 59 | end 60 | 61 | 62 | **IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore, 63 | every Lua Page must begin with HTTP reply line and headers**, like this: 64 | 65 | 66 | 67 | ... the rest of the web page ... 68 | 69 | To serve Lua Page, mongoose creates Lua context. That context is used for 70 | all Lua blocks within the page. That means, all Lua blocks on the same page 71 | share the same context. If one block defines a variable, for example, that 72 | variable is visible in the block that follows. 73 | 74 | 75 | -------------------------------------------------------------------------------- /docs/Options.md: -------------------------------------------------------------------------------- 1 | # Mongoose Configuration Options 2 | 3 | Every option is followed by it's default value. 4 | If default value is not present, then it is empty. 5 | 6 | ### cgi_pattern `**.cgi$|**.pl$|**.php$` 7 | All files that match `cgi_pattern` are treated as CGI files. Default pattern 8 | allows CGI files be anywhere. To restrict CGIs to a certain directory, 9 | use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is 10 | matched against the pattern, not the URI. 11 | 12 | ### cgi_environment 13 | Extra environment variables to be passed to the CGI script in 14 | addition to standard ones. The list must be comma-separated list 15 | of name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`. 16 | 17 | ### put\_delete\_auth\_file 18 | Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests 19 | will fail. The format of the passwords file is the same as for `.htpasswd` file 20 | used for Digest authentication. It can be created and managed by means 21 | of `mongoose -A` command. 22 | 23 | ### cgi_interpreter 24 | Path to an executable to use as CGI interpreter for __all__ CGI scripts 25 | regardless script extension. If this option is not set (which is a default), 26 | Mongoose looks at first line of a CGI script, 27 | [shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\)), 28 | for an interpreter. 29 | 30 | For example, if both PHP and perl CGIs are used, then 31 | `#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the 32 | respective CGI scripts. Note that paths should be either full file paths, 33 | or file paths relative to the current working directory of mongoose server. 34 | If mongoose is started by mouse double-click on Windows, current working 35 | directory is a directory where mongoose executable is located. 36 | 37 | If all CGIs use the same interpreter, for example they are all PHP, then 38 | `cgi_interpreter` can be set to the path to `php-cgi.exe` executable and 39 | shebang line in the CGI scripts can be omitted. 40 | Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`. 41 | 42 | ### protect_uri 43 | Comma separated list of URI=PATH pairs, specifying that given 44 | URIs must be protected with respected password files. Paths must be full 45 | file paths. 46 | 47 | ### authentication_domain `mydomain.com` 48 | Authorization realm used in `.htpasswd` authorization. 49 | 50 | ### ssi_pattern `**.shtml$|**.shtm$` 51 | All files that match `ssi_pattern` are treated as SSI. 52 | 53 | Server Side Includes (SSI) is a simple interpreted server-side scripting 54 | language which is most commonly used to include the contents of a file into 55 | a web page. It can be useful when it is desirable to include a common piece 56 | of code throughout a website, for example, headers and footers. 57 | 58 | In order for a webpage to recognize an SSI-enabled HTML file, the filename 59 | should end with a special extension, by default the extension should be 60 | either `.shtml` or `.shtm`. 61 | 62 | Unknown SSI directives are silently ignored by mongoose. Currently, two SSI 63 | directives are supported, ` 78 | 79 | For more information on Server Side Includes, take a look at the Wikipedia: 80 | [Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes) 81 | 82 | ### throttle 83 | Limit download speed for clients. `throttle` is a comma-separated 84 | list of key=value pairs, where key could be: 85 | 86 | * limit speed for all connections 87 | x.x.x.x/mask limit speed for specified subnet 88 | uri_prefix_pattern limit speed for given URIs 89 | 90 | The value is a floating-point number of bytes per second, optionally 91 | followed by a `k` or `m` character, meaning kilobytes and 92 | megabytes respectively. A limit of 0 means unlimited rate. The 93 | last matching rule wins. Examples: 94 | 95 | *=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second, 96 | but give connections from 10.0.0.0/8 subnet 97 | unlimited speed 98 | 99 | /downloads/=5k limit accesses to all URIs in `/downloads/` to 100 | 5 kilobytes per secods. All other accesses are unlimited 101 | 102 | ### access\_log\_file 103 | Path to a file for access logs. Either full path, or relative to current 104 | working directory. If absent (default), then accesses are not logged. 105 | 106 | ### error\_log\_file 107 | Path to a file for error logs. Either full path, or relative to current 108 | working directory. If absent (default), then errors are not logged. 109 | 110 | ### enable\_directory\_listing `yes` 111 | Enable directory listing, either `yes` or `no`. 112 | 113 | ### enable\_keep\_alive `no` 114 | Enable connection keep alive, either `yes` or `no`. 115 | 116 | Experimental feature. Allows clients to reuse TCP connection for 117 | subsequent HTTP requests, which improves performance. 118 | For this to work when using request handlers it's important to add the correct 119 | Content-Length HTTP header for each request. If this is forgotten the client 120 | will time out. 121 | 122 | 123 | ### global\_auth\_file 124 | Path to a global passwords file, either full path or relative to the current 125 | working directory. If set, per-directory `.htpasswd` files are ignored, 126 | and all requests are authorised against that file. 127 | 128 | The file has to include the realm set through `authentication_domain` and the password in digest format: 129 | 130 | user:realm:digest 131 | test:test.com:ce0220efc2dd2fad6185e1f1af5a4327 132 | 133 | (e.g. use [this generator](http://www.askapache.com/online-tools/htpasswd-generator)) 134 | 135 | ### index_files `index.html,index.htm,index.cgi,index.shtml,index.php` 136 | Comma-separated list of files to be treated as directory index 137 | files. 138 | 139 | ### access\_control\_list 140 | An Access Control List (ACL) allows restrictions to be put on the list of IP 141 | addresses which have access to the web server. In the case of the Mongoose 142 | web server, the ACL is a comma separated list of IP subnets, where each 143 | subnet is prepended by either a `-` or a `+` sign. A plus sign means allow, 144 | where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, 145 | this means to deny only that single IP address. 146 | 147 | Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow 148 | all accesses. On each request the full list is traversed, and 149 | the last match wins. Examples: 150 | 151 | -0.0.0.0/0,+192.168/16 deny all acccesses, only allow 192.168/16 subnet 152 | 153 | To learn more about subnet masks, see the 154 | [Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork) 155 | 156 | ### extra\_mime\_types 157 | Extra mime types to recognize, in form `extension1=type1,exten- 158 | sion2=type2,...`. Extension must include dot. Example: 159 | `.cpp=plain/text,.java=plain/text` 160 | 161 | ### listening_ports `8080` 162 | Comma-separated list of ports to listen on. If the port is SSL, a 163 | letter `s` must be appeneded, for example, `80,443s` will open 164 | port 80 and port 443, and connections on port 443 will be SSL-ed. 165 | For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'. 166 | Redirect ports will redirect all their traffic to the first configured 167 | SSL port. For example, if `listening_ports` is `80r,443s`, then all 168 | HTTP traffic coming at port 80 will be redirected to HTTPS port 443. 169 | 170 | It is possible to specify an IP address to bind to. In this case, 171 | an IP address and a colon must be prepended to the port number. 172 | For example, to bind to a loopback interface on port 80 and to 173 | all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`. 174 | 175 | ### document_root `.` 176 | A directory to serve. By default, currect directory is served. Current 177 | directory is commonly referenced as dot (`.`). 178 | 179 | ### ssl_certificate 180 | Path to SSL certificate file. This option is only required when at least one 181 | of the `listening_ports` is SSL. The file must be in PEM format, 182 | and it must have both private key and certificate, see for example 183 | [ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem) 184 | 185 | ### num_threads `50` 186 | Number of worker threads. Mongoose handles each incoming connection in a 187 | separate thread. Therefore, the value of this option is effectively a number 188 | of concurrent HTTP connections Mongoose can handle. 189 | 190 | ### run\_as\_user 191 | Switch to given user credentials after startup. Usually, this option is 192 | required when mongoose needs to bind on privileged port on UNIX. To do 193 | that, mongoose needs to be started as root. But running as root is a bad idea, 194 | therefore this option can be used to drop privileges. Example: 195 | 196 | mongoose -listening_ports 80 -run_as_user nobody 197 | 198 | ### request\_timeout\_ms `30000` 199 | Timeout for network read and network write operations, in milliseconds. 200 | If client intends to keep long-running connection, either increase this value 201 | or use keep-alive messages. 202 | 203 | 204 | ### url\_rewrite\_patterns 205 | Comma-separated list of URL rewrites in the form of 206 | `uri_pattern=file_or_directory_path`. When Mongoose receives the request, 207 | it constructs the file name to show by combining `document_root` and the URI. 208 | However, if the rewrite option is used and `uri_pattern` matches the 209 | requested URI, then `document_root` is ignored. Insted, 210 | `file_or_directory_path` is used, which should be a full path name or 211 | a path relative to the web server's current working directory. Note that 212 | `uri_pattern`, as all mongoose patterns, is a prefix pattern. 213 | 214 | This makes it possible to serve many directories outside from `document_root`, 215 | redirect all requests to scripts, and do other tricky things. For example, 216 | to redirect all accesses to `.doc` files to a special script, do: 217 | 218 | mongoose -url_rewrite_patterns **.doc$=/path/to/cgi-bin/handle_doc.cgi 219 | 220 | Or, to imitate user home directories support, do: 221 | 222 | mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/ 223 | 224 | ### hide\_files\_patterns 225 | A pattern for the files to hide. Files that match the pattern will not 226 | show up in directory listing and return `404 Not Found` if requested. Pattern 227 | must be for a file name only, not including directory name. Example: 228 | 229 | mongoose -hide_files_patterns secret.txt|even_more_secret.txt 230 | 231 | 232 | -------------------------------------------------------------------------------- /docs/SSL.md: -------------------------------------------------------------------------------- 1 | # Mongoose SSL guide 2 | 3 | SSL is a protocol that makes web communication secure. To enable SSL 4 | in mongoose, 3 steps are required: 5 | 6 | 1. Valid certificate file must be created 7 | 2. `ssl_certificate` options must be set to contain path to the 8 | certificate file. 9 | 3. `listening_ports` option must contain a port number with letter `s` 10 | appended to it, which instructs Mongoose to use SSL for all connections 11 | made to that port. 12 | 13 | Below is the `mongoose.conf` file snippet for typical SSL setup: 14 | 15 | document_root www_root # Serve files in www_root directory 16 | listening_ports 80r,443s # Redirect all HTTP requests to HTTPS 17 | ssl_certificate ssl_cert.pem # Location of certificate file 18 | 19 | ## How to create SSL certificate file 20 | 21 | SSL certificate file is a text file that must contain at least two 22 | sections: 23 | 24 | 1. A private key 25 | 2. A certificate 26 | 27 | Both sections should be chunks of text in PEM format. When PEM file is 28 | opened in a text editor, it looks like this: 29 | 30 | -----BEGIN RSA PRIVATE KEY----- 31 | MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH 32 | hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= 33 | -----END RSA PRIVATE KEY----- 34 | -----BEGIN CERTIFICATE----- 35 | MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB 36 | SEGI4JSxV56lYg== 37 | -----END CERTIFICATE----- 38 | 39 | Two aforementioned sections are clearly seen. Typically, those section 40 | are bigger then in the example shown. The text between the `BEGIN` and 41 | `END` is the text representation of binary data, a private key and a 42 | certificate. Therefore, in order to create a certificate file, 43 | 44 | * private key must be converted to PEM format 45 | * certificate must be converted to PEM format 46 | * those two should be concatenated into a single file 47 | 48 | If the certificate chain in used, a chain file also needs to be 49 | converted into PEM format and appended to the certificate file. 50 | 51 | ## How SSL works 52 | 53 | SSL is a protocol that can encrypt communication between two parties. If third 54 | party observes all messages passed by, it would be very 55 | hard for the third party (though not impossible) to decrypt the communication. 56 | 57 | The idea is based on so-called public key encryption. Communicating parties 58 | have two keys: a public key and a private key. A public key is advertised 59 | to everybody, and it is contained in a certificate. A private key is kept 60 | secret. Security algorithm works in a way that anybody can encrypt 61 | a message using public key, and only private key can decrypt it. 62 | 63 | This is why web server needs both private key and certificate: private key 64 | is used to decrypt incoming messages, and certificate is used to tell the 65 | public key to the other party. When communication starts, parties exchange 66 | their public keys, and keep private keys to themselves. Man-in-the-middle 67 | who observes the communication is unable to decrypt the messages cause 68 | private keys are required for decryption. 69 | 70 | Encryption algorithms are built on top of hard mathematical problem, which 71 | makes it very expensive for man-in-the-middle to compute private keys. 72 | For example, RSA algorithm is based on a mathematical problem of factorization. 73 | It is easy to generate two very large prime numbers `P` and `Q` and make 74 | a product `P * Q`. But given a product, it is very hard to recover these 75 | two prime numbers - this is called factorization. 76 | -------------------------------------------------------------------------------- /docs/Usage.md: -------------------------------------------------------------------------------- 1 | # Mongoose Usage Guide 2 | 3 | Mongoose is small and easy to use web server. It is self-contained, and does 4 | not require any external software to run. 5 | 6 | On Windows, mongoose iconifies itself to the system tray icon when started. 7 | Right-click on the icon pops up a menu, where it is possible to stop 8 | mongoose, or configure it, or install it as Windows service. The easiest way 9 | to share a folder on Windows is to copy `mongoose.exe` to a folder, 10 | double-click the exe, and launch a browser at 11 | [http://localhost:8080](http://localhost:8080). Note that 'localhost' should 12 | be changed to a machine's name if a folder is accessed from other computer. 13 | 14 | On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in 15 | terminal, optionally followed by configuration parameters 16 | (`mongoose [OPTIONS]`) or configuration file name 17 | (`mongoose [config_file_name]`) starts the 18 | web server. Mongoose does not detach from terminal. Pressing `Ctrl-C` keys 19 | would stop the server. 20 | 21 | When started, mongoose first searches for the configuration file. 22 | If configuration file is specified explicitly in the command line, i.e. 23 | `mongoose path_to_config_file`, then specified configuration file is used. 24 | Otherwise, mongoose would search for file `mongoose.conf` in the same directory 25 | where binary is located, and use it. Configuration file can be absent. 26 | 27 | 28 | Configuration file is a sequence of lines, each line containing 29 | command line argument name and it's value. Empty lines, and lines beginning 30 | with `#`, are ignored. Here is the example of `mongoose.conf` file: 31 | 32 | document_root c:\www 33 | listening_ports 8080,8043s 34 | ssl_certificate c:\mongoose\ssl_cert.pem 35 | 36 | When configuration file is processed, mongoose process command line arguments, 37 | if they are specified. Command line arguments therefore can override 38 | configuration file settings. Command line arguments must start with `-`. 39 | For example, if `mongoose.conf` has line 40 | `document_root /var/www`, and mongoose has been started as 41 | `mongoose -document_root /etc`, then `/etc` directory will be served as 42 | document root, because command line options take priority over 43 | configuration file. Configuration options section below provide a good 44 | overview of Mongoose features. 45 | 46 | Note that configuration options on the command line must start with `-`, 47 | but their names are the same as in the config file. All option names are 48 | listed in the next section. Thus, the following two setups are equivalent: 49 | 50 | # Using command line arguments 51 | $ mongoose -listening_ports 1234 -document_root /var/www 52 | 53 | # Using config file 54 | $ cat mongoose.conf 55 | listening_ports 1234 56 | document_root /var/www 57 | $ mongoose 58 | 59 | Mongoose can also be used to modify `.htpasswd` passwords file: 60 | 61 | mongoose -A 62 | 63 | Unlike other web servers, mongoose does not require CGI scripts be located in 64 | a special directory. CGI scripts can be anywhere. CGI (and SSI) files are 65 | recognized by the file name pattern. Mongoose uses shell-like glob 66 | patterns. Pattern match starts at the beginning of the string, so essentially 67 | patterns are prefix patterns. Syntax is as follows: 68 | 69 | ** Matches everything 70 | * Matches everything but slash character, '/' 71 | ? Matches any character 72 | $ Matches the end of the string 73 | | Matches if pattern on the left side or the right side matches. 74 | 75 | All other characters in the pattern match themselves. Examples: 76 | 77 | **.cgi$ Any string that ends with .cgi 78 | /foo Any string that begins with /foo 79 | **a$|**b$ Any string that ends with a or b 80 | 81 | 82 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT) 2 | DLL_FLAGS = -DLUA_COMPAT_ALL -I../build 3 | RM = rm -rf 4 | 5 | ifeq ($(OS),Windows_NT) 6 | RM = del /q /f 7 | else 8 | UNAME_S := $(shell uname -s) 9 | DLL_FLAGS += -shared 10 | 11 | ifeq ($(UNAME_S),Linux) 12 | CFLAGS += -ldl 13 | endif 14 | 15 | ifeq ($(UNAME_S),Darwin) 16 | # DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib 17 | DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib 18 | endif 19 | endif 20 | 21 | all: websocket_html.c 22 | $(CC) hello.c ../mongoose.c -o hello $(CFLAGS) 23 | $(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS) 24 | $(CC) post.c ../mongoose.c -o post $(CFLAGS) 25 | $(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS) 26 | $(CC) upload.c ../mongoose.c -o upload $(CFLAGS) 27 | $(CC) auth.c ../mongoose.c -o auth $(CFLAGS) 28 | $(CC) server.c ../mongoose.c -o server $(CFLAGS) 29 | 30 | # $(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS) 31 | # $(CC) chat.c ../mongoose.c -o chat $(CFLAGS) 32 | # $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS) 33 | 34 | websocket_html.c: websocket.html 35 | perl mkdata.pl $< > $@ 36 | 37 | MSVC = ../../vc6 38 | CL = $(MSVC)/bin/cl 39 | CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \ 40 | /I$(MSVC)/include /I.. /Dsnprintf=_snprintf 41 | LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 42 | 43 | windows: websocket_html.c 44 | $(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 45 | $(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 46 | $(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 47 | $(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 48 | $(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 49 | $(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 50 | $(CL) server.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 51 | 52 | # $(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(LFLAGS) 53 | #$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll 54 | 55 | clean: 56 | -@$(RM) hello upload post websocket chat *.exe *.dSYM *.obj .*o 57 | -------------------------------------------------------------------------------- /examples/c#/example.cs: -------------------------------------------------------------------------------- 1 | // This file is part of mongoose web server project, 2 | // https://github.com/cesanta/mongoose 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | public class Program { 8 | static private int EventHandler(MongooseEvent ev) { 9 | if (ev.type != 1) { 10 | return 0; // Mark as unhandled 11 | } 12 | 13 | MongooseRequestInfo request_info = (MongooseRequestInfo) 14 | Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo)); 15 | 16 | if (request_info.uri != "/test") { 17 | return 0; // Mark as unhandled 18 | } 19 | 20 | Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n"); 21 | Mongoose.write(ev.conn, "Hello from C#!\n"); 22 | 23 | return 1; // Mark as handled 24 | } 25 | 26 | static void Main() { 27 | Mongoose web_server = new Mongoose(".", "9000", 28 | new MongooseEventHandler(EventHandler)); 29 | 30 | Console.WriteLine("Mongoose v." + web_server.version_ + " started."); 31 | Console.WriteLine("Press enter to exit program."); 32 | 33 | // Serve requests until user presses "enter" on a keyboard 34 | Console.ReadLine(); 35 | web_server.stop(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/c#/mongoose.cs: -------------------------------------------------------------------------------- 1 | // This file is part of mongoose web server project, 2 | // https://github.com/cesanta/mongoose 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | [StructLayout(LayoutKind.Sequential)] public struct MongooseHeader { 8 | [MarshalAs(UnmanagedType.LPTStr)] public IntPtr name; 9 | [MarshalAs(UnmanagedType.LPTStr)] public IntPtr value; 10 | }; 11 | 12 | // mongoose.h :: struct mg_request_info 13 | [StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo { 14 | [MarshalAs(UnmanagedType.LPTStr)] public string request_method; 15 | [MarshalAs(UnmanagedType.LPTStr)] public string uri; 16 | [MarshalAs(UnmanagedType.LPTStr)] public string http_version; 17 | [MarshalAs(UnmanagedType.LPTStr)] public string query_string; 18 | [MarshalAs(UnmanagedType.LPTStr)] public string remote_user; 19 | public int remote_ip; 20 | public int remote_port; 21 | public int is_ssl; 22 | [MarshalAs(UnmanagedType.ByValArray,SizeConst=64)] 23 | public MongooseHeader[] http_headers; 24 | }; 25 | 26 | [StructLayout(LayoutKind.Sequential)] public struct MongooseEvent { 27 | public int type; 28 | public IntPtr user_data; 29 | public IntPtr conn_data; 30 | public IntPtr event_param; 31 | public IntPtr conn; 32 | public IntPtr request_info; 33 | }; 34 | 35 | public delegate int MongooseEventHandlerN(ref MongooseEvent ev); 36 | public delegate int MongooseEventHandler(MongooseEvent ev); 37 | 38 | public class Mongoose { 39 | public const string dll_name_ = "mongoose"; 40 | public string version_ = "??"; 41 | 42 | // These are here to store a ref to the callbacks 43 | // while they are over in unmanaged code, to prevent garbage collection. 44 | private event MongooseEventHandlerN delegates; 45 | 46 | private IntPtr ctx_; 47 | 48 | [DllImport(dll_name_)] private static extern 49 | IntPtr mg_start([MarshalAs(UnmanagedType.LPArray, 50 | ArraySubType=UnmanagedType.LPTStr)] string[] options, 51 | MongooseEventHandlerN callback, 52 | IntPtr user_data); 53 | [DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx); 54 | [DllImport(dll_name_)] private static extern IntPtr mg_version(); 55 | [DllImport(dll_name_)] public static extern int mg_write(IntPtr conn, 56 | string data, int length); 57 | 58 | public Mongoose(string document_root, 59 | string listening_ports, 60 | MongooseEventHandler event_handler) { 61 | version_ = Marshal.PtrToStringAnsi(mg_version()); 62 | 63 | string[] options = { 64 | "document_root", document_root, 65 | "listening_ports", listening_ports, 66 | null 67 | }; 68 | 69 | MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) { 70 | return event_handler(ev); 71 | }; 72 | 73 | // Prevent garbage collection 74 | delegates += cb; 75 | 76 | ctx_ = mg_start(options, cb, IntPtr.Zero); 77 | } 78 | 79 | public static int write(IntPtr conn, string data) { 80 | return mg_write(conn, data, data.Length); 81 | } 82 | 83 | public void stop() { 84 | if (this.ctx_ != IntPtr.Zero) { 85 | mg_stop(this.ctx_); 86 | } 87 | this.ctx_ = IntPtr.Zero; 88 | } 89 | 90 | ~Mongoose() { 91 | stop(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/chat.c: -------------------------------------------------------------------------------- 1 | // This file is part of the Mongoose project, http://code.google.com/p/mongoose 2 | // It implements an online chat server. For more details, 3 | // see the documentation on the project web site. 4 | // To test the application, 5 | // 1. type "make" in the directory where this file lives 6 | // 2. point your browser to http://127.0.0.1:8081 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "mongoose.h" 17 | 18 | #define MAX_USER_LEN 20 19 | #define MAX_MESSAGE_LEN 100 20 | #define MAX_MESSAGES 5 21 | #define MAX_SESSIONS 2 22 | #define SESSION_TTL 120 23 | 24 | static const char *authorize_url = "/authorize"; 25 | static const char *login_url = "/login.html"; 26 | static const char *ajax_reply_start = 27 | "HTTP/1.1 200 OK\r\n" 28 | "Cache: no-cache\r\n" 29 | "Content-Type: application/x-javascript\r\n" 30 | "\r\n"; 31 | 32 | // Describes single message sent to a chat. If user is empty (0 length), 33 | // the message is then originated from the server itself. 34 | struct message { 35 | long id; // Message ID 36 | char user[MAX_USER_LEN]; // User that have sent the message 37 | char text[MAX_MESSAGE_LEN]; // Message text 38 | time_t timestamp; // Message timestamp, UTC 39 | }; 40 | 41 | // Describes web session. 42 | struct session { 43 | char session_id[33]; // Session ID, must be unique 44 | char random[20]; // Random data used for extra user validation 45 | char user[MAX_USER_LEN]; // Authenticated user 46 | time_t expire; // Expiration timestamp, UTC 47 | }; 48 | 49 | static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages 50 | static struct session sessions[MAX_SESSIONS]; // Current sessions 51 | static long last_message_id; 52 | 53 | // Protects messages, sessions, last_message_id 54 | static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 55 | 56 | // Get session object for the connection. Caller must hold the lock. 57 | static struct session *get_session(const struct mg_connection *conn) { 58 | int i; 59 | const char *cookie = mg_get_header(conn, "Cookie"); 60 | char session_id[33]; 61 | time_t now = time(NULL); 62 | mg_get_cookie(cookie, "session", session_id, sizeof(session_id)); 63 | for (i = 0; i < MAX_SESSIONS; i++) { 64 | if (sessions[i].expire != 0 && 65 | sessions[i].expire > now && 66 | strcmp(sessions[i].session_id, session_id) == 0) { 67 | break; 68 | } 69 | } 70 | return i == MAX_SESSIONS ? NULL : &sessions[i]; 71 | } 72 | 73 | static void get_qsvar(const struct mg_request_info *request_info, 74 | const char *name, char *dst, size_t dst_len) { 75 | const char *qs = request_info->query_string; 76 | mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len); 77 | } 78 | 79 | // Get a get of messages with IDs greater than last_id and transform them 80 | // into a JSON string. Return that string to the caller. The string is 81 | // dynamically allocated, caller must free it. If there are no messages, 82 | // NULL is returned. 83 | static char *messages_to_json(long last_id) { 84 | const struct message *message; 85 | int max_msgs, len; 86 | char buf[sizeof(messages)]; // Large enough to hold all messages 87 | 88 | // Read-lock the ringbuffer. Loop over all messages, making a JSON string. 89 | pthread_rwlock_rdlock(&rwlock); 90 | len = 0; 91 | max_msgs = sizeof(messages) / sizeof(messages[0]); 92 | // If client is too far behind, return all messages. 93 | if (last_message_id - last_id > max_msgs) { 94 | last_id = last_message_id - max_msgs; 95 | } 96 | for (; last_id < last_message_id; last_id++) { 97 | message = &messages[last_id % max_msgs]; 98 | if (message->timestamp == 0) { 99 | break; 100 | } 101 | // buf is allocated on stack and hopefully is large enough to hold all 102 | // messages (it may be too small if the ringbuffer is full and all 103 | // messages are large. in this case asserts will trigger). 104 | len += snprintf(buf + len, sizeof(buf) - len, 105 | "{user: '%s', text: '%s', timestamp: %lu, id: %lu},", 106 | message->user, message->text, message->timestamp, message->id); 107 | assert(len > 0); 108 | assert((size_t) len < sizeof(buf)); 109 | } 110 | pthread_rwlock_unlock(&rwlock); 111 | 112 | return len == 0 ? NULL : strdup(buf); 113 | } 114 | 115 | // If "callback" param is present in query string, this is JSONP call. 116 | // Return 1 in this case, or 0 if "callback" is not specified. 117 | // Wrap an output in Javascript function call. 118 | static int handle_jsonp(struct mg_connection *conn, 119 | const struct mg_request_info *request_info) { 120 | char cb[64]; 121 | 122 | get_qsvar(request_info, "callback", cb, sizeof(cb)); 123 | if (cb[0] != '\0') { 124 | mg_printf(conn, "%s(", cb); 125 | } 126 | 127 | return cb[0] == '\0' ? 0 : 1; 128 | } 129 | 130 | // A handler for the /ajax/get_messages endpoint. 131 | // Return a list of messages with ID greater than requested. 132 | static void ajax_get_messages(struct mg_connection *conn, 133 | const struct mg_request_info *request_info) { 134 | char last_id[32], *json; 135 | int is_jsonp; 136 | 137 | mg_printf(conn, "%s", ajax_reply_start); 138 | is_jsonp = handle_jsonp(conn, request_info); 139 | 140 | get_qsvar(request_info, "last_id", last_id, sizeof(last_id)); 141 | if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) { 142 | mg_printf(conn, "[%s]", json); 143 | free(json); 144 | } 145 | 146 | if (is_jsonp) { 147 | mg_printf(conn, "%s", ")"); 148 | } 149 | } 150 | 151 | // Allocate new message. Caller must hold the lock. 152 | static struct message *new_message(void) { 153 | static int size = sizeof(messages) / sizeof(messages[0]); 154 | struct message *message = &messages[last_message_id % size]; 155 | message->id = last_message_id++; 156 | message->timestamp = time(0); 157 | return message; 158 | } 159 | 160 | static void my_strlcpy(char *dst, const char *src, size_t len) { 161 | strncpy(dst, src, len); 162 | dst[len - 1] = '\0'; 163 | } 164 | 165 | // A handler for the /ajax/send_message endpoint. 166 | static void ajax_send_message(struct mg_connection *conn, 167 | const struct mg_request_info *request_info) { 168 | struct message *message; 169 | struct session *session; 170 | char text[sizeof(message->text) - 1]; 171 | int is_jsonp; 172 | 173 | mg_printf(conn, "%s", ajax_reply_start); 174 | is_jsonp = handle_jsonp(conn, request_info); 175 | 176 | get_qsvar(request_info, "text", text, sizeof(text)); 177 | if (text[0] != '\0') { 178 | // We have a message to store. Write-lock the ringbuffer, 179 | // grab the next message and copy data into it. 180 | pthread_rwlock_wrlock(&rwlock); 181 | message = new_message(); 182 | // TODO(lsm): JSON-encode all text strings 183 | session = get_session(conn); 184 | assert(session != NULL); 185 | my_strlcpy(message->text, text, sizeof(text)); 186 | my_strlcpy(message->user, session->user, sizeof(message->user)); 187 | pthread_rwlock_unlock(&rwlock); 188 | } 189 | 190 | mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true"); 191 | 192 | if (is_jsonp) { 193 | mg_printf(conn, "%s", ")"); 194 | } 195 | } 196 | 197 | // Redirect user to the login form. In the cookie, store the original URL 198 | // we came from, so that after the authorization we could redirect back. 199 | static void redirect_to_login(struct mg_connection *conn, 200 | const struct mg_request_info *request_info) { 201 | mg_printf(conn, "HTTP/1.1 302 Found\r\n" 202 | "Set-Cookie: original_url=%s\r\n" 203 | "Location: %s\r\n\r\n", 204 | request_info->uri, login_url); 205 | } 206 | 207 | // Return 1 if username/password is allowed, 0 otherwise. 208 | static int check_password(const char *user, const char *password) { 209 | // In production environment we should ask an authentication system 210 | // to authenticate the user. 211 | // Here however we do trivial check that user and password are not empty 212 | return (user[0] && password[0]); 213 | } 214 | 215 | // Allocate new session object 216 | static struct session *new_session(void) { 217 | int i; 218 | time_t now = time(NULL); 219 | pthread_rwlock_wrlock(&rwlock); 220 | for (i = 0; i < MAX_SESSIONS; i++) { 221 | if (sessions[i].expire == 0 || sessions[i].expire < now) { 222 | sessions[i].expire = time(0) + SESSION_TTL; 223 | break; 224 | } 225 | } 226 | pthread_rwlock_unlock(&rwlock); 227 | return i == MAX_SESSIONS ? NULL : &sessions[i]; 228 | } 229 | 230 | // Generate session ID. buf must be 33 bytes in size. 231 | // Note that it is easy to steal session cookies by sniffing traffic. 232 | // This is why all communication must be SSL-ed. 233 | static void generate_session_id(char *buf, const char *random, 234 | const char *user) { 235 | mg_md5(buf, random, user, NULL); 236 | } 237 | 238 | static void send_server_message(const char *fmt, ...) { 239 | va_list ap; 240 | struct message *message; 241 | 242 | pthread_rwlock_wrlock(&rwlock); 243 | message = new_message(); 244 | message->user[0] = '\0'; // Empty user indicates server message 245 | va_start(ap, fmt); 246 | vsnprintf(message->text, sizeof(message->text), fmt, ap); 247 | va_end(ap); 248 | 249 | pthread_rwlock_unlock(&rwlock); 250 | } 251 | 252 | // A handler for the /authorize endpoint. 253 | // Login page form sends user name and password to this endpoint. 254 | static void authorize(struct mg_connection *conn, 255 | const struct mg_request_info *request_info) { 256 | char user[MAX_USER_LEN], password[MAX_USER_LEN]; 257 | struct session *session; 258 | 259 | // Fetch user name and password. 260 | get_qsvar(request_info, "user", user, sizeof(user)); 261 | get_qsvar(request_info, "password", password, sizeof(password)); 262 | 263 | if (check_password(user, password) && (session = new_session()) != NULL) { 264 | // Authentication success: 265 | // 1. create new session 266 | // 2. set session ID token in the cookie 267 | // 3. remove original_url from the cookie - not needed anymore 268 | // 4. redirect client back to the original URL 269 | // 270 | // The most secure way is to stay HTTPS all the time. However, just to 271 | // show the technique, we redirect to HTTP after the successful 272 | // authentication. The danger of doing this is that session cookie can 273 | // be stolen and an attacker may impersonate the user. 274 | // Secure application must use HTTPS all the time. 275 | my_strlcpy(session->user, user, sizeof(session->user)); 276 | snprintf(session->random, sizeof(session->random), "%d", rand()); 277 | generate_session_id(session->session_id, session->random, session->user); 278 | send_server_message("<%s> joined", session->user); 279 | mg_printf(conn, "HTTP/1.1 302 Found\r\n" 280 | "Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID 281 | "Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code 282 | "Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url 283 | "Location: /\r\n\r\n", 284 | session->session_id, session->user); 285 | } else { 286 | // Authentication failure, redirect to login. 287 | redirect_to_login(conn, request_info); 288 | } 289 | } 290 | 291 | // Return 1 if request is authorized, 0 otherwise. 292 | static int is_authorized(const struct mg_connection *conn, 293 | const struct mg_request_info *request_info) { 294 | struct session *session; 295 | char valid_id[33]; 296 | int authorized = 0; 297 | 298 | // Always authorize accesses to login page and to authorize URI 299 | if (!strcmp(request_info->uri, login_url) || 300 | !strcmp(request_info->uri, authorize_url)) { 301 | return 1; 302 | } 303 | 304 | pthread_rwlock_rdlock(&rwlock); 305 | if ((session = get_session(conn)) != NULL) { 306 | generate_session_id(valid_id, session->random, session->user); 307 | if (strcmp(valid_id, session->session_id) == 0) { 308 | session->expire = time(0) + SESSION_TTL; 309 | authorized = 1; 310 | } 311 | } 312 | pthread_rwlock_unlock(&rwlock); 313 | 314 | return authorized; 315 | } 316 | 317 | static void redirect_to_ssl(struct mg_connection *conn, 318 | const struct mg_request_info *request_info) { 319 | const char *p, *host = mg_get_header(conn, "Host"); 320 | if (host != NULL && (p = strchr(host, ':')) != NULL) { 321 | mg_printf(conn, "HTTP/1.1 302 Found\r\n" 322 | "Location: https://%.*s:8082/%s:8082\r\n\r\n", 323 | (int) (p - host), host, request_info->uri); 324 | } else { 325 | mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set"); 326 | } 327 | } 328 | 329 | static int event_handler(struct mg_event *event) { 330 | struct mg_request_info *request_info = event->request_info; 331 | struct mg_connection *conn = event->conn; 332 | int result = 1; 333 | 334 | if (event->type != MG_REQUEST_BEGIN) return 0; 335 | 336 | if (!request_info->is_ssl) { 337 | redirect_to_ssl(conn, request_info); 338 | } else if (!is_authorized(conn, request_info)) { 339 | redirect_to_login(conn, request_info); 340 | } else if (strcmp(request_info->uri, authorize_url) == 0) { 341 | authorize(conn, request_info); 342 | } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { 343 | ajax_get_messages(conn, request_info); 344 | } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { 345 | ajax_send_message(conn, request_info); 346 | } else { 347 | // No suitable handler found, mark as not processed. Mongoose will 348 | // try to serve the request. 349 | result = 0; 350 | } 351 | 352 | return result; 353 | } 354 | 355 | static const char *options[] = { 356 | "document_root", "html", 357 | "listening_ports", "8081,8082s", 358 | "ssl_certificate", "ssl_cert.pem", 359 | "num_threads", "5", 360 | NULL 361 | }; 362 | 363 | int main(void) { 364 | struct mg_context *ctx; 365 | 366 | // Initialize random number generator. It will be used later on for 367 | // the session identifier creation. 368 | srand((unsigned) time(0)); 369 | 370 | // Setup and start Mongoose 371 | if ((ctx = mg_start(options, event_handler, NULL)) == NULL) { 372 | printf("%s\n", "Cannot start chat server, fatal exit"); 373 | exit(EXIT_FAILURE); 374 | } 375 | 376 | // Wait until enter is pressed, then exit 377 | printf("Chat server started on ports %s, press enter to quit.\n", 378 | mg_get_option(ctx, "listening_ports")); 379 | getchar(); 380 | mg_stop(ctx); 381 | printf("%s\n", "Chat server stopped."); 382 | 383 | return EXIT_SUCCESS; 384 | } 385 | 386 | // vim:ts=2:sw=2:et 387 | -------------------------------------------------------------------------------- /examples/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "mongoose.h" 4 | 5 | // This function will be called by mongoose on every new request 6 | static int index_html(struct mg_connection *conn) { 7 | mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri); 8 | return 1; 9 | } 10 | 11 | int main(void) { 12 | struct mg_server *server; 13 | 14 | // Create and configure the server 15 | server = mg_create_server(NULL); 16 | mg_set_option(server, "listening_port", "8080"); 17 | mg_add_uri_handler(server, "/", index_html); 18 | 19 | // Serve request. Hit Ctrl-C to terminate the program 20 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); 21 | for (;;) { 22 | mg_poll_server(server, 1000); 23 | } 24 | 25 | // Cleanup, and free server instance 26 | mg_destroy_server(&server); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /examples/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #include 3 | #else 4 | #include 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | using namespace Mongoose; 13 | 14 | class MyController : public WebController 15 | { 16 | public: 17 | void hello(Request &request, StreamResponse &response) 18 | { 19 | response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; 20 | } 21 | 22 | void setup() 23 | { 24 | addRoute("GET", "/hello", MyController, hello); 25 | } 26 | }; 27 | 28 | 29 | int main() 30 | { 31 | MyController myController; 32 | Server server(8080); 33 | server.registerController(&myController); 34 | 35 | server.start(); 36 | 37 | while (1) { 38 | #ifdef WIN32 39 | Sleep(10000); 40 | #else 41 | sleep(10); 42 | #endif 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gregwar/mongoose-cpp/e7c87d3b9c79cffce41752583544586ec6ad365b/examples/html/favicon.ico -------------------------------------------------------------------------------- /examples/html/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | Mongoose chat server 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 27 |
28 | 29 |
30 |
31 | 32 | 37 | 38 |
39 | 40 |
41 |
42 | Main room 43 |
44 |
45 |
46 | 47 | 48 | Type your message here and press enter 49 |
50 |
51 |
52 | 53 | 64 | 65 |
66 |
67 | 68 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/html/login.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | Mongoose chat: login 8 | 9 | 16 | 17 | 18 | 28 | 29 | 30 |
31 |

Mongoose chat server login

32 |
33 | Username and password can be any non-empty strings. 34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/html/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gregwar/mongoose-cpp/e7c87d3b9c79cffce41752583544586ec6ad365b/examples/html/logo.png -------------------------------------------------------------------------------- /examples/html/main.js: -------------------------------------------------------------------------------- 1 | // This file is part of Mongoose project, http://code.google.com/p/mongoose 2 | 3 | var chat = { 4 | // Backend URL, string. 5 | // 'http://backend.address.com' or '' if backend is the same as frontend 6 | backendUrl: '', 7 | maxVisibleMessages: 10, 8 | errorMessageFadeOutTimeoutMs: 2000, 9 | errorMessageFadeOutTimer: null, 10 | lastMessageId: 0, 11 | getMessagesIntervalMs: 1000, 12 | }; 13 | 14 | chat.normalizeText = function(text) { 15 | return text.replace('<', '<').replace('>', '>'); 16 | }; 17 | 18 | chat.refresh = function(data) { 19 | 20 | if (data === undefined) { 21 | return; 22 | } 23 | 24 | $.each(data, function(index, entry) { 25 | var row = $('
').addClass('message-row').appendTo('#mml'); 26 | var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString(); 27 | $('') 28 | .addClass('message-timestamp') 29 | .html('[' + timestamp + ']') 30 | .prependTo(row); 31 | $('') 32 | .addClass('message-user') 33 | .addClass(entry.user ? '' : 'message-user-server') 34 | .html(chat.normalizeText((entry.user || '[server]') + ':')) 35 | .appendTo(row); 36 | $('') 37 | .addClass('message-text') 38 | .addClass(entry.user ? '' : 'message-text-server') 39 | .html(chat.normalizeText(entry.text)) 40 | .appendTo(row); 41 | chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1; 42 | }); 43 | 44 | // Keep only chat.maxVisibleMessages, delete older ones. 45 | while ($('#mml').children().length > chat.maxVisibleMessages) { 46 | $('#mml div:first-child').remove(); 47 | } 48 | }; 49 | 50 | chat.getMessages = function(enter_loop) { 51 | $.ajax({ 52 | dataType: 'jsonp', 53 | url: chat.backendUrl + '/ajax/get_messages', 54 | data: {last_id: chat.lastMessageId}, 55 | success: chat.refresh, 56 | error: function() { 57 | }, 58 | }); 59 | if (enter_loop) { 60 | window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs); 61 | } 62 | }; 63 | 64 | chat.handleMenuItemClick = function(ev) { 65 | $('.menu-item').removeClass('menu-item-selected'); // Deselect menu buttons 66 | $(this).addClass('menu-item-selected'); // Select clicked button 67 | $('.main').addClass('hidden'); // Hide all main windows 68 | $('#' + $(this).attr('name')).removeClass('hidden'); // Show main window 69 | }; 70 | 71 | chat.showError = function(message) { 72 | $('#error').html(message).fadeIn('fast'); 73 | window.clearTimeout(chat.errorMessageFadeOutTimer); 74 | chat.errorMessageFadeOutTimer = window.setTimeout(function() { 75 | $('#error').fadeOut('slow'); 76 | }, chat.errorMessageFadeOutTimeoutMs); 77 | }; 78 | 79 | chat.handleMessageInput = function(ev) { 80 | var input = ev.target; 81 | if (ev.keyCode != 13 || !input.value) 82 | return; 83 | //input.disabled = true; 84 | $.ajax({ 85 | dataType: 'jsonp', 86 | url: chat.backendUrl + '/ajax/send_message', 87 | data: {text: input.value}, 88 | success: function(ev) { 89 | input.value = ''; 90 | input.disabled = false; 91 | chat.getMessages(false); 92 | }, 93 | error: function(ev) { 94 | chat.showError('Error sending message'); 95 | input.disabled = false; 96 | }, 97 | }); 98 | }; 99 | 100 | $(document).ready(function() { 101 | $('.menu-item').click(chat.handleMenuItemClick); 102 | $('.message-input').keypress(chat.handleMessageInput); 103 | chat.getMessages(true); 104 | }); 105 | 106 | // vim:ts=2:sw=2:et 107 | -------------------------------------------------------------------------------- /examples/html/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * vim:ts=2:sw=2:et:ai 3 | */ 4 | 5 | body { 6 | font: 13px Arial; margin: 0.5em 1em; 7 | } 8 | 9 | #logo { 10 | background: url('logo.png') no-repeat ; 11 | width: 160px; 12 | height: 40px; 13 | float: left; 14 | } 15 | 16 | td { 17 | text-align: left; 18 | } 19 | 20 | #motd { 21 | margin-left: 170px; 22 | } 23 | 24 | .infobox { 25 | background: #eed; 26 | padding: 1px 1em; 27 | } 28 | 29 | .help-message { 30 | color: #aaa; 31 | } 32 | 33 | #middle { 34 | margin: 0.5em 0; 35 | } 36 | 37 | #error { 38 | background: #c44; 39 | color: white; 40 | font-weight: bold; 41 | } 42 | 43 | #content, .menu-item-selected, .chat-title, .chat-content { 44 | background: #c3d9ff; 45 | } 46 | 47 | #content { 48 | overflow: hidden; 49 | min-height: 7em; 50 | padding: 1em; 51 | } 52 | 53 | .chat-title { 54 | padding: 1px 1ex; 55 | } 56 | 57 | .chat-content { 58 | padding: 1ex; 59 | } 60 | 61 | .chat-window { 62 | } 63 | 64 | .message-row { 65 | margin: 2px; 66 | border-bottom: 1px solid #bbb; 67 | } 68 | 69 | .message-timestamp { 70 | color: #484; 71 | } 72 | 73 | .message-user { 74 | margin-left: 0.5em; 75 | font-weight: bold; 76 | } 77 | 78 | .message-text { 79 | margin-left: 0.5em; 80 | } 81 | 82 | .message-user-server { 83 | color: purple; 84 | } 85 | 86 | .message-text-server { 87 | font-style: italic; 88 | } 89 | 90 | .main { 91 | padding: 0.5em; 92 | background: #f0fcff; 93 | } 94 | 95 | #menu { 96 | margin-top: 1em; 97 | min-width: 7em; 98 | float: left; 99 | } 100 | 101 | #footer { 102 | position: fixed; 103 | bottom: 0; 104 | right: 0; 105 | color: #ccc; 106 | padding: 0.5em; 107 | } 108 | 109 | .section { 110 | clear: both; 111 | } 112 | 113 | .hidden { 114 | display: none; 115 | } 116 | 117 | .menu-item { 118 | cursor: pointer; 119 | padding: 0.1em 0.5em; 120 | } 121 | 122 | .menu-item-selected { 123 | font-weight: bold; 124 | } 125 | 126 | .message-list { 127 | min-height: 1em; 128 | background: white; 129 | margin: 0.5em 0; 130 | } 131 | 132 | .rounded { 133 | border-radius: 6px; 134 | -moz-border-radius: 6px; 135 | -webkit-border-radius: 6px; 136 | } 137 | 138 | .left-rounded { 139 | border-radius: 6px 0 0 6px; 140 | -moz-border-radius: 6px 0 0 6px; 141 | -webkit-border-radius: 6px 0 0 6px; 142 | } 143 | 144 | .bottom-rounded { 145 | border-radius: 0 0 6px 6px; 146 | -moz-border-radius: 0 0 6px 6px; 147 | -webkit-border-radius: 0 0 6px 6px; 148 | } 149 | 150 | .top-rounded { 151 | border-radius: 6px 6px 0 0; 152 | -moz-border-radius: 6px 6px 0 0; 153 | -webkit-border-radius: 6px 6px 0 0; 154 | } 155 | -------------------------------------------------------------------------------- /examples/json.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace Mongoose; 9 | 10 | class MyController : public JsonController 11 | { 12 | public: 13 | void hello(Request &request, JsonResponse &response) 14 | { 15 | int i; 16 | 17 | for (i=0; i<12; i++) { 18 | response["users"][i]["Name"] = "Bob"; 19 | } 20 | 21 | response["timestamp"] = (int)time(NULL); 22 | } 23 | 24 | void setup() 25 | { 26 | // Example of prefix, putting all the urls into "/api" 27 | setPrefix("/api"); 28 | 29 | // Hello demo 30 | addRouteResponse("GET", "/", MyController, hello, JsonResponse); 31 | addRouteResponse("GET", "/hello", MyController, hello, JsonResponse); 32 | } 33 | }; 34 | 35 | volatile static bool running = true; 36 | 37 | void handle_signal(int sig) 38 | { 39 | if (running) { 40 | cout << "Exiting..." << endl; 41 | running = false; 42 | } 43 | } 44 | 45 | int main() 46 | { 47 | srand(time(NULL)); 48 | 49 | signal(SIGINT, handle_signal); 50 | 51 | MyController myController; 52 | Server server(8080); 53 | server.registerController(&myController); 54 | server.setOption("enable_directory_listing", "false"); 55 | server.start(); 56 | 57 | cout << "Server started, routes:" << endl; 58 | myController.dumpRoutes(); 59 | 60 | while (running) { 61 | #ifdef WIN32 62 | Sleep(10000); 63 | #else 64 | sleep(10); 65 | #endif 66 | } 67 | 68 | server.stop(); 69 | 70 | return EXIT_SUCCESS; 71 | } 72 | -------------------------------------------------------------------------------- /examples/lua/dirscan.lp: -------------------------------------------------------------------------------- 1 | HTTP/1.0 200 OK 2 | Content-Type: text/plain 3 | 4 | 16 | -------------------------------------------------------------------------------- /examples/lua/prime_numbers.lp: -------------------------------------------------------------------------------- 1 | HTTP/1.0 200 OK 2 | Content-Type: text/html 3 | 4 | 5 |

Prime numbers from 0 to 100, calculated by Lua:

6 | ' .. i .. '
 ') end 19 | end 20 | 21 | ?> 22 | 23 |

Reading POST data from Lua (click submit):

24 |
25 | 26 |
27 |    POST data: []
34 |    request method: []
35 |    IP/port: []
37 |    URI: []
38 |    HTTP version []
39 |    HEADERS:
40 |    
45 | 
46 | 47 | -------------------------------------------------------------------------------- /examples/lua_dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lua_5.2.1.h" 3 | 4 | static int smile(lua_State *L) { 5 | (void) L; // Unused 6 | printf("%s\n", ":-)"); 7 | return 0; 8 | } 9 | 10 | int LUA_API luaopen_lua_dll(lua_State *L) { 11 | static const struct luaL_Reg api[] = { 12 | {"smile", smile}, 13 | {NULL, NULL}, 14 | }; 15 | luaL_openlib(L, "lua_dll", api, 0); 16 | return 1; 17 | } 18 | -------------------------------------------------------------------------------- /examples/main.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _MSC_VER 2 | #include 3 | #include 4 | #else 5 | #include 6 | #endif 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | using namespace Mongoose; 13 | 14 | class MyController : public WebController 15 | { 16 | public: 17 | void hello(Request &request, StreamResponse &response) 18 | { 19 | response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; 20 | } 21 | 22 | void form(Request &request, StreamResponse &response) 23 | { 24 | response << "
" << endl; 25 | response << "
" << endl; 26 | response << "" << endl; 27 | response << "
" << endl; 28 | } 29 | 30 | void formPost(Request &request, StreamResponse &response) 31 | { 32 | response << "Test=" << htmlEntities(request.get("test", "(unknown)")); 33 | } 34 | 35 | void session(Request &request, StreamResponse &response) 36 | { 37 | Session &session = getSession(request, response); 38 | 39 | if (session.hasValue("try")) { 40 | response << "Session value: " << session.get("try"); 41 | } else { 42 | ostringstream val; 43 | val << time(NULL); 44 | session.setValue("try", val.str()); 45 | response << "Session value set to: " << session.get("try"); 46 | } 47 | } 48 | 49 | void forbid(Request &request, StreamResponse &response) 50 | { 51 | response.setCode(HTTP_FORBIDDEN); 52 | response << "403 forbidden demo"; 53 | } 54 | 55 | void exception(Request &request, StreamResponse &response) 56 | { 57 | throw string("Exception example"); 58 | } 59 | 60 | void uploadForm(Request &request, StreamResponse &response) 61 | { 62 | response << "

File upload demo (don't forget to create a tmp/ directory)

"; 63 | response << "
"; 64 | response << "Choose a file to upload:
"; 65 | response << ""; 66 | response << "
"; 67 | } 68 | 69 | void upload(Request &request, StreamResponse &response) 70 | { 71 | request.handleUploads(); 72 | 73 | // Iterate through all the uploaded files 74 | vector::iterator it = request.uploadFiles.begin(); 75 | for (; it != request.uploadFiles.end(); it++) { 76 | UploadFile file = *it; 77 | file.saveTo("tmp/"); 78 | response << "Uploaded file: " << file.getName() << endl; 79 | } 80 | } 81 | 82 | void setup() 83 | { 84 | // Hello demo 85 | addRoute("GET", "/hello", MyController, hello); 86 | addRoute("GET", "/", MyController, hello); 87 | 88 | // Form demo 89 | addRoute("GET", "/form", MyController, form); 90 | addRoute("POST", "/form", MyController, formPost); 91 | 92 | // Session demo 93 | addRoute("GET", "/session", MyController, session); 94 | 95 | // Exception example 96 | addRoute("GET", "/exception", MyController, exception); 97 | 98 | // 403 demo 99 | addRoute("GET", "/403", MyController, forbid); 100 | 101 | // File upload demo 102 | addRoute("GET", "/upload", MyController, uploadForm); 103 | addRoute("POST", "/upload", MyController, upload); 104 | } 105 | }; 106 | 107 | volatile static bool running = true; 108 | 109 | void handle_signal(int sig) 110 | { 111 | if (running) { 112 | cout << "Exiting..." << endl; 113 | running = false; 114 | } 115 | } 116 | 117 | int main() 118 | { 119 | int t; 120 | #ifdef _MSC_VER 121 | time_t ltime; 122 | time(<ime); 123 | t = ltime; 124 | #else 125 | t = time(NULL); 126 | #endif 127 | srand(t); 128 | 129 | signal(SIGINT, handle_signal); 130 | 131 | MyController myController; 132 | Server server(8080); 133 | server.registerController(&myController); 134 | server.setOption("enable_directory_listing", "false"); 135 | server.start(); 136 | 137 | cout << "Server started, routes:" << endl; 138 | myController.dumpRoutes(); 139 | 140 | while (running) { 141 | #ifdef WIN32 142 | Sleep(1000); 143 | #else 144 | sleep(1); 145 | #endif 146 | } 147 | 148 | server.stop(); 149 | 150 | return EXIT_SUCCESS; 151 | } 152 | -------------------------------------------------------------------------------- /examples/post.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "mongoose.h" 4 | 5 | static const char *html_form = 6 | "POST example." 7 | "
" 8 | "Input 1:
" 9 | "Input 2:
" 10 | "" 11 | "
"; 12 | 13 | static int handler(struct mg_connection *conn) { 14 | char var1[500], var2[500]; 15 | 16 | if (strcmp(conn->uri, "/handle_post_request") == 0) { 17 | // User has submitted a form, show submitted data and a variable value 18 | // Parse form data. var1 and var2 are guaranteed to be NUL-terminated 19 | mg_get_var(conn, "input_1", var1, sizeof(var1)); 20 | mg_get_var(conn, "input_2", var2, sizeof(var2)); 21 | 22 | // Send reply to the client, showing submitted form values. 23 | // POST data is in conn->content, data length is in conn->content_len 24 | mg_send_header(conn, "Content-Type", "text/plain"); 25 | mg_printf_data(conn, 26 | "Submitted data: [%.*s]\n" 27 | "Submitted data length: %d bytes\n" 28 | "input_1: [%s]\n" 29 | "input_2: [%s]\n", 30 | conn->content_len, conn->content, 31 | conn->content_len, var1, var2); 32 | } else { 33 | // Show HTML form. 34 | mg_send_data(conn, html_form, strlen(html_form)); 35 | } 36 | 37 | return 1; 38 | } 39 | 40 | int main(void) { 41 | struct mg_server *server = mg_create_server(NULL); 42 | mg_set_option(server, "listening_port", "8080"); 43 | mg_add_uri_handler(server, "/", handler); 44 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); 45 | for (;;) { 46 | mg_poll_server(server, 1000); 47 | } 48 | mg_destroy_server(&server); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /examples/qcomm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "core.h" 4 | 5 | static void iterate_callback(struct mg_connection *c, void *param) { 6 | if (c->is_websocket) { 7 | char buf[20]; 8 | int len = snprintf(buf, sizeof(buf), "%d", * (int *) param); 9 | mg_websocket_write(c, 1, buf, len); 10 | } 11 | } 12 | 13 | // This thread sends heartbeats to all websocket connections with 1s interval. 14 | // The heartbeat message is simply an iteration counter. 15 | static void *timer_thread(void *param) { 16 | struct mg_server *server = (struct mg_server *) param; 17 | int i; 18 | 19 | for (i = 0; i < 9999999; i++) { 20 | sleep(1); 21 | mg_iterate_over_connections(server, iterate_callback, &i); 22 | } 23 | 24 | return NULL; 25 | } 26 | 27 | // This handler is called for each incoming websocket frame, one or more 28 | // times for connection lifetime. 29 | static int handler(struct mg_connection *conn) { 30 | static const char oops[] = "HTTP/1.0 200 OK\r\n\r\nwebsocket data expected\n"; 31 | 32 | if (!conn->is_websocket) { 33 | mg_write(conn, oops, sizeof(oops) - 1); 34 | return 1; 35 | } 36 | 37 | mg_websocket_write(conn, 1, conn->content, conn->content_len); 38 | 39 | return conn->content_len == 4 && !memcmp(conn->content, "exit", 4); 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | struct mg_server *server = mg_create_server(NULL); 44 | 45 | mg_set_option(server, "listening_port", "8080"); 46 | mg_set_option(server, "document_root", argc > 1 ? argv[1] : "."); 47 | mg_add_uri_handler(server, "/ws", handler); 48 | mg_start_thread(timer_thread, server); 49 | 50 | printf("Started on port %s\n", mg_get_option(server, "listening_port")); 51 | for (;;) { 52 | mg_poll_server(server, 3000); 53 | } 54 | 55 | mg_destroy_server(&server); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /examples/ssl_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH 3 | hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC 4 | EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1 5 | di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB 6 | Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH 7 | gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN 8 | HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP 9 | trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN 10 | x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK 11 | SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6 12 | +LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa 13 | N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS 14 | to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf 15 | BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6 16 | WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy 17 | Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG 18 | +AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF 19 | kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D 20 | g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b 21 | qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA 22 | d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a 23 | iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ 24 | BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5 25 | ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy 26 | hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= 27 | -----END RSA PRIVATE KEY----- 28 | -----BEGIN CERTIFICATE----- 29 | MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB 30 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 31 | cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG 32 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 33 | IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 34 | AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4 35 | akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH 36 | kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO 37 | kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1 38 | N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf 39 | uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB 40 | oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+ 41 | plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr 42 | P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW 43 | W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ 44 | 5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f 45 | SEGI4JSxV56lYg== 46 | -----END CERTIFICATE----- 47 | -----BEGIN DH PARAMETERS----- 48 | MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS 49 | 6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC 50 | -----END DH PARAMETERS----- 51 | -------------------------------------------------------------------------------- /examples/upload.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2004-2012 Sergey Lyubka 2 | // This file is a part of mongoose project, http://github.com/valenok/mongoose 3 | 4 | #include 5 | #include 6 | #include "mongoose.h" 7 | 8 | static int index_html(struct mg_connection *conn) { 9 | const char *data; 10 | int data_len; 11 | char var_name[100], file_name[100]; 12 | 13 | mg_printf_data(conn, "%s", 14 | "Upload example." 15 | "
" 17 | "
" 18 | "" 19 | "
"); 20 | 21 | if (mg_parse_multipart(conn->content, conn->content_len, 22 | var_name, sizeof(var_name), 23 | file_name, sizeof(file_name), 24 | &data, &data_len) > 0) { 25 | 26 | mg_printf_data(conn, "%s", "Uploaded file:
");
27 |     mg_send_data(conn, data, data_len);
28 |     mg_printf_data(conn, "%s", "/pre>");
29 |   }
30 | 
31 |   mg_printf_data(conn, "%s", "");
32 | 
33 |   return 1;
34 | }
35 | 
36 | int main(void) {
37 |   struct mg_server *server;
38 | 
39 |   // Create and configure the server
40 |   server = mg_create_server(NULL);
41 |   mg_set_option(server, "listening_port", "8080");
42 |   mg_add_uri_handler(server, "/", index_html);
43 | 
44 |   // Serve request. Hit Ctrl-C to terminate the program
45 |   printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
46 |   for (;;) {
47 |     mg_poll_server(server, 1000);
48 |   }
49 | 
50 |   // Cleanup, and free server instance
51 |   mg_destroy_server(&server);
52 | 
53 |   return 0;
54 | }
55 | 


--------------------------------------------------------------------------------
/examples/websocket.c:
--------------------------------------------------------------------------------
 1 | #include 
 2 | #include "mongoose.h"
 3 | 
 4 | extern const char *find_embedded_file(const char *, size_t *);
 5 | 
 6 | static int iterate_callback(struct mg_connection *c) {
 7 |   if (c->is_websocket) {
 8 |     char buf[20];
 9 |     int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
10 |     mg_websocket_write(c, 1, buf, len);
11 |   }
12 |   return 1;
13 | }
14 | 
15 | static int index_html(struct mg_connection *conn) {
16 |   size_t index_size;
17 |   const char *index_html = find_embedded_file("websocket.html", &index_size);
18 | 
19 |   if (conn->is_websocket) {
20 |     // This handler is called for each incoming websocket frame, one or more
21 |     // times for connection lifetime.
22 |     // Echo websocket data back to the client.
23 |     mg_websocket_write(conn, 1, conn->content, conn->content_len);
24 |     return conn->content_len == 4 && !memcmp(conn->content, "exit", 4);
25 |   } else {
26 |     mg_send_header(conn, "Content-Type", "text/html");
27 |     mg_send_data(conn, index_html, index_size);
28 |     return 1;
29 |   }
30 | }
31 | 
32 | int main(void) {
33 |   struct mg_server *server = mg_create_server(NULL);
34 |   unsigned int current_timer = 0, last_timer = 0;
35 | 
36 |   mg_set_option(server, "listening_port", "8080");
37 |   mg_add_uri_handler(server, "/", index_html);
38 | 
39 |   printf("Started on port %s\n", mg_get_option(server, "listening_port"));
40 |   for (;;) {
41 |     current_timer = mg_poll_server(server, 100);
42 |     if (current_timer - last_timer > 1) {
43 |       last_timer = current_timer;
44 |       mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
45 |     }
46 |   }
47 | 
48 |   mg_destroy_server(&server);
49 |   return 0;
50 | }
51 | 


--------------------------------------------------------------------------------
/examples/websocket.cpp:
--------------------------------------------------------------------------------
 1 | #ifdef _MSC_VER
 2 | #include 
 3 | #include 
 4 | #else
 5 | #include 
 6 | #endif
 7 | #include 
 8 | #include 
 9 | #include 
10 | #include 
11 | 
12 | using namespace std;
13 | using namespace Mongoose;
14 | 
15 | class MyController : public WebController
16 | {
17 |     public: 
18 |         void webSocketReady(WebSocket *websocket)
19 |         {
20 |             cout << "Opening new websocket on " << websocket->getRequest().getUrl() << endl;
21 |             websocket->send("server ready");
22 |         
23 |             ostringstream oss;
24 |             oss << "Your id is: " << websocket->getId();
25 |             websocket->send(oss.str());
26 |         }
27 | 
28 |         void webSocketData(WebSocket *websocket, string data)
29 |         {
30 |             cout << "[recv] " << data << endl;
31 |             websocket->send(data);
32 | 
33 |             if (data == "exit") {
34 |                 cout << "Client exiting" << endl;
35 |                 websocket->close();
36 |             }
37 |         }
38 | };
39 | 
40 | volatile static bool running = true;
41 | 
42 | void handle_signal(int sig)
43 | {
44 |     if (running) {
45 |         cout << "Exiting..." << endl;
46 |         running = false;
47 |     }
48 | }
49 | 
50 | int main()
51 | {
52 | 	int t;
53 | #ifdef _MSC_VER
54 |     time_t ltime;
55 | 	time(<ime);
56 | 	t = ltime;
57 | #else
58 | 	t = time(NULL);
59 | #endif
60 | 	srand(t);
61 | 
62 |     signal(SIGINT, handle_signal);
63 | 
64 |     MyController myController;
65 |     Server server(8080);
66 |     server.registerController(&myController);
67 |     server.setOption("enable_directory_listing", "false");
68 |     server.setOption("document_root", "websocket_html_root");
69 |     server.start();
70 | 
71 |     cout << "Server started, routes:" << endl;
72 |     myController.dumpRoutes();
73 | 
74 |     while (running) {
75 | #ifdef WIN32
76 | 		Sleep(10000);
77 | #else
78 |         sleep(10);
79 | #endif
80 |     }
81 | 
82 |     server.stop();
83 | 
84 |     return EXIT_SUCCESS;
85 | }
86 | 


--------------------------------------------------------------------------------
/examples/websocket_html_root/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | WebSocket Test
 4 | 
33 | 
34 | 

Mongoose WebSocket Test

35 | 36 |
37 | This page code creates websocket to the URI "/websocket", 38 | sends a message to it, waits for the reply, then sends an "exit" message. 39 | Server must echo all messages back, and terminate the conversation after 40 | receiving the "exit" message. 41 |
42 | 43 |
44 | 45 | -------------------------------------------------------------------------------- /examples/websocket_html_root/ws.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebSocket Test 4 | 38 | 39 |

Qualcomm WebSocket Test

40 | 41 |
42 | 43 | -------------------------------------------------------------------------------- /mongoose.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2004-2013 Sergey Lyubka 2 | // Copyright (c) 2013-2014 Cesanta Software Limited 3 | // All rights reserved 4 | // 5 | // This library is dual-licensed: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License version 2 as 7 | // published by the Free Software Foundation. For the terms of this 8 | // license, see . 9 | // 10 | // You are free to use this library under the terms of the GNU General 11 | // Public License, but WITHOUT ANY WARRANTY; without even the implied 12 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | // See the GNU General Public License for more details. 14 | // 15 | // Alternatively, you can license this library under a commercial 16 | // license, as set out in . 17 | // 18 | // NOTE: Detailed API documentation is at http://cesanta.com/docs.html 19 | 20 | #ifndef MONGOOSE_HEADER_INCLUDED 21 | #define MONGOOSE_HEADER_INCLUDED 22 | 23 | #define MONGOOSE_VERSION "5.2" 24 | 25 | #include // required for FILE 26 | #include // required for size_t 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif // __cplusplus 31 | 32 | // This structure contains information about HTTP request. 33 | struct mg_connection { 34 | const char *request_method; // "GET", "POST", etc 35 | const char *uri; // URL-decoded URI 36 | const char *http_version; // E.g. "1.0", "1.1" 37 | const char *query_string; // URL part after '?', not including '?', or NULL 38 | 39 | char remote_ip[48]; // Max IPv6 string length is 45 characters 40 | int remote_port; // Client's port 41 | 42 | int num_headers; // Number of HTTP headers 43 | struct mg_header { 44 | const char *name; // HTTP header name 45 | const char *value; // HTTP header value 46 | } http_headers[30]; 47 | 48 | char *content; // POST (or websocket message) data, or NULL 49 | int content_len; // content length 50 | 51 | int is_websocket; // Connection is a websocket connection 52 | int status_code; // HTTP status code for HTTP error handler 53 | unsigned char wsbits; // First byte of the websocket frame 54 | void *server_param; // Parameter passed to mg_add_uri_handler() 55 | void *connection_param; // Placeholder for connection-specific data 56 | }; 57 | 58 | struct mg_server; // Opaque structure describing server instance 59 | typedef int (*mg_handler_t)(struct mg_connection *); 60 | 61 | // Server management functions 62 | struct mg_server *mg_create_server(void *server_param); 63 | void mg_destroy_server(struct mg_server **); 64 | const char *mg_set_option(struct mg_server *, const char *opt, const char *val); 65 | unsigned int mg_poll_server(struct mg_server *, int milliseconds); 66 | void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t); 67 | void mg_set_http_error_handler(struct mg_server *, mg_handler_t); 68 | const char **mg_get_valid_option_names(void); 69 | const char *mg_get_option(const struct mg_server *server, const char *name); 70 | void mg_set_listening_socket(struct mg_server *, int sock); 71 | int mg_get_listening_socket(struct mg_server *); 72 | void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *); 73 | 74 | // Connection management functions 75 | void mg_send_status(struct mg_connection *, int status_code); 76 | void mg_send_header(struct mg_connection *, const char *name, const char *val); 77 | void mg_send_data(struct mg_connection *, const void *data, int data_len); 78 | void mg_printf_data(struct mg_connection *, const char *format, ...); 79 | 80 | int mg_websocket_write(struct mg_connection *, int opcode, 81 | const char *data, size_t data_len); 82 | 83 | // Deprecated in favor of mg_send_* interface 84 | int mg_write(struct mg_connection *, const void *buf, int len); 85 | int mg_printf(struct mg_connection *conn, const char *fmt, ...); 86 | 87 | 88 | const char *mg_get_header(const struct mg_connection *, const char *name); 89 | const char *mg_get_mime_type(const char *file_name); 90 | int mg_get_var(const struct mg_connection *conn, const char *var_name, 91 | char *buf, size_t buf_len); 92 | int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t); 93 | int mg_parse_multipart(const char *buf, int buf_len, 94 | char *var_name, int var_name_len, 95 | char *file_name, int file_name_len, 96 | const char **data, int *data_len); 97 | 98 | // Utility functions 99 | void *mg_start_thread(void *(*func)(void *), void *param); 100 | char *mg_md5(char buf[33], ...); 101 | int mg_authorize_digest(struct mg_connection *c, FILE *fp); 102 | void mg_send_digest_auth_request(struct mg_connection *conn); 103 | 104 | void mg_server_do_i_handle(struct mg_server *server, mg_handler_t handler); 105 | 106 | #ifdef __cplusplus 107 | } 108 | #endif // __cplusplus 109 | 110 | #endif // MONGOOSE_HEADER_INCLUDED 111 | -------------------------------------------------------------------------------- /mongoose/Controller.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Controller.h" 3 | #include "StreamResponse.h" 4 | 5 | using namespace std; 6 | 7 | namespace Mongoose 8 | { 9 | Controller::Controller() 10 | : sessions(NULL), server(NULL), prefix("") 11 | { 12 | } 13 | 14 | void Controller::setup() 15 | { 16 | } 17 | 18 | void Controller::setServer(Server *server_) 19 | { 20 | server = server_; 21 | } 22 | 23 | void Controller::webSocketReady(WebSocket *websocket) 24 | { 25 | } 26 | 27 | void Controller::webSocketData(WebSocket *websocket, string data) 28 | { 29 | } 30 | 31 | Controller::~Controller() 32 | { 33 | map::iterator it; 34 | 35 | for (it=routes.begin(); it!=routes.end(); it++) { 36 | delete (*it).second; 37 | } 38 | 39 | routes.clear(); 40 | } 41 | 42 | bool Controller::handles(string method, string url) 43 | { 44 | string key = method + ":" + url; 45 | 46 | return (routes.find(key) != routes.end()); 47 | } 48 | 49 | Response *Controller::process(Request &request) 50 | { 51 | Response *response = NULL; 52 | 53 | #ifdef ENABLE_REGEX_URL 54 | map::iterator it; 55 | for (it=routes.begin(); it!=routes.end(); it++) { 56 | if (request.match(it->first)){ 57 | response = it->second->process(request); 58 | break; 59 | } 60 | } 61 | #else 62 | string key = request.getMethod() + ":" + request.getUrl(); 63 | if (routes.find(key) != routes.end()) { 64 | response = routes[key]->process(request); 65 | } 66 | #endif 67 | 68 | return response; 69 | } 70 | 71 | void Controller::preProcess(Request &request, Response &response) 72 | { 73 | } 74 | 75 | void Controller::postProcess(Request &request, Response &response) 76 | { 77 | } 78 | 79 | Response *Controller::handleRequest(Request &request) 80 | { 81 | Response *response = process(request); 82 | 83 | if (response != NULL) { 84 | postProcess(request, *response); 85 | } 86 | 87 | return response; 88 | } 89 | 90 | void Controller::setPrefix(string prefix_) 91 | { 92 | prefix = prefix_; 93 | } 94 | 95 | void Controller::registerRoute(string httpMethod, string route, RequestHandlerBase *handler) 96 | { 97 | string key = httpMethod + ":" + prefix + route; 98 | routes[key] = handler; 99 | urls.push_back(prefix + route); 100 | } 101 | 102 | void Controller::dumpRoutes() 103 | { 104 | map::iterator it; 105 | 106 | for (it=routes.begin(); it!=routes.end(); it++) { 107 | cout << (*it).first << endl; 108 | } 109 | 110 | } 111 | 112 | Response *Controller::serverInternalError(string message) 113 | { 114 | StreamResponse *response = new StreamResponse; 115 | 116 | response->setCode(HTTP_SERVER_ERROR); 117 | *response << "[500] Server internal error: " << message; 118 | 119 | return response; 120 | } 121 | 122 | vector Controller::getUrls() 123 | { 124 | return urls; 125 | } 126 | 127 | Session &Controller::getSession(Request &request, Response &response) 128 | { 129 | return sessions->get(request, response); 130 | } 131 | 132 | void Controller::setSessions(Sessions *sessions_) 133 | { 134 | sessions = sessions_; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /mongoose/Controller.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_CONTROLLER_H 2 | #define _MONGOOSE_CONTROLLER_H 3 | 4 | #include 5 | #include 6 | #include "Request.h" 7 | #include "RequestHandler.h" 8 | #include "StreamResponse.h" 9 | #include "WebSocket.h" 10 | #include "Sessions.h" 11 | 12 | using namespace std; 13 | 14 | #define addRoute(httpMethod, url, controllerType, method) \ 15 | registerRoute(httpMethod, url, new RequestHandler(this, &controllerType::method )); 16 | 17 | #define addRouteResponse(httpMethod, url, controllerType, method, responseType) \ 18 | registerRoute(httpMethod, url, new RequestHandler(this, &controllerType::method )); 19 | 20 | /** 21 | * A controller is a module that respond to requests 22 | * 23 | * You can override the preProcess, process and postProcess to answer to 24 | * the requests 25 | */ 26 | namespace Mongoose 27 | { 28 | class Server; 29 | 30 | class Controller 31 | { 32 | public: 33 | Controller(); 34 | virtual ~Controller(); 35 | 36 | /** 37 | * Sets the reference to the server hosting this controller 38 | * 39 | * @param Server the hosting server 40 | */ 41 | virtual void setServer(Server *server); 42 | 43 | /** 44 | * Called before a request is processed 45 | * 46 | * @param Request the request 47 | * @param Response the response 48 | */ 49 | virtual void preProcess(Request &request, Response &response); 50 | 51 | /** 52 | * Called to process a request 53 | * 54 | * @param Request the request 55 | * 56 | * @return Response the created response, or NULL if the controller 57 | * does not handle this request 58 | */ 59 | virtual Response *process(Request &request); 60 | 61 | /** 62 | * Called after a request is processed, if the controller responded 63 | * 64 | * @param Request the request 65 | * @param Response the response 66 | */ 67 | virtual void postProcess(Request &request, Response &response); 68 | 69 | /** 70 | * Handle a request, this will try to match the request, if this 71 | * controller handles it, it will preProcess, process then postProcess it 72 | * 73 | * @param Request the request 74 | * 75 | * @return Response the created response, or NULL if the controller 76 | * does not handle this request 77 | */ 78 | virtual Response *handleRequest(Request &request); 79 | 80 | /** 81 | * Sets the controller prefix, for instance "/api" 82 | * 83 | * @param string the prefix of all urls for this controller 84 | */ 85 | void setPrefix(string prefix); 86 | 87 | /** 88 | * Called when a new websocket connection is ready 89 | * 90 | * @param WebSocket the instance of the connection 91 | */ 92 | virtual void webSocketReady(WebSocket *websocket); 93 | 94 | /** 95 | * Called when data arrive in a websocket connection 96 | * 97 | * @param WebSocket the instance of the connection 98 | * @param string the data arriving 99 | */ 100 | virtual void webSocketData(WebSocket *websocket, string data); 101 | 102 | /** 103 | * Registers a route to the controller 104 | * 105 | * @param string the route path 106 | * @param RequestHandlerBase the request handler for this route 107 | */ 108 | virtual void registerRoute(string httpMethod, string route, RequestHandlerBase *handler); 109 | 110 | /** 111 | * Initializes the route and settings 112 | */ 113 | virtual void setup(); 114 | 115 | /** 116 | * Dump all routes 117 | */ 118 | void dumpRoutes(); 119 | 120 | /** 121 | * Called when an exception occur during the rendering 122 | * 123 | * @param string the error message 124 | * 125 | * @return response a response to send, 404 will occur if NULL 126 | */ 127 | virtual Response *serverInternalError(string message); 128 | 129 | /** 130 | * Gets the session for a request/response 131 | * 132 | * @param Request the request 133 | * @param Response the response 134 | * 135 | * @return Session the session for the request/response 136 | */ 137 | Session &getSession(Request &request, Response &response); 138 | 139 | /** 140 | * Sets the sessions 141 | * 142 | * @param Sessions* the pointer to the sessions jar 143 | */ 144 | void setSessions(Sessions *sessions); 145 | 146 | virtual bool handles(string method, string url); 147 | vector getUrls(); 148 | 149 | protected: 150 | Sessions *sessions; 151 | Server *server; 152 | string prefix; 153 | map routes; 154 | vector urls; 155 | }; 156 | } 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /mongoose/JsonController.cpp: -------------------------------------------------------------------------------- 1 | #include "JsonController.h" 2 | #include "Session.h" 3 | 4 | namespace Mongoose 5 | { 6 | JsonController::JsonController(int gcDivisor_) : 7 | WebController(gcDivisor_) 8 | { 9 | } 10 | 11 | void JsonController::preProcess(Request &request, Response &response) 12 | { 13 | WebController::preProcess(request, response); 14 | 15 | // RFC 4627 16 | // Json content type is application/json 17 | response.setHeader("Content-Type", "application/json"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mongoose/JsonController.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_JSON_CONTROLLER_H 2 | #define _MONGOOSE_JSON_CONTROLLER_H 3 | 4 | #include "Request.h" 5 | #include "Response.h" 6 | #include "WebController.h" 7 | #include "JsonResponse.h" 8 | 9 | using namespace std; 10 | 11 | /** 12 | * A json controller is a controller that serves JSON API 13 | */ 14 | namespace Mongoose 15 | { 16 | class JsonController : public WebController 17 | { 18 | public: 19 | /** 20 | * Creates a json controller, each gcDivisor request, the sessions will be 21 | * garbage collected 22 | */ 23 | JsonController(int gcDivisor = 100); 24 | 25 | /** 26 | * Pre process the request, this will set the content type to text/html 27 | * and ping the user session 28 | * 29 | * @param Request the request 30 | * @param Response the response 31 | */ 32 | void preProcess(Request &request, Response &response); 33 | }; 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /mongoose/JsonResponse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "JsonResponse.h" 4 | 5 | using namespace std; 6 | 7 | namespace Mongoose 8 | { 9 | JsonResponse::JsonResponse() 10 | : humanReadable(false) 11 | { 12 | } 13 | 14 | string JsonResponse::getBody() 15 | { 16 | if (humanReadable) { 17 | Json::StyledWriter writer; 18 | return writer.write(*this); 19 | } else { 20 | Json::FastWriter writer; 21 | return writer.write(*this); 22 | } 23 | } 24 | 25 | void JsonResponse::setHuman(bool human) 26 | { 27 | humanReadable = human; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mongoose/JsonResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_JSON_RESPONSE_H 2 | #define _MONGOOSE_JSON_RESPONSE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Response.h" 10 | 11 | using namespace std; 12 | 13 | /** 14 | * A stream response to a request 15 | */ 16 | namespace Mongoose 17 | { 18 | class JsonResponse : public Json::Value, public Response 19 | { 20 | public: 21 | JsonResponse(); 22 | 23 | /** 24 | * Gets the response body 25 | * 26 | * @return string the response body 27 | */ 28 | virtual string getBody(); 29 | 30 | /** 31 | * Sets the human readability of the response 32 | * 33 | * @param bool true for human readable 34 | */ 35 | void setHuman(bool human); 36 | 37 | protected: 38 | bool humanReadable; 39 | }; 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /mongoose/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <2013> Grégoire Passault 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /mongoose/Mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Mutex.h" 5 | 6 | using namespace std; 7 | 8 | namespace Mongoose 9 | { 10 | #ifdef _MSC_VER 11 | static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) { 12 | (void) unused; 13 | *mutex = CreateMutex(NULL, FALSE, NULL); 14 | return *mutex == NULL ? -1 : 0; 15 | } 16 | 17 | static int pthread_mutex_destroy(pthread_mutex_t *mutex) { 18 | return CloseHandle(*mutex) == 0 ? -1 : 0; 19 | } 20 | 21 | static int pthread_mutex_lock(pthread_mutex_t *mutex) { 22 | return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1; 23 | } 24 | 25 | static int pthread_mutex_unlock(pthread_mutex_t *mutex) { 26 | return ReleaseMutex(*mutex) == 0 ? -1 : 0; 27 | } 28 | #endif 29 | 30 | Mutex::Mutex() 31 | { 32 | pthread_mutex_init(&_mutex, NULL); 33 | } 34 | 35 | Mutex::~Mutex() 36 | { 37 | pthread_mutex_destroy(&_mutex); 38 | } 39 | 40 | void Mutex::lock() 41 | { 42 | pthread_mutex_lock(&_mutex); 43 | } 44 | 45 | void Mutex::unlock() 46 | { 47 | pthread_mutex_unlock(&_mutex); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mongoose/Mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_MUTEX_H 2 | #define _MONGOOSE_MUTEX_H 3 | 4 | #ifndef _MSC_VER 5 | #include 6 | #else 7 | #include 8 | typedef HANDLE pthread_mutex_t; 9 | #endif 10 | 11 | /** 12 | * A mutex allow thread-safe operations 13 | */ 14 | namespace Mongoose 15 | { 16 | class Mutex 17 | { 18 | public: 19 | Mutex(); 20 | virtual ~Mutex(); 21 | 22 | /** 23 | * Locks the mutex 24 | */ 25 | virtual void lock(); 26 | 27 | /** 28 | * Unlock the mutex 29 | */ 30 | virtual void unlock(); 31 | 32 | protected: 33 | pthread_mutex_t _mutex; 34 | }; 35 | } 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /mongoose/Request.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "Request.h" 6 | 7 | using namespace std; 8 | 9 | static int lowercase(const char *s) { 10 | return tolower(* (const unsigned char *) s); 11 | } 12 | 13 | static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { 14 | int diff = 0; 15 | 16 | if (len > 0) 17 | do { 18 | diff = lowercase(s1++) - lowercase(s2++); 19 | } while (diff == 0 && s1[-1] != '\0' && --len > 0); 20 | 21 | return diff; 22 | } 23 | 24 | static void mg_strlcpy(register char *dst, register const char *src, size_t n) { 25 | for (; *src != '\0' && n > 1; n--) { 26 | *dst++ = *src++; 27 | } 28 | *dst = '\0'; 29 | } 30 | 31 | /* 32 | static int mg_strcasecmp(const char *s1, const char *s2) { 33 | int diff; 34 | 35 | do { 36 | diff = lowercase(s1++) - lowercase(s2++); 37 | } while (diff == 0 && s1[-1] != '\0'); 38 | 39 | return diff; 40 | } 41 | */ 42 | 43 | static const char *mg_strcasestr(const char *big_str, const char *small_str) { 44 | int i, big_len = strlen(big_str), small_len = strlen(small_str); 45 | 46 | for (i = 0; i <= big_len - small_len; i++) { 47 | if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { 48 | return big_str + i; 49 | } 50 | } 51 | 52 | return NULL; 53 | } 54 | 55 | static int mg_get_cookie(const char *cookie_header, const char *var_name, 56 | char *dst, size_t dst_size) { 57 | const char *s, *p, *end; 58 | int name_len, len = -1; 59 | 60 | if (dst == NULL || dst_size == 0) { 61 | len = -2; 62 | } else if (var_name == NULL || (s = cookie_header) == NULL) { 63 | len = -1; 64 | dst[0] = '\0'; 65 | } else { 66 | name_len = (int) strlen(var_name); 67 | end = s + strlen(s); 68 | dst[0] = '\0'; 69 | 70 | for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { 71 | if (s[name_len] == '=') { 72 | s += name_len + 1; 73 | if ((p = strchr(s, ' ')) == NULL) 74 | p = end; 75 | if (p[-1] == ';') 76 | p--; 77 | if (*s == '"' && p[-1] == '"' && p > s + 1) { 78 | s++; 79 | p--; 80 | } 81 | if ((size_t) (p - s) < dst_size) { 82 | len = p - s; 83 | mg_strlcpy(dst, s, (size_t) len + 1); 84 | } else { 85 | len = -3; 86 | } 87 | break; 88 | } 89 | } 90 | } 91 | return len; 92 | } 93 | 94 | namespace Mongoose 95 | { 96 | Request::Request(struct mg_connection *connection_) : 97 | connection(connection_) 98 | { 99 | url = string(connection->uri); 100 | method = string(connection->request_method); 101 | 102 | // Downloading POST data 103 | ostringstream postData; 104 | postData.write(connection->content, connection->content_len); 105 | data = postData.str(); 106 | } 107 | 108 | string Request::getUrl() 109 | { 110 | return url; 111 | } 112 | 113 | string Request::getMethod() 114 | { 115 | return method; 116 | } 117 | 118 | string Request::getData() 119 | { 120 | return data; 121 | } 122 | 123 | #ifdef ENABLE_REGEX_URL 124 | smatch Request::getMatches() 125 | { 126 | return matches; 127 | } 128 | 129 | bool Request::match(string pattern) 130 | { 131 | key = method + ":" + url; 132 | return regex_match(key, matches, regex(pattern)); 133 | } 134 | #endif 135 | 136 | void Request::writeResponse(Response *response) 137 | { 138 | string data = response->getData(); 139 | 140 | mg_write(connection, data.c_str(), data.size()); 141 | } 142 | 143 | bool Request::hasVariable(string key) 144 | { 145 | const char *dataField; 146 | char dummy[10]; 147 | 148 | // Looking on the query string 149 | dataField = connection->query_string; 150 | if (dataField != NULL && mg_get_var(connection, key.c_str(), dummy, 1) != -1) { 151 | return true; 152 | } 153 | 154 | return false; 155 | } 156 | 157 | map Request::getAllVariable() 158 | { 159 | map mapKeyValue; 160 | stringstream ss(data); 161 | string param; 162 | while(std::getline(ss, param, '&')){ //block for '&' 163 | const string& key = param.substr(0, param.find('=')); 164 | const string& value = param.substr(param.find('=')+1); 165 | mapKeyValue[key] = value; // insert map 166 | } 167 | return mapKeyValue; 168 | } 169 | 170 | bool Request::readVariable(const char *data, string key, string &output) 171 | { 172 | int size = 1024, ret; 173 | char *buffer = new char[size]; 174 | 175 | do { 176 | ret = mg_get_var(connection, key.c_str(), buffer, size); 177 | 178 | if (ret == -1) { 179 | return false; 180 | } 181 | 182 | if (ret == -2) { 183 | size *= 2; 184 | delete[] buffer; 185 | buffer = new char[size]; 186 | } 187 | } while (ret == -2); 188 | 189 | output = string(buffer); 190 | delete[] buffer; 191 | 192 | return true; 193 | } 194 | 195 | 196 | string Request::get(string key, string fallback) 197 | { 198 | const char *dataField; 199 | string output; 200 | 201 | // Looking on the query string 202 | dataField = connection->query_string; 203 | if (dataField != NULL && readVariable(dataField, key, output)) { 204 | return output; 205 | } 206 | 207 | // Looking on the POST data 208 | dataField = data.c_str(); 209 | if (dataField != NULL && readVariable(dataField, key, output)) { 210 | return output; 211 | } 212 | 213 | return fallback; 214 | } 215 | 216 | bool Request::hasCookie(string key) 217 | { 218 | int i; 219 | char dummy[10]; 220 | 221 | for (i=0; inum_headers; i++) { 222 | const struct mg_connection::mg_header *header = &connection->http_headers[i]; 223 | 224 | if (strcmp(header->name, "Cookie") == 0) { 225 | if (mg_get_cookie(header->value, key.c_str(), dummy, sizeof(dummy)) != -1) { 226 | return true; 227 | } 228 | } 229 | } 230 | 231 | return false; 232 | } 233 | 234 | string Request::getCookie(string key, string fallback) 235 | { 236 | string output; 237 | int i; 238 | int size = 1024; 239 | int ret; 240 | char *buffer = new char[size]; 241 | char dummy[10]; 242 | const char *place = NULL; 243 | 244 | for (i=0; inum_headers; i++) { 245 | const struct mg_connection::mg_header *header = &connection->http_headers[i]; 246 | 247 | if (strcmp(header->name, "Cookie") == 0) { 248 | if (mg_get_cookie(header->value, key.c_str(), dummy, sizeof(dummy)) != -1) { 249 | place = header->value; 250 | } 251 | } 252 | } 253 | 254 | if (place == NULL) { 255 | return fallback; 256 | } 257 | 258 | do { 259 | ret = mg_get_cookie(place, key.c_str(), buffer, size); 260 | 261 | if (ret == -2) { 262 | size *= 2; 263 | delete[] buffer; 264 | buffer = new char[size]; 265 | } 266 | } while (ret == -2); 267 | 268 | output = string(buffer); 269 | delete[] buffer; 270 | 271 | return output; 272 | } 273 | 274 | string Request::getHeaderKeyValue(const std::string& header_key) { 275 | string output; 276 | for (int i=0; inum_headers; i++) { 277 | const struct mg_connection::mg_header *header = &connection->http_headers[i]; 278 | if (strcmp(header->name, header_key.c_str()) == 0) { 279 | output = header->value; 280 | break; 281 | } 282 | } 283 | return output; 284 | } 285 | 286 | void Request::handleUploads() 287 | { 288 | char var_name[1024]; 289 | char file_name[1024]; 290 | const char *data; 291 | int data_len; 292 | 293 | if (mg_parse_multipart(connection->content, connection->content_len, 294 | var_name, sizeof(var_name), file_name, sizeof(file_name), &data, &data_len)) { 295 | uploadFiles.push_back(UploadFile(string(file_name), string(data, data_len))); 296 | } 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /mongoose/Request.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_REQUEST_H 2 | #define _MONGOOSE_REQUEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #ifdef ENABLE_REGEX_URL 8 | #include 9 | #endif 10 | #include 11 | #include "UploadFile.h" 12 | #include "Response.h" 13 | 14 | using namespace std; 15 | 16 | /** 17 | * Request is a wrapper for the clients requests 18 | */ 19 | namespace Mongoose 20 | { 21 | class Request 22 | { 23 | public: 24 | Request(struct mg_connection *connection); 25 | 26 | /** 27 | * Sends a given response to the client 28 | * 29 | * @param Response a response for this request 30 | */ 31 | void writeResponse(Response *response); 32 | 33 | /** 34 | * Check if the variable given by key is present in GET or POST data 35 | * 36 | * @param string the name of the variable 37 | * 38 | * @return bool true if the param is present, false else 39 | */ 40 | bool hasVariable(string key); 41 | 42 | /** 43 | * Get All variable present in GET or POST data 44 | * 45 | * @brief getAllVariable 46 | * @return map with all variables 47 | */ 48 | map getAllVariable(); 49 | 50 | /** 51 | * Get the value for a certain variable 52 | * 53 | * @param string the name of the variable 54 | * @param string the fallback value if the variable doesn't exists 55 | * 56 | * @return string the value of the variable if it exists, fallback else 57 | */ 58 | string get(string key, string fallback = ""); 59 | 60 | /** 61 | * Checks if the given cookie exists 62 | * 63 | * @param string the name of the cookie 64 | * 65 | * @return bool true if the given cookie is set 66 | */ 67 | bool hasCookie(string key); 68 | 69 | /** 70 | * Try to get the cookie value 71 | * 72 | * @param string the name of the cookie 73 | * @param string the fallback value 74 | * 75 | * @retun the value of the cookie if it exists, fallback else 76 | */ 77 | string getCookie(string key, string fallback = ""); 78 | 79 | 80 | string getHeaderKeyValue(const std::string& header_key); 81 | 82 | /** 83 | * Handle uploads to the target directory 84 | * 85 | * @param string the target directory 86 | * @param path the posted file path 87 | */ 88 | void handleUploads(); 89 | 90 | string getUrl(); 91 | string getMethod(); 92 | string getData(); 93 | 94 | #ifdef ENABLE_REGEX_URL 95 | smatch getMatches(); 96 | bool match(string pattern); 97 | #endif 98 | bool readVariable(const char *data, string key, string &output); 99 | 100 | /** 101 | * Files uploaded in this request 102 | */ 103 | vector uploadFiles; 104 | 105 | protected: 106 | string method; 107 | string url; 108 | string data; 109 | struct mg_connection *connection; 110 | }; 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /mongoose/RequestHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_REQUEST_HANDLER_H 2 | #define _MONGOOSE_REQUEST_HANDLER_H 3 | 4 | #include "Request.h" 5 | #include "Response.h" 6 | #include 7 | 8 | namespace Mongoose 9 | { 10 | class RequestHandlerBase 11 | { 12 | public: 13 | virtual Response *process(Request &request)=0; 14 | }; 15 | 16 | template 17 | class RequestHandler : public RequestHandlerBase 18 | { 19 | public: 20 | typedef void (T::*fPtr)(Request &request, R &response); 21 | 22 | RequestHandler(T *controller_, fPtr function_) 23 | : controller(controller_), function(function_) 24 | { 25 | } 26 | 27 | Response *process(Request &request) 28 | { 29 | R *response = new R; 30 | 31 | try { 32 | controller->preProcess(request, *response); 33 | (controller->*function)(request, *response); 34 | } catch (string exception) { 35 | return controller->serverInternalError(exception); 36 | } catch (...) { 37 | return controller->serverInternalError("Unknown error"); 38 | } 39 | 40 | return response; 41 | } 42 | 43 | protected: 44 | T *controller; 45 | fPtr function; 46 | }; 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /mongoose/Response.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Response.h" 3 | 4 | using namespace std; 5 | 6 | namespace Mongoose 7 | { 8 | Response::Response() : code(HTTP_OK), headers() 9 | { 10 | } 11 | 12 | Response::~Response() 13 | { 14 | } 15 | 16 | void Response::setHeader(string key, string value) 17 | { 18 | headers[key] = value; 19 | } 20 | 21 | bool Response::hasHeader(string key) 22 | { 23 | return headers.find(key) != headers.end(); 24 | } 25 | 26 | string Response::getData() 27 | { 28 | string body = getBody(); 29 | ostringstream data; 30 | 31 | data << "HTTP/1.0 " << code << "\r\n"; 32 | 33 | if (!hasHeader("Content-Length")) { 34 | ostringstream length; 35 | length << body.size(); 36 | setHeader("Content-Length", length.str()); 37 | } 38 | 39 | map::iterator it; 40 | for (it=headers.begin(); it!=headers.end(); it++) { 41 | data << (*it).first << ": " << (*it).second << "\r\n"; 42 | } 43 | 44 | data << "\r\n"; 45 | 46 | data << body; 47 | 48 | return data.str(); 49 | } 50 | 51 | void Response::setCookie(string key, string value) 52 | { 53 | ostringstream definition; 54 | definition << key << "=" << value << "; path=/"; 55 | 56 | setHeader("Set-cookie", definition.str()); 57 | } 58 | 59 | void Response::setCode(int code_) 60 | { 61 | code = code_; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /mongoose/Response.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_RESPONSE_H 2 | #define _MONGOOSE_RESPONSE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HTTP_OK 200 9 | #define HTTP_NOT_FOUND 404 10 | #define HTTP_FORBIDDEN 403 11 | #define HTTP_SERVER_ERROR 500 12 | 13 | using namespace std; 14 | 15 | /** 16 | * A response to a request 17 | */ 18 | namespace Mongoose 19 | { 20 | class Response 21 | { 22 | public: 23 | Response(); 24 | virtual ~Response(); 25 | 26 | /** 27 | * Test if the given header is present 28 | * 29 | * @param string the header key 30 | * 31 | * @return bool true if the header is set 32 | */ 33 | virtual bool hasHeader(string key); 34 | 35 | /** 36 | * Sets the header 37 | * 38 | * @param key the header key 39 | * 40 | * @param value the header value 41 | */ 42 | virtual void setHeader(string key, string value); 43 | 44 | /** 45 | * Get the data of the response, this will contain headers and 46 | * body 47 | * 48 | * @return string the response data 49 | */ 50 | virtual string getData(); 51 | 52 | /** 53 | * Gets the response body 54 | * 55 | * @return string the response body 56 | */ 57 | virtual string getBody()=0; 58 | 59 | /** 60 | * Sets the cookie, note that you can only define one cookie by request 61 | * for now 62 | * 63 | * @param string the key of the cookie 64 | * @param string value the cookie value 65 | */ 66 | virtual void setCookie(string key, string value); 67 | 68 | /** 69 | * Sets the response code 70 | */ 71 | virtual void setCode(int code); 72 | 73 | protected: 74 | int code; 75 | map headers; 76 | }; 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /mongoose/Server.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _MSC_VER 2 | #include 3 | #else 4 | #include 5 | #include 6 | #include 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include "Server.h" 12 | #include "Utils.h" 13 | 14 | using namespace std; 15 | using namespace Mongoose; 16 | 17 | static int getTime() 18 | { 19 | #ifdef _MSC_VER 20 | time_t ltime; 21 | time(<ime); 22 | return ltime; 23 | #else 24 | return time(NULL); 25 | #endif 26 | } 27 | 28 | static int do_i_handle(struct mg_connection *connection) 29 | { 30 | Server *server = (Server *)connection->server_param; 31 | 32 | return server->handles(connection->request_method, connection->uri); 33 | } 34 | 35 | /** 36 | * The handlers below are written in C to do the binding of the C mongoose with 37 | * the C++ API 38 | */ 39 | static int event_handler(struct mg_connection *connection) 40 | { 41 | Server *server = (Server *)connection->server_param; 42 | 43 | if (server != NULL) { 44 | #ifndef NO_WEBSOCKET 45 | if (connection->is_websocket) { 46 | server->_webSocketReady(connection); 47 | } 48 | #endif 49 | server->_handleRequest(connection); 50 | } 51 | 52 | return 1; 53 | } 54 | 55 | #ifndef NO_WEBSOCKET 56 | static int iterate_callback(struct mg_connection *connection) 57 | { 58 | if (connection->is_websocket && connection->content_len) { 59 | Server *server = (Server *)connection->server_param; 60 | server->_webSocketData(connection, string(connection->content, connection->content_len)); 61 | } 62 | 63 | return 1; 64 | } 65 | #endif 66 | 67 | static void *server_poll(void *param) 68 | { 69 | Server *server = (Server *)param; 70 | server->poll(); 71 | 72 | return NULL; 73 | } 74 | 75 | namespace Mongoose 76 | { 77 | Server::Server(int port, const char *documentRoot) 78 | : 79 | stopped(false), 80 | destroyed(false), 81 | server(NULL) 82 | #ifndef NO_WEBSOCKET 83 | ,websockets(NULL) 84 | #endif 85 | 86 | { 87 | ostringstream portOss; 88 | portOss << port; 89 | optionsMap["listening_port"] = portOss.str(); 90 | optionsMap["document_root"] = string(documentRoot); 91 | optionsMap["enable_keep_alive"] = "yes"; 92 | } 93 | 94 | Server::~Server() 95 | { 96 | stop(); 97 | } 98 | 99 | void Server::start() 100 | { 101 | if (server == NULL) { 102 | #ifdef ENABLE_STATS 103 | requests = 0; 104 | startTime = getTime(); 105 | #endif 106 | server = mg_create_server(this); 107 | 108 | map::iterator it; 109 | for (it=optionsMap.begin(); it!=optionsMap.end(); it++) { 110 | mg_set_option(server, (*it).first.c_str(), (*it).second.c_str()); 111 | } 112 | 113 | mg_add_uri_handler(server, "/", event_handler); 114 | mg_server_do_i_handle(server, do_i_handle); 115 | 116 | stopped = false; 117 | mg_start_thread(server_poll, this); 118 | } else { 119 | throw string("Server is already running"); 120 | } 121 | } 122 | 123 | void Server::poll() 124 | { 125 | #ifndef NO_WEBSOCKET 126 | unsigned int current_timer = 0; 127 | #endif 128 | while (!stopped) { 129 | mg_poll_server(server, 1000); 130 | #ifndef NO_WEBSOCKET 131 | mg_iterate_over_connections(server, iterate_callback, ¤t_timer); 132 | #endif 133 | } 134 | 135 | mg_destroy_server(&server); 136 | destroyed = true; 137 | } 138 | 139 | void Server::stop() 140 | { 141 | stopped = true; 142 | while (!destroyed) { 143 | Utils::sleep(100); 144 | } 145 | } 146 | 147 | void Server::registerController(Controller *controller) 148 | { 149 | controller->setSessions(&sessions); 150 | controller->setServer(this); 151 | controller->setup(); 152 | controllers.push_back(controller); 153 | } 154 | 155 | #ifndef NO_WEBSOCKET 156 | void Server::_webSocketReady(struct mg_connection *conn) 157 | { 158 | WebSocket *websocket = new WebSocket(conn); 159 | websockets.add(websocket); 160 | websockets.clean(); 161 | 162 | vector::iterator it; 163 | for (it=controllers.begin(); it!=controllers.end(); it++) { 164 | (*it)->webSocketReady(websocket); 165 | } 166 | } 167 | 168 | int Server::_webSocketData(struct mg_connection *conn, string data) 169 | { 170 | WebSocket *websocket = websockets.getWebSocket(conn); 171 | 172 | if (websocket != NULL) { 173 | websocket->appendData(data); 174 | 175 | string fullPacket = websocket->flushData(); 176 | vector::iterator it; 177 | for (it=controllers.begin(); it!=controllers.end(); it++) { 178 | (*it)->webSocketData(websocket, fullPacket); 179 | } 180 | 181 | if (websocket->isClosed()) { 182 | websockets.remove(websocket); 183 | return 0; 184 | } else { 185 | return -1; 186 | } 187 | } else { 188 | return 0; 189 | } 190 | } 191 | #endif 192 | 193 | int Server::_handleRequest(struct mg_connection *conn) 194 | { 195 | Request request(conn); 196 | 197 | mutex.lock(); 198 | currentRequests[conn] = &request; 199 | mutex.unlock(); 200 | 201 | Response *response = handleRequest(request); 202 | 203 | mutex.lock(); 204 | currentRequests.erase(conn); 205 | mutex.unlock(); 206 | 207 | if (response == NULL) { 208 | return 0; 209 | } else { 210 | request.writeResponse(response); 211 | delete response; 212 | return 1; 213 | } 214 | } 215 | 216 | bool Server::handles(string method, string url) 217 | { 218 | #ifndef NO_WEBSOCKET 219 | if (url == "/websocket") { 220 | return true; 221 | } 222 | #endif 223 | 224 | vector::iterator it; 225 | for (it=controllers.begin(); it!=controllers.end(); it++) { 226 | if ((*it)->handles(method, url)) { 227 | return true; 228 | } 229 | } 230 | 231 | return false; 232 | } 233 | 234 | Response *Server::handleRequest(Request &request) 235 | { 236 | Response *response; 237 | vector::iterator it; 238 | 239 | mutex.lock(); 240 | requests++; 241 | mutex.unlock(); 242 | 243 | for (it=controllers.begin(); it!=controllers.end(); it++) { 244 | Controller *controller = *it; 245 | response = controller->process(request); 246 | 247 | if (response != NULL) { 248 | return response; 249 | } 250 | } 251 | 252 | return NULL; 253 | } 254 | 255 | void Server::setOption(string key, string value) 256 | { 257 | optionsMap[key] = value; 258 | } 259 | 260 | #ifndef NO_WEBSOCKET 261 | WebSockets &Server::getWebSockets() 262 | { 263 | return websockets; 264 | } 265 | #endif 266 | 267 | void Server::printStats() 268 | { 269 | int delta = getTime()-startTime; 270 | 271 | if (delta) { 272 | cout << "Requests: " << requests << ", Requests/s: " << (requests*1.0/delta) << endl; 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /mongoose/Server.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_SERVER_H 2 | #define _MONGOOSE_SERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Request.h" 8 | #include "Response.h" 9 | #include "Controller.h" 10 | #ifndef NO_WEBSOCKET 11 | #include "WebSocket.h" 12 | #include "WebSockets.h" 13 | #endif 14 | #include "Mutex.h" 15 | #include "Sessions.h" 16 | 17 | using namespace std; 18 | 19 | /** 20 | * Wrapper for the Mongoose server 21 | */ 22 | namespace Mongoose 23 | { 24 | class Server 25 | { 26 | public: 27 | /** 28 | * Constructs the server 29 | * 30 | * @param int the number of the port to listen to 31 | * @param string documentRoot the root that should be used for static files 32 | */ 33 | Server(int port = 80, const char *documentRoot = "www"); 34 | virtual ~Server(); 35 | 36 | /** 37 | * Runs the Mongoose server 38 | */ 39 | void start(); 40 | 41 | /** 42 | * Stops the Mongoose server 43 | */ 44 | void stop(); 45 | 46 | /** 47 | * Register a new controller on the server 48 | * 49 | * @param Controller* a pointer to a controller 50 | */ 51 | void registerController(Controller *); 52 | 53 | /** 54 | * Internally used to process a request 55 | * 56 | * @param struct mg_connection* the mongoose connection 57 | */ 58 | int _handleRequest(struct mg_connection *conn); 59 | 60 | /** 61 | * Internally used to process a file upload 62 | * 63 | * @param struct mg_connection* the mongoose conneciton 64 | * @param const char * the name of the uploaded file 65 | */ 66 | void _upload(struct mg_connection *conn, const char *fileName); 67 | 68 | /** 69 | * Handles a web socket connection 70 | * 71 | * @param struct mg_connection* the mongoose connection with the client 72 | */ 73 | void _webSocketReady(struct mg_connection *conn); 74 | 75 | /** 76 | * Handles web sockets data 77 | * 78 | * @param struct mg_connection* the mongoose connection 79 | * @param int flags the data flags 80 | * @param char* the data 81 | * @param size the data size 82 | * 83 | * @return int if we have to keep the connection opened 84 | */ 85 | int _webSocketData(struct mg_connection *conn, string data); 86 | 87 | /** 88 | * Process the request by controllers 89 | * 90 | * @param Request the request 91 | * 92 | * @return Response the response if one of the controllers can handle it, 93 | * NULL else 94 | */ 95 | Response *handleRequest(Request &request); 96 | 97 | /** 98 | * Sets a mongoose extra option 99 | * 100 | * @param string the name of the option 101 | * @param string the value of the option 102 | */ 103 | void setOption(string key, string value); 104 | 105 | #ifndef NO_WEBSOCKET 106 | /** 107 | * Returns the WebSockets container 108 | * 109 | * @return WebSockets the web sockets container 110 | */ 111 | WebSockets &getWebSockets(); 112 | #endif 113 | 114 | /** 115 | * Print statistics 116 | */ 117 | void printStats(); 118 | 119 | /** 120 | * Polls the server 121 | */ 122 | void poll(); 123 | 124 | /** 125 | * Does the server handles url? 126 | */ 127 | bool handles(string method, string url); 128 | 129 | protected: 130 | volatile bool stopped; 131 | volatile bool destroyed; 132 | Sessions sessions; 133 | Mutex mutex; 134 | map optionsMap; 135 | map currentRequests; 136 | struct mg_server *server; 137 | 138 | #ifndef NO_WEBSOCKET 139 | WebSockets websockets; 140 | #endif 141 | 142 | vector controllers; 143 | 144 | // Statistics 145 | int requests; 146 | int startTime; 147 | }; 148 | } 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /mongoose/Session.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Session.h" 5 | 6 | using namespace std; 7 | 8 | namespace Mongoose 9 | { 10 | Session::Session() 11 | { 12 | ping(); 13 | } 14 | 15 | void Session::ping() 16 | { 17 | mutex.lock(); 18 | date = time(NULL); 19 | mutex.unlock(); 20 | } 21 | 22 | void Session::setValue(string key, string value) 23 | { 24 | mutex.lock(); 25 | values[key] = value; 26 | mutex.unlock(); 27 | } 28 | 29 | void Session::unsetValue(string key) 30 | { 31 | mutex.lock(); 32 | values.erase(key); 33 | mutex.unlock(); 34 | } 35 | 36 | bool Session::hasValue(string key) 37 | { 38 | return values.find(key) != values.end(); 39 | } 40 | 41 | string Session::get(string key, string fallback) 42 | { 43 | mutex.lock(); 44 | if (hasValue(key)) { 45 | string value = values[key]; 46 | mutex.unlock(); 47 | 48 | return value; 49 | } else { 50 | mutex.unlock(); 51 | return fallback; 52 | } 53 | } 54 | 55 | int Session::getAge() 56 | { 57 | return time(NULL)-date; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /mongoose/Session.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_SESSION_H 2 | #define _MONGOOSE_SESSION_H 3 | 4 | #include 5 | #include "Mutex.h" 6 | 7 | using namespace std; 8 | 9 | /** 10 | * A session contains the user specific values 11 | */ 12 | namespace Mongoose 13 | { 14 | class Session 15 | { 16 | public: 17 | Session(); 18 | 19 | /** 20 | * Sets the value of a session variable 21 | * 22 | * @param string the name of the variable 23 | * @param string the value of the variable 24 | */ 25 | void setValue(string key, string value); 26 | 27 | /** 28 | * Unset a session varaible 29 | * 30 | * @param string the variable name 31 | */ 32 | void unsetValue(string key); 33 | 34 | /** 35 | * Check if the given variable exists 36 | * 37 | * @param string the name of the variable 38 | */ 39 | bool hasValue(string key); 40 | 41 | /** 42 | * Try to get the value for the given variable 43 | * 44 | * @pram string the name of the variable 45 | * @param string the fallback value 46 | * 47 | * @return string the value of the variable if it exists, fallback else 48 | */ 49 | string get(string key, string fallback = ""); 50 | 51 | /** 52 | * Pings the session, this will update the creation date to now 53 | * and "keeping it alive" 54 | */ 55 | void ping(); 56 | 57 | /** 58 | * Returns the session age, in seconds 59 | * 60 | * @return int the number of sessions since the last activity of the session 61 | */ 62 | int getAge(); 63 | 64 | protected: 65 | map values; 66 | int date; 67 | Mutex mutex; 68 | }; 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /mongoose/Sessions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Sessions.h" 5 | 6 | using namespace std; 7 | 8 | static char charset[] = "abcdeghijklmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 9 | #define CHARSET_SIZE (sizeof(charset)/sizeof(char)) 10 | 11 | namespace Mongoose 12 | { 13 | Sessions::Sessions(string key_) 14 | : sessions(), key(key_) 15 | { 16 | } 17 | 18 | Sessions::~Sessions() 19 | { 20 | map::iterator it; 21 | for (it=sessions.begin(); it!=sessions.end(); it++) { 22 | delete (*it).second; 23 | } 24 | } 25 | 26 | string Sessions::getId(Request &request, Response &response) 27 | { 28 | if (request.hasCookie(key)) { 29 | return request.getCookie(key); 30 | } else { 31 | ostringstream newCookie; 32 | int i; 33 | 34 | for (i=0; i<30; i++) { 35 | newCookie << charset[rand()%CHARSET_SIZE]; 36 | } 37 | 38 | response.setCookie(key, newCookie.str()); 39 | 40 | return newCookie.str(); 41 | } 42 | } 43 | 44 | Session &Sessions::get(Request &request, Response &response) 45 | { 46 | string id = getId(request, response); 47 | Session *session = NULL; 48 | 49 | mutex.lock(); 50 | if (sessions.find(id) != sessions.end()) { 51 | session = sessions[id]; 52 | } else { 53 | session = new Session(); 54 | sessions[id] = session; 55 | } 56 | mutex.unlock(); 57 | 58 | return *session; 59 | } 60 | 61 | void Sessions::garbageCollect(int oldAge) 62 | { 63 | vector deleteList; 64 | map::iterator it; 65 | vector::iterator vit; 66 | 67 | mutex.lock(); 68 | for (it=sessions.begin(); it!=sessions.end(); it++) { 69 | string name = (*it).first; 70 | Session *session = (*it).second; 71 | 72 | if (session->getAge() > oldAge) { 73 | delete session; 74 | deleteList.push_back(name); 75 | } 76 | } 77 | 78 | for (vit=deleteList.begin(); vit!=deleteList.end(); vit++) { 79 | sessions.erase(*vit); 80 | } 81 | mutex.unlock(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /mongoose/Sessions.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_SESSIONS_H 2 | #define _MONGOOSE_SESSIONS_H 3 | 4 | #include "Request.h" 5 | #include "Session.h" 6 | #include "Mutex.h" 7 | 8 | using namespace std; 9 | 10 | /** 11 | * A session contains the user specific values 12 | */ 13 | namespace Mongoose 14 | { 15 | class Sessions 16 | { 17 | public: 18 | Sessions(string key = "sessid"); 19 | virtual ~Sessions(); 20 | 21 | /** 22 | * Gets the session ID of a certain request, 23 | * this will look in the cookies and create it if 24 | * necessary 25 | * 26 | * @param Request the request 27 | * @param Response the response in which the cookie should be write 28 | * 29 | * @return string the session ID for this request 30 | */ 31 | string getId(Request &request, Response &response); 32 | 33 | /** 34 | * Gets the session for a certain request 35 | * 36 | * @param Request the request 37 | * @param Response the response inwhich the cookie should be write 38 | * 39 | * @return Session the session corresponding 40 | */ 41 | Session &get(Request &request, Response &response); 42 | 43 | /** 44 | * Remove all the sessions older than age 45 | * 46 | * @param int the age of the too old sessions in second 47 | */ 48 | void garbageCollect(int oldAge = 3600); 49 | 50 | protected: 51 | map sessions; 52 | string key; 53 | Mutex mutex; 54 | }; 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /mongoose/StreamResponse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "StreamResponse.h" 3 | 4 | using namespace std; 5 | 6 | namespace Mongoose 7 | { 8 | string StreamResponse::getBody() 9 | { 10 | return this->str(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mongoose/StreamResponse.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_STREAM_RESPONSE_H 2 | #define _MONGOOSE_STREAM_RESPONSE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Response.h" 9 | 10 | using namespace std; 11 | 12 | /** 13 | * A stream response to a request 14 | */ 15 | namespace Mongoose 16 | { 17 | class StreamResponse : public ostringstream, public Response 18 | { 19 | public: 20 | /** 21 | * Gets the response body 22 | * 23 | * @return string the response body 24 | */ 25 | virtual string getBody(); 26 | }; 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /mongoose/UploadFile.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "UploadFile.h" 5 | 6 | using namespace std; 7 | 8 | namespace Mongoose 9 | { 10 | UploadFile::UploadFile(string filename_, string data_) 11 | : filename(filename_), 12 | data(data_) 13 | { 14 | } 15 | 16 | string UploadFile::getName() 17 | { 18 | return filename; 19 | } 20 | 21 | string UploadFile::getData() 22 | { 23 | return data; 24 | } 25 | 26 | void UploadFile::saveTo(string directory) 27 | { 28 | ostringstream oss; 29 | oss << directory << "/" << filename; 30 | fstream file; 31 | file.open(oss.str().c_str(), fstream::out); 32 | file << data; 33 | file.close(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mongoose/UploadFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_UPLOAD_FILE_H 2 | #define _MONGOOSE_UPLOAD_FILE_H 3 | #include 4 | 5 | using namespace std; 6 | 7 | /** 8 | * A file uploaded in a request 9 | */ 10 | namespace Mongoose 11 | { 12 | class UploadFile 13 | { 14 | public: 15 | UploadFile(string filename, string data); 16 | 17 | string getName(); 18 | string getData(); 19 | 20 | void saveTo(string directory); 21 | 22 | protected: 23 | string filename; 24 | string data; 25 | }; 26 | } 27 | 28 | #endif // _MONGOOSE_UPLOAD_FILE_H 29 | -------------------------------------------------------------------------------- /mongoose/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Utils.h" 4 | #ifndef WIN32 5 | #include 6 | #endif 7 | #ifdef WIN32 8 | #include 9 | #endif 10 | 11 | using namespace std; 12 | 13 | namespace Mongoose 14 | { 15 | string Utils::htmlEntities(string data) 16 | { 17 | string buffer; 18 | buffer.reserve(data.size()); 19 | 20 | for(size_t pos = 0; pos != data.size(); ++pos) { 21 | switch(data[pos]) { 22 | case '&': buffer.append("&"); break; 23 | case '\"': buffer.append("""); break; 24 | case '\'': buffer.append("'"); break; 25 | case '<': buffer.append("<"); break; 26 | case '>': buffer.append(">"); break; 27 | default: buffer.append(1, data[pos]); break; 28 | } 29 | } 30 | 31 | return buffer; 32 | } 33 | 34 | void Utils::sleep(int ms) 35 | { 36 | #ifdef WIN32 37 | Sleep(ms); 38 | #else 39 | usleep(1000 * ms); 40 | #endif 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /mongoose/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_UTILS_H 2 | #define _MONGOOSE_UTILS_H 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace Mongoose 9 | { 10 | class Utils 11 | { 12 | public: 13 | static string htmlEntities(string data); 14 | static void sleep(int ms); 15 | }; 16 | } 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /mongoose/WebController.cpp: -------------------------------------------------------------------------------- 1 | #include "WebController.h" 2 | #include "Session.h" 3 | 4 | namespace Mongoose 5 | { 6 | WebController::WebController(int gcDivisor_) 7 | : 8 | Controller(), 9 | gcDivisor(gcDivisor_), 10 | counter(0) 11 | { 12 | } 13 | 14 | void WebController::preProcess(Request &request, Response &response) 15 | { 16 | mutex.lock(); 17 | counter++; 18 | 19 | if (counter > gcDivisor) { 20 | counter = 0; 21 | sessions->garbageCollect(); 22 | } 23 | mutex.unlock(); 24 | 25 | Session &session = sessions->get(request, response); 26 | session.ping(); 27 | response.setHeader("Content-Type", "text/html"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mongoose/WebController.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_WEB_CONTROLLER_H 2 | #define _MONGOOSE_WEB_CONTROLLER_H 3 | 4 | #include "Request.h" 5 | #include "Response.h" 6 | #include "Controller.h" 7 | #include "Mutex.h" 8 | #include "StreamResponse.h" 9 | #include "Utils.h" 10 | 11 | using namespace std; 12 | 13 | /** 14 | * A web controller is a controller that serves HTML pages 15 | */ 16 | namespace Mongoose 17 | { 18 | class WebController : public Controller, public Utils 19 | { 20 | public: 21 | /** 22 | * Creates a web controller, each gcDivisor request, the sessions will be 23 | * garbage collected 24 | */ 25 | WebController(int gcDivisor = 100); 26 | 27 | /** 28 | * Pre process the request, this will set the content type to text/html 29 | * and ping the user session 30 | * 31 | * @param Request the request 32 | * @param Response the response 33 | */ 34 | void preProcess(Request &request, Response &response); 35 | 36 | protected: 37 | Mutex mutex; 38 | int gcDivisor; 39 | int counter; 40 | }; 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /mongoose/WebSocket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "WebSocket.h" 4 | #include "WebSockets.h" 5 | 6 | using namespace std; 7 | 8 | namespace Mongoose 9 | { 10 | WebSocket::WebSocket(struct mg_connection *connection_) 11 | : connection(connection_), closed(false), request(connection_), data(""), id(-1) 12 | { 13 | } 14 | 15 | void WebSocket::setId(int id_) 16 | { 17 | id = id_; 18 | } 19 | 20 | int WebSocket::getId() 21 | { 22 | return id; 23 | } 24 | 25 | void WebSocket::appendData(string data_) 26 | { 27 | data += data_; 28 | } 29 | 30 | string WebSocket::flushData() 31 | { 32 | string oldData = ""; 33 | data.swap(oldData); 34 | 35 | return oldData; 36 | } 37 | 38 | Request &WebSocket::getRequest() 39 | { 40 | return request; 41 | } 42 | 43 | void WebSocket::send(string data, int opcode) 44 | { 45 | if (isClosed()) { 46 | return; 47 | } 48 | 49 | mutex.lock(); 50 | if (!mg_websocket_write(connection, opcode, data.c_str(), data.size())) { 51 | closed = true; 52 | } 53 | mutex.unlock(); 54 | } 55 | 56 | void WebSocket::notifyContainers() 57 | { 58 | vector::iterator it; 59 | 60 | mutex.lock(); 61 | for (it=containers.begin(); it!=containers.end(); it++) { 62 | (*it)->remove(this); 63 | } 64 | mutex.unlock(); 65 | } 66 | 67 | void WebSocket::close() 68 | { 69 | closed = true; 70 | } 71 | 72 | bool WebSocket::isClosed() 73 | { 74 | return closed; 75 | } 76 | 77 | void WebSocket::addContainer(WebSockets *websockets) 78 | { 79 | mutex.lock(); 80 | containers.push_back(websockets); 81 | mutex.unlock(); 82 | } 83 | 84 | void WebSocket::removeContainer(WebSockets *websockets) 85 | { 86 | mutex.lock(); 87 | vector::iterator it; 88 | 89 | for (it=containers.begin(); it!=containers.end(); it++) { 90 | if (*it == websockets) { 91 | containers.erase(it); 92 | break; 93 | } 94 | } 95 | mutex.unlock(); 96 | } 97 | 98 | struct mg_connection *WebSocket::getConnection() 99 | { 100 | return connection; 101 | } 102 | }; 103 | 104 | -------------------------------------------------------------------------------- /mongoose/WebSocket.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_WEBSOCKET_H 2 | #define _MONGOOSE_WEBSOCKET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Request.h" 8 | #include "Mutex.h" 9 | 10 | using namespace std; 11 | 12 | #define WEBSOCKET_FIN 0x80 13 | 14 | enum { 15 | WEBSOCKET_OPCODE_CONTINUATION = 0x0, 16 | WEBSOCKET_OPCODE_TEXT = 0x1, 17 | WEBSOCKET_OPCODE_BINARY = 0x2, 18 | WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, 19 | WEBSOCKET_OPCODE_PING = 0x9, 20 | WEBSOCKET_OPCODE_PONG = 0xa 21 | }; 22 | 23 | namespace Mongoose 24 | { 25 | class WebSockets; 26 | 27 | class WebSocket 28 | { 29 | public: 30 | WebSocket(struct mg_connection *connection_); 31 | 32 | /** 33 | * Sends data through the web socket 34 | * 35 | * @param string the data to send 36 | */ 37 | void send(string data, int opcode = WEBSOCKET_OPCODE_TEXT); 38 | 39 | /** 40 | * Returns the connection request 41 | * 42 | * @return Request& the request 43 | */ 44 | Request &getRequest(); 45 | 46 | /** 47 | * Closes the connection 48 | */ 49 | void close(); 50 | 51 | /** 52 | * Is the connection closed ? 53 | * 54 | * @return bool true if the connection is closed 55 | */ 56 | bool isClosed(); 57 | 58 | /** 59 | * Append data to the internal receive buffer 60 | * 61 | * @param string data 62 | */ 63 | void appendData(string data); 64 | 65 | /** 66 | * Gets the internal receive buffer and clear it 67 | * 68 | * @return string data 69 | */ 70 | string flushData(); 71 | 72 | /** 73 | * Gets the internal mg connection 74 | * 75 | * @return struct mg_connection* the connection 76 | */ 77 | struct mg_connection *getConnection(); 78 | 79 | /** 80 | * Adding this websocket in a container 81 | * 82 | * @param WebSockets* the container 83 | */ 84 | void addContainer(WebSockets *websockets); 85 | 86 | /** 87 | * Removing this websocket from a container 88 | */ 89 | void removeContainer(WebSockets *websockets); 90 | 91 | /** 92 | * Notify all the containers that the websocket is now closed and unusable 93 | */ 94 | void notifyContainers(); 95 | 96 | /** 97 | * Sets the identifier of this websocket 98 | * 99 | * @param int the websocket identifier 100 | */ 101 | void setId(int id_); 102 | 103 | /** 104 | * Gets the websocket identifier 105 | * 106 | * @return int the identifier of this websocket 107 | */ 108 | int getId(); 109 | 110 | protected: 111 | int id; 112 | Mutex mutex; 113 | string data; 114 | Request request; 115 | struct mg_connection *connection; 116 | bool closed; 117 | 118 | vector containers; 119 | }; 120 | } 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /mongoose/WebSockets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "WebSockets.h" 3 | 4 | 5 | namespace Mongoose 6 | { 7 | WebSockets::WebSockets(bool responsible_) 8 | : responsible(responsible_), id(0) 9 | { 10 | } 11 | 12 | WebSockets::~WebSockets() 13 | { 14 | if (responsible) { 15 | vector toDelete; 16 | 17 | map::iterator it; 18 | 19 | for (it=websockets.begin(); it!=websockets.end(); it++) { 20 | toDelete.push_back((*it).second); 21 | } 22 | 23 | vector::iterator vit; 24 | 25 | for (vit=toDelete.begin(); vit!=toDelete.end(); vit++) { 26 | remove(*vit); 27 | } 28 | } 29 | } 30 | 31 | void WebSockets::add(WebSocket *websocket) 32 | { 33 | if (websocket == NULL) { 34 | return; 35 | } 36 | 37 | if (responsible) { 38 | mutex.lock(); 39 | int newId = id++; 40 | mutex.unlock(); 41 | websocket->setId(newId); 42 | } 43 | 44 | struct mg_connection *connection = websocket->getConnection(); 45 | 46 | mutex.lock(); 47 | if (websockets.find(connection) != websockets.end()) { 48 | remove(websockets[connection], false); 49 | } 50 | 51 | websocketsById[websocket->getId()] = websocket; 52 | websockets[connection] = websocket; 53 | mutex.unlock(); 54 | } 55 | 56 | WebSocket *WebSockets::getWebSocket(int id) 57 | { 58 | if (websocketsById.find(id) != websocketsById.end()) { 59 | return websocketsById[id]; 60 | } 61 | 62 | return NULL; 63 | } 64 | 65 | void WebSockets::sendAll(string data) 66 | { 67 | vector toClean; 68 | map::iterator it; 69 | 70 | mutex.lock(); 71 | for (it=websockets.begin(); it!=websockets.end(); it++) { 72 | WebSocket *websocket = (*it).second; 73 | 74 | websocket->send(data); 75 | } 76 | mutex.unlock(); 77 | 78 | clean(); 79 | } 80 | 81 | void WebSockets::remove(WebSocket *websocket, bool lock) 82 | { 83 | struct mg_connection *connection = websocket->getConnection(); 84 | 85 | if (lock) { 86 | mutex.lock(); 87 | } 88 | if (websockets.find(connection) != websockets.end()) { 89 | websocket->removeContainer(this); 90 | websockets.erase(connection); 91 | websocketsById.erase(websocket->getId()); 92 | 93 | if (responsible) { 94 | websocket->close(); 95 | websocket->notifyContainers(); 96 | delete websocket; 97 | } 98 | } 99 | if (lock) { 100 | mutex.unlock(); 101 | } 102 | } 103 | 104 | WebSocket *WebSockets::getWebSocket(struct mg_connection *connection) 105 | { 106 | if (websockets.find(connection) != websockets.end()) { 107 | return websockets[connection]; 108 | } 109 | 110 | return NULL; 111 | } 112 | 113 | void WebSockets::clean() 114 | { 115 | vector toDelete; 116 | map::iterator it; 117 | 118 | mutex.lock(); 119 | for (it=websockets.begin(); it!=websockets.end(); it++) { 120 | if ((*it).second->isClosed()) { 121 | toDelete.push_back((*it).second); 122 | } 123 | } 124 | 125 | vector::iterator vit; 126 | for (vit=toDelete.begin(); vit!=toDelete.end(); vit++) { 127 | remove(*vit, false); 128 | } 129 | mutex.unlock(); 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /mongoose/WebSockets.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONGOOSE_WEBSOCKETS_H 2 | #define _MONGOOSE_WEBSOCKETS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "WebSocket.h" 8 | #include "Mutex.h" 9 | 10 | using namespace std; 11 | 12 | /** 13 | * WebSockets is an array that contains WebSocket connections, this 14 | * can be used for instance to broadcast informations to them. 15 | * 16 | * The function clean() allow to remove closed connections from the 17 | * array. 18 | */ 19 | namespace Mongoose 20 | { 21 | class WebSockets 22 | { 23 | public: 24 | /** 25 | * Creates a websockets array, the responsible false specify whether the 26 | * container will be responsible for cleaning the websocket 27 | */ 28 | WebSockets(bool responsible = false); 29 | virtual ~WebSockets(); 30 | 31 | /** 32 | * Adds the given websocket to the poll. This container will not 33 | * become responsible for cleaning this object 34 | * 35 | * @param WebSocket* the websocket object 36 | */ 37 | void add(WebSocket *websocket); 38 | 39 | /** 40 | * Send data to all sockets in this container 41 | */ 42 | void sendAll(string data); 43 | 44 | /** 45 | * Gets the websocket corresponding to the given connection 46 | * 47 | * @param strut mg_connection* the mongoose connection 48 | */ 49 | WebSocket *getWebSocket(struct mg_connection *connection); 50 | 51 | /** 52 | * Cleans all the connections that are hold in this object and that are 53 | * closed 54 | */ 55 | void clean(); 56 | 57 | /** 58 | * Removes the websocket from the container 59 | * 60 | * @param WebSocket* the websocket object 61 | */ 62 | void remove(WebSocket *websocket, bool lock = true); 63 | 64 | /** 65 | * Gets the websockets having the id 66 | * 67 | * @param int id 68 | */ 69 | WebSocket *getWebSocket(int id); 70 | 71 | protected: 72 | Mutex mutex; 73 | map websockets; 74 | map websocketsById; 75 | bool responsible; 76 | 77 | int id; 78 | }; 79 | } 80 | 81 | #endif 82 | --------------------------------------------------------------------------------