diff --git a/.gitignore b/.gitignore index 378eac25d3..4449c26401 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,37 @@ -build +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + + +build/* +*.user +*.autosave diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..6f345700b6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mongoose"] + path = vendor/mongoose + url = https://github.com/saidinesh5/mongoose/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a0c5e8ed7..5d122747f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,156 +1,104 @@ cmake_minimum_required (VERSION 2.6) project (mongoose) -find_package (Threads) - -option (MAIN - "Compile the main" OFF) - -option (EXAMPLES - "Compile examples" OFF) - -option (WEBSOCKET - "Enables websocket" OFF) - -option (CPP_BINDING - "Enables C++ binding" ON) -option (HAS_JSONCPP - "Enables JsonCpp" OFF) +option (MAIN "Compile the main" OFF) +option (EXAMPLES "Compile examples" ON) +option (HAS_JSON11 "Enables support for Json11 (https://github.com/dropbox/json11)" OFF) +option (ENABLE_REGEX_URL "Enable url regex matching dispatcher" OFF) -option (ENABLE_STATS - "Enable server statistics" ON) +set (JSON11_DIR "${PROJECT_SOURCE_DIR}/../json11" CACHE STRING "Json11 (https://github.com/dropbox/json11) directory") -option (ENABLE_REGEX_URL - "Enable url regex matching dispatcher" OFF) - -set (JSONCPP_DIR "${PROJECT_SOURCE_DIR}/../jsoncpp" - CACHE STRING "Json C++ directory") - -set (SOURCES - mongoose.c - ) - -set (MONGOOSE_CPP "${PROJECT_SOURCE_DIR}/mongoose") +if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + endif () +else () + set (CMAKE_CXX_STANDARD 11) +endif () -include_directories ("${PROJECT_SOURCE_DIR}") -if (ENABLE_STATS) - add_definitions("-DENABLE_STATS") -endif (ENABLE_STATS) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules") +include(GetVersionFromGitTag) if (ENABLE_REGEX_URL) add_definitions("-DENABLE_REGEX_URL") SET (CMAKE_CXX_FLAGS "-std=c++11") endif (ENABLE_REGEX_URL) -if (CPP_BINDING) - set (SOURCES - ${SOURCES} - ${MONGOOSE_CPP}/Utils.cpp - ${MONGOOSE_CPP}/Controller.cpp - ${MONGOOSE_CPP}/Mutex.cpp - ${MONGOOSE_CPP}/Request.cpp - ${MONGOOSE_CPP}/Response.cpp - ${MONGOOSE_CPP}/Server.cpp - ${MONGOOSE_CPP}/Session.cpp - ${MONGOOSE_CPP}/Sessions.cpp - ${MONGOOSE_CPP}/StreamResponse.cpp - ${MONGOOSE_CPP}/UploadFile.cpp - ${MONGOOSE_CPP}/WebController.cpp - ) - - if (HAS_JSONCPP) - set (SOURCES - ${SOURCES} - ${MONGOOSE_CPP}/JsonResponse.cpp - ${MONGOOSE_CPP}/JsonController.cpp - ) - - include_directories ("${JSONCPP_DIR}/include/") - endif (HAS_JSONCPP) - - if (WEBSOCKET) - set (SOURCES - ${SOURCES} - ${MONGOOSE_CPP}/WebSocket.cpp - ${MONGOOSE_CPP}/WebSockets.cpp - ) - endif (WEBSOCKET) - - include_directories ("${MONGOOSE_CPP}") -endif (CPP_BINDING) - -if (NOT WEBSOCKET) - add_definitions("-DNO_WEBSOCKET") -endif (NOT WEBSOCKET) - -# Adding dl -if (NOT WIN32) - set (EXTRA_LIBS ${EXTRA_LIBS} dl) -endif (NOT WIN32) +include_directories(${CMAKE_BINARY_DIR}) +include_directories ("${PROJECT_SOURCE_DIR}/lib") +include_directories ("vendor/mongoose") +include_directories ("vendor/libyuarel") +add_definitions("-DMG_ENABLE_CALLBACK_USERDATA") +add_definitions("-DMG_ENABLE_HTTP_STREAMING_MULTIPART") +add_definitions("-DMG_ENABLE_THREADSAFE_MBUF") +add_definitions("-DMG_ENABLE_HTTP_WEBSOCKET=0") + +find_package (Threads) + +set(HEADERS + lib/Utils.h + lib/Controller.h + lib/Request.h + lib/AbstractRequestCoprocessor.h + lib/Response.h + lib/Server.h + lib/Session.h + lib/Sessions.h +) + +set(SOURCES + lib/Utils.cpp + lib/Controller.cpp + lib/Request.cpp + lib/Response.cpp + lib/Server.cpp + lib/Session.cpp + lib/Sessions.cpp + vendor/mongoose/mongoose.c + vendor/libyuarel/yuarel.c +) # Adding sockets for Win32 if (WIN32) set (EXTRA_LIBS ${EXTRA_LIBS} ws2_32) +else(WIN32) + set (EXTRA_LIBS ${EXTRA_LIBS} dl) endif (WIN32) # Compiling library add_library (mongoose ${SOURCES}) target_link_libraries (mongoose ${EXTRA_LIBS} ${CMAKE_THREAD_LIBS_INIT}) -if (HAS_JSONCPP) - target_link_libraries (mongoose json) -endif (HAS_JSONCPP) - -if (EXAMPLES OR MAIN) - if (HAS_JSONCPP) - add_subdirectory("${JSONCPP_DIR}" jsoncpp) - endif (HAS_JSONCPP) -endif () +if (HAS_JSON11) + add_definitions("-DHAS_JSON11") + include_directories ("${JSON11_DIR}/include/") + link_directories("${JSON11_DIR}/lib/") + target_link_libraries (mongoose json11) +endif (HAS_JSON11) -# Compiling executable -if (MAIN) - add_executable (main main.c) - target_link_libraries (main mongoose) -endif (MAIN) - # Compiling tests if (EXAMPLES) - add_executable (post examples/post.c) - target_link_libraries (post mongoose) - - if (NOT WIN32) - add_executable (hello examples/hello.c) - target_link_libraries (hello mongoose) - endif (NOT WIN32) - - if (CPP_BINDING) - add_executable (helloworld examples/helloworld.cpp) - add_executable (basic_auth examples/basic_auth.cpp) - target_link_libraries (helloworld mongoose) - target_link_libraries (basic_auth mongoose) - - add_executable (cpp examples/main.cpp) - target_link_libraries (cpp mongoose) - - if (HAS_JSONCPP) - add_executable (json_api examples/json.cpp) - target_link_libraries (json_api mongoose) - endif (HAS_JSONCPP) - - if (WEBSOCKET) - add_executable (cpp_websocket examples/websocket.cpp) - target_link_libraries (cpp_websocket mongoose) - endif (WEBSOCKET) - endif (CPP_BINDING) + add_executable (basic_auth examples/basic_auth.cpp) + target_link_libraries (basic_auth mongoose) + + add_executable (examples examples/examples.cpp) + target_link_libraries (examples mongoose) + if (HAS_JSON11) + target_link_libraries (examples json11) + endif (HAS_JSON11) endif (EXAMPLES) # install -set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") -configure_file(MongooseConfig.cmake.in MongooseConfig.cmake @ONLY) +set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/mongoose-cpp/" CACHE PATH "The directory the headers are installed in") +set (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/" CACHE PATH "The directory the library is installed in") -install (FILES mongoose.h DESTINATION "${INCLUDE_INSTALL_DIR}") -install (DIRECTORY mongoose DESTINATION "${INCLUDE_INSTALL_DIR}" PATTERN "*.cpp" EXCLUDE) +install (FILES ${HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}") install (TARGETS mongoose DESTINATION lib EXPORT mongoose-targets) install (EXPORT mongoose-targets DESTINATION "lib/cmake/mongoose" FILE MongooseTargets.cmake) + +configure_file(MongooseConfig.cmake.in MongooseConfig.cmake @ONLY) +configure_file("version.h.in" "version.h" @ONLY) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/MongooseConfig.cmake DESTINATION "lib/cmake/mongoose") +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/version.h DESTINATION "${INCLUDE_INSTALL_DIR}") diff --git a/LICENSE b/LICENSE index 2473d6dd71..0c74dcbae1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,16 +1,24 @@ +The MIT License + +Copyright (c) 2007-2019 Dinesh Manajipet Copyright (c) 2004-2013 Sergey Lyubka -Copyright (c) 2013 Cesanta Software Limited -All rights reserved -This code is dual-licensed: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. For the terms of this -license, see http://www.gnu.org/licenses. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. -You are free to use this code under the terms of the GNU General -Public License, but WITHOUT ANY WARRANTY; without even the implied -warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -See the GNU General Public License for more details. -Alternatively, you can license this code under a commercial -license, as set out in http://cesanta.com/products.html. diff --git a/MongooseConfig.cmake.in b/MongooseConfig.cmake.in index b7bf94ac67..76625d7b7b 100644 --- a/MongooseConfig.cmake.in +++ b/MongooseConfig.cmake.in @@ -3,4 +3,4 @@ get_filename_component (myDir ${CMAKE_CURRENT_LIST_FILE} PATH) set (MONGOOSE_LIBRARIES mongoose) set (MONGOOSE_INCLUDE_DIR "@INCLUDE_INSTALL_DIR@") -include(${myDir}/MongooseTargets.cmake) \ No newline at end of file +include(${myDir}/MongooseTargets.cmake) diff --git a/README.md b/README.md index 048cdcaa3c..cb3bd9e05a 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,17 @@ # Mongoose-C++ -Mongoose-C++ is a fork of the popular [mongoose](https://github.com/valenok/mongoose) -lightweight web server which aims to add C++ bindings and a easy-to-use -API. +Mongoose-C++ is a C++ wrapper around the popular [mongoose](https://github.com/cesanta/mongoose/) +lightweight web server. This started off as a fork of https://github.com/Gregwar/mongoose-cpp. It is now moved to it's own repo, because mongoose-cpp itself is a fork of upstream mongoose - which we now use as vendor/mongoose submodule. # Features - Object-Oriented high level API, keeping the lightweight mongoose implementation as a backend - Easy-to-use controllers sytem to build an application with modules -- Possibility of enabling JsonCPP to create a json compliant web application +- Possibility of enabling Json11 to create a json compliant web application - URL dispatcher using regex matches (C++11) - Session system to store data about an user using cookies and garbage collect cleaning - Simple access to GET & POST requests -- Websockets support # Hello world @@ -24,41 +22,51 @@ the string "Hello bob". Default parameter value, if not provided, will be "... waht's your name ?". This is the `helloworld` program build in the examples: ```c++ -#include -#include -#include -#include +#include + +#include "Controller.h" +#include "Request.h" +#include "Response.h" +#include "Server.h" using namespace std; using namespace Mongoose; -class MyController : public WebController +class MyController : public Controller { - public: - void hello(Request &request, StreamResponse &response) - { - response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; - } - - void setup() - { - addRoute("GET", "/hello", MyController, hello); - } +public: + bool hello(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::string body = "Hello " + req->getVariable("name", "... what's your name ?\n"); + return res->send(body); + } + + void setup() + { + addRoute("GET", "/hello", MyController, hello); + addRoute("GET", "/", MyController, hello); + } }; int main() { MyController myController; - Server server(8080); + Server server("8080"); server.registerController(&myController); - server.start(); + signal(SIGINT, handle_signal); - while (1) { - sleep(10); + server.start(); + + while (true) + { + server.poll(1000); } + + return EXIT_SUCCESS; } + ``` # Building examples @@ -75,26 +83,20 @@ make This will build you the `cpp` program with examples of GET, POST (form), session and HTTP response code -You can also enable Json example using the `-DHAS_JSONCPP=ON` option when cmake'ing, -this will build the `json` executable. You also have to specify the `JSONCPP_DIR` that is the [JsonCpp](http://jsoncpp.sourceforge.net/) installation directory. +You can also enable Json example using the `-DHAS_JSON11=ON` option when cmake'ing, +this will build the `json` executable. You also have to specify the `JSON11_DIR` that is the [Json11](https://github.com/dropbox/json11) installation directory. -Websockets are also supported and will be compiled if the `-DWEBSOCKET=ON` option is -set with cmake (which is the default). `websocket.cpp` will be compiled to the -`cpp_websocket` executable which let you see an example. Note that references to the -`WebSocket*` clients can be keeped to dispatch data to them, which can be really -useful to push data to some clients. To enable url regex matching dispatcher use `-DENABLE_REGEX_URL=ON` option. Note that this depends on C++11. # Development -The code writing take places in the `mongoose/` directory and the whole repository -will be merged as often as possible from the original mongoose project. +We maintain a patched fork The upstream mongoose web server library is present as a submodule in vendor/mongoose. +It is patched to enable multithreaded operations on mongoose buffers # License -The mongoose binding (`mongoose/` folder) is under MIT license - +This project is licensed under MIT license. However, the original mongoose project license is different, have a look to the -`LICENSE` file for more information. +`vendor/mongoose/LICENSE` file for more information. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..8acdd82b76 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/cmake/Modules/GetVersionFromGitTag.cmake b/cmake/Modules/GetVersionFromGitTag.cmake new file mode 100644 index 0000000000..f519e7fe4a --- /dev/null +++ b/cmake/Modules/GetVersionFromGitTag.cmake @@ -0,0 +1,116 @@ +# +# This cmake module sets the project version and partial version +# variables by analysing the git tag and commit history. It expects git +# tags defined with semantic versioning 2.0.0 (http://semver.org/). +# +# The module expects the PROJECT_NAME variable to be set, and recognizes +# the GIT_FOUND, GIT_EXECUTABLE and VERSION_UPDATE_FROM_GIT variables. +# If Git is found and VERSION_UPDATE_FROM_GIT is set to boolean TRUE, +# the project version will be updated using information fetched from the +# most recent git tag and commit. Otherwise, the module will try to read +# a VERSION file containing the full and partial versions. The module +# will update this file each time the project version is updated. +# +# Once done, this module will define the following variables: +# +# ${PROJECT_NAME}_VERSION_STRING - Version string without metadata +# such as "v2.0.0" or "v1.2.41-beta.1". This should correspond to the +# most recent git tag. +# ${PROJECT_NAME}_VERSION_STRING_FULL - Version string with metadata +# such as "v2.0.0+3.a23fbc" or "v1.3.1-alpha.2+4.9c4fd1" +# ${PROJECT_NAME}_VERSION - Same as ${PROJECT_NAME}_VERSION_STRING, +# without the preceding 'v', e.g. "2.0.0" or "1.2.41-beta.1" +# ${PROJECT_NAME}_VERSION_MAJOR - Major version integer (e.g. 2 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_MINOR - Minor version integer (e.g. 3 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_PATCH - Patch version integer (e.g. 1 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_TWEAK - Tweak version string (e.g. "RC.2" in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_AHEAD - How many commits ahead of last tag (e.g. 21 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_GIT_SHA - The git sha1 of the most recent commit (e.g. the "ef12c8" in v2.3.1-RC.2+21.ef12c8) +# +# This module is public domain, use it as it fits you best. +# +# Author: Nuno Fachada + +# Check if git is found... +if (GIT_FOUND AND VERSION_UPDATE_FROM_GIT) + + # Get last tag from git + execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE) + + #How many commits since last tag + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list master ${${PROJECT_NAME}_VERSION_STRING}^..HEAD --count + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_AHEAD + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get current commit SHA from git + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_GIT_SHA + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get partial versions into a list + string(REGEX MATCHALL "-.*$|[0-9]+" ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${${PROJECT_NAME}_VERSION_STRING}) + + # Set the version numbers + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 0 ${PROJECT_NAME}_VERSION_MAJOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 1 ${PROJECT_NAME}_VERSION_MINOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 2 ${PROJECT_NAME}_VERSION_PATCH) + + # The tweak part is optional, so check if the list contains it + list(LENGTH ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN) + if (${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_TWEAK) + string(SUBSTRING ${${PROJECT_NAME}_VERSION_TWEAK} 1 -1 ${PROJECT_NAME}_VERSION_TWEAK) + endif() + + # Unset the list + unset(${PROJECT_NAME}_PARTIAL_VERSION_LIST) + + # Set full project version string + set(${PROJECT_NAME}_VERSION_STRING_FULL + ${${PROJECT_NAME}_VERSION_STRING}+${${PROJECT_NAME}_VERSION_AHEAD}.${${PROJECT_NAME}_VERSION_GIT_SHA}) + + # Save version to file (which will be used when Git is not available + # or VERSION_UPDATE_FROM_GIT is disabled) + file(WRITE ${CMAKE_SOURCE_DIR}/VERSION ${${PROJECT_NAME}_VERSION_STRING_FULL} + "*" ${${PROJECT_NAME}_VERSION_STRING} + "*" ${${PROJECT_NAME}_VERSION_MAJOR} + "*" ${${PROJECT_NAME}_VERSION_MINOR} + "*" ${${PROJECT_NAME}_VERSION_PATCH} + "*" ${${PROJECT_NAME}_VERSION_TWEAK} + "*" ${${PROJECT_NAME}_VERSION_AHEAD} + "*" ${${PROJECT_NAME}_VERSION_GIT_SHA}) + +else() + + # Git not available, get version from file - which contains just one line - "major.minor.revision" + file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION ${PROJECT_NAME}_VERSION_LIST) + set(${PROJECT_NAME}_VERSION_STRING_FULL ${${PROJECT_NAME}_VERSION_LIST}) + set(${PROJECT_NAME}_VERSION_STRING ${${PROJECT_NAME}_VERSION_LIST}) + set(${PROJECT_NAME}_VERSION_TWEAK "") + set(${PROJECT_NAME}_VERSION_AHEAD "") + set(${PROJECT_NAME}_VERSION_GIT_SHA "") + + string(REPLACE "." ";" ${PROJECT_NAME}_VERSION_LIST ${${PROJECT_NAME}_VERSION_LIST}) + # Set partial versions + list(GET ${PROJECT_NAME}_VERSION_LIST 0 ${PROJECT_NAME}_VERSION_MAJOR) + list(GET ${PROJECT_NAME}_VERSION_LIST 1 ${PROJECT_NAME}_VERSION_MINOR) + list(GET ${PROJECT_NAME}_VERSION_LIST 2 ${PROJECT_NAME}_VERSION_PATCH) + +endif() + + +# Set project version (without the preceding 'v') +set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}) +if (${PROJECT_NAME}_VERSION_TWEAK) + set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION}-${${PROJECT_NAME}_VERSION_TWEAK}) +endif() diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index 76299fce2b..0000000000 --- a/docs/API.md +++ /dev/null @@ -1,146 +0,0 @@ -# Mongoose API Reference - - struct mg_server *mg_create_server(void *server_param); - -Creates web server instance. Returns opaque instance pointer, or NULL if -there is not enough memory. Note that this function doesn't make the -server instance to serve. Serving is done by `mg_poll_server()` function. -Web server instance has a list of active connections, initially empty, -and a list of URI handlers, initially empty, a list of configuration -options that can be modified by `mg_set_option()`. - - `user_param`: Could be any pointer, or NULL. This pointer will be passed - to callback functions as `struct mg_connection::server_param` field. - A common use case is to pass `this` pointer of the C++ wrapper class - as `user_param`, to let the callback get the pointer to the C++ - object. - -Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom -processing is required SIGPIPE, signal handler must be set up after -calling `mg_create_server()`. - -Important: Mongoose does not install `SIGCHLD` handler. If CGI is used, -`SIGCHLD` handler must be set up to reap CGI zombie processes. - - - void mg_destroy_server(struct mg_server **server); - -Deallocates web server instance, closes all pending connections, and makes -server pointer a NULL pointer. - - const char mg_set_option(struct mg_server *server, const char *name, - const char *value); - -Sets a particular server option. Please refer to a separate documentation page -that lists all available option names. Note that at least one option, -`listening_port`, must be specified. To serve static files, `document_root` -must be specified too. If `document_root` option is left unset, Mongoose -will not access filesystem at all. This function returns NULL if option was -set successfully, otherwise it returns human-readable error string. It is -allowed to call `mg_set_option()` by the same thread that does -`mg_poll_server()` (an IO thread) and change server configuration while it -is serving, in between `mg_poll_server()` calls. - - void mg_poll_server(struct mg_server *server, int milliseconds); - -This function performs one iteration of IO loop by iterating over all -active connections, performing `select()` syscall on all sockets, and sleeping -for `milliseconds` number of milliseconds. When `select()` returns, Mongoose -does an IO for each socket that has data to be sent or received. Application -code must call `mg_poll_server()` in a loop. It is an error to have more then -one thread calling `mg_poll_server()`, `mg_set_option()` or any other function -that take `struct mg_server *` parameter. Mongoose does not -mutex-protect `struct mg_server *`, therefore the best practice is -to call server management functions from the same thread (an IO thread). - - void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t); - -Adds an URI handler. If Mongoose gets a request and request's URI starts -with `uri`, then specified handler is called to serve the request. Thus, an -`uri` is a match prefix. For example, if `uri` is "/", then all requests will -be routed to the handler, because all URIs start with `/` character. - - void mg_set_http_error_handler(struct mg_server *, mg_handler_t); - -Adds HTTP error handler. An actual HTTP error is passed as -`struct mg_connection::status_code` parameter. If handler returns 0, it -means a handler has not processed the connection, and mongoose proceeds -with sending HTTP error to the client. Otherwise, mongoose does nothing. - - const char **mg_get_valid_option_names(void); - -Returns a NULL-terminated array of option names and their default values. -There are two entries per option in an array: an option name followed by a -default value. A default value could be NULL. A NULL name indicates an end -of the array. - - const char *mg_get_option(const struct mg_server *server, const char *name); - -Returns the value of particular configuration parameter. If -given parameter name is not valid, NULL is returned. For valid names, return -value is guaranteed to be non-NULL. If parameter is not set, zero-length string -is returned. - - - int mg_iterate_over_connections(struct mg_server *, - void (*func)(struct mg_connection *, void *), - void *param); - -This is an interface primarily designed to push arbitrary data to websocket -connections at any time. This function could be called from any thread. When -it returns, an IO thread called `func()` on each active websocket connection, -passing `param` as an extra parameter. It is allowed to call `mg_write()` or -`mg_websocket_write()` within a callback, cause these write functions are -thread-safe. - - int mg_write(struct mg_connection *, const void *buf, int len); - -Send data to the client. This function is thread-safe. This function appends -given buffer to a send queue of a given connection. It may return 0 if -there is not enough memory, in which case the data will not be sent. - - int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len); - -Similar to `mg_write()`, but wraps the data into a websocket frame with a -given websocket `opcode`. This function is available when mongoose is -compiled with `-DUSE_WEBSOCKET`. - - const char *mg_get_header(const struct mg_connection *, const char *name); - -Get the value of particular HTTP header. This is a helper function. -It traverses http_headers array, and if the header is present in the array, -returns its value. If it is not present, NULL is returned. - - - int mg_get_var(const struct mg_connection *conn, const char *var_name, - char *buf, size_t buf_len); - -Gets HTTP form variable. Both POST buffer and query string are inspected. -Form variable is url-decoded and written to the buffer. On success, this -function returns the length of decoded variable. On error, -1 is returned if -variable not found, and -2 is returned if destination buffer is too small -to hold the variable. Destination buffer is guaranteed to be -'\0' - terminated if it is not NULL or zero length. - - int mg_parse_header(const char *hdr, const char *var_name, char *buf, - size_t buf_size); - -This function parses HTTP header and fetches given variable's value in a buffer. -A header should be like `x=123, y=345, z="other value"`. This function is -designed to parse Cookie headers, Authorization headers, and similar. Returns -the length of the fetched value, or 0 if variable not found. - - int mg_modify_passwords_file(const char *passwords_file_name, - const char *domain, - const char *user, - const char *password); - -Add, edit or delete the entry in the passwords file. -This function allows an application to manipulate .htpasswd files on the -fly by adding, deleting and changing user records. This is one of the -several ways of implementing authentication on the server side. For another, -cookie-based way please refer to the examples/chat.c in the source tree. -If password is not NULL, entry is added (or modified if already exists). -If password is NULL, entry is deleted. -Return: 1 on success, 0 on error. diff --git a/docs/AndroidBuild.md b/docs/AndroidBuild.md deleted file mode 100644 index 486b19b647..0000000000 --- a/docs/AndroidBuild.md +++ /dev/null @@ -1,27 +0,0 @@ -# Mongoose Build on Android - -This is a small guide to help you run mongoose on Android. Currently it is -tested on the HTC Wildfire. If you have managed to run it on other devices -as well, please comment or drop an email in the mailing list. -Note : You dont need root access to run mongoose on Android. - -- Clone Mongoose Git repo -- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html) -- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build` - That should generate mongoose/lib/armeabi/mongoose -- Using the adb tool (you need to have Android SDK installed for that), - push the generated mongoose binary to `/data/local` folder on device. -- From adb shell, navigate to `/data/local` and execute `./mongoose`. -- To test if the server is running fine, visit your web-browser and - navigate to `http://127.0.0.1:8080` You should see the `Index of /` page. - -![screenshot](https://a248.e.akamai.net/camo.github.com/b88428bf009a2b6141000937ab684e04cc8586af/687474703a2f2f692e696d6775722e636f6d2f62676f6b702e706e67) - - -Notes: - -- `jni` stands for Java Native Interface. Read up on Android NDK if you want - to know how to interact with the native C functions of mongoose in Android - Java applications. -- TODO: A Java application that interacts with the native binary or a - shared library. diff --git a/docs/Embed.md b/docs/Embed.md deleted file mode 100644 index 5d7bba0ad6..0000000000 --- a/docs/Embed.md +++ /dev/null @@ -1,87 +0,0 @@ -# Mongoose Embedding Guide - -Embedding Mongoose is done in two steps: - - 1. Copy - [mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and - [mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h) - to your application's source tree and include these two files in the build. - 2. Somewhere in the application code, call `mg_create_server()` to create - a server, configure it with `mg_set_option()` and loop with - `mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup. - -Here's a minimal application `app.c` that embeds mongoose: - - #include "mongoose.h" - int main(void) { - struct mg_server *server = mg_create_server(NULL); - mg_set_option(server, "document_root", "."); - mg_set_option(server, "listening_port", "8080"); - for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop - mg_destroy_server(&server); - return 0; - } - -To compile it, put `mongoose.c`, `mongoose.h` and `minimal.c` into one -folder, then run the following UNIX command: - - cc app.c mongoose.c -o app - -If you're on Windows, run this in a Visual Studio shell: - - cl app.c mongoose.c /TC /MD - -When run, this simple application opens port 8080 and serves static files, -CGI files and lists directory content in the current working directory. - -Mongoose can call user-defined functions when certain URIs are requested. -These functions are _called uri handlers_. `mg_add_uri_handler()` registers -an URI handler, and there is no restriction exist on the number of URI handlers. -Also, mongoose can call a user-defined function when it is about to send -HTTP error back to client. That function is called _http error handler_ and -can be registered by `mg_set_http_error_handler()`. Handlers are called -by Mongoose with `struct mg_connection *` pointer as a parameter, which -has all information about the request: HTTP headers, POST or websocket -data buffer, etcetera. - -Let's extend our minimal application example and -create an URI that will be served by user's C code. The app will handle -`/hello` URI by showing a hello message. So, when app is run, -http://127.0.0.1:8080/hello will say hello, and here's the code: - - #include - #include "mongoose.h" - - static int handle_hello(struct mg_connection *conn) { - static const char *reply = "HTTP/1.0 200 OK\r\n\r\nHello world!\n"; - mg_write(conn, reply, strlen(reply)); - return 1; - } - - int main(void) { - struct mg_server *server = mg_create_server(NULL); - mg_set_option(server, "document_root", "."); - mg_set_option(server, "listening_port", "8080"); - mg_add_uri_handler(server, "/hello", &handle_hello); - for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop - mg_destroy_server(&server); - return 0; - } - -Note that URI handler must output valid HTTP response, which includes -the reply line with status code `HTTP/1.0 200 OK`, HTTP headers which are -empty in our example, and message body `Hello world!\n`. Note that reply -line is ended with `\r\n`, and HTTP headers are also ended with `\r\n`. - -Mongoose source code contains a well-commented example code, listed below: - - * [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c) - shows how to handle form input, file upload, websocket communication, get - cookie values. - - * [chat](https://github.com/cesanta/mongoose/blob/master/examples/chat) - implements basic online chat functionality using Lua scripting capabilities - of Mongoose. Not a single line of C is written for that example. - Demostrates usage of database, cookie-based authentication, session - support, RESTful interface. No additional software is required to run it - on any platform. diff --git a/docs/FAQ.md b/docs/FAQ.md deleted file mode 100644 index a3c2bdd187..0000000000 --- a/docs/FAQ.md +++ /dev/null @@ -1,23 +0,0 @@ -# Mongoose FAQ - -### PHP doesn't work: getting empty page, or 'File not found' error - -The reason for that is wrong paths to the interpreter. Remember that with PHP, -correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify -full path to the PHP interpreter, e.g.: - - mongoose -cgi_interpreter /full/path/to/php-cgi - -### Mongoose fails to start - -If Mongoose exits immediately when run, this -usually indicates a syntax error in the configuration file -(named `mongoose.conf` by default) or the command-line arguments. -Syntax checking is omitted from Mongoose to keep its size low. However, -the Manual should be of help. Note: the syntax changes from time to time, -so updating the config file might be necessary after executable update. - -### Embedding with OpenSSL on Windows might fail because of calling convention - -To force Mongoose to use `__stdcall` convention, add `/Gz` compilation -flag to the Visual Studio project settings. diff --git a/docs/Internals.md b/docs/Internals.md deleted file mode 100644 index d3e4d65c5c..0000000000 --- a/docs/Internals.md +++ /dev/null @@ -1,59 +0,0 @@ -# Mongoose Internals - - diff --git a/docs/LuaSqlite.md b/docs/LuaSqlite.md deleted file mode 100644 index f504035505..0000000000 --- a/docs/LuaSqlite.md +++ /dev/null @@ -1,74 +0,0 @@ -# Mongoose Lua Server Pages - -Pre-built Windows and Mac mongoose binaries have built-in Lua Server Pages -support. That means it is possible to write PHP-like scripts with mongoose, -using Lua programming language instead of PHP. Lua is known -for it's speed and small size. Mongoose uses Lua version 5.2.1, the -documentation for it can be found at -[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/). - -To create a Lua Page, make sure a file has `.lp` extension. For example, -let's say it is going to be `my_page.lp`. The contents of the file, just like -with PHP, is HTML with embedded Lua code. Lua code must be enclosed in -`` blocks, and can appear anywhere on the page. For example, to -print current weekday name, one can write: - -

- Today is: - -

- -Note that this example uses function `mg.write()`, which prints data to the -web page. Using function `mg.write()` is the way to generate web content from -inside Lua code. In addition to `mg.write()`, all standard library functions -are accessible from the Lua code (please check reference manual for details), -and also information about the request is available in `mg.request_info` object, -like request method, all headers, etcetera. Please refer to -`struct mg_request_info` definition in -[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h) -to see what kind of information is present in `mg.request_info` object. Also, -[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp) and -[prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) -contains some example code that uses `request_info` and other functions(form submitting for example). - -Mongoose exports the following to the Lua server page: - - mg.read() -- reads a chunk from POST data, returns it as a string - mg.write(str) -- writes string to the client - mg.include(path) -- sources another Lua file - mg.redirect(uri) -- internal redirect to a given URI - mg.onerror(msg) -- error handler, can be overridden - mg.version -- a string that holds Mongoose version - mg.request_info -- a table with request information - - -- Connect to the remote TCP server. This function is an implementation - -- of simple socket interface. It returns a socket object with three - -- methods: send, recv, close, which are synchronous (blocking). - -- connect() throws an exception on connection error. - connect(host, port, use_ssl) - - -- Example of using connect() interface: - local host = 'code.google.com' -- IP address or domain name - local ok, sock = pcall(connect, host, 80, 1) - if ok then - sock:send('GET /p/mongoose/ HTTP/1.0\r\n' .. - 'Host: ' .. host .. '\r\n\r\n') - local reply = sock:recv() - sock:close() - -- reply now contains the web page https://code.google.com/p/mongoose - end - - -**IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore, -every Lua Page must begin with HTTP reply line and headers**, like this: - - - - ... the rest of the web page ... - -To serve Lua Page, mongoose creates Lua context. That context is used for -all Lua blocks within the page. That means, all Lua blocks on the same page -share the same context. If one block defines a variable, for example, that -variable is visible in the block that follows. - - diff --git a/docs/Options.md b/docs/Options.md deleted file mode 100644 index 6dccd9e595..0000000000 --- a/docs/Options.md +++ /dev/null @@ -1,231 +0,0 @@ -# Mongoose Configuration Options - -Every option is followed by it's default value. -If default value is not present, then it is empty. - -### cgi_pattern `**.cgi$|**.pl$|**.php$` -All files that match `cgi_pattern` are treated as CGI files. Default pattern -allows CGI files be anywhere. To restrict CGIs to a certain directory, -use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is -matched against the pattern, not the URI. - -### cgi_environment -Extra environment variables to be passed to the CGI script in -addition to standard ones. The list must be comma-separated list -of name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`. - -### put\_delete\_auth\_file -Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests -will fail. The format of the passwords file is the same as for `.htpasswd` file -used for Digest authentication. It can be created and managed by means -of `mongoose -A` command. - -### cgi_interpreter -Path to an executable to use as CGI interpreter for __all__ CGI scripts -regardless script extension. If this option is not set (which is a default), -Mongoose looks at first line of a CGI script, -[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\)), -for an interpreter. - -For example, if both PHP and perl CGIs are used, then -`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the -respective CGI scripts. Note that paths should be either full file paths, -or file paths relative to the current working directory of mongoose server. -If mongoose is started by mouse double-click on Windows, current working -directory is a directory where mongoose executable is located. - -If all CGIs use the same interpreter, for example they are all PHP, then -`cgi_interpreter` can be set to the path to `php-cgi.exe` executable and -shebang line in the CGI scripts can be omitted. -Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`. - -### protect_uri -Comma separated list of URI=PATH pairs, specifying that given -URIs must be protected with respected password files. Paths must be full -file paths. - -### authentication_domain `mydomain.com` -Authorization realm used in `.htpasswd` authorization. - -### ssi_pattern `**.shtml$|**.shtm$` -All files that match `ssi_pattern` are treated as SSI. - -Server Side Includes (SSI) is a simple interpreted server-side scripting -language which is most commonly used to include the contents of a file into -a web page. It can be useful when it is desirable to include a common piece -of code throughout a website, for example, headers and footers. - -In order for a webpage to recognize an SSI-enabled HTML file, the filename -should end with a special extension, by default the extension should be -either `.shtml` or `.shtm`. - -Unknown SSI directives are silently ignored by mongoose. Currently, two SSI -directives are supported, ` - -For more information on Server Side Includes, take a look at the Wikipedia: -[Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes) - -### throttle -Limit download speed for clients. `throttle` is a comma-separated -list of key=value pairs, where key could be: - - * limit speed for all connections - x.x.x.x/mask limit speed for specified subnet - uri_prefix_pattern limit speed for given URIs - -The value is a floating-point number of bytes per second, optionally -followed by a `k` or `m` character, meaning kilobytes and -megabytes respectively. A limit of 0 means unlimited rate. The -last matching rule wins. Examples: - - *=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second, - but give connections from 10.0.0.0/8 subnet - unlimited speed - - /downloads/=5k limit accesses to all URIs in `/downloads/` to - 5 kilobytes per secods. All other accesses are unlimited - -### access\_log\_file -Path to a file for access logs. Either full path, or relative to current -working directory. If absent (default), then accesses are not logged. - -### error\_log\_file -Path to a file for error logs. Either full path, or relative to current -working directory. If absent (default), then errors are not logged. - -### enable\_directory\_listing `yes` -Enable directory listing, either `yes` or `no`. - -### enable\_keep\_alive `no` -Enable connection keep alive, either `yes` or `no`. - -Experimental feature. Allows clients to reuse TCP connection for -subsequent HTTP requests, which improves performance. -For this to work when using request handlers it's important to add the correct -Content-Length HTTP header for each request. If this is forgotten the client -will time out. - - -### global\_auth\_file -Path to a global passwords file, either full path or relative to the current -working directory. If set, per-directory `.htpasswd` files are ignored, -and all requests are authorised against that file. - -The file has to include the realm set through `authentication_domain` and the password in digest format: - - user:realm:digest - test:test.com:ce0220efc2dd2fad6185e1f1af5a4327 - -(e.g. use [this generator](http://www.askapache.com/online-tools/htpasswd-generator)) - -### index_files `index.html,index.htm,index.cgi,index.shtml,index.php` -Comma-separated list of files to be treated as directory index -files. - -### access\_control\_list -An Access Control List (ACL) allows restrictions to be put on the list of IP -addresses which have access to the web server. In the case of the Mongoose -web server, the ACL is a comma separated list of IP subnets, where each -subnet is prepended by either a `-` or a `+` sign. A plus sign means allow, -where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, -this means to deny only that single IP address. - -Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow -all accesses. On each request the full list is traversed, and -the last match wins. Examples: - - -0.0.0.0/0,+192.168/16 deny all acccesses, only allow 192.168/16 subnet - -To learn more about subnet masks, see the -[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork) - -### extra\_mime\_types -Extra mime types to recognize, in form `extension1=type1,exten- -sion2=type2,...`. Extension must include dot. Example: -`.cpp=plain/text,.java=plain/text` - -### listening_ports `8080` -Comma-separated list of ports to listen on. If the port is SSL, a -letter `s` must be appeneded, for example, `80,443s` will open -port 80 and port 443, and connections on port 443 will be SSL-ed. -For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'. -Redirect ports will redirect all their traffic to the first configured -SSL port. For example, if `listening_ports` is `80r,443s`, then all -HTTP traffic coming at port 80 will be redirected to HTTPS port 443. - -It is possible to specify an IP address to bind to. In this case, -an IP address and a colon must be prepended to the port number. -For example, to bind to a loopback interface on port 80 and to -all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`. - -### document_root `.` -A directory to serve. By default, currect directory is served. Current -directory is commonly referenced as dot (`.`). - -### ssl_certificate -Path to SSL certificate file. This option is only required when at least one -of the `listening_ports` is SSL. The file must be in PEM format, -and it must have both private key and certificate, see for example -[ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem) - -### num_threads `50` -Number of worker threads. Mongoose handles each incoming connection in a -separate thread. Therefore, the value of this option is effectively a number -of concurrent HTTP connections Mongoose can handle. - -### run\_as\_user -Switch to given user credentials after startup. Usually, this option is -required when mongoose needs to bind on privileged port on UNIX. To do -that, mongoose needs to be started as root. But running as root is a bad idea, -therefore this option can be used to drop privileges. Example: - - mongoose -listening_ports 80 -run_as_user nobody - -### request\_timeout\_ms `30000` -Timeout for network read and network write operations, in milliseconds. -If client intends to keep long-running connection, either increase this value -or use keep-alive messages. - - -### url\_rewrite\_patterns -Comma-separated list of URL rewrites in the form of -`uri_pattern=file_or_directory_path`. When Mongoose receives the request, -it constructs the file name to show by combining `document_root` and the URI. -However, if the rewrite option is used and `uri_pattern` matches the -requested URI, then `document_root` is ignored. Insted, -`file_or_directory_path` is used, which should be a full path name or -a path relative to the web server's current working directory. Note that -`uri_pattern`, as all mongoose patterns, is a prefix pattern. - -This makes it possible to serve many directories outside from `document_root`, -redirect all requests to scripts, and do other tricky things. For example, -to redirect all accesses to `.doc` files to a special script, do: - - mongoose -url_rewrite_patterns **.doc$=/path/to/cgi-bin/handle_doc.cgi - -Or, to imitate user home directories support, do: - - mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/ - -### hide\_files\_patterns -A pattern for the files to hide. Files that match the pattern will not -show up in directory listing and return `404 Not Found` if requested. Pattern -must be for a file name only, not including directory name. Example: - - mongoose -hide_files_patterns secret.txt|even_more_secret.txt - - diff --git a/docs/SSL.md b/docs/SSL.md deleted file mode 100644 index df9b005caa..0000000000 --- a/docs/SSL.md +++ /dev/null @@ -1,75 +0,0 @@ -# Mongoose SSL guide - -SSL is a protocol that makes web communication secure. To enable SSL -in mongoose, 3 steps are required: - - 1. Valid certificate file must be created - 2. `ssl_certificate` options must be set to contain path to the - certificate file. - 3. `listening_ports` option must contain a port number with letter `s` - appended to it, which instructs Mongoose to use SSL for all connections - made to that port. - -Below is the `mongoose.conf` file snippet for typical SSL setup: - - document_root www_root # Serve files in www_root directory - listening_ports 80r,443s # Redirect all HTTP requests to HTTPS - ssl_certificate ssl_cert.pem # Location of certificate file - -## How to create SSL certificate file - -SSL certificate file is a text file that must contain at least two -sections: - - 1. A private key - 2. A certificate - -Both sections should be chunks of text in PEM format. When PEM file is -opened in a text editor, it looks like this: - - -----BEGIN RSA PRIVATE KEY----- - MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH - hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= - -----END RSA PRIVATE KEY----- - -----BEGIN CERTIFICATE----- - MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB - SEGI4JSxV56lYg== - -----END CERTIFICATE----- - -Two aforementioned sections are clearly seen. Typically, those section -are bigger then in the example shown. The text between the `BEGIN` and -`END` is the text representation of binary data, a private key and a -certificate. Therefore, in order to create a certificate file, - - * private key must be converted to PEM format - * certificate must be converted to PEM format - * those two should be concatenated into a single file - -If the certificate chain in used, a chain file also needs to be -converted into PEM format and appended to the certificate file. - -## How SSL works - -SSL is a protocol that can encrypt communication between two parties. If third -party observes all messages passed by, it would be very -hard for the third party (though not impossible) to decrypt the communication. - -The idea is based on so-called public key encryption. Communicating parties -have two keys: a public key and a private key. A public key is advertised -to everybody, and it is contained in a certificate. A private key is kept -secret. Security algorithm works in a way that anybody can encrypt -a message using public key, and only private key can decrypt it. - -This is why web server needs both private key and certificate: private key -is used to decrypt incoming messages, and certificate is used to tell the -public key to the other party. When communication starts, parties exchange -their public keys, and keep private keys to themselves. Man-in-the-middle -who observes the communication is unable to decrypt the messages cause -private keys are required for decryption. - -Encryption algorithms are built on top of hard mathematical problem, which -makes it very expensive for man-in-the-middle to compute private keys. -For example, RSA algorithm is based on a mathematical problem of factorization. -It is easy to generate two very large prime numbers `P` and `Q` and make -a product `P * Q`. But given a product, it is very hard to recover these -two prime numbers - this is called factorization. diff --git a/docs/Usage.md b/docs/Usage.md deleted file mode 100644 index f0cc916209..0000000000 --- a/docs/Usage.md +++ /dev/null @@ -1,81 +0,0 @@ -# Mongoose Usage Guide - -Mongoose is small and easy to use web server. It is self-contained, and does -not require any external software to run. - -On Windows, mongoose iconifies itself to the system tray icon when started. -Right-click on the icon pops up a menu, where it is possible to stop -mongoose, or configure it, or install it as Windows service. The easiest way -to share a folder on Windows is to copy `mongoose.exe` to a folder, -double-click the exe, and launch a browser at -[http://localhost:8080](http://localhost:8080). Note that 'localhost' should -be changed to a machine's name if a folder is accessed from other computer. - -On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in -terminal, optionally followed by configuration parameters -(`mongoose [OPTIONS]`) or configuration file name -(`mongoose [config_file_name]`) starts the -web server. Mongoose does not detach from terminal. Pressing `Ctrl-C` keys -would stop the server. - -When started, mongoose first searches for the configuration file. -If configuration file is specified explicitly in the command line, i.e. -`mongoose path_to_config_file`, then specified configuration file is used. -Otherwise, mongoose would search for file `mongoose.conf` in the same directory -where binary is located, and use it. Configuration file can be absent. - - -Configuration file is a sequence of lines, each line containing -command line argument name and it's value. Empty lines, and lines beginning -with `#`, are ignored. Here is the example of `mongoose.conf` file: - - document_root c:\www - listening_ports 8080,8043s - ssl_certificate c:\mongoose\ssl_cert.pem - -When configuration file is processed, mongoose process command line arguments, -if they are specified. Command line arguments therefore can override -configuration file settings. Command line arguments must start with `-`. -For example, if `mongoose.conf` has line -`document_root /var/www`, and mongoose has been started as -`mongoose -document_root /etc`, then `/etc` directory will be served as -document root, because command line options take priority over -configuration file. Configuration options section below provide a good -overview of Mongoose features. - -Note that configuration options on the command line must start with `-`, -but their names are the same as in the config file. All option names are -listed in the next section. Thus, the following two setups are equivalent: - - # Using command line arguments - $ mongoose -listening_ports 1234 -document_root /var/www - - # Using config file - $ cat mongoose.conf - listening_ports 1234 - document_root /var/www - $ mongoose - -Mongoose can also be used to modify `.htpasswd` passwords file: - - mongoose -A - -Unlike other web servers, mongoose does not require CGI scripts be located in -a special directory. CGI scripts can be anywhere. CGI (and SSI) files are -recognized by the file name pattern. Mongoose uses shell-like glob -patterns. Pattern match starts at the beginning of the string, so essentially -patterns are prefix patterns. Syntax is as follows: - - ** Matches everything - * Matches everything but slash character, '/' - ? Matches any character - $ Matches the end of the string - | Matches if pattern on the left side or the right side matches. - -All other characters in the pattern match themselves. Examples: - - **.cgi$ Any string that ends with .cgi - /foo Any string that begins with /foo - **a$|**b$ Any string that ends with a or b - - diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 51537035a1..0000000000 --- a/examples/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT) -DLL_FLAGS = -DLUA_COMPAT_ALL -I../build -RM = rm -rf - -ifeq ($(OS),Windows_NT) - RM = del /q /f -else - UNAME_S := $(shell uname -s) - DLL_FLAGS += -shared - - ifeq ($(UNAME_S),Linux) - CFLAGS += -ldl - endif - - ifeq ($(UNAME_S),Darwin) - # DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib - DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib - endif -endif - -all: websocket_html.c - $(CC) hello.c ../mongoose.c -o hello $(CFLAGS) - $(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS) - $(CC) post.c ../mongoose.c -o post $(CFLAGS) - $(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS) - $(CC) upload.c ../mongoose.c -o upload $(CFLAGS) - $(CC) auth.c ../mongoose.c -o auth $(CFLAGS) - $(CC) server.c ../mongoose.c -o server $(CFLAGS) - -# $(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS) -# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS) -# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS) - -websocket_html.c: websocket.html - perl mkdata.pl $< > $@ - -MSVC = ../../vc6 -CL = $(MSVC)/bin/cl -CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \ - /I$(MSVC)/include /I.. /Dsnprintf=_snprintf -LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 - -windows: websocket_html.c - $(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - $(CL) server.c ../mongoose.c $(CLFLAGS) $(LFLAGS) - -# $(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(LFLAGS) -#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll - -clean: - -@$(RM) hello upload post websocket chat *.exe *.dSYM *.obj .*o diff --git a/examples/basic_auth.cpp b/examples/basic_auth.cpp index 26a7d45035..cc24bd63bc 100644 --- a/examples/basic_auth.cpp +++ b/examples/basic_auth.cpp @@ -5,42 +5,67 @@ #endif #include #include -#include -#include +#include + +#include "Controller.h" +#include "Request.h" +#include "Response.h" +#include "Server.h" using namespace std; using namespace Mongoose; -class MyController : public WebController +class MyController : public Controller { - public: - void hello(Request &request, StreamResponse &response) - { - response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; - } - - void setup() - { - addRoute("GET", "/hello", MyController, hello); - } +public: + bool hello(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::string body; + body = "Hello " + req->getVariable("name", "... what's your name ?\n"); + res->send(body); + return true; + } + + void setup() + { + addRoute("GET", "/hello", MyController, hello); + addRoute("GET", "/", MyController, hello); + } }; +volatile static bool running = false; + +void handle_signal(int sig) +{ + if (running) + { + std::cout << "Exiting..." << std::endl; + running = false; + } +} + int main() { MyController myController; - Server server(8080); - server.setOption("basic_auth_username", "admin"); - server.setOption("basic_auth_password", "admin"); + Server server("8080"); + server.setBasicAuthUsername("admin"); + server.setBasicAuthPassword("admin"); server.registerController(&myController); - server.start(); + signal(SIGINT, handle_signal); - while (1) { -#ifdef WIN32 - Sleep(10000); -#else - sleep(10); -#endif + if (server.start()) + { + std::cout << "Server started, routes:" << std::endl; + running = true; } + + + while (running) + { + server.poll(1000); + } + + return EXIT_SUCCESS; } diff --git a/examples/c#/example.cs b/examples/c#/example.cs deleted file mode 100644 index e7fc453b4c..0000000000 --- a/examples/c#/example.cs +++ /dev/null @@ -1,37 +0,0 @@ -// This file is part of mongoose web server project, -// https://github.com/cesanta/mongoose - -using System; -using System.Runtime.InteropServices; - -public class Program { - static private int EventHandler(MongooseEvent ev) { - if (ev.type != 1) { - return 0; // Mark as unhandled - } - - MongooseRequestInfo request_info = (MongooseRequestInfo) - Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo)); - - if (request_info.uri != "/test") { - return 0; // Mark as unhandled - } - - Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n"); - Mongoose.write(ev.conn, "Hello from C#!\n"); - - return 1; // Mark as handled - } - - static void Main() { - Mongoose web_server = new Mongoose(".", "9000", - new MongooseEventHandler(EventHandler)); - - Console.WriteLine("Mongoose v." + web_server.version_ + " started."); - Console.WriteLine("Press enter to exit program."); - - // Serve requests until user presses "enter" on a keyboard - Console.ReadLine(); - web_server.stop(); - } -} diff --git a/examples/c#/mongoose.cs b/examples/c#/mongoose.cs deleted file mode 100644 index 61ad282b3d..0000000000 --- a/examples/c#/mongoose.cs +++ /dev/null @@ -1,93 +0,0 @@ -// This file is part of mongoose web server project, -// https://github.com/cesanta/mongoose - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader { - [MarshalAs(UnmanagedType.LPTStr)] public IntPtr name; - [MarshalAs(UnmanagedType.LPTStr)] public IntPtr value; -}; - -// mongoose.h :: struct mg_request_info -[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo { - [MarshalAs(UnmanagedType.LPTStr)] public string request_method; - [MarshalAs(UnmanagedType.LPTStr)] public string uri; - [MarshalAs(UnmanagedType.LPTStr)] public string http_version; - [MarshalAs(UnmanagedType.LPTStr)] public string query_string; - [MarshalAs(UnmanagedType.LPTStr)] public string remote_user; - public int remote_ip; - public int remote_port; - public int is_ssl; - [MarshalAs(UnmanagedType.ByValArray,SizeConst=64)] - public MongooseHeader[] http_headers; -}; - -[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent { - public int type; - public IntPtr user_data; - public IntPtr conn_data; - public IntPtr event_param; - public IntPtr conn; - public IntPtr request_info; -}; - -public delegate int MongooseEventHandlerN(ref MongooseEvent ev); -public delegate int MongooseEventHandler(MongooseEvent ev); - -public class Mongoose { - public const string dll_name_ = "mongoose"; - public string version_ = "??"; - - // These are here to store a ref to the callbacks - // while they are over in unmanaged code, to prevent garbage collection. - private event MongooseEventHandlerN delegates; - - private IntPtr ctx_; - - [DllImport(dll_name_)] private static extern - IntPtr mg_start([MarshalAs(UnmanagedType.LPArray, - ArraySubType=UnmanagedType.LPTStr)] string[] options, - MongooseEventHandlerN callback, - IntPtr user_data); - [DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx); - [DllImport(dll_name_)] private static extern IntPtr mg_version(); - [DllImport(dll_name_)] public static extern int mg_write(IntPtr conn, - string data, int length); - - public Mongoose(string document_root, - string listening_ports, - MongooseEventHandler event_handler) { - version_ = Marshal.PtrToStringAnsi(mg_version()); - - string[] options = { - "document_root", document_root, - "listening_ports", listening_ports, - null - }; - - MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) { - return event_handler(ev); - }; - - // Prevent garbage collection - delegates += cb; - - ctx_ = mg_start(options, cb, IntPtr.Zero); - } - - public static int write(IntPtr conn, string data) { - return mg_write(conn, data, data.Length); - } - - public void stop() { - if (this.ctx_ != IntPtr.Zero) { - mg_stop(this.ctx_); - } - this.ctx_ = IntPtr.Zero; - } - - ~Mongoose() { - stop(); - } -} diff --git a/examples/chat.c b/examples/chat.c deleted file mode 100644 index 0afb5c0da1..0000000000 --- a/examples/chat.c +++ /dev/null @@ -1,386 +0,0 @@ -// This file is part of the Mongoose project, http://code.google.com/p/mongoose -// It implements an online chat server. For more details, -// see the documentation on the project web site. -// To test the application, -// 1. type "make" in the directory where this file lives -// 2. point your browser to http://127.0.0.1:8081 - -#include -#include -#include -#include -#include -#include -#include - -#include "mongoose.h" - -#define MAX_USER_LEN 20 -#define MAX_MESSAGE_LEN 100 -#define MAX_MESSAGES 5 -#define MAX_SESSIONS 2 -#define SESSION_TTL 120 - -static const char *authorize_url = "/authorize"; -static const char *login_url = "/login.html"; -static const char *ajax_reply_start = - "HTTP/1.1 200 OK\r\n" - "Cache: no-cache\r\n" - "Content-Type: application/x-javascript\r\n" - "\r\n"; - -// Describes single message sent to a chat. If user is empty (0 length), -// the message is then originated from the server itself. -struct message { - long id; // Message ID - char user[MAX_USER_LEN]; // User that have sent the message - char text[MAX_MESSAGE_LEN]; // Message text - time_t timestamp; // Message timestamp, UTC -}; - -// Describes web session. -struct session { - char session_id[33]; // Session ID, must be unique - char random[20]; // Random data used for extra user validation - char user[MAX_USER_LEN]; // Authenticated user - time_t expire; // Expiration timestamp, UTC -}; - -static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages -static struct session sessions[MAX_SESSIONS]; // Current sessions -static long last_message_id; - -// Protects messages, sessions, last_message_id -static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; - -// Get session object for the connection. Caller must hold the lock. -static struct session *get_session(const struct mg_connection *conn) { - int i; - const char *cookie = mg_get_header(conn, "Cookie"); - char session_id[33]; - time_t now = time(NULL); - mg_get_cookie(cookie, "session", session_id, sizeof(session_id)); - for (i = 0; i < MAX_SESSIONS; i++) { - if (sessions[i].expire != 0 && - sessions[i].expire > now && - strcmp(sessions[i].session_id, session_id) == 0) { - break; - } - } - return i == MAX_SESSIONS ? NULL : &sessions[i]; -} - -static void get_qsvar(const struct mg_request_info *request_info, - const char *name, char *dst, size_t dst_len) { - const char *qs = request_info->query_string; - mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len); -} - -// Get a get of messages with IDs greater than last_id and transform them -// into a JSON string. Return that string to the caller. The string is -// dynamically allocated, caller must free it. If there are no messages, -// NULL is returned. -static char *messages_to_json(long last_id) { - const struct message *message; - int max_msgs, len; - char buf[sizeof(messages)]; // Large enough to hold all messages - - // Read-lock the ringbuffer. Loop over all messages, making a JSON string. - pthread_rwlock_rdlock(&rwlock); - len = 0; - max_msgs = sizeof(messages) / sizeof(messages[0]); - // If client is too far behind, return all messages. - if (last_message_id - last_id > max_msgs) { - last_id = last_message_id - max_msgs; - } - for (; last_id < last_message_id; last_id++) { - message = &messages[last_id % max_msgs]; - if (message->timestamp == 0) { - break; - } - // buf is allocated on stack and hopefully is large enough to hold all - // messages (it may be too small if the ringbuffer is full and all - // messages are large. in this case asserts will trigger). - len += snprintf(buf + len, sizeof(buf) - len, - "{user: '%s', text: '%s', timestamp: %lu, id: %lu},", - message->user, message->text, message->timestamp, message->id); - assert(len > 0); - assert((size_t) len < sizeof(buf)); - } - pthread_rwlock_unlock(&rwlock); - - return len == 0 ? NULL : strdup(buf); -} - -// If "callback" param is present in query string, this is JSONP call. -// Return 1 in this case, or 0 if "callback" is not specified. -// Wrap an output in Javascript function call. -static int handle_jsonp(struct mg_connection *conn, - const struct mg_request_info *request_info) { - char cb[64]; - - get_qsvar(request_info, "callback", cb, sizeof(cb)); - if (cb[0] != '\0') { - mg_printf(conn, "%s(", cb); - } - - return cb[0] == '\0' ? 0 : 1; -} - -// A handler for the /ajax/get_messages endpoint. -// Return a list of messages with ID greater than requested. -static void ajax_get_messages(struct mg_connection *conn, - const struct mg_request_info *request_info) { - char last_id[32], *json; - int is_jsonp; - - mg_printf(conn, "%s", ajax_reply_start); - is_jsonp = handle_jsonp(conn, request_info); - - get_qsvar(request_info, "last_id", last_id, sizeof(last_id)); - if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) { - mg_printf(conn, "[%s]", json); - free(json); - } - - if (is_jsonp) { - mg_printf(conn, "%s", ")"); - } -} - -// Allocate new message. Caller must hold the lock. -static struct message *new_message(void) { - static int size = sizeof(messages) / sizeof(messages[0]); - struct message *message = &messages[last_message_id % size]; - message->id = last_message_id++; - message->timestamp = time(0); - return message; -} - -static void my_strlcpy(char *dst, const char *src, size_t len) { - strncpy(dst, src, len); - dst[len - 1] = '\0'; -} - -// A handler for the /ajax/send_message endpoint. -static void ajax_send_message(struct mg_connection *conn, - const struct mg_request_info *request_info) { - struct message *message; - struct session *session; - char text[sizeof(message->text) - 1]; - int is_jsonp; - - mg_printf(conn, "%s", ajax_reply_start); - is_jsonp = handle_jsonp(conn, request_info); - - get_qsvar(request_info, "text", text, sizeof(text)); - if (text[0] != '\0') { - // We have a message to store. Write-lock the ringbuffer, - // grab the next message and copy data into it. - pthread_rwlock_wrlock(&rwlock); - message = new_message(); - // TODO(lsm): JSON-encode all text strings - session = get_session(conn); - assert(session != NULL); - my_strlcpy(message->text, text, sizeof(text)); - my_strlcpy(message->user, session->user, sizeof(message->user)); - pthread_rwlock_unlock(&rwlock); - } - - mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true"); - - if (is_jsonp) { - mg_printf(conn, "%s", ")"); - } -} - -// Redirect user to the login form. In the cookie, store the original URL -// we came from, so that after the authorization we could redirect back. -static void redirect_to_login(struct mg_connection *conn, - const struct mg_request_info *request_info) { - mg_printf(conn, "HTTP/1.1 302 Found\r\n" - "Set-Cookie: original_url=%s\r\n" - "Location: %s\r\n\r\n", - request_info->uri, login_url); -} - -// Return 1 if username/password is allowed, 0 otherwise. -static int check_password(const char *user, const char *password) { - // In production environment we should ask an authentication system - // to authenticate the user. - // Here however we do trivial check that user and password are not empty - return (user[0] && password[0]); -} - -// Allocate new session object -static struct session *new_session(void) { - int i; - time_t now = time(NULL); - pthread_rwlock_wrlock(&rwlock); - for (i = 0; i < MAX_SESSIONS; i++) { - if (sessions[i].expire == 0 || sessions[i].expire < now) { - sessions[i].expire = time(0) + SESSION_TTL; - break; - } - } - pthread_rwlock_unlock(&rwlock); - return i == MAX_SESSIONS ? NULL : &sessions[i]; -} - -// Generate session ID. buf must be 33 bytes in size. -// Note that it is easy to steal session cookies by sniffing traffic. -// This is why all communication must be SSL-ed. -static void generate_session_id(char *buf, const char *random, - const char *user) { - mg_md5(buf, random, user, NULL); -} - -static void send_server_message(const char *fmt, ...) { - va_list ap; - struct message *message; - - pthread_rwlock_wrlock(&rwlock); - message = new_message(); - message->user[0] = '\0'; // Empty user indicates server message - va_start(ap, fmt); - vsnprintf(message->text, sizeof(message->text), fmt, ap); - va_end(ap); - - pthread_rwlock_unlock(&rwlock); -} - -// A handler for the /authorize endpoint. -// Login page form sends user name and password to this endpoint. -static void authorize(struct mg_connection *conn, - const struct mg_request_info *request_info) { - char user[MAX_USER_LEN], password[MAX_USER_LEN]; - struct session *session; - - // Fetch user name and password. - get_qsvar(request_info, "user", user, sizeof(user)); - get_qsvar(request_info, "password", password, sizeof(password)); - - if (check_password(user, password) && (session = new_session()) != NULL) { - // Authentication success: - // 1. create new session - // 2. set session ID token in the cookie - // 3. remove original_url from the cookie - not needed anymore - // 4. redirect client back to the original URL - // - // The most secure way is to stay HTTPS all the time. However, just to - // show the technique, we redirect to HTTP after the successful - // authentication. The danger of doing this is that session cookie can - // be stolen and an attacker may impersonate the user. - // Secure application must use HTTPS all the time. - my_strlcpy(session->user, user, sizeof(session->user)); - snprintf(session->random, sizeof(session->random), "%d", rand()); - generate_session_id(session->session_id, session->random, session->user); - send_server_message("<%s> joined", session->user); - mg_printf(conn, "HTTP/1.1 302 Found\r\n" - "Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID - "Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code - "Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url - "Location: /\r\n\r\n", - session->session_id, session->user); - } else { - // Authentication failure, redirect to login. - redirect_to_login(conn, request_info); - } -} - -// Return 1 if request is authorized, 0 otherwise. -static int is_authorized(const struct mg_connection *conn, - const struct mg_request_info *request_info) { - struct session *session; - char valid_id[33]; - int authorized = 0; - - // Always authorize accesses to login page and to authorize URI - if (!strcmp(request_info->uri, login_url) || - !strcmp(request_info->uri, authorize_url)) { - return 1; - } - - pthread_rwlock_rdlock(&rwlock); - if ((session = get_session(conn)) != NULL) { - generate_session_id(valid_id, session->random, session->user); - if (strcmp(valid_id, session->session_id) == 0) { - session->expire = time(0) + SESSION_TTL; - authorized = 1; - } - } - pthread_rwlock_unlock(&rwlock); - - return authorized; -} - -static void redirect_to_ssl(struct mg_connection *conn, - const struct mg_request_info *request_info) { - const char *p, *host = mg_get_header(conn, "Host"); - if (host != NULL && (p = strchr(host, ':')) != NULL) { - mg_printf(conn, "HTTP/1.1 302 Found\r\n" - "Location: https://%.*s:8082/%s:8082\r\n\r\n", - (int) (p - host), host, request_info->uri); - } else { - mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set"); - } -} - -static int event_handler(struct mg_event *event) { - struct mg_request_info *request_info = event->request_info; - struct mg_connection *conn = event->conn; - int result = 1; - - if (event->type != MG_REQUEST_BEGIN) return 0; - - if (!request_info->is_ssl) { - redirect_to_ssl(conn, request_info); - } else if (!is_authorized(conn, request_info)) { - redirect_to_login(conn, request_info); - } else if (strcmp(request_info->uri, authorize_url) == 0) { - authorize(conn, request_info); - } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { - ajax_get_messages(conn, request_info); - } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { - ajax_send_message(conn, request_info); - } else { - // No suitable handler found, mark as not processed. Mongoose will - // try to serve the request. - result = 0; - } - - return result; -} - -static const char *options[] = { - "document_root", "html", - "listening_ports", "8081,8082s", - "ssl_certificate", "ssl_cert.pem", - "num_threads", "5", - NULL -}; - -int main(void) { - struct mg_context *ctx; - - // Initialize random number generator. It will be used later on for - // the session identifier creation. - srand((unsigned) time(0)); - - // Setup and start Mongoose - if ((ctx = mg_start(options, event_handler, NULL)) == NULL) { - printf("%s\n", "Cannot start chat server, fatal exit"); - exit(EXIT_FAILURE); - } - - // Wait until enter is pressed, then exit - printf("Chat server started on ports %s, press enter to quit.\n", - mg_get_option(ctx, "listening_ports")); - getchar(); - mg_stop(ctx); - printf("%s\n", "Chat server stopped."); - - return EXIT_SUCCESS; -} - -// vim:ts=2:sw=2:et diff --git a/examples/examples.cpp b/examples/examples.cpp new file mode 100644 index 0000000000..7047eb317e --- /dev/null +++ b/examples/examples.cpp @@ -0,0 +1,201 @@ +#ifndef _MSC_VER +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include "Server.h" +#include "Sessions.h" +#include "Controller.h" +#include "Utils.h" + +using namespace Mongoose; + +class MyController : public Controller +{ + Sessions mSessions; + + public: + bool hello(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::stringstream body; + body << "Hello " << req->getVariable("name", "... what's your name ?\n"); + res->send(body.str()); + return true; + } + + bool hello_delayed(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::thread([=] + { + int duration = std::stoi(req->getVariable("duration", "3")); + std::this_thread::sleep_for(std::chrono::seconds(duration)); + res->send("Hello after " + std::to_string(duration) + " seconds\n"); + }).detach(); + + return true; + } + + bool form(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::stringstream responseBody; + responseBody << "
" << std::endl; + responseBody << "
" << std::endl; + responseBody << "" << std::endl; + responseBody << "
" << std::endl; + + return res->send(responseBody.str()); + } + + bool formPost(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::stringstream responseBody; + responseBody << "Test=" << req->getVariable("test", "(unknown)"); + return res->send(responseBody.str()); + } + + bool session(const std::shared_ptr& req, const std::shared_ptr& res) + { + Session *session = mSessions.get(req, res); + std::stringstream responseBody; + + if (session->hasValue("try")) { + responseBody << "Session value: " << session->value("try"); + } else { + std::ostringstream val; + val << time(NULL); + session->setValue("try", val.str()); + responseBody << "Session value set to: " << session->value("try"); + } + + return res->send(responseBody.str()); + } + + bool forbid(const std::shared_ptr& req, const std::shared_ptr& res) + { + res->setCode(HTTP_FORBIDDEN); + return res->send("403 forbidden demo"); + } + + bool exception(const std::shared_ptr& req, const std::shared_ptr& res) + { + throw std::string("Exception example"); + } + + bool uploadForm(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::stringstream responseBody; + responseBody << ""; + responseBody << "

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

"; + responseBody << "
"; + responseBody << "Choose a file to upload:
"; + responseBody << ""; + responseBody << ""; + responseBody << "
"; + responseBody << ""; + + return res->sendHtml(responseBody.str()); + } + + bool upload(const std::shared_ptr& req, const std::shared_ptr& res) + { + std::stringstream responseBody; + responseBody << "Your form variables: " << std::endl; + + for (const auto& variable: req->variables()) + { + responseBody << variable.first << " : " << variable.second << std::endl; + } + + return res->send(responseBody.str()); + } + + void setup() + { + // Hello demo + addRoute("GET", "/hello", MyController, hello); + addRoute("GET", "/hello_delayed", MyController, hello_delayed); + addRoute("GET", "/", MyController, hello); + + // Form demo + addRoute("GET", "/form", MyController, form); + addRoute("POST", "/form", MyController, formPost); + + // Session demo + addRoute("GET", "/session", MyController, session); + + // Exception example + addRoute("GET", "/exception", MyController, exception); + + // 403 demo + addRoute("GET", "/403", MyController, forbid); + + // File upload demo + addRoute("GET", "/upload", MyController, uploadForm); + addRoute("POST", "/upload", MyController, upload); + + //Generic register route + registerRoute("GET", "/hello_lambda", [=](const std::shared_ptr& req, const std::shared_ptr& res) + { + res->send("Hello lambda " + req->getVariable("name", "... what's your name ?") + "\n"); + return true; + }); + +#ifdef HAS_JSON11 + //Generic register route + registerRoute("GET", "/json", [=](const std::shared_ptr& req, const std::shared_ptr& res) + { + json11::Json body = json11::Json::object { + {"hello", "world"}, + {"status", 5} + }; + res->sendJson(body); + return true; + }); +#endif + } +}; + +volatile static bool running = false; + +void handle_signal(int sig) +{ + if (running) + { + std::cout << "Exiting..." << std::endl; + running = false; + } +} + +int main() +{ + srand(Utils::getTime()); + signal(SIGINT, handle_signal); + + MyController myController; + Server server("8080"); + server.registerController(&myController); + server.setDirectoryListingEnabled(false); + + if (server.start()) + { + std::cout << "Server started, routes:" << std::endl; + myController.dumpRoutes(); + running = true; + } + + + while (running) + { + server.poll(1000); + } + + return EXIT_SUCCESS; +} diff --git a/examples/hello.c b/examples/hello.c deleted file mode 100644 index 3481af5b7e..0000000000 --- a/examples/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include "mongoose.h" - -// This function will be called by mongoose on every new request -static int index_html(struct mg_connection *conn) { - mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri); - return 1; -} - -int main(void) { - struct mg_server *server; - - // Create and configure the server - server = mg_create_server(NULL); - mg_set_option(server, "listening_port", "8080"); - mg_add_uri_handler(server, "/", index_html); - - // Serve request. Hit Ctrl-C to terminate the program - printf("Starting on port %s\n", mg_get_option(server, "listening_port")); - for (;;) { - mg_poll_server(server, 1000); - } - - // Cleanup, and free server instance - mg_destroy_server(&server); - - return 0; -} diff --git a/examples/helloworld.cpp b/examples/helloworld.cpp deleted file mode 100644 index 7c71030d35..0000000000 --- a/examples/helloworld.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifdef _MSC_VER -#include -#else -#include -#endif -#include -#include -#include -#include - -using namespace std; -using namespace Mongoose; - -class MyController : public WebController -{ - public: - void hello(Request &request, StreamResponse &response) - { - response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; - } - - void setup() - { - addRoute("GET", "/hello", MyController, hello); - } -}; - - -int main() -{ - MyController myController; - Server server(8080); - server.registerController(&myController); - - server.start(); - - while (1) { -#ifdef WIN32 - Sleep(10000); -#else - sleep(10); -#endif - } -} diff --git a/examples/html/favicon.ico b/examples/html/favicon.ico deleted file mode 100644 index 2179aba894..0000000000 Binary files a/examples/html/favicon.ico and /dev/null differ diff --git a/examples/html/index.html b/examples/html/index.html deleted file mode 100644 index ee53eada47..0000000000 --- a/examples/html/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Mongoose chat server - - - - - - - - -
- -
-
- - - -
- -
-
- Main room -
-
-
- - - Type your message here and press enter -
-
-
- - - -
-
- - - - - diff --git a/examples/html/jquery.js b/examples/html/jquery.js deleted file mode 100644 index 7c24308023..0000000000 --- a/examples/html/jquery.js +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/examples/html/login.html b/examples/html/login.html deleted file mode 100644 index 38dcc89f09..0000000000 --- a/examples/html/login.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Mongoose chat: login - - - - - - - -
-

Mongoose chat server login

-
- Username and password can be any non-empty strings. -
-
-
-
-
- -
-
- - diff --git a/examples/html/logo.png b/examples/html/logo.png deleted file mode 100644 index 8a47c0ab8d..0000000000 Binary files a/examples/html/logo.png and /dev/null differ diff --git a/examples/html/main.js b/examples/html/main.js deleted file mode 100644 index 6204d1ad18..0000000000 --- a/examples/html/main.js +++ /dev/null @@ -1,106 +0,0 @@ -// This file is part of Mongoose project, http://code.google.com/p/mongoose - -var chat = { - // Backend URL, string. - // 'http://backend.address.com' or '' if backend is the same as frontend - backendUrl: '', - maxVisibleMessages: 10, - errorMessageFadeOutTimeoutMs: 2000, - errorMessageFadeOutTimer: null, - lastMessageId: 0, - getMessagesIntervalMs: 1000, -}; - -chat.normalizeText = function(text) { - return text.replace('<', '<').replace('>', '>'); -}; - -chat.refresh = function(data) { - - if (data === undefined) { - return; - } - - $.each(data, function(index, entry) { - var row = $('
').addClass('message-row').appendTo('#mml'); - var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString(); - $('') - .addClass('message-timestamp') - .html('[' + timestamp + ']') - .prependTo(row); - $('') - .addClass('message-user') - .addClass(entry.user ? '' : 'message-user-server') - .html(chat.normalizeText((entry.user || '[server]') + ':')) - .appendTo(row); - $('') - .addClass('message-text') - .addClass(entry.user ? '' : 'message-text-server') - .html(chat.normalizeText(entry.text)) - .appendTo(row); - chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1; - }); - - // Keep only chat.maxVisibleMessages, delete older ones. - while ($('#mml').children().length > chat.maxVisibleMessages) { - $('#mml div:first-child').remove(); - } -}; - -chat.getMessages = function(enter_loop) { - $.ajax({ - dataType: 'jsonp', - url: chat.backendUrl + '/ajax/get_messages', - data: {last_id: chat.lastMessageId}, - success: chat.refresh, - error: function() { - }, - }); - if (enter_loop) { - window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs); - } -}; - -chat.handleMenuItemClick = function(ev) { - $('.menu-item').removeClass('menu-item-selected'); // Deselect menu buttons - $(this).addClass('menu-item-selected'); // Select clicked button - $('.main').addClass('hidden'); // Hide all main windows - $('#' + $(this).attr('name')).removeClass('hidden'); // Show main window -}; - -chat.showError = function(message) { - $('#error').html(message).fadeIn('fast'); - window.clearTimeout(chat.errorMessageFadeOutTimer); - chat.errorMessageFadeOutTimer = window.setTimeout(function() { - $('#error').fadeOut('slow'); - }, chat.errorMessageFadeOutTimeoutMs); -}; - -chat.handleMessageInput = function(ev) { - var input = ev.target; - if (ev.keyCode != 13 || !input.value) - return; - //input.disabled = true; - $.ajax({ - dataType: 'jsonp', - url: chat.backendUrl + '/ajax/send_message', - data: {text: input.value}, - success: function(ev) { - input.value = ''; - input.disabled = false; - chat.getMessages(false); - }, - error: function(ev) { - chat.showError('Error sending message'); - input.disabled = false; - }, - }); -}; - -$(document).ready(function() { - $('.menu-item').click(chat.handleMenuItemClick); - $('.message-input').keypress(chat.handleMessageInput); - chat.getMessages(true); -}); - -// vim:ts=2:sw=2:et diff --git a/examples/html/style.css b/examples/html/style.css deleted file mode 100644 index 716351d2d4..0000000000 --- a/examples/html/style.css +++ /dev/null @@ -1,154 +0,0 @@ -/* - * vim:ts=2:sw=2:et:ai - */ - -body { - font: 13px Arial; margin: 0.5em 1em; -} - -#logo { - background: url('logo.png') no-repeat ; - width: 160px; - height: 40px; - float: left; -} - -td { - text-align: left; -} - -#motd { - margin-left: 170px; -} - -.infobox { - background: #eed; - padding: 1px 1em; -} - -.help-message { - color: #aaa; -} - -#middle { - margin: 0.5em 0; -} - -#error { - background: #c44; - color: white; - font-weight: bold; -} - -#content, .menu-item-selected, .chat-title, .chat-content { - background: #c3d9ff; -} - -#content { - overflow: hidden; - min-height: 7em; - padding: 1em; -} - -.chat-title { - padding: 1px 1ex; -} - -.chat-content { - padding: 1ex; -} - -.chat-window { -} - -.message-row { - margin: 2px; - border-bottom: 1px solid #bbb; -} - -.message-timestamp { - color: #484; -} - -.message-user { - margin-left: 0.5em; - font-weight: bold; -} - -.message-text { - margin-left: 0.5em; -} - -.message-user-server { - color: purple; -} - -.message-text-server { - font-style: italic; -} - -.main { - padding: 0.5em; - background: #f0fcff; -} - -#menu { - margin-top: 1em; - min-width: 7em; - float: left; -} - -#footer { - position: fixed; - bottom: 0; - right: 0; - color: #ccc; - padding: 0.5em; -} - -.section { - clear: both; -} - -.hidden { - display: none; -} - -.menu-item { - cursor: pointer; - padding: 0.1em 0.5em; -} - -.menu-item-selected { - font-weight: bold; -} - -.message-list { - min-height: 1em; - background: white; - margin: 0.5em 0; -} - -.rounded { - border-radius: 6px; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; -} - -.left-rounded { - border-radius: 6px 0 0 6px; - -moz-border-radius: 6px 0 0 6px; - -webkit-border-radius: 6px 0 0 6px; -} - -.bottom-rounded { - border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - -webkit-border-radius: 0 0 6px 6px; -} - -.top-rounded { - border-radius: 6px 6px 0 0; - -moz-border-radius: 6px 6px 0 0; - -webkit-border-radius: 6px 6px 0 0; -} diff --git a/examples/json.cpp b/examples/json.cpp deleted file mode 100644 index 7923502b2e..0000000000 --- a/examples/json.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace std; -using namespace Mongoose; - -class MyController : public JsonController -{ - public: - void hello(Request &request, JsonResponse &response) - { - int i; - - for (i=0; i<12; i++) { - response["users"][i]["Name"] = "Bob"; - } - - response["timestamp"] = (int)time(NULL); - } - - void setup() - { - // Example of prefix, putting all the urls into "/api" - setPrefix("/api"); - - // Hello demo - addRouteResponse("GET", "/", MyController, hello, JsonResponse); - addRouteResponse("GET", "/hello", MyController, hello, JsonResponse); - } -}; - -volatile static bool running = true; - -void handle_signal(int sig) -{ - if (running) { - cout << "Exiting..." << endl; - running = false; - } -} - -int main() -{ - srand(time(NULL)); - - signal(SIGINT, handle_signal); - - MyController myController; - Server server(8080); - server.registerController(&myController); - server.setOption("enable_directory_listing", "false"); - server.start(); - - cout << "Server started, routes:" << endl; - myController.dumpRoutes(); - - while (running) { -#ifdef WIN32 - Sleep(10000); -#else - sleep(10); -#endif - } - - server.stop(); - - return EXIT_SUCCESS; -} diff --git a/examples/lua/dirscan.lp b/examples/lua/dirscan.lp deleted file mode 100644 index 5525d2ce60..0000000000 --- a/examples/lua/dirscan.lp +++ /dev/null @@ -1,15 +0,0 @@ -HTTP/1.0 200 OK -Content-Type: text/plain - - diff --git a/examples/lua/prime_numbers.lp b/examples/lua/prime_numbers.lp deleted file mode 100644 index 0c71bb824f..0000000000 --- a/examples/lua/prime_numbers.lp +++ /dev/null @@ -1,46 +0,0 @@ -HTTP/1.0 200 OK -Content-Type: text/html - - -

Prime numbers from 0 to 100, calculated by Lua:

- ' .. i .. '
 ') end - end - - ?> - -

Reading POST data from Lua (click submit):

-
- -
-   POST data: []
-   request method: []
-   IP/port: []
-   URI: []
-   HTTP version []
-   HEADERS:
-   
-
- diff --git a/examples/lua_dll.c b/examples/lua_dll.c deleted file mode 100644 index 64c3ef46a5..0000000000 --- a/examples/lua_dll.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include "lua_5.2.1.h" - -static int smile(lua_State *L) { - (void) L; // Unused - printf("%s\n", ":-)"); - return 0; -} - -int LUA_API luaopen_lua_dll(lua_State *L) { - static const struct luaL_Reg api[] = { - {"smile", smile}, - {NULL, NULL}, - }; - luaL_openlib(L, "lua_dll", api, 0); - return 1; -} diff --git a/examples/main.cpp b/examples/main.cpp deleted file mode 100644 index 7e0b8b3cff..0000000000 --- a/examples/main.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef _MSC_VER -#include -#include -#else -#include -#endif -#include -#include -#include - -using namespace std; -using namespace Mongoose; - -class MyController : public WebController -{ - public: - void hello(Request &request, StreamResponse &response) - { - response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl; - } - - void form(Request &request, StreamResponse &response) - { - response << "
" << endl; - response << "
" << endl; - response << "" << endl; - response << "
" << endl; - } - - void formPost(Request &request, StreamResponse &response) - { - response << "Test=" << htmlEntities(request.get("test", "(unknown)")); - } - - void session(Request &request, StreamResponse &response) - { - Session &session = getSession(request, response); - - if (session.hasValue("try")) { - response << "Session value: " << session.get("try"); - } else { - ostringstream val; - val << time(NULL); - session.setValue("try", val.str()); - response << "Session value set to: " << session.get("try"); - } - } - - void forbid(Request &request, StreamResponse &response) - { - response.setCode(HTTP_FORBIDDEN); - response << "403 forbidden demo"; - } - - void exception(Request &request, StreamResponse &response) - { - throw string("Exception example"); - } - - void uploadForm(Request &request, StreamResponse &response) - { - response << "

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

"; - response << "
"; - response << "Choose a file to upload:
"; - response << ""; - response << "
"; - } - - void upload(Request &request, StreamResponse &response) - { - request.handleUploads(); - - // Iterate through all the uploaded files - vector::iterator it = request.uploadFiles.begin(); - for (; it != request.uploadFiles.end(); it++) { - UploadFile file = *it; - file.saveTo("tmp/"); - response << "Uploaded file: " << file.getName() << endl; - } - } - - void setup() - { - // Hello demo - addRoute("GET", "/hello", MyController, hello); - addRoute("GET", "/", MyController, hello); - - // Form demo - addRoute("GET", "/form", MyController, form); - addRoute("POST", "/form", MyController, formPost); - - // Session demo - addRoute("GET", "/session", MyController, session); - - // Exception example - addRoute("GET", "/exception", MyController, exception); - - // 403 demo - addRoute("GET", "/403", MyController, forbid); - - // File upload demo - addRoute("GET", "/upload", MyController, uploadForm); - addRoute("POST", "/upload", MyController, upload); - } -}; - -volatile static bool running = true; - -void handle_signal(int sig) -{ - if (running) { - cout << "Exiting..." << endl; - running = false; - } -} - -int main() -{ - int t; -#ifdef _MSC_VER - time_t ltime; - time(<ime); - t = ltime; -#else - t = time(NULL); -#endif - srand(t); - - signal(SIGINT, handle_signal); - - MyController myController; - Server server(8080); - server.registerController(&myController); - server.setOption("enable_directory_listing", "false"); - server.start(); - - cout << "Server started, routes:" << endl; - myController.dumpRoutes(); - - while (running) { -#ifdef WIN32 - Sleep(1000); -#else - sleep(1); -#endif - } - - server.stop(); - - return EXIT_SUCCESS; -} diff --git a/examples/post.c b/examples/post.c deleted file mode 100644 index 127160a405..0000000000 --- a/examples/post.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include "mongoose.h" - -static const char *html_form = - "POST example." - "
" - "Input 1:
" - "Input 2:
" - "" - "
"; - -static int handler(struct mg_connection *conn) { - char var1[500], var2[500]; - - if (strcmp(conn->uri, "/handle_post_request") == 0) { - // User has submitted a form, show submitted data and a variable value - // Parse form data. var1 and var2 are guaranteed to be NUL-terminated - mg_get_var(conn, "input_1", var1, sizeof(var1)); - mg_get_var(conn, "input_2", var2, sizeof(var2)); - - // Send reply to the client, showing submitted form values. - // POST data is in conn->content, data length is in conn->content_len - mg_send_header(conn, "Content-Type", "text/plain"); - mg_printf_data(conn, - "Submitted data: [%.*s]\n" - "Submitted data length: %d bytes\n" - "input_1: [%s]\n" - "input_2: [%s]\n", - conn->content_len, conn->content, - conn->content_len, var1, var2); - } else { - // Show HTML form. - mg_send_data(conn, html_form, strlen(html_form)); - } - - return 1; -} - -int main(void) { - struct mg_server *server = mg_create_server(NULL); - mg_set_option(server, "listening_port", "8080"); - mg_add_uri_handler(server, "/", handler); - printf("Starting on port %s\n", mg_get_option(server, "listening_port")); - for (;;) { - mg_poll_server(server, 1000); - } - mg_destroy_server(&server); - return 0; -} diff --git a/examples/qcomm.c b/examples/qcomm.c deleted file mode 100644 index ce25b9542c..0000000000 --- a/examples/qcomm.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include "core.h" - -static void iterate_callback(struct mg_connection *c, void *param) { - if (c->is_websocket) { - char buf[20]; - int len = snprintf(buf, sizeof(buf), "%d", * (int *) param); - mg_websocket_write(c, 1, buf, len); - } -} - -// This thread sends heartbeats to all websocket connections with 1s interval. -// The heartbeat message is simply an iteration counter. -static void *timer_thread(void *param) { - struct mg_server *server = (struct mg_server *) param; - int i; - - for (i = 0; i < 9999999; i++) { - sleep(1); - mg_iterate_over_connections(server, iterate_callback, &i); - } - - return NULL; -} - -// This handler is called for each incoming websocket frame, one or more -// times for connection lifetime. -static int handler(struct mg_connection *conn) { - static const char oops[] = "HTTP/1.0 200 OK\r\n\r\nwebsocket data expected\n"; - - if (!conn->is_websocket) { - mg_write(conn, oops, sizeof(oops) - 1); - return 1; - } - - mg_websocket_write(conn, 1, conn->content, conn->content_len); - - return conn->content_len == 4 && !memcmp(conn->content, "exit", 4); -} - -int main(int argc, char *argv[]) { - struct mg_server *server = mg_create_server(NULL); - - mg_set_option(server, "listening_port", "8080"); - mg_set_option(server, "document_root", argc > 1 ? argv[1] : "."); - mg_add_uri_handler(server, "/ws", handler); - mg_start_thread(timer_thread, server); - - printf("Started on port %s\n", mg_get_option(server, "listening_port")); - for (;;) { - mg_poll_server(server, 3000); - } - - mg_destroy_server(&server); - return 0; -} diff --git a/examples/ssl_cert.pem b/examples/ssl_cert.pem deleted file mode 100644 index f7e15a0e7a..0000000000 --- a/examples/ssl_cert.pem +++ /dev/null @@ -1,50 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH -hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC -EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1 -di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB -Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH -gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN -HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP -trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN -x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK -SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6 -+LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa -N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS -to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf -BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6 -WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy -Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG -+AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF -kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D -g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b -qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA -d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a -iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ -BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5 -ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy -hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG -A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4 -akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH -kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO -kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1 -N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf -uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB -oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+ -plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr -P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW -W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ -5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f -SEGI4JSxV56lYg== ------END CERTIFICATE----- ------BEGIN DH PARAMETERS----- -MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS -6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC ------END DH PARAMETERS----- diff --git a/examples/upload.c b/examples/upload.c deleted file mode 100644 index b83693952e..0000000000 --- a/examples/upload.c +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2004-2012 Sergey Lyubka -// This file is a part of mongoose project, http://github.com/valenok/mongoose - -#include -#include -#include "mongoose.h" - -static int index_html(struct mg_connection *conn) { - const char *data; - int data_len; - char var_name[100], file_name[100]; - - mg_printf_data(conn, "%s", - "Upload example." - "
" - "
" - "" - "
"); - - if (mg_parse_multipart(conn->content, conn->content_len, - var_name, sizeof(var_name), - file_name, sizeof(file_name), - &data, &data_len) > 0) { - - mg_printf_data(conn, "%s", "Uploaded file:
");
-    mg_send_data(conn, data, data_len);
-    mg_printf_data(conn, "%s", "/pre>");
-  }
-
-  mg_printf_data(conn, "%s", "");
-
-  return 1;
-}
-
-int main(void) {
-  struct mg_server *server;
-
-  // Create and configure the server
-  server = mg_create_server(NULL);
-  mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", index_html);
-
-  // Serve request. Hit Ctrl-C to terminate the program
-  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
-  for (;;) {
-    mg_poll_server(server, 1000);
-  }
-
-  // Cleanup, and free server instance
-  mg_destroy_server(&server);
-
-  return 0;
-}
diff --git a/examples/websocket.c b/examples/websocket.c
deleted file mode 100644
index 50039f00f9..0000000000
--- a/examples/websocket.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include 
-#include "mongoose.h"
-
-extern const char *find_embedded_file(const char *, size_t *);
-
-static int iterate_callback(struct mg_connection *c) {
-  if (c->is_websocket) {
-    char buf[20];
-    int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
-    mg_websocket_write(c, 1, buf, len);
-  }
-  return 1;
-}
-
-static int index_html(struct mg_connection *conn) {
-  size_t index_size;
-  const char *index_html = find_embedded_file("websocket.html", &index_size);
-
-  if (conn->is_websocket) {
-    // This handler is called for each incoming websocket frame, one or more
-    // times for connection lifetime.
-    // Echo websocket data back to the client.
-    mg_websocket_write(conn, 1, conn->content, conn->content_len);
-    return conn->content_len == 4 && !memcmp(conn->content, "exit", 4);
-  } else {
-    mg_send_header(conn, "Content-Type", "text/html");
-    mg_send_data(conn, index_html, index_size);
-    return 1;
-  }
-}
-
-int main(void) {
-  struct mg_server *server = mg_create_server(NULL);
-  unsigned int current_timer = 0, last_timer = 0;
-
-  mg_set_option(server, "listening_port", "8080");
-  mg_add_uri_handler(server, "/", index_html);
-
-  printf("Started on port %s\n", mg_get_option(server, "listening_port"));
-  for (;;) {
-    current_timer = mg_poll_server(server, 100);
-    if (current_timer - last_timer > 1) {
-      last_timer = current_timer;
-      mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
-    }
-  }
-
-  mg_destroy_server(&server);
-  return 0;
-}
diff --git a/examples/websocket.cpp b/examples/websocket.cpp
deleted file mode 100644
index 52ba3ec9c8..0000000000
--- a/examples/websocket.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifdef _MSC_VER
-#include 
-#include 
-#else
-#include 
-#endif
-#include 
-#include 
-#include 
-#include 
-
-using namespace std;
-using namespace Mongoose;
-
-class MyController : public WebController
-{
-    public: 
-        void webSocketReady(WebSocket *websocket)
-        {
-            cout << "Opening new websocket on " << websocket->getRequest().getUrl() << endl;
-            websocket->send("server ready");
-        
-            ostringstream oss;
-            oss << "Your id is: " << websocket->getId();
-            websocket->send(oss.str());
-        }
-
-        void webSocketData(WebSocket *websocket, string data)
-        {
-            cout << "[recv] " << data << endl;
-            websocket->send(data);
-
-            if (data == "exit") {
-                cout << "Client exiting" << endl;
-                websocket->close();
-            }
-        }
-};
-
-volatile static bool running = true;
-
-void handle_signal(int sig)
-{
-    if (running) {
-        cout << "Exiting..." << endl;
-        running = false;
-    }
-}
-
-int main()
-{
-	int t;
-#ifdef _MSC_VER
-    time_t ltime;
-	time(<ime);
-	t = ltime;
-#else
-	t = time(NULL);
-#endif
-	srand(t);
-
-    signal(SIGINT, handle_signal);
-
-    MyController myController;
-    Server server(8080);
-    server.registerController(&myController);
-    server.setOption("enable_directory_listing", "false");
-    server.setOption("document_root", "websocket_html_root");
-    server.start();
-
-    cout << "Server started, routes:" << endl;
-    myController.dumpRoutes();
-
-    while (running) {
-#ifdef WIN32
-		Sleep(10000);
-#else
-        sleep(10);
-#endif
-    }
-
-    server.stop();
-
-    return EXIT_SUCCESS;
-}
diff --git a/examples/websocket_html_root/index.html b/examples/websocket_html_root/index.html
deleted file mode 100644
index dd92db3e75..0000000000
--- a/examples/websocket_html_root/index.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-WebSocket Test
-
-
-

Mongoose WebSocket Test

- -
- This page code creates websocket to the URI "/websocket", - sends a message to it, waits for the reply, then sends an "exit" message. - Server must echo all messages back, and terminate the conversation after - receiving the "exit" message. -
- -
- diff --git a/examples/websocket_html_root/ws.html b/examples/websocket_html_root/ws.html deleted file mode 100644 index 680b9bdc95..0000000000 --- a/examples/websocket_html_root/ws.html +++ /dev/null @@ -1,42 +0,0 @@ - - -WebSocket Test - - -

Qualcomm WebSocket Test

- -
- diff --git a/lib/AbstractRequestCoprocessor.h b/lib/AbstractRequestCoprocessor.h new file mode 100644 index 0000000000..e304b852a2 --- /dev/null +++ b/lib/AbstractRequestCoprocessor.h @@ -0,0 +1,37 @@ +#ifndef _MONGOOSE_REQUEST_HANDLER_H +#define _MONGOOSE_REQUEST_HANDLER_H + +#include "Request.h" +#include "Response.h" +#include +#include + +namespace Mongoose +{ + class Server; + class Controller; + class AbstractRequestCoprocessor + { + public: + AbstractRequestCoprocessor(Controller *controller = nullptr, Server *server = nullptr): + mController(controller), + mServer(server) + { + } + + Controller* controller() const { return mController; } + void setController(Controller *controller) { mController = controller; } + + Server* server() const { return mServer; } + void setServer(Server *server) { mServer = server; } + + virtual bool preProcess(const std::shared_ptr& request, const std::shared_ptr& response) { return true; } + virtual bool postProcess(const std::shared_ptr& request, const std::shared_ptr& response) { return true; } + + protected: + Server *mServer; + Controller *mController; + }; +} + +#endif diff --git a/lib/Controller.cpp b/lib/Controller.cpp new file mode 100644 index 0000000000..d4adf3ce5a --- /dev/null +++ b/lib/Controller.cpp @@ -0,0 +1,164 @@ +#include +#include + +#include "Controller.h" +#include "Request.h" +#include "AbstractRequestCoprocessor.h" +#include "Response.h" + +namespace Mongoose +{ + Controller::Controller(Server *server): + mServer(server), + mPrefix("") + { + } + + void Controller::setup() + { + } + + Controller::~Controller() + { + mRoutes.clear(); + } + + bool Controller::preProcess(const std::shared_ptr &request, const std::shared_ptr &response) + { + bool result = true; + + for (int i = 0; i < mCoprocessors.size() && result; i++) + { + result = mCoprocessors[i]->preProcess(request, response); + } + + return result; + } + + bool Controller::process(const std::shared_ptr &request, const std::shared_ptr &response) + { + bool result = false; + +#ifdef ENABLE_REGEX_URL + std::map::iterator it; + for (it=routes.begin(); it!=routes.end(); it++) + { + if (request->match(it->first)) + { + result = it->second->process(request, response); + break; + } + } +#else + std::string key = request->method() + ":" + request->url(); + if (mRoutes.find(key) != mRoutes.end()) + { + result = mRoutes[key](request, response); + } +#endif + + return result; + } + + bool Controller::postProcess(const std::shared_ptr &request, const std::shared_ptr &response) + { + bool result = true; + + for (int i = 0; i < mCoprocessors.size() && result; i++) + { + result = mCoprocessors[i]->postProcess(request, response); + } + + return result; + } + + + bool Controller::handles(const std::string &method, const std::string &url) const + { + std::string key = method + ":" + url; + return (mRoutes.find(key) != mRoutes.end()); + } + + bool Controller::handleRequest(const std::shared_ptr &request, const std::shared_ptr &response) + { + return preProcess(request, response) + && process(request, response); + } + + Server *Controller::server() const + { + return mServer; + } + + void Controller::setServer(Server *server) + { + mServer = server; + } + + std::string Controller::prefix() const + { + return mPrefix; + } + + void Controller::setPrefix(const std::string &prefix) + { + mPrefix = prefix; + } + + void Controller::registerCoprocessor(AbstractRequestCoprocessor *preprocessor) + { + if (std::find(mCoprocessors.begin(), mCoprocessors.end(), preprocessor) == mCoprocessors.end()) + { + mCoprocessors.push_back(preprocessor); + } + } + + void Controller::deregisterCoprocessor(AbstractRequestCoprocessor *preprocessor) + { + auto position = std::find(mCoprocessors.begin(), mCoprocessors.end(), preprocessor); + + if (position != mCoprocessors.end()) + { + mCoprocessors.erase(position); + } + } + + void Controller::registerRoute(std::string httpMethod, std::string httpRoute, RequestHandler handler) + { + std::string key = httpMethod + ":" + mPrefix + httpRoute; + mRoutes[key] = handler; + mUrls.push_back(mPrefix + httpRoute); + } + + void Controller::deregisterRoute(std::string httpMethod, std::string httpRoute) + { + std::string key = httpMethod + ":" + mPrefix + httpRoute; + if (mRoutes.find(key) != mRoutes.end()) + { + mRoutes.erase(key); + } + + std::string url = mPrefix + httpRoute; + auto pos = std::find(mUrls.begin(), mUrls.end(), url); + + if (pos != mUrls.end()) + { + mUrls.erase(pos); + } + } + + void Controller::dumpRoutes() const + { + std::cout << "Routes:" << std::endl; + for (const auto &route : mRoutes) + { + std::cout << " " << route.first << std::endl; + } + + } + + std::vector Controller::urls() const + { + return mUrls; + } +} diff --git a/lib/Controller.h b/lib/Controller.h new file mode 100644 index 0000000000..4bafcf2379 --- /dev/null +++ b/lib/Controller.h @@ -0,0 +1,151 @@ +#ifndef _MONGOOSE_CONTROLLER_H +#define _MONGOOSE_CONTROLLER_H + +#include +#include +#include +#include + +//Helper define for binding class methods +#define addRoute(httpMethod, httpRoute, className, methodName) \ + registerRoute(httpMethod, httpRoute, std::bind(&className::methodName, this, std::placeholders::_1, std::placeholders::_2)) + +/** + * A controller is a module that respond to requests + * + * You can override the preProcess, process and postProcess to answer to + * the requests + */ +namespace Mongoose +{ + class AbstractRequestCoprocessor; + class Server; + class Request; + class Response; + + typedef std::function&, const std::shared_ptr&)> RequestHandler; + + class Controller + { + public: + Controller(Server *server = nullptr); + virtual ~Controller(); + + /** + * @brief setup - initializes routes, preprocessors etc.. + */ + virtual void setup(); + + /** + * @brief preProcess - Called before a request is processed + * @param request - the incoming request to preProcess + * @param response - the response + */ + virtual bool preProcess(const std::shared_ptr& request, const std::shared_ptr& response); + + /** + * @brief process - Called to process a request + * @param request the request + * @return response the response + */ + virtual bool process(const std::shared_ptr& request, const std::shared_ptr& response); + + /** + * @brief postProcess - Called after a request is processed + * @param request - the incoming request to postProcess + * @param response - the response - NOTE - that the response may not always be "valid" and do not attempt + * to use any response->send*() methods while post processing + * @return + */ + virtual bool postProcess(const std::shared_ptr& request, const std::shared_ptr& response); + + /** + * @brief handles - check if this controller can handle a http method + url + * @param method + * @param url + * @return true if this controller handles the http method + url combo + */ + virtual bool handles(const std::string& method, const std::string& url) const; + + /** + * @brief handleRequest - Handle a request, this will try to match the request, if this + * controller handles it, it will preProcess, process then postProcess it + * @param Request the incoming request + * @return Response the created response, or NULL if the controller + * does not handle this request + */ + virtual bool handleRequest(const std::shared_ptr& request, const std::shared_ptr& response); + + /** + * @brief server + * @return returns the reference to the server hosting this controller + */ + Server* server() const; + + /** + * @brief Sets the reference to the server hosting this controller + * @param Server the hosting server + */ + void setServer(Server *server); + + /** + * @brief prefix - gets the controller prefix + * @return the prefix of all urls for this controller + */ + std::string prefix() const; + + /** + * @brief Sets the controller prefix, for instance "/api" + * @param string the prefix of all urls for this controller + */ + void setPrefix(const std::string& prefix); + + /** + * @brief registerCoprocessor - a co processor can alter the request/response params before/after processing. + * Useful for keeping track of session variables, altering error pages, logging requests/responses etc. + * @param preprocessor + */ + void registerCoprocessor(AbstractRequestCoprocessor* preprocessor); + + /** + * @brief deregisterCoprocessor - unregister preprocessor from executing + * @param preprocessor + */ + void deregisterCoprocessor(AbstractRequestCoprocessor* preprocessor); + + /** + * @brief registerRoute + * @param httpMethod - GET, POST etc.. + * @param httpRoute - http endpoint pat . like /users + */ + void registerRoute(std::string httpMethod, std::string httpRoute, RequestHandler handler); + + /** + * @brief deregisterRoute + * @param httpMethod - GET, POST etc.. + * @param httpRoute - http endpoint pat . like /users + */ + void deregisterRoute(std::string httpMethod, std::string httpRoute); + + + /** + * @brief dumpRoutes - prints all http routes registered + */ + void dumpRoutes() const; + + /** + * @brief urls + * @return return all the urls handled by this controller + */ + std::vector urls() const; + + protected: + Server *mServer; + std::string mPrefix; + std::map mRoutes; + std::vector mUrls; + std::vector mCoprocessors; + }; +} + +#endif diff --git a/lib/Request.cpp b/lib/Request.cpp new file mode 100644 index 0000000000..b4e9b1566f --- /dev/null +++ b/lib/Request.cpp @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "Request.h" + +static const int MAX_QUERY_STRING_LENGTH = 32768; +static const int MAX_QUERY_STRING_ITEMS = 200; + +static int lowercase(const char *s) { + return tolower(* (const unsigned char *) s); +} + +static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { + int diff = 0; + + if (len > 0) + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + + return diff; +} + +static void mg_strlcpy(register char *dst, register const char *src, size_t n) { + for (; *src != '\0' && n > 1; n--) { + *dst++ = *src++; + } + *dst = '\0'; +} + +/* +static int mg_strcasecmp(const char *s1, const char *s2) { + int diff; + + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0'); + + return diff; +} +*/ + +static const char *mg_strcasestr(const char *big_str, const char *small_str) { + int i, big_len = strlen(big_str), small_len = strlen(small_str); + + for (i = 0; i <= big_len - small_len; i++) { + if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { + return big_str + i; + } + } + + return NULL; +} + +static int mg_get_cookie(const char *cookie_header, const char *var_name, + char *dst, size_t dst_size) { + const char *s, *p, *end; + int name_len, len = -1; + + if (dst == NULL || dst_size == 0) { + len = -2; + } else if (var_name == NULL || (s = cookie_header) == NULL) { + len = -1; + dst[0] = '\0'; + } else { + name_len = (int) strlen(var_name); + end = s + strlen(s); + dst[0] = '\0'; + + for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { + if (s[name_len] == '=') { + s += name_len + 1; + if ((p = strchr(s, ' ')) == NULL) + p = end; + if (p[-1] == ';') + p--; + if (*s == '"' && p[-1] == '"' && p > s + 1) { + s++; + p--; + } + if ((size_t) (p - s) < dst_size) { + len = p - s; + mg_strlcpy(dst, s, (size_t) len + 1); + } else { + len = -3; + } + break; + } + } + } + return len; +} + +namespace Mongoose +{ + Request::Request(struct mg_connection *connection, http_message *message, bool isMultipart): + mIsValid(true), + mIsMultipartRequest(isMultipart), + mConnection(connection) + { + mMethod = std::string(message->method.p, message->method.len); + mUrl = std::string(message->uri.p, message->uri.len); + mQuerystring = std::string(message->query_string.p, message->query_string.len); + + for(int i = 0; i < MG_MAX_HTTP_HEADERS && message->header_names[i].len != 0; i++) + { + mHeaders[message->header_names[i].p] = message->header_values[i].p; + } + + if(!mIsMultipartRequest) + { + mBody = std::string(message->body.p, message->body.len); + std::string data; + + if (mMethod == "GET") + { + data = mQuerystring; + } + else if (mMethod == "POST") + { + data = mBody; + } + else + { + //Nothing to do. + } + + char querystring[MAX_QUERY_STRING_LENGTH] = {0}; + strncpy(querystring, data.c_str(), MAX_QUERY_STRING_LENGTH); + + //TODO: Replace yuarel with https://github.com/bartgrantham/qs_parse + struct yuarel_param params[MAX_QUERY_STRING_ITEMS]; + int count = yuarel_parse_query(querystring, '&', params, MAX_QUERY_STRING_ITEMS); + for(int i = 0; i < count; i++) + { + struct yuarel_param p = params[i]; + int keyLen = strnlen(p.key, data.size()); + int valLen = strnlen(p.val, data.size() - keyLen); + std::string key = std::string(p.key, keyLen); + std::string value = std::string(p.val, valLen); + mVariables[key] = value; + } + } + + //FIXME: Parse cookies, and improve Cookie handling code with another actual Cookie class + } + + Request::~Request() + { + for (const auto& entity : mMultipartEntities) + { + //Remove all the temporary files created for this request + if (entity.filePath.size() > 0) + { + remove(entity.filePath.c_str()); + } + } + } + + bool Request::hasVariable(const std::string &key) const + { + return mVariables.find(key) != mVariables.end(); + } + + std::string Request::getVariable(const std::string &key, const std::string &fallback) const + { + std::string result = fallback; + + if (hasVariable(key)) + { + result = mVariables.at(key); + } + + return result; + } + + +#ifdef ENABLE_REGEX_URL + smatch Request::getMatches() + { + return matches; + } + + bool Request::match(string pattern) + { + key = method + ":" + url; + return regex_match(key, matches, regex(pattern)); + } +#endif + + + bool Request::hasCookie(const std::string &key) const + { + int i; + char dummy[10]; + + for (auto header: mHeaders) + { + if (header.first.find("Cookie") != std::string::npos) + { + if (mg_get_cookie(header.second.c_str(), key.c_str(), dummy, sizeof(dummy)) != -1) + { + return true; + } + } + } + + return false; + } + + std::string Request::getCookie(const std::string &key, const std::string &fallback) const + { + std::string output; + int i; + int size = 1024; + int ret; + char *buffer = new char[size]; + char dummy[10]; + const char *place = NULL; + + for (auto header: mHeaders) + { + if (header.first.find("Cookie") != std::string::npos) + { + if (mg_get_cookie(header.second.c_str(), key.c_str(), dummy, sizeof(dummy)) != -1) + { + place = header.second.c_str(); + break; + } + } + } + + if (place == NULL) { + return fallback; + } + + do { + ret = mg_get_cookie(place, key.c_str(), buffer, size); + + if (ret == -2) { + size *= 2; + delete[] buffer; + buffer = new char[size]; + } + } while (ret == -2); + + output = std::string(buffer); + delete[] buffer; + + return output; + } + + bool Request::hasHeader(const std::string &key) const + { + return mHeaders.find(key) != mHeaders.end(); + } + + std::string Request::getHeaderValue(const std::string& key) const + { + std::string result; + + if (hasHeader(key)) + { + result = mHeaders.at(key); + } + + return result; + } + + std::map Request::headers() const + { + return mHeaders; + } + + std::string Request::url() const + { + return mUrl; + } + + std::string Request::method() const + { + return mMethod; + } + + std::string Request::body() const + { + return mBody; + } + + bool Request::isValid() const + { + return mIsValid; + } + + void Request::setIsValid(bool value) + { + mIsValid = value; + } + + bool Request::isMultipartRequest() const + { + return mIsMultipartRequest; + } + + std::vector Request::multipartEntities() const + { + return mMultipartEntities; + } + + void Request::setMultipartEntities(const std::vector &entities) + { + mMultipartEntities = entities; + + //For easier access + for (const auto& entity : entities) + { + mVariables[entity.variableName] = entity.filePath.size() > 0 + ? entity.filePath + : entity.variableData; + } + } + +} diff --git a/lib/Request.h b/lib/Request.h new file mode 100644 index 0000000000..0bca17ce37 --- /dev/null +++ b/lib/Request.h @@ -0,0 +1,82 @@ +#ifndef _MONGOOSE_REQUEST_H +#define _MONGOOSE_REQUEST_H + +#include +#include +#include +#include +#ifdef ENABLE_REGEX_URL +#include +#endif + +struct mg_connection; +struct http_message; + +/** + * Request is a wrapper for the clients requests + */ +namespace Mongoose +{ +class Request +{ + +public: + + struct MultipartEntity + { + std::string variableName; + std::string variableData; + std::string fileName; + std::string filePath; + }; + + Request(struct mg_connection *connection, struct http_message* message, bool isMultipart = false); + ~Request(); + + bool hasVariable(const std::string& key) const; + std::string getVariable(const std::string& key, const std::string& fallback = "") const; + std::map variables() const { return mVariables; } + + bool hasCookie(const std::string& key) const; + std::string getCookie(const std::string& key, const std::string& fallback = "") const; + std::map cookies() const; + + bool hasHeader(const std::string& key) const; + std::string getHeaderValue(const std::string& key) const; + std::map headers() const; + + std::string url() const; + std::string method() const; + std::string body() const; + +#ifdef ENABLE_REGEX_URL + smatch getMatches(); + bool match(string pattern); +#endif + bool isValid() const; + void setIsValid(bool value); + + bool isMultipartRequest() const; + std::vector multipartEntities() const; + void setMultipartEntities(const std::vector& entities); + +private: + + std::atomic_bool mIsValid; + bool mIsMultipartRequest; + std::string mMethod; + std::string mUrl; + std::string mQuerystring; + std::string mBody; + + std::map mHeaders; + std::map mCookies; + + //For multipart form uploads + std::vector mMultipartEntities; + std::map mVariables; + struct mg_connection *mConnection; +}; +} + +#endif diff --git a/lib/Response.cpp b/lib/Response.cpp new file mode 100644 index 0000000000..d5f307b5d5 --- /dev/null +++ b/lib/Response.cpp @@ -0,0 +1,233 @@ +#include +#include +#include + +#include "Response.h" + + +namespace Mongoose +{ + Response::Response(mg_connection *connection): + mCode(HTTP_OK), + mConnection(connection), + mIsValid(true) + { + } + + Response::~Response() + { + } + + bool Response::hasHeader(const std::string &key) const + { + return mHeaders.find(key) != mHeaders.end(); + } + + std::string Response::getHeaderValue(const std::string &key) const + { + std::string result; + + if (hasHeader(key)) + { + result = mHeaders.at(key); + } + + return result; + } + + void Response::setHeader(const std::string &key, const std::string &value) + { + mHeaders[key] = value; + } + + std::map Response::headers() const + { + return mHeaders; + } + + void Response::setCookie(const std::string &key, const std::string &value) + { + std::ostringstream definition; + definition << key << "=" << value << "; path=/"; + setHeader("Set-cookie", definition.str()); + } + + int Response::code() const + { + return mCode; + } + + void Response::setCode(int code) + { + mCode = code; + } + + std::string Response::body() const + { + return mBody; + } + + void Response::setBody(const std::string &body) + { + mBody = body; + } + + bool Response::send() + { + if (!mIsValid) + return false; + + if (mHeaders.find("Content-Type") == mHeaders.end()) + { + mHeaders["Content-Type"] = "text/plain"; + } + + if (mHeaders.find("Content-Length") == mHeaders.end()) + { + std::ostringstream length; + length << mBody.size(); + setHeader("Content-Length", length.str()); + } + + std::string headers = headerString(); + + if(mIsValid) + { + mg_printf(mConnection, "%s", headers.c_str()); + mg_printf(mConnection, "%s", mBody.c_str()); + } + mConnection->flags |= MG_F_SEND_AND_CLOSE; + mIsValid = false; + return true; + } + + bool Response::send(int statusCode, const std::string &body) + { + if (mIsValid) + { + setCode(statusCode); + mBody = body; + return send(); + } + return false; + } + + bool Response::send(const std::string &body) + { + if (mIsValid) + { + mBody = body; + return send(); + } + return false; + } + + bool Response::sendHtml(const std::string &body) + { + if (mIsValid) + { + mHeaders["Content-Type"] = "text/html"; + mBody = body; + return send(); + } + return false; + } + + bool Response::sendFile(const std::string& path, const std::string& type) + { + std::ifstream in(path, std::ifstream::ate | std::ifstream::binary); + + if (!in.good() || !mIsValid) + return false; + + //TODO: make type optional + char buf[2048]; + size_t n; + size_t fileSize = in.tellg(); + + mHeaders["Content-Type"] = type; + mHeaders["Content-Length"] = fileSize; + + std::string headers = headerString(); + mg_printf(mConnection, "%s", headers.c_str()); + + FILE *fp = fopen(path.c_str(), "rb"); + + while (mIsValid && ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0)) + { + mg_send(mConnection, buf, n); + } + + fclose(fp); + mConnection->flags |= MG_F_SEND_AND_CLOSE; + mIsValid = false; + return true; + } + + bool Response::sendError(const std::string &message) + { + if (mIsValid) + { + setCode(HTTP_SERVER_ERROR); + mBody = "[500] Server internal error: " + message; + return send(); + } + + return false; + } + + bool Response::sendRedirect(const std::string &url, bool permanent) + { + bool result = false; + + if (mIsValid) + { + mg_http_send_redirect(mConnection, (permanent? 301 : 302), mg_mk_str(url.c_str()), mg_mk_str(NULL)); + mConnection->flags |= MG_F_SEND_AND_CLOSE; + mIsValid = false; + result = true; + } + + return true; + } + +#ifdef HAS_JSON11 + bool Response::sendJson(const json11::Json& body) + { + if (mIsValid) + { + setHeader("Content-Type", "application/json"); + mBody = body.dump(); + return send(); + } + + return false; + } +#endif + + bool Response::isValid() const + { + return mIsValid; + } + + void Response::setIsValid(bool value) + { + mIsValid = value; + } + + std::string Response::headerString() const + { + std::ostringstream data; + data << "HTTP/1.0 " << mCode << "\r\n"; + + for (const auto& header : mHeaders) + { + data << header.first << ": " << header.second << "\r\n"; + } + + data << "\r\n"; + + return data.str(); + } + +} diff --git a/lib/Response.h b/lib/Response.h new file mode 100644 index 0000000000..a909d807e6 --- /dev/null +++ b/lib/Response.h @@ -0,0 +1,74 @@ +#ifndef _MONGOOSE_RESPONSE_H +#define _MONGOOSE_RESPONSE_H + +#include +#include +#include + +struct mg_connection;; + +#ifdef HAS_JSON11 +#include +#endif + +#define HTTP_OK 200 +#define HTTP_NOT_FOUND 404 +#define HTTP_FORBIDDEN 403 +#define HTTP_SERVER_ERROR 500 + +/** + * A response to a request + */ +namespace Mongoose +{ +class Response +{ +public: + explicit Response(struct mg_connection *connection); + ~Response(); + + bool hasHeader(const std::string& key) const; + std::string getHeaderValue(const std::string& key) const; + void setHeader(const std::string& key, const std::string& value); + std::map headers() const; + + void setCookie(const std::string& key, const std::string& value); + + int code() const; + void setCode(int code); + + std::string body() const; + void setBody(const std::string& body); + + /** + * @brief sends the body as plain text and attempts to close the connection + * @return + */ + bool send(); + bool send(int statusCode, const std::string& body); + bool send(const std::string& body); + bool sendHtml(const std::string& body); + bool sendFile(const std::string& path, const std::string& type = "text/plain"); + bool sendError(const std::string& message); + bool sendRedirect(const std::string& url, bool permanent = false); + +#ifdef HAS_JSON11 + bool sendJson(const json11::Json &body); +#endif + + bool isValid() const; + void setIsValid(bool value); + +private: + + std::string headerString() const; + + int mCode; + std::map mHeaders; + std::string mBody; + struct mg_connection *mConnection; + std::atomic_bool mIsValid; +}; +} + +#endif diff --git a/lib/Server.cpp b/lib/Server.cpp new file mode 100644 index 0000000000..874eaa8aaf --- /dev/null +++ b/lib/Server.cpp @@ -0,0 +1,561 @@ +#include +#include +#include + +#include + +#include "Controller.h" +#include "Request.h" +#include "Response.h" +#include "Server.h" +#include "Utils.h" + +using namespace std; +using namespace Mongoose; + + +namespace Mongoose +{ + +struct MultipartData +{ + FILE *currentFilePointer{nullptr}; + size_t currentEntityBytesWritten{0}; + std::string currentVariableData; + std::vector multipartEntities; +}; + +void sendErrorNow(struct mg_connection* c, int errorCode, const char* errorString) +{ + mg_printf(c, "HTTP/1.0 %d %s\r\nContent-Length: 0\r\n\r\n", errorCode, errorString); + c->flags |= MG_F_SEND_AND_CLOSE; +} + +static struct mg_serve_http_opts sHttpOptions = {0}; + +/** + * @brief Server::ev_handler + * The main event handler - takes an incoming event + * and creates a http Request/Response pair from it and hands it off to Server. + * Upon a disocnnect event, it also deletes the Request/Response pair + * + */ +void Server::ev_handler(struct mg_connection *c, int ev, void *p, void *ud) +{ + Server *server = static_cast(c->mgr->user_data); + assert(server); + + if (server->requiresBasicAuthentication()) + { + if(ev == MG_EV_HTTP_REQUEST + || ev == MG_EV_HTTP_MULTIPART_REQUEST + || ev == MG_EV_HTTP_PART_BEGIN + || ev == MG_EV_HTTP_PART_DATA + || ev == MG_EV_HTTP_PART_END + || ev == MG_EV_HTTP_MULTIPART_REQUEST_END) + { + char username[256] = {0}; + char password[256] = {0}; + if (mg_get_http_basic_auth((struct http_message *) p, + username, 256, + password, 256) < 0 + || server->basicAuthUsername() != username + || server->basicAuthPassword() != password) + { + mg_printf(c, + "HTTP/1.0 401 Unauthorized\r\n" + "WWW-Authenticate: Basic realm=\"%s\"\r\n" + "Content-Length: 0\r\n\r\n", + server->authDomain().c_str()); + c->flags |= MG_F_SEND_AND_CLOSE; + return; + } + } + } + + switch (ev) + { + case MG_EV_HTTP_REQUEST: + { + struct http_message *hm = (struct http_message *) p; + c->user_data = NULL; + + //If server handles this request , let it. + if (server->handles(std::string(hm->method.p, hm->method.len), std::string(hm->uri.p, hm->uri.len))) + { + auto request = std::make_shared(c, hm); + auto response = std::make_shared(c); + + server->mCurrentRequests[c] = request; + server->mCurrentResponses[c] = response; + server->handleRequest(request, response); + } + else + { + //Else, simply let mongoose handle it - like a normal http server + mg_serve_http(c, (struct http_message *) p, sHttpOptions); + } + + break; + } + case MG_EV_HTTP_MULTIPART_REQUEST: + { + struct http_message *hm = (struct http_message *) p; + + if (server->handles(std::string(hm->method.p, hm->method.len), std::string(hm->uri.p, hm->uri.len))) + { + //Create a request/response pair now, because hm won't be available when we get + //MG_EV_HTTP_MULTIPART_REQUEST + auto request = std::make_shared(c, hm, true); + auto response = std::make_shared(c); + server->mCurrentRequests[c] = request; + server->mCurrentResponses[c] = response; + + c->user_data = new MultipartData(); + } + else + { + mg_printf(c, "%s", + "HTTP/1.0 404 Path not found\r\n" + "Content-Length: 0\r\n\r\n"); + c->user_data = NULL; + c->flags |= MG_F_SEND_AND_CLOSE; + return; + } + + break; + } + case MG_EV_HTTP_PART_BEGIN: + { + struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p; + struct MultipartData *data = (MultipartData*)c->user_data; + + if (data != NULL) + { + data->currentEntityBytesWritten = 0; + data->currentVariableData = ""; + + if (std::string(mp->file_name).size() > 0) + { + std::string tmpFile = server->tmpDir() + "/" + Utils::sanitizeFilename(mp->file_name); + data->currentFilePointer = fopen(tmpFile.c_str(), "wb"); + if (data->currentFilePointer == NULL) + { + sendErrorNow(c, 500, "Failed to open a file"); + delete data; + return; + } + } + } + else + { + sendErrorNow(c, 500, "Internal Server Error"); + return; + } + + break; + } + case MG_EV_HTTP_PART_DATA: + { + struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p; + struct MultipartData *data = (MultipartData*)c->user_data; + + if (data != NULL) + { + if (server->uploadSizeLimit() < data->currentEntityBytesWritten + mp->data.len) + { + sendErrorNow(c, 413, "Requested Entity Too Large"); + return; + } + + //If the uploaded data is a file, write it to a file. + if (std::string(mp->file_name).size() > 0) + { + if (fwrite(mp->data.p, 1, mp->data.len, data->currentFilePointer) != mp->data.len) + { + sendErrorNow(c, 500, "Failed to write a file"); + return; + } + } + else + { + data->currentVariableData += std::string(mp->data.p, mp->data.len); + } + + data->currentEntityBytesWritten += mp->data.len; + } + else + { + sendErrorNow(c, 500, "Internal Server Error"); + c->flags |= MG_F_SEND_AND_CLOSE; + return; + } + + break; + } + case MG_EV_HTTP_PART_END: + { + struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p; + struct MultipartData *data = (MultipartData*)c->user_data; + + if (data != NULL) + { + data->multipartEntities.push_back(Request::MultipartEntity()); + Request::MultipartEntity& entity = data->multipartEntities.back(); + entity.fileName = mp->file_name; + entity.variableName = mp->var_name; + + if (std::string(mp->file_name).size() > 0) + { + entity.filePath = server->tmpDir() + "/" + Utils::sanitizeFilename(entity.fileName); + fclose(data->currentFilePointer); + } + else + { + entity.variableData = data->currentVariableData; + } + } + else + { + sendErrorNow(c, 500, "Internal Server Error"); + return; + } + + + break; + } + case MG_EV_HTTP_MULTIPART_REQUEST_END: + { + struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p; + struct MultipartData *data = (MultipartData*)c->user_data; + + if (data != NULL) + { + auto request = server->mCurrentRequests[c]; + auto response = server->mCurrentResponses[c]; + assert(request); + assert(response); + + //Argument: mg_http_multipart_part, var_name and file_name are NULL, + //status = 0 means request was properly closed, < 0 means connection was terminated + if (mp->status == 0 + && server->handles(request->method(), request->url())) + { + request->setMultipartEntities(data->multipartEntities); + server->handleRequest(request, response); + } + + if (data != NULL) + { + delete data; + c->user_data = NULL; + } + } + else + { + sendErrorNow(c, 500, "Internal Server Error"); + return; + } + break; + } + case MG_EV_CLOSE: + { + if (server->mCurrentRequests.find(c) != server->mCurrentRequests.end()) + { + server->mCurrentRequests[c]->setIsValid(false); + server->mCurrentRequests.erase(c); + } + + if (server->mCurrentResponses.find(c) != server->mCurrentResponses.end()) + { + //To make sure any current mCurrentResponse.send() will fail + server->mCurrentResponses[c]->setIsValid(false); + server->mCurrentResponses.erase(c); + } + break; + } + } +} + +Server::Server(const char *address, const char *documentRoot): + mIsRunning(false), + mUploadSizeLimit(1024*1024*100), + mTmpDir("/tmp") +{ + memset(&sHttpOptions, 0, sizeof(sHttpOptions)); + setBindAddress(address); + setDocumentRoot(documentRoot); + setIndexFiles("index.html"); + setDirectoryListingEnabled(true); +} + +Server::~Server() +{ + stop(); +} + +bool Server::start() +{ + + if (!mIsRunning) + { + mManager = new (struct mg_mgr); + mg_mgr_init(mManager, this); + + mRequests = 0; + mStartTime = Utils::getTime(); + + mConnection = mg_bind(mManager, + mBindAddress.c_str(), + ev_handler, + this); + + if (mConnection) + { + mg_set_protocol_http_websocket(mConnection); + mIsRunning = true; + } + } + + if (mConnection == nullptr) + { + mg_mgr_free(mManager); + mIsRunning = false; + delete mManager; + mManager = nullptr; + std::cerr << "Error, unable to start server" << std::endl; + } + + return mIsRunning; +} + +void Server::poll(int duration) +{ + if (mIsRunning) + { + mg_mgr_poll(mManager, duration); + } +} + +void Server::stop() +{ + if (mIsRunning) + { + mg_mgr_free(mManager); + delete mManager; + mManager = nullptr; + mIsRunning = false; + } +} + +void Server::registerController(Controller *controller) +{ + controller->setServer(this); + controller->setup(); + mControllers.push_back(controller); +} + +void Server::deregisterController(Controller *c) +{ + auto it = std::find(mControllers.begin(), mControllers.end(), c); + + if (it != mControllers.end()) + { + mControllers.erase(it); + } +} + +bool Server::handleRequest(const std::shared_ptr &request, const std::shared_ptr &response) +{ + mRequests++; + + bool result = false; + + for (auto controller: mControllers) + { + if (controller->handles(request->method(), request->url())) + { + try + { + result = controller->handleRequest(request, response); + } + catch(...) + { + result = false; + } + + if (response->isValid() && !result) + { + response->sendError("Server error trying to handle the request"); + } + break; + } + } + + return result; +} + +bool Server::handles(const string &method, const string &url) +{ + for (auto controller: mControllers) + { + if (controller->handles(method, url)) + { + return true; + } + } + + return false; +} + +bool Server::allowMultipleClients() const +{ + return mAllowMultipleClients; +} + +void Server::setAllowMultipleClients(bool value) +{ + mAllowMultipleClients = value; +} + +size_t Server::uploadSizeLimit() const +{ + return mUploadSizeLimit; +} + +void Server::setUploadSizeLimit(size_t limit) +{ + mUploadSizeLimit = limit; +} + +std::string Server::bindAddress() const +{ + return mBindAddress; +} + +void Server::setBindAddress(const string &address) +{ + mBindAddress = address; +} + +bool Server::directoryListingEnabled() const +{ + return mEnableDirectoryListing != "no"; +} + +void Server::setDirectoryListingEnabled(bool value) +{ + mEnableDirectoryListing = value? "yes" : "no"; + sHttpOptions.enable_directory_listing = mEnableDirectoryListing.c_str(); +} + +string Server::documentRoot() const +{ + return mDocumentRoot; +} + +void Server::setDocumentRoot(const string &root) +{ + mDocumentRoot = root; + sHttpOptions.document_root = mDocumentRoot.c_str(); +} + +string Server::indexFiles() const +{ + return mIndexFiles; +} + +void Server::setIndexFiles(const string &files) +{ + mIndexFiles = files; + sHttpOptions.index_files = mIndexFiles.c_str(); +} + +string Server::authDomain() const +{ + return mAuthDomain; +} + +void Server::setAuthDomain(const string &domain) +{ + mAuthDomain = domain; + sHttpOptions.auth_domain = mAuthDomain.c_str(); +} + +string Server::basicAuthUsername() const +{ + return mBasicAuthUsername; +} + +void Server::setBasicAuthUsername(const string &user) +{ + mBasicAuthUsername = user; +} + +string Server::basicAuthPassword() const +{ + return mBasicAuthPassword; +} + +void Server::setBasicAuthPassword(const string &password) +{ + mBasicAuthPassword = password; +} + +bool Server::requiresBasicAuthentication() const +{ + return mBasicAuthUsername.size() > 0 && mBasicAuthPassword.size() > 0; +} + +string Server::ipAccessControlList() const +{ + return mIpAccessControlList; +} + +void Server::setIpAccessControlList(const string &acl) +{ + mIpAccessControlList = acl; + sHttpOptions.ip_acl = mIpAccessControlList.c_str(); +} + +string Server::hiddenFilePattern() const +{ + return mHiddenFilePattern; +} + +void Server::setHiddenFilePattern(const string &pattern) +{ + mHiddenFilePattern = pattern; + sHttpOptions.hidden_file_pattern = mHiddenFilePattern.c_str(); +} + +string Server::extraHeaders() const +{ + return mExtraHeaders; +} + +void Server::setExtraHeaders(const string &headers) +{ + mExtraHeaders = headers; +} + +string Server::tmpDir() const +{ + return mTmpDir; +} + +void Server::setTmpDir(const string &tmpDir) +{ + mTmpDir = tmpDir; +} + +void Server::printStats() +{ + int delta = Utils::getTime()-mStartTime; + + if (delta) + { + cout << "Requests: " << mRequests << ", Requests/s: " << (mRequests*1.0/delta) << endl; + } +} +} diff --git a/lib/Server.h b/lib/Server.h new file mode 100644 index 0000000000..21dfd37c8c --- /dev/null +++ b/lib/Server.h @@ -0,0 +1,157 @@ +#ifndef _MONGOOSE_SERVER_H +#define _MONGOOSE_SERVER_H + +#include +#include +#include +#include + +struct mg_connection; +struct mg_mgr; + +/** + * Wrapper for the Mongoose server + */ +namespace Mongoose +{ +class Controller; +class Request; +class Response; +class Server +{ +public: + /** + * @brief Constructs the Server + * @param bindAddress something like ":80", "0.0.0.0:80", etc... + * @param documentRoot Path to serve files from. + */ + Server(const char *bindAddress = ":8080", const char *documentRoot = "www"); + virtual ~Server(); + + /** + * @brief isRunning + * @return true if the server is running + */ + bool isRunning() const; + + /** + * @brief start the server if it is already not started + * @return true if the server is started + */ + bool start(); + + /** + * @brief poll the server for incoming connections/requests for duration + * @param duration - the number of milliseconds to poll the server for + */ + void poll(int duration); + + /** + * @brief stops the server if it is already running + */ + void stop(); + + /** + * @brief registerController - add another controller that provides custom http routes + */ + void registerController(Controller *c); + + /** + * @brief deregisterController - removes the controller from all processing + * @param c + */ + void deregisterController(Controller *c); + + /** + * @brief printStats prints basic statistics about the server to stdout + */ + void printStats(); + + /** + * @brief handles + * @param method + * @param url + * @return true if the seerver handles a URL + */ + bool handles(const std::string& method, const std::string& url); + + bool allowMultipleClients() const; + void setAllowMultipleClients(bool value); + + size_t uploadSizeLimit() const; + void setUploadSizeLimit(size_t limit); + + std::string bindAddress() const; + void setBindAddress(const std::string& address); + + bool directoryListingEnabled() const; + void setDirectoryListingEnabled(bool value); + + std::string documentRoot() const; + void setDocumentRoot(const std::string& root); + + std::string indexFiles() const; + void setIndexFiles(const std::string& files); + + std::string authDomain() const; + void setAuthDomain(const std::string& domain); + + std::string basicAuthUsername() const; + void setBasicAuthUsername(const std::string& user); + + std::string basicAuthPassword() const; + void setBasicAuthPassword(const std::string& password); + + bool requiresBasicAuthentication() const; + + std::string ipAccessControlList() const; + void setIpAccessControlList(const std::string& acl); + + std::string hiddenFilePattern() const; + void setHiddenFilePattern(const std::string& pattern); + + std::string extraHeaders() const; + void setExtraHeaders(const std::string& headers); + + std::string tmpDir() const; + void setTmpDir(const std::string& tmpDir); + +private: + static void ev_handler(struct mg_connection *c, int ev, void *p, void* ud); + + bool handleRequest(const std::shared_ptr& request, const std::shared_ptr& response); + + bool mIsRunning; + struct mg_mgr *mManager{nullptr}; + struct mg_connection *mConnection{nullptr}; + + //Internals + std::map> mCurrentRequests; + std::map> mCurrentResponses; + std::vector mControllers; + + // Bind options + std::string mBindAddress; + bool mAllowMultipleClients; + + // Http Options + std::string mDocumentRoot; + std::string mIndexFiles; + std::string mEnableDirectoryListing; + std::string mAuthDomain; + std::string mBasicAuthUsername; + std::string mBasicAuthPassword; + std::string mIpAccessControlList; + std::string mHiddenFilePattern; + std::string mExtraHeaders; + size_t mUploadSizeLimit; + + // Statistics + int mRequests{0}; + int mStartTime{0}; + + std::string mTmpDir; +}; +} + +#endif diff --git a/lib/Session.cpp b/lib/Session.cpp new file mode 100644 index 0000000000..e03fb416d0 --- /dev/null +++ b/lib/Session.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "Session.h" + +namespace Mongoose +{ +Session::Session() +{ + ping(); +} + +void Session::ping() +{ + mDate = time(NULL); +} + +void Session::setValue(const std::string &key, const std::string &value) +{ + mValues[key] = value; +} + +void Session::unsetValue(const std::string &key) +{ + mValues.erase(key); +} + +bool Session::hasValue(const std::string &key) const +{ + return mValues.find(key) != mValues.end(); +} + +std::string Session::value(const std::string &key, const std::string &fallback) const +{ + if (hasValue(key)) { + std::string value = mValues.at(key); + return value; + } else { + return fallback; + } +} + +int Session::getAge() +{ + return time(NULL)-mDate; +} +} diff --git a/lib/Session.h b/lib/Session.h new file mode 100644 index 0000000000..caa5a00fb9 --- /dev/null +++ b/lib/Session.h @@ -0,0 +1,62 @@ +#ifndef _MONGOOSE_SESSION_H +#define _MONGOOSE_SESSION_H + +#include +#include + +/** + * A session contains the user specific values + */ +namespace Mongoose +{ +class Session +{ +public: + Session(); + + /** + * @brief setValue Set the value of a session variable + * @param string the name of the variable + * @param string the value of the variable + */ + void setValue(const std::string& key, const std::string& value); + + /** + * @brief unsetValue Unset a session varaible + * @param string the variable name + */ + void unsetValue(const std::string& key); + + /** + * @brief hasValue Check if the given variable exists + * @param string the name of the variable + */ + bool hasValue(const std::string& key) const; + + /** + * @brief get Try to get the value for the given variable + * @param string the name of the variable + * @param string the fallback value + * @return string the value of the variable if it exists, fallback else + */ + std::string value(const std::string& key, const std::string& fallback = "") const; + + /** + * @brief Pings the session, this will update the creation date to now + * and "keeping it alive" + */ + void ping(); + + /** + * @brief Returns the session age, in seconds + * @return int the number of sessions since the last activity of the session + */ + int getAge(); + +protected: + std::map mValues; + int mDate; +}; +} + +#endif diff --git a/lib/Sessions.cpp b/lib/Sessions.cpp new file mode 100644 index 0000000000..6bfa943e69 --- /dev/null +++ b/lib/Sessions.cpp @@ -0,0 +1,89 @@ +#include + +#include "Sessions.h" +#include "Utils.h" + + +namespace Mongoose +{ + Sessions::Sessions(const std::string &key, Controller *controller, Server *server) + : + AbstractRequestCoprocessor(controller, server), + mGcDivisor(100), + mGcCounter(0), + mSessions(), + mKey(key) + { + } + + Sessions::~Sessions() + { + std::map::iterator it; + for (it=mSessions.begin(); it!=mSessions.end(); it++) + { + delete (*it).second; + } + } + + std::string Sessions::getId(const std::shared_ptr &request, const std::shared_ptr &response) + { + if (request->hasCookie(mKey)) { + return request->getCookie(mKey); + } else { + + std::string newCookie = Utils::randomAlphanumericString(30); + response->setCookie(mKey, newCookie); + return newCookie; + } + } + + Session* Sessions::get(const std::shared_ptr& request, const std::shared_ptr& response) + { + std::string id = getId(request, response); + Session *session = NULL; + + if (mSessions.find(id) != mSessions.end()) { + session = mSessions[id]; + } else { + session = new Session(); + mSessions[id] = session; + } + + return session; + } + + void Sessions::garbageCollect(int oldAge) + { + std::vector deleteList; + std::map::iterator it; + std::vector::iterator vit; + + for (it=mSessions.begin(); it!=mSessions.end(); it++) { + std::string name = (*it).first; + Session *session = (*it).second; + + if (session->getAge() > oldAge) { + delete session; + deleteList.push_back(name); + } + } + + for (vit=deleteList.begin(); vit!=deleteList.end(); vit++) { + mSessions.erase(*vit); + } + } + + bool Sessions::preProcess(const std::shared_ptr &request, const std::shared_ptr &response) + { + mGcCounter++; + + if (mGcCounter > mGcDivisor) + { + mGcCounter = 0; + garbageCollect(); + } + + get(request, response)->ping(); + return true; + } +} diff --git a/mongoose/Sessions.h b/lib/Sessions.h similarity index 53% rename from mongoose/Sessions.h rename to lib/Sessions.h index 089562fd37..7971d2cfff 100644 --- a/mongoose/Sessions.h +++ b/lib/Sessions.h @@ -1,22 +1,26 @@ #ifndef _MONGOOSE_SESSIONS_H #define _MONGOOSE_SESSIONS_H +#include + #include "Request.h" +#include "AbstractRequestCoprocessor.h" +#include "Response.h" #include "Session.h" -#include "Mutex.h" -using namespace std; /** * A session contains the user specific values */ namespace Mongoose { - class Sessions + class Sessions: public AbstractRequestCoprocessor { public: - Sessions(string key = "sessid"); - virtual ~Sessions(); + Sessions(const std::string& key = "sessid", + Controller *controller = nullptr, + Server *server = nullptr); + virtual ~Sessions(); /** * Gets the session ID of a certain request, @@ -28,7 +32,7 @@ namespace Mongoose * * @return string the session ID for this request */ - string getId(Request &request, Response &response); + std::string getId(const std::shared_ptr &request, const std::shared_ptr &response); /** * Gets the session for a certain request @@ -38,7 +42,7 @@ namespace Mongoose * * @return Session the session corresponding */ - Session &get(Request &request, Response &response); + Session* get(const std::shared_ptr &request, const std::shared_ptr &response); /** * Remove all the sessions older than age @@ -47,10 +51,17 @@ namespace Mongoose */ void garbageCollect(int oldAge = 3600); - protected: - map sessions; - string key; - Mutex mutex; + bool preProcess(const std::shared_ptr& request, const std::shared_ptr& response) override; + + unsigned int gcDivisor() const { return mGcDivisor; } + void setGcDivisor(unsigned int divisor) { mGcDivisor = divisor; } + + private: + + unsigned int mGcDivisor; + int mGcCounter; + std::map mSessions; + std::string mKey; }; } diff --git a/lib/Utils.cpp b/lib/Utils.cpp new file mode 100644 index 0000000000..0121622d94 --- /dev/null +++ b/lib/Utils.cpp @@ -0,0 +1,102 @@ +#include +#include +#include + +#ifndef WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#include "Utils.h" + +static char charset[] = "abcdeghijklmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +#define CHARSET_SIZE (sizeof(charset)/sizeof(char)) + +namespace Mongoose +{ + std::string Utils::htmlEntities(const std::string& data) + { + std::string buffer; + buffer.reserve(data.size()); + + for(size_t pos = 0; pos != data.size(); ++pos) { + switch(data[pos]) { + case '&': buffer.append("&"); break; + case '\"': buffer.append("""); break; + case '\'': buffer.append("'"); break; + case '<': buffer.append("<"); break; + case '>': buffer.append(">"); break; + default: buffer.append(1, data[pos]); break; + } + } + + return buffer; + } + + void Utils::sleep(int ms) + { +#ifdef WIN32 + Sleep(ms); +#else + usleep(1000 * ms); +#endif + } + + int Utils::getTime() + { +#ifdef WIN32 + time_t ltime; + time(<ime); + return ltime; +#else + return time(NULL); +#endif + } + + std::string Utils::randomAlphanumericString(int length) + { + std::string result; + result.reserve(length); + + for (int i=0; i < length; i++) { + result[i] = charset[rand()%CHARSET_SIZE]; + } + + return result; + } + + std::string Utils::sanitizeFilename(const std::string &filename) + { + //Only whitelist [0-9A-Za-z.] and replace non compliant characters with a "_" + std::string result; + result.reserve(filename.size()); + + for(int i = 0; i < filename.size() && result.size() < 256; i++) + { + auto c = filename[i]; + if(isalnum(c) || c == '.') + { + result.push_back(c); + } + else + { + if (result.back() != '_') + { + result.push_back('_'); + } + } + } + + if (result == "..") + { + result = Utils::randomAlphanumericString(30); + } + + return result; + } + +} diff --git a/lib/Utils.h b/lib/Utils.h new file mode 100644 index 0000000000..79498dc739 --- /dev/null +++ b/lib/Utils.h @@ -0,0 +1,20 @@ +#ifndef _MONGOOSE_UTILS_H +#define _MONGOOSE_UTILS_H + +#include + +namespace Mongoose +{ + class Utils + { + public: + static std::string htmlEntities(const std::string& data); + static void sleep(int ms); + static int getTime(); + static std::string randomAlphanumericString(int length = 30); + static std::string sanitizeFilename(const std::string& filename); + }; +} + +#endif + diff --git a/mongoose.c b/mongoose.c deleted file mode 100644 index ef52f3b280..0000000000 --- a/mongoose.c +++ /dev/null @@ -1,4138 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2014 Cesanta Software Limited -// All rights reserved -// -// This library is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see . -// -// You are free to use this library under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this library under a commercial -// license, as set out in . - -#undef UNICODE // Use ANSI WinAPI functions -#undef _UNICODE // Use multibyte encoding on Windows -#define _MBCS // Use multibyte encoding on Windows -#define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows -#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+ -#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h -#define _XOPEN_SOURCE 600 // For flockfile() on Linux -#define __STDC_FORMAT_MACROS // wants this for C++ -#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX -#define _LARGEFILE_SOURCE // Enable fseeko() and ftello() functions -#define _FILE_OFFSET_BITS 64 // Enable 64-bit file offsets - -#ifdef _MSC_VER -#pragma warning (disable : 4127) // FD_SET() emits warning, disable it -#pragma warning (disable : 4204) // missing c99 support -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include // For _beginthread -#include // For _lseeki64 -#include // For _mkdir -typedef int socklen_t; -typedef HANDLE pid_t; -typedef SOCKET sock_t; -typedef unsigned char uint8_t; -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; -typedef unsigned __int64 uint64_t; -typedef __int64 int64_t; -typedef CRITICAL_SECTION mutex_t; -typedef struct _stati64 file_stat_t; -#pragma comment(lib, "ws2_32.lib") -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#define INT64_FMT "I64d" -#define EINPROGRESS WSAEINPROGRESS -#define mutex_init(x) InitializeCriticalSection(x) -#define mutex_destroy(x) DeleteCriticalSection(x) -#define mutex_lock(x) EnterCriticalSection(x) -#define mutex_unlock(x) LeaveCriticalSection(x) -#define get_thread_id() ((unsigned long) GetCurrentThreadId()) -#define S_ISDIR(x) ((x) & _S_IFDIR) -#define sleep(x) Sleep((x) * 1000) -#define stat(x, y) mg_stat((x), (y)) -#define fopen(x, y) mg_fopen((x), (y)) -#define open(x, y) mg_open((x), (y)) -#define lseek(x, y, z) _lseeki64((x), (y), (z)) -#define mkdir(x, y) _mkdir(x) -#define to64(x) _atoi64(x) -#define flockfile(x) -#define funlockfile(x) -#ifndef va_copy -#define va_copy(x,y) x = y -#endif // MINGW #defines va_copy -#ifndef __func__ -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#endif -#else -#include -#include -#include -#include -#include -#include -#include -#include // For inet_pton() when USE_IPV6 is defined -#include -#include -#include -#define closesocket(x) close(x) -typedef int sock_t; -typedef pthread_mutex_t mutex_t; -typedef struct stat file_stat_t; -#define mutex_init(x) pthread_mutex_init(x, NULL) -#define mutex_destroy(x) pthread_mutex_destroy(x) -#define mutex_lock(x) pthread_mutex_lock(x) -#define mutex_unlock(x) pthread_mutex_unlock(x) -#define get_thread_id() ((unsigned long) pthread_self()) -#define INVALID_SOCKET ((sock_t) -1) -#define INT64_FMT PRId64 -#define to64(x) strtoll(x, NULL, 10) -#define __cdecl -#define O_BINARY 0 -#endif - -#ifdef USE_SSL -// Following define gets rid of openssl deprecation messages -#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 - -#ifdef USE_CYASSL -#include -#else -typedef struct ssl_ctx_st SSL_CTX; -typedef struct ssl_st SSL; -typedef struct ssl_method_st SSL_METHOD; - -extern void __cdecl SSL_free(SSL *); -extern int __cdecl SSL_accept(SSL *); -extern int __cdecl SSL_connect(SSL *); -extern int __cdecl SSL_read(SSL *, void *, int); -extern int __cdecl SSL_write(SSL *, const void *, int); -extern int __cdecl SSL_set_fd(SSL *, int); -extern SSL * __cdecl SSL_new(SSL_CTX *); -extern SSL_CTX * __cdecl SSL_CTX_new(SSL_METHOD *); -extern SSL_METHOD * __cdecl SSLv23_server_method(void); -extern int __cdecl SSL_library_init(void); -extern int __cdecl SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int); -extern int __cdecl SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int); -extern int __cdecl SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *); -extern void __cdecl SSL_CTX_free(SSL_CTX *); -#endif -#endif - -#include "mongoose.h" - -struct ll { struct ll *prev, *next; }; -#define LINKED_LIST_INIT(N) ((N)->next = (N)->prev = (N)) -#define LINKED_LIST_DECLARE_AND_INIT(H) struct ll H = { &H, &H } -#define LINKED_LIST_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N))) -#define LINKED_LIST_IS_EMPTY(N) ((N)->next == (N)) -#define LINKED_LIST_FOREACH(H,N,T) \ - for (N = (H)->next, T = (N)->next; N != (H); N = (T), T = (N)->next) -#define LINKED_LIST_ADD_TO_FRONT(H,N) do { ((H)->next)->prev = (N); \ - (N)->next = ((H)->next); (N)->prev = (H); (H)->next = (N); } while (0) -#define LINKED_LIST_ADD_TO_TAIL(H,N) do { ((H)->prev)->next = (N); \ - (N)->prev = ((H)->prev); (N)->next = (H); (H)->prev = (N); } while (0) -#define LINKED_LIST_REMOVE(N) do { ((N)->next)->prev = ((N)->prev); \ - ((N)->prev)->next = ((N)->next); LINKED_LIST_INIT(N); } while (0) - -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#define MAX_REQUEST_SIZE 16384 -#define IOBUF_SIZE 8192 -#define MAX_PATH_SIZE 8192 -#define LUA_SCRIPT_PATTERN "**.lp$" -#define CGI_ENVIRONMENT_SIZE 4096 -#define MAX_CGI_ENVIR_VARS 64 -#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI" -#define PASSWORDS_FILE_NAME ".htpasswd" - -#ifndef USE_WEBSOCKET_PING_INTERVAL -#define USE_WEBSOCKET_PING_INTERVAL 5 -#endif - -// Extra HTTP headers to send in every static file reply -#if !defined(USE_EXTRA_HTTP_HEADERS) -#define USE_EXTRA_HTTP_HEADERS "" -#endif - -#ifndef USE_POST_SIZE_LIMIT -#define USE_POST_SIZE_LIMIT 0 -#endif - -#ifndef USE_IDLE_TIMEOUT_SECONDS -#define USE_IDLE_TIMEOUT_SECONDS 30 -#endif - -#ifdef ENABLE_DBG -#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ - fflush(stdout); } while(0) -#else -#define DBG(x) -#endif - -#ifdef NO_FILESYSTEM -#define NO_AUTH -#define NO_CGI -#define NO_DAV -#define NO_DIRECTORY_LISTING -#define NO_LOGGING -#endif - -union socket_address { - struct sockaddr sa; - struct sockaddr_in sin; -#ifdef USE_IPV6 - struct sockaddr_in6 sin6; -#endif -}; - -struct vec { - const char *ptr; - int len; -}; - -struct uri_handler { - struct ll link; - char *uri; - mg_handler_t handler; -}; - -// For directory listing and WevDAV support -struct dir_entry { - struct connection *conn; - char *file_name; - file_stat_t st; -}; - -// NOTE(lsm): this enum shoulds be in sync with the config_options. -enum { - ACCESS_CONTROL_LIST, -#ifndef NO_FILESYSTEM - ACCESS_LOG_FILE, AUTH_DOMAIN, CGI_INTERPRETER, - CGI_PATTERN, DAV_AUTH_FILE, DOCUMENT_ROOT, ENABLE_DIRECTORY_LISTING, - BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD, -#endif - EXTRA_MIME_TYPES, -#ifndef NO_FILESYSTEM - GLOBAL_AUTH_FILE, -#endif - HIDE_FILES_PATTERN, -#ifndef NO_FILESYSTEM - INDEX_FILES, -#endif - LISTENING_PORT, -#ifndef _WIN32 - RUN_AS_USER, -#endif -#ifdef USE_SSL - SSL_CERTIFICATE, -#endif - URL_REWRITES, NUM_OPTIONS -}; - -static const char *static_config_options[] = { - "access_control_list", NULL, -#ifndef NO_FILESYSTEM - "access_log_file", NULL, - "auth_domain", "mydomain.com", - "cgi_interpreter", NULL, - "cgi_pattern", "**.cgi$|**.pl$|**.php$", - "dav_auth_file", NULL, - "document_root", NULL, - "enable_directory_listing", "yes", - "basic_auth_username", NULL, - "basic_auth_password", NULL, -#endif - "extra_mime_types", NULL, -#ifndef NO_FILESYSTEM - "global_auth_file", NULL, -#endif - "hide_files_patterns", NULL, -#ifndef NO_FILESYSTEM - "index_files","index.html,index.htm,index.cgi,index.php,index.lp", -#endif - "listening_port", NULL, -#ifndef _WIN32 - "run_as_user", NULL, -#endif -#ifdef USE_SSL - "ssl_certificate", NULL, -#endif - "url_rewrites", NULL, - NULL -}; - -struct mg_server { - sock_t listening_sock; - union socket_address lsa; // Listening socket address - struct ll active_connections; - struct ll uri_handlers; - mg_handler_t error_handler; - char *config_options[NUM_OPTIONS]; - void *server_data; - void *ssl_ctx; // SSL context - mg_handler_t do_i_handle; - sock_t ctl[2]; // Control socketpair. Used to wake up from select() call -}; - -// Expandable IO buffer -struct iobuf { - char *buf; // Buffer that holds the data - int size; // Buffer size - int len; // Number of bytes currently in a buffer -}; - -// Local endpoint representation -union endpoint { - int fd; // Opened regular local file - sock_t cgi_sock; // CGI socket - void *ssl; // SSL descriptor - struct uri_handler *uh; // URI handler user function -}; - -enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT }; -enum connection_flags { - CONN_CLOSE = 1, // Connection must be closed at the end of the poll - CONN_SPOOL_DONE = 2, // All data has been buffered for sending - CONN_SSL_HANDS_SHAKEN = 4, // SSL handshake has completed. Only for SSL - CONN_HEADERS_SENT = 8, // User callback has sent HTTP headers - CONN_BUFFER = 16, // CGI only. Holds data send until CGI prints - // all HTTP headers - CONN_CONNECTED = 32, // HTTP client has connected - CONN_LONG_RUNNING = 64 // Long-running URI handlers -}; - -struct connection { - struct mg_connection mg_conn; // XXX: Must be first - struct ll link; // Linkage to server->active_connections - struct mg_server *server; - sock_t client_sock; // Connected client - struct iobuf local_iobuf; - struct iobuf remote_iobuf; - union endpoint endpoint; - enum endpoint_type endpoint_type; - time_t birth_time; - time_t last_activity_time; - char *path_info; - char *request; - int64_t num_bytes_sent; // Total number of bytes sent - int64_t cl; // Reply content length, for Range support - int request_len; // Request length, including last \r\n after last header - int flags; // CONN_* flags: CONN_CLOSE, CONN_SPOOL_DONE, etc - void *ssl; // SSL descriptor - mg_handler_t handler; // Callback for HTTP client -}; - -static void close_local_endpoint(struct connection *conn); - -static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; -} static_builtin_mime_types[] = { - {".html", 5, "text/html"}, - {".htm", 4, "text/html"}, - {".shtm", 5, "text/html"}, - {".shtml", 6, "text/html"}, - {".css", 4, "text/css"}, - {".js", 3, "application/x-javascript"}, - {".ico", 4, "image/x-icon"}, - {".gif", 4, "image/gif"}, - {".jpg", 4, "image/jpeg"}, - {".jpeg", 5, "image/jpeg"}, - {".png", 4, "image/png"}, - {".svg", 4, "image/svg+xml"}, - {".txt", 4, "text/plain"}, - {".torrent", 8, "application/x-bittorrent"}, - {".wav", 4, "audio/x-wav"}, - {".mp3", 4, "audio/x-mp3"}, - {".mid", 4, "audio/mid"}, - {".m3u", 4, "audio/x-mpegurl"}, - {".ogg", 4, "application/ogg"}, - {".ram", 4, "audio/x-pn-realaudio"}, - {".xml", 4, "text/xml"}, - {".json", 5, "text/json"}, - {".xslt", 5, "application/xml"}, - {".xsl", 4, "application/xml"}, - {".ra", 3, "audio/x-pn-realaudio"}, - {".doc", 4, "application/msword"}, - {".exe", 4, "application/octet-stream"}, - {".zip", 4, "application/x-zip-compressed"}, - {".xls", 4, "application/excel"}, - {".tgz", 4, "application/x-tar-gz"}, - {".tar", 4, "application/x-tar"}, - {".gz", 3, "application/x-gunzip"}, - {".arj", 4, "application/x-arj-compressed"}, - {".rar", 4, "application/x-arj-compressed"}, - {".rtf", 4, "application/rtf"}, - {".pdf", 4, "application/pdf"}, - {".swf", 4, "application/x-shockwave-flash"}, - {".mpg", 4, "video/mpeg"}, - {".webm", 5, "video/webm"}, - {".mpeg", 5, "video/mpeg"}, - {".mov", 4, "video/quicktime"}, - {".mp4", 4, "video/mp4"}, - {".m4v", 4, "video/x-m4v"}, - {".asf", 4, "video/x-ms-asf"}, - {".avi", 4, "video/x-msvideo"}, - {".bmp", 4, "image/bmp"}, - {".ttf", 4, "application/x-font-ttf"}, - {NULL, 0, NULL} -}; - -#ifndef NO_THREADS -void *mg_start_thread(void *(*f)(void *), void *p) { -#ifdef _WIN32 - return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p); -#else - pthread_t thread_id = (pthread_t) 0; - pthread_attr_t attr; - - (void) pthread_attr_init(&attr); - (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#if USE_STACK_SIZE > 1 - // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 - (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); -#endif - - pthread_create(&thread_id, &attr, f, p); - pthread_attr_destroy(&attr); - - return (void *) thread_id; -#endif -} -#endif // NO_THREADS - -#ifdef _WIN32 -// Encode 'path' which is assumed UTF-8 string, into UNICODE string. -// wbuf and wbuf_len is a target buffer and its length. -static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { - char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; - - strncpy(buf, path, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - - // Trim trailing slashes - p = buf + strlen(buf) - 1; - while (p > buf && p[0] == '\\' || p[0] == '/') *p-- = '\0'; - //change_slashes_to_backslashes(buf); - - // Convert to Unicode and back. If doubly-converted string does not - // match the original, something is fishy, reject. - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); - WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - } -} - -static int mg_stat(const char *path, file_stat_t *st) { - wchar_t wpath[MAX_PATH_SIZE]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); - return _wstati64(wpath, st); -} - -static FILE *mg_fopen(const char *path, const char *mode) { - wchar_t wpath[MAX_PATH_SIZE], wmode[10]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - to_wchar(mode, wmode, ARRAY_SIZE(wmode)); - return _wfopen(wpath, wmode); -} - -static int mg_open(const char *path, int flag) { - wchar_t wpath[MAX_PATH_SIZE]; - to_wchar(path, wpath, ARRAY_SIZE(wpath)); - return _wopen(wpath, flag); -} -#endif - -static void set_close_on_exec(int fd) { -#ifdef _WIN32 - (void) SetHandleInformation((HANDLE) fd, HANDLE_FLAG_INHERIT, 0); -#else - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif -} - -static void set_non_blocking_mode(sock_t sock) { -#ifdef _WIN32 - unsigned long on = 1; - ioctlsocket(sock, FIONBIO, &on); -#else - int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); -#endif -} - -// A helper function for traversing a comma separated list of values. -// It returns a list pointer shifted to the next value, or NULL if the end -// of the list found. -// Value is stored in val vector. If value has form "x=y", then eq_val -// vector is initialized to point to the "y" part, and val vector length -// is adjusted to point only to "x". -static const char *next_option(const char *list, struct vec *val, - struct vec *eq_val) { - if (list == NULL || *list == '\0') { - // End of the list - list = NULL; - } else { - val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - // Comma found. Store length and shift the list ptr - val->len = list - val->ptr; - list++; - } else { - // This value is the last one - list = val->ptr + strlen(val->ptr); - val->len = list - val->ptr; - } - - if (eq_val != NULL) { - // Value has form "x=y", adjust pointers and lengths - // so that val points to "x", and eq_val points to "y". - eq_val->len = 0; - eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); - if (eq_val->ptr != NULL) { - eq_val->ptr++; // Skip over '=' character - eq_val->len = val->ptr + val->len - eq_val->ptr; - val->len = (eq_val->ptr - val->ptr) - 1; - } - } - } - - return list; -} - -static int spool(struct iobuf *io, const void *buf, int len) { - static const double mult = 1.2; - char *p = NULL; - int new_len = 0; - - assert(io->len >= 0); - assert(io->len <= io->size); - - //DBG(("1. %d %d %d", len, io->len, io->size)); - if (len <= 0) { - } else if ((new_len = io->len + len) < io->size) { - memcpy(io->buf + io->len, buf, len); - io->len = new_len; - } else if ((p = (char *) realloc(io->buf, (int) (new_len * mult))) != NULL) { - io->buf = p; - memcpy(io->buf + io->len, buf, len); - io->len = new_len; - io->size = (int) (new_len * mult); - } else { - len = 0; - } - //DBG(("%d %d %d", len, io->len, io->size)); - - return len; -} - -// Like snprintf(), but never returns negative value, or a value -// that is larger than a supplied buffer. -static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) { - int n; - if (buflen < 1) return 0; - n = vsnprintf(buf, buflen, fmt, ap); - if (n < 0) { - n = 0; - } else if (n >= (int) buflen) { - n = (int) buflen - 1; - } - buf[n] = '\0'; - return n; -} - -static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { - va_list ap; - int n; - va_start(ap, fmt); - n = mg_vsnprintf(buf, buflen, fmt, ap); - va_end(ap); - return n; -} - -// Check whether full request is buffered. Return: -// -1 if request is malformed -// 0 if request is not yet fully buffered -// >0 actual request length, including last \r\n\r\n -static int get_request_len(const char *s, int buf_len) { - const unsigned char *buf = (unsigned char *) s; - int i; - - for (i = 0; i < buf_len; i++) { - // Control characters are not allowed but >=128 are. - // Abort scan as soon as one malformed character is found. - if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { - return -1; - } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { - return i + 2; - } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && - buf[i + 2] == '\n') { - return i + 3; - } - } - - return 0; -} - -// Skip the characters until one of the delimiters characters found. -// 0-terminate resulting word. Skip the rest of the delimiters if any. -// Advance pointer to buffer to the next word. Return found 0-terminated word. -static char *skip(char **buf, const char *delimiters) { - char *p, *begin_word, *end_word, *end_delimiters; - - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); - end_delimiters = end_word + strspn(end_word, delimiters); - - for (p = end_word; p < end_delimiters; p++) { - *p = '\0'; - } - - *buf = end_delimiters; - - return begin_word; -} - -// Parse HTTP headers from the given buffer, advance buffer to the point -// where parsing stopped. -static void parse_http_headers(char **buf, struct mg_connection *ri) { - size_t i; - - for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) { - ri->http_headers[i].name = skip(buf, ": "); - ri->http_headers[i].value = skip(buf, "\r\n"); - if (ri->http_headers[i].name[0] == '\0') - break; - ri->num_headers = i + 1; - } -} - -static const char *status_code_to_str(int status_code) { - switch (status_code) { - case 200: return "OK"; - case 201: return "Created"; - case 204: return "No Content"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 304: return "Not Modified"; - case 400: return "Bad Request"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 409: return "Conflict"; - case 411: return "Length Required"; - case 413: return "Request Entity Too Large"; - case 415: return "Unsupported Media Type"; - case 423: return "Locked"; - case 500: return "Server Error"; - case 501: return "Not Implemented"; - default: return "Server Error"; - } -} - -static void send_http_error(struct connection *conn, int code, - const char *fmt, ...) { - const char *message = status_code_to_str(code); - const char *rewrites = conn->server->config_options[URL_REWRITES]; - char headers[200], body[200]; - struct vec a, b; - va_list ap; - int body_len, headers_len, match_code; - - conn->mg_conn.status_code = code; - - // Invoke error handler if it is set - if (conn->server->error_handler != NULL && - conn->server->error_handler(&conn->mg_conn)) { - close_local_endpoint(conn); - return; - } - - // Handle error code rewrites - while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { - if ((match_code = atoi(a.ptr)) > 0 && match_code == code) { - conn->mg_conn.status_code = 302; - mg_printf(&conn->mg_conn, "HTTP/1.1 %d Moved\r\n" - "Location: %.*s?code=%d&orig_uri=%s\r\n\r\n", - conn->mg_conn.status_code, b.len, b.ptr, code, - conn->mg_conn.uri); - close_local_endpoint(conn); - return; - } - } - - body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message); - if (fmt != NULL) { - body[body_len++] = '\n'; - va_start(ap, fmt); - body_len += mg_snprintf(body + body_len, sizeof(body) - body_len, fmt, ap); - va_end(ap); - } - if (code >= 300 && code <= 399) { - // 3xx errors do not have body - body_len = 0; - } - headers_len = mg_snprintf(headers, sizeof(headers), - "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n" - "Content-Type: text/plain\r\n\r\n", - code, message, body_len); - spool(&conn->remote_iobuf, headers, headers_len); - spool(&conn->remote_iobuf, body, body_len); - close_local_endpoint(conn); // This will write to the log file -} - -// Print message to buffer. If buffer is large enough to hold the message, -// return buffer. If buffer is to small, allocate large enough buffer on heap, -// and return allocated buffer. -static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { - va_list ap_copy; - int len; - - // Windows is not standard-compliant, and vsnprintf() returns -1 if - // buffer is too small. Also, older versions of msvcrt.dll do not have - // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - // Therefore, we make two passes: on first pass, get required message length. - // On second pass, actually print the message. - va_copy(ap_copy, ap); - len = vsnprintf(NULL, 0, fmt, ap_copy); - - if (len > (int) size && - (size = len + 1) > 0 && - (*buf = (char *) malloc(size)) == NULL) { - len = -1; // Allocation failed, mark failure - } else { - va_copy(ap_copy, ap); - vsnprintf(*buf, size, fmt, ap_copy); - } - - return len; -} - -static void write_chunk(struct connection *conn, const char *buf, int len) { - char chunk_size[50]; - int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len); - spool(&conn->remote_iobuf, chunk_size, n); - spool(&conn->remote_iobuf, buf, len); - spool(&conn->remote_iobuf, "\r\n", 2); -} - -int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap, - int chunked) { - char mem[IOBUF_SIZE], *buf = mem; - int len; - - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - if (chunked) { - write_chunk((struct connection *) conn, buf, len); - } else { - len = mg_write(conn, buf, (size_t) len); - } - } - if (buf != mem && buf != NULL) { - free(buf); - } - - return len; -} - -int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - int len; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(conn, fmt, ap, 0); - va_end(ap); - return len; -} - -static int mg_socketpair(sock_t sp[2]) { - struct sockaddr_in sa; - sock_t sock, ret = -1; - socklen_t len = sizeof(sa); - - sp[0] = sp[1] = INVALID_SOCKET; - - (void) memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(0); - sa.sin_addr.s_addr = htonl(0x7f000001); - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET && - !bind(sock, (struct sockaddr *) &sa, len) && - !listen(sock, 1) && - !getsockname(sock, (struct sockaddr *) &sa, &len) && - (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 && - !connect(sp[0], (struct sockaddr *) &sa, len) && - (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != INVALID_SOCKET) { - set_close_on_exec(sp[0]); - set_close_on_exec(sp[1]); - ret = 0; - } else { - if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); - if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); - sp[0] = sp[1] = INVALID_SOCKET; - } - closesocket(sock); - - return ret; -} - -static int is_error(int n) { - return n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN); -} - -#ifndef NO_CGI -#ifdef _WIN32 -struct threadparam { - sock_t s; - HANDLE hPipe; -}; - -static int wait_until_ready(sock_t sock, int for_read) { - fd_set set; - FD_ZERO(&set); - FD_SET(sock, &set); - select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0); - return 1; -} - -static void *push_to_stdin(void *arg) { - struct threadparam *tp = arg; - int n, sent, stop = 0; - DWORD k; - char buf[IOBUF_SIZE]; - - while (!stop && wait_until_ready(tp->s, 1) && - (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { - if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; - for (sent = 0; !stop && sent < n; sent += k) { - if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; - } - } - DBG(("%s", "FORWARED EVERYTHING TO CGI")); - CloseHandle(tp->hPipe); - free(tp); - _endthread(); - return NULL; -} - -static void *pull_from_stdout(void *arg) { - struct threadparam *tp = arg; - int k, stop = 0; - DWORD n, sent; - char buf[IOBUF_SIZE]; - - while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { - for (sent = 0; !stop && sent < n; sent += k) { - if (wait_until_ready(tp->s, 0) && - (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1; - } - } - DBG(("%s", "EOF FROM CGI")); - CloseHandle(tp->hPipe); - shutdown(tp->s, 2); // Without this, IO thread may get truncated data - closesocket(tp->s); - free(tp); - _endthread(); - return NULL; -} - -static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, - void *(*func)(void *)) { - struct threadparam *tp = malloc(sizeof(*tp)); - if (tp != NULL) { - tp->s = sock; - tp->hPipe = hPipe; - mg_start_thread(func, tp); - } -} - -static void abs_path(const char *utf8_path, char *abs_path, size_t len) { - wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE]; - to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); - GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); - WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); -} - -static pid_t start_process(char *interp, const char *cmd, const char *env, - const char *envp[], const char *dir, sock_t sock) { - STARTUPINFOW si = {0}; - PROCESS_INFORMATION pi = {0}; - HANDLE a[2], b[2], me = GetCurrentProcess(); - wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE]; - char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE], - cmdline[MAX_PATH_SIZE], *p; - DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; - FILE *fp; - - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - CreatePipe(&a[0], &a[1], NULL, 0); - CreatePipe(&b[0], &b[1], NULL, 0); - DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); - DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); - - if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) { - buf[0] = buf[1] = '\0'; - fgets(buf, sizeof(buf), fp); - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#' && buf[1] == '!') { - interp = buf + 2; - for (p = interp + strlen(interp); - isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0'; - } - fclose(fp); - } - - if (interp != NULL) { - abs_path(interp, buf4, ARRAY_SIZE(buf4)); - interp = buf4; - } - abs_path(dir, buf5, ARRAY_SIZE(buf5)); - to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); - mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"", - interp ? interp : "", interp ? " " : "", cmd); - to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); - - if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, - (void *) env, full_dir, &si, &pi) != 0) { - spawn_stdio_thread(sock, a[1], push_to_stdin); - spawn_stdio_thread(sock, b[0], pull_from_stdout); - } else { - CloseHandle(a[1]); - CloseHandle(b[0]); - closesocket(sock); - } - DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); - - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdInput); - CloseHandle(a[0]); - CloseHandle(b[1]); - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - - return pi.hProcess; -} -#else -static pid_t start_process(const char *interp, const char *cmd, const char *env, - const char *envp[], const char *dir, sock_t sock) { - char buf[500]; - pid_t pid = fork(); - (void) env; - - if (pid == 0) { - chdir(dir); - dup2(sock, 0); - dup2(sock, 1); - closesocket(sock); - - // After exec, all signal handlers are restored to their default values, - // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's - // implementation, SIGCHLD's handler will leave unchanged after exec - // if it was set to be ignored. Restore it to default action. - signal(SIGCHLD, SIG_DFL); - - if (interp == NULL) { - execle(cmd, cmd, NULL, envp); - } else { - execle(interp, interp, cmd, NULL, envp); - } - snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n" - "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp, - interp == NULL ? "" : " ", cmd, strerror(errno)); - send(1, buf, strlen(buf), 0); - exit(EXIT_FAILURE); // exec call failed - } - - return pid; -} -#endif // _WIN32 - -// This structure helps to create an environment for the spawned CGI program. -// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, -// last element must be NULL. -// However, on Windows there is a requirement that all these VARIABLE=VALUE\0 -// strings must reside in a contiguous buffer. The end of the buffer is -// marked by two '\0' characters. -// We satisfy both worlds: we create an envp array (which is vars), all -// entries are actually pointers inside buf. -struct cgi_env_block { - struct mg_connection *conn; - char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer - const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[] - int len; // Space taken - int nvars; // Number of variables in envp[] -}; - -// Append VARIABLE=VALUE\0 string to the buffer, and add a respective -// pointer into the vars array. -static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { - int n, space; - char *added; - va_list ap; - - // Calculate how much space is left in the buffer - space = sizeof(block->buf) - block->len - 2; - assert(space >= 0); - - // Make a pointer to the free space int the buffer - added = block->buf + block->len; - - // Copy VARIABLE=VALUE\0 string into the free space - va_start(ap, fmt); - n = mg_vsnprintf(added, (size_t) space, fmt, ap); - va_end(ap); - - // Make sure we do not overflow buffer and the envp array - if (n > 0 && n + 1 < space && - block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { - // Append a pointer to the added string into the envp array - block->vars[block->nvars++] = added; - // Bump up used length counter. Include \0 terminator - block->len += n + 1; - } - - return added; -} - -static void addenv2(struct cgi_env_block *blk, const char *name) { - const char *s; - if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s); -} - -static void prepare_cgi_environment(struct connection *conn, - const char *prog, - struct cgi_env_block *blk) { - struct mg_connection *ri = &conn->mg_conn; - const char *s, *slash; - char *p, **opts = conn->server->config_options; - int i; - - blk->len = blk->nvars = 0; - blk->conn = ri; - - addenv(blk, "SERVER_NAME=%s", opts[AUTH_DOMAIN]); - addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]); - addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]); - addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION); - - // Prepare the environment block - addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); - addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP - - // TODO(lsm): fix this for IPv6 case - //addenv(blk, "SERVER_PORT=%d", ri->remote_port); - - addenv(blk, "REQUEST_METHOD=%s", ri->request_method); - addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip); - addenv(blk, "REMOTE_PORT=%d", ri->remote_port); - addenv(blk, "REQUEST_URI=%s%s%s", ri->uri, - ri->query_string == NULL ? "" : "?", - ri->query_string == NULL ? "" : ri->query_string); - - // SCRIPT_NAME - if (conn->path_info != NULL) { - addenv(blk, "SCRIPT_NAME=%.*s", - (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri); - addenv(blk, "PATH_INFO=%s", conn->path_info); - } else { - s = strrchr(prog, '/'); - slash = strrchr(ri->uri, '/'); - addenv(blk, "SCRIPT_NAME=%.*s%s", - slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri, - s == NULL ? prog : s); - } - - addenv(blk, "SCRIPT_FILENAME=%s", prog); - addenv(blk, "PATH_TRANSLATED=%s", prog); - addenv(blk, "HTTPS=%s", conn->ssl != NULL ? "on" : "off"); - - if ((s = mg_get_header(ri, "Content-Type")) != NULL) - addenv(blk, "CONTENT_TYPE=%s", s); - - if (ri->query_string != NULL) - addenv(blk, "QUERY_STRING=%s", ri->query_string); - - if ((s = mg_get_header(ri, "Content-Length")) != NULL) - addenv(blk, "CONTENT_LENGTH=%s", s); - - addenv2(blk, "PATH"); - addenv2(blk, "PERLLIB"); - addenv2(blk, ENV_EXPORT_TO_CGI); - -#if defined(_WIN32) - addenv2(blk, "COMSPEC"); - addenv2(blk, "SYSTEMROOT"); - addenv2(blk, "SystemDrive"); - addenv2(blk, "ProgramFiles"); - addenv2(blk, "ProgramFiles(x86)"); - addenv2(blk, "CommonProgramFiles(x86)"); -#else - addenv2(blk, "LD_LIBRARY_PATH"); -#endif // _WIN32 - - // Add all headers as HTTP_* variables - for (i = 0; i < ri->num_headers; i++) { - p = addenv(blk, "HTTP_%s=%s", - ri->http_headers[i].name, ri->http_headers[i].value); - - // Convert variable name into uppercase, and change - to _ - for (; *p != '=' && *p != '\0'; p++) { - if (*p == '-') - *p = '_'; - *p = (char) toupper(* (unsigned char *) p); - } - } - - blk->vars[blk->nvars++] = NULL; - blk->buf[blk->len++] = '\0'; - - assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); - assert(blk->len > 0); - assert(blk->len < (int) sizeof(blk->buf)); -} - -static const char cgi_status[] = "HTTP/1.1 200 OK\r\n"; - -static void open_cgi_endpoint(struct connection *conn, const char *prog) { - struct cgi_env_block blk; - char dir[MAX_PATH_SIZE], *p = NULL; - sock_t fds[2]; - - prepare_cgi_environment(conn, prog, &blk); - // CGI must be executed in its own directory. 'dir' must point to the - // directory containing executable program, 'p' must point to the - // executable program name relative to 'dir'. - mg_snprintf(dir, sizeof(dir), "%s", prog); - if ((p = strrchr(dir, '/')) != NULL) { - *p++ = '\0'; - } else { - dir[0] = '.', dir[1] = '\0'; - p = (char *) prog; - } - - // Try to create socketpair in a loop until success. mg_socketpair() - // can be interrupted by a signal and fail. - // TODO(lsm): use sigaction to restart interrupted syscall - do { - mg_socketpair(fds); - } while (fds[0] == INVALID_SOCKET); - - if (start_process(conn->server->config_options[CGI_INTERPRETER], - prog, blk.buf, blk.vars, dir, fds[1]) > 0) { - conn->endpoint_type = EP_CGI; - conn->endpoint.cgi_sock = fds[0]; - spool(&conn->remote_iobuf, cgi_status, sizeof(cgi_status) - 1); - conn->flags |= CONN_BUFFER; - } else { - closesocket(fds[0]); - send_http_error(conn, 500, "start_process(%s) failed", prog); - } - -#ifndef _WIN32 - closesocket(fds[1]); // On Windows, CGI stdio thread closes that socket -#endif -} - -static void read_from_cgi(struct connection *conn) { - struct iobuf *io = &conn->remote_iobuf; - char buf[IOBUF_SIZE], buf2[sizeof(buf)], *s = buf2; - const char *status = "500"; - struct mg_connection c; - int len, s_len = sizeof(cgi_status) - 1, - n = recv(conn->endpoint.cgi_sock, buf, sizeof(buf), 0); - - DBG(("%p %d", conn, n)); - if (is_error(n)) { - close_local_endpoint(conn); - } else if (n > 0) { - spool(&conn->remote_iobuf, buf, n); - if (conn->flags & CONN_BUFFER) { - len = get_request_len(io->buf + s_len, io->len - s_len); - if (len == 0) return; - if (len > 0) { - memset(&c, 0, sizeof(c)); - memcpy(buf2, io->buf + s_len, len); - buf2[len - 1] = '\0'; - parse_http_headers(&s, &c); - if (mg_get_header(&c, "Location") != NULL) { - status = "302"; - } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) { - status = "200"; - } - } - memcpy(io->buf + 9, status, 3); - conn->flags &= ~CONN_BUFFER; - } - } -} - -static void forward_post_data(struct connection *conn) { - struct iobuf *io = &conn->local_iobuf; - int n = send(conn->endpoint.cgi_sock, io->buf, io->len, 0); - if (n > 0) { - memmove(io->buf, io->buf + n, io->len - n); - io->len -= n; - } -} -#endif // !NO_CGI - -// 'sa' must be an initialized address to bind to -static sock_t open_listening_socket(union socket_address *sa) { - sock_t on = 1, sock = INVALID_SOCKET; - - if ((sock = socket(sa->sa.sa_family, SOCK_STREAM, 6)) == INVALID_SOCKET || - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) || - bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ? - sizeof(sa->sin) : sizeof(sa->sa)) != 0 || - listen(sock, SOMAXCONN) != 0) { - closesocket(sock); - sock = INVALID_SOCKET; - } - - return sock; -} - -static char *mg_strdup(const char *str) { - char *copy = (char *) malloc(strlen(str) + 1); - if (copy != NULL) { - strcpy(copy, str); - } - return copy; -} - -static int isbyte(int n) { - return n >= 0 && n <= 255; -} - -static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { - int n, a, b, c, d, slash = 32, len = 0; - - if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || - sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && - isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && - slash >= 0 && slash < 33) { - len = n; - *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d; - *mask = slash ? 0xffffffffU << (32 - slash) : 0; - } - - return len; -} - -// Verify given socket address against the ACL. -// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. -static int check_acl(const char *acl, uint32_t remote_ip) { - int allowed, flag; - uint32_t net, mask; - struct vec vec; - - // If any ACL is set, deny by default - allowed = acl == NULL ? '+' : '-'; - - while ((acl = next_option(acl, &vec, NULL)) != NULL) { - flag = vec.ptr[0]; - if ((flag != '+' && flag != '-') || - parse_net(&vec.ptr[1], &net, &mask) == 0) { - return -1; - } - - if (net == (remote_ip & mask)) { - allowed = flag; - } - } - - return allowed == '+'; -} - -static void sockaddr_to_string(char *buf, size_t len, - const union socket_address *usa) { - buf[0] = '\0'; -#if defined(USE_IPV6) - inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ? - (void *) &usa->sin.sin_addr : - (void *) &usa->sin6.sin6_addr, buf, len); -#elif defined(_WIN32) - // Only Windoze Vista (and newer) have inet_ntop() - strncpy(buf, inet_ntoa(usa->sin.sin_addr), len); -#else - inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len); -#endif -} - -static struct connection *accept_new_connection(struct mg_server *server) { - union socket_address sa; - socklen_t len = sizeof(sa); - sock_t sock = INVALID_SOCKET; - struct connection *conn = NULL; - - // NOTE(lsm): on Windows, sock is always > FD_SETSIZE - if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) { - } else if (!check_acl(server->config_options[ACCESS_CONTROL_LIST], - ntohl(* (uint32_t *) &sa.sin.sin_addr))) { - // NOTE(lsm): check_acl doesn't work for IPv6 - closesocket(sock); - } else if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) { - closesocket(sock); -#ifdef USE_SSL - } else if (server->ssl_ctx != NULL && - ((conn->ssl = SSL_new(server->ssl_ctx)) == NULL || - SSL_set_fd(conn->ssl, sock) != 1)) { - DBG(("SSL error")); - closesocket(sock); - free(conn); - conn = NULL; -#endif - } else { - set_close_on_exec(sock); - set_non_blocking_mode(sock); - conn->server = server; - conn->client_sock = sock; - sockaddr_to_string(conn->mg_conn.remote_ip, - sizeof(conn->mg_conn.remote_ip), &sa); - conn->mg_conn.remote_port = ntohs(sa.sin.sin_port); - conn->mg_conn.server_param = server->server_data; - LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link); - DBG(("added conn %p", conn)); - } - - return conn; -} - -static void close_conn(struct connection *conn) { - DBG(("%p %d %d", conn, conn->flags, conn->endpoint_type)); - LINKED_LIST_REMOVE(&conn->link); - closesocket(conn->client_sock); - free(conn->request); // It's OK to free(NULL), ditto below - free(conn->path_info); - free(conn->remote_iobuf.buf); - free(conn->local_iobuf.buf); -#ifdef USE_SSL - if (conn->ssl != NULL) SSL_free(conn->ssl); -#endif - free(conn); -} - -// Protect against directory disclosure attack by removing '..', -// excessive '/' and '\' characters -static void remove_double_dots_and_double_slashes(char *s) { - char *p = s; - - while (*s != '\0') { - *p++ = *s++; - if (s[-1] == '/' || s[-1] == '\\') { - // Skip all following slashes, backslashes and double-dots - while (s[0] != '\0') { - if (s[0] == '/' || s[0] == '\\') { s++; } - else if (s[0] == '.' && s[1] == '.') { s += 2; } - else { break; } - } - } - } - *p = '\0'; -} - -int mg_url_decode(const char *src, int src_len, char *dst, - int dst_len, int is_form_url_encoded) { - int i, j, a, b; -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%' && i < src_len - 2 && - isxdigit(* (const unsigned char *) (src + i + 1)) && - isxdigit(* (const unsigned char *) (src + i + 2))) { - a = tolower(* (const unsigned char *) (src + i + 1)); - b = tolower(* (const unsigned char *) (src + i + 2)); - dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; // Null-terminate the destination - - return i >= src_len ? j : -1; -} - -static int is_valid_http_method(const char *method) { - return !strcmp(method, "GET") || !strcmp(method, "POST") || - !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || - !strcmp(method, "PUT") || !strcmp(method, "DELETE") || - !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND") - || !strcmp(method, "MKCOL"); -} - -// Parse HTTP request, fill in mg_request structure. -// This function modifies the buffer by NUL-terminating -// HTTP request components, header names and header values. -// Note that len must point to the last \n of HTTP headers. -static int parse_http_message(char *buf, int len, struct mg_connection *ri) { - int is_request, n; - - // Reset the connection. Make sure that we don't touch fields that are - // set elsewhere: remote_ip, remote_port, server_param - ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL; - ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0; - - buf[len - 1] = '\0'; - - // RFC says that all initial whitespaces should be ingored - while (*buf != '\0' && isspace(* (unsigned char *) buf)) { - buf++; - } - ri->request_method = skip(&buf, " "); - ri->uri = skip(&buf, " "); - ri->http_version = skip(&buf, "\r\n"); - - // HTTP message could be either HTTP request or HTTP response, e.g. - // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." - is_request = is_valid_http_method(ri->request_method); - if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || - (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { - len = -1; - } else { - if (is_request) { - ri->http_version += 5; - } - parse_http_headers(&buf, ri); - - if ((ri->query_string = strchr(ri->uri, '?')) != NULL) { - *(char *) ri->query_string++ = '\0'; - } - n = (int) strlen(ri->uri); - mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0); - remove_double_dots_and_double_slashes((char *) ri->uri); - } - - return len; -} - -static int lowercase(const char *s) { - return tolower(* (const unsigned char *) s); -} - -static int mg_strcasecmp(const char *s1, const char *s2) { - int diff; - - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); - - return diff; -} - -static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; - - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - - return diff; -} - -// Return HTTP header value, or NULL if not found. -const char *mg_get_header(const struct mg_connection *ri, const char *s) { - int i; - - for (i = 0; i < ri->num_headers; i++) - if (!mg_strcasecmp(s, ri->http_headers[i].name)) - return ri->http_headers[i].value; - - return NULL; -} - -#ifndef NO_FILESYSTEM -// Perform case-insensitive match of string against pattern -static int match_prefix(const char *pattern, int pattern_len, const char *str) { - const char *or_str; - int i, j, len, res; - - if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { - res = match_prefix(pattern, or_str - pattern, str); - return res > 0 ? res : - match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str); - } - - i = j = 0; - res = -1; - for (; i < pattern_len; i++, j++) { - if (pattern[i] == '?' && str[j] != '\0') { - continue; - } else if (pattern[i] == '$') { - return str[j] == '\0' ? j : -1; - } else if (pattern[i] == '*') { - i++; - if (pattern[i] == '*') { - i++; - len = (int) strlen(str + j); - } else { - len = (int) strcspn(str + j, "/"); - } - if (i == pattern_len) { - return j + len; - } - do { - res = match_prefix(pattern + i, pattern_len - i, str + j + len); - } while (res == -1 && len-- > 0); - return res == -1 ? -1 : j + res + len; - } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { - return -1; - } - } - return j; -} - -// Return 1 if real file has been found, 0 otherwise -static int convert_uri_to_file_name(struct connection *conn, char *buf, - size_t buf_len, file_stat_t *st) { - struct vec a, b; - const char *rewrites = conn->server->config_options[URL_REWRITES], - *root = conn->server->config_options[DOCUMENT_ROOT], - *cgi_pat = conn->server->config_options[CGI_PATTERN], - *uri = conn->mg_conn.uri; - char *p; - int match_len; - - // No filesystem access - if (root == NULL) return 0; - - // Handle URL rewrites - mg_snprintf(buf, buf_len, "%s%s", root, uri); - while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { - if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { - mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len); - break; - } - } - - if (stat(buf, st) == 0) return 1; - -#ifndef NO_CGI - // Support PATH_INFO for CGI scripts. - for (p = buf + strlen(root) + 2; *p != '\0'; p++) { - if (*p == '/') { - *p = '\0'; - if (match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 && !stat(buf, st)) { - DBG(("!!!! [%s]", buf)); - *p = '/'; - conn->path_info = mg_strdup(p); - *p = '\0'; - return 1; - } - *p = '/'; - } - } -#endif - - return 0; -} -#endif // NO_FILESYSTEM - -static int should_keep_alive(const struct mg_connection *conn) { - const char *method = conn->request_method; - const char *http_version = conn->http_version; - const char *header = mg_get_header(conn, "Connection"); - return method != NULL && (!strcmp(method, "GET") || - ((struct connection *) conn)->endpoint_type == EP_USER) && - ((header != NULL && !mg_strcasecmp(header, "keep-alive")) || - (header == NULL && http_version && !strcmp(http_version, "1.1"))); -} - -int mg_write(struct mg_connection *c, const void *buf, int len) { - return spool(&((struct connection *) c)->remote_iobuf, buf, len); -} - -void mg_send_status(struct mg_connection *c, int status) { - if (c->status_code == 0) { - c->status_code = status; - mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status)); - } -} - -void mg_send_header(struct mg_connection *c, const char *name, const char *v) { - if (c->status_code == 0) { - c->status_code = 200; - mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200)); - } - mg_printf(c, "%s: %s\r\n", name, v); -} - -static void terminate_headers(struct mg_connection *c) { - struct connection *conn = (struct connection *) c; - if (!(conn->flags & CONN_HEADERS_SENT)) { - mg_send_header(c, "Transfer-Encoding", "chunked"); - mg_write(c, "\r\n", 2); - conn->flags |= CONN_HEADERS_SENT; - } -} - -void mg_send_data(struct mg_connection *c, const void *data, int data_len) { - terminate_headers(c); - write_chunk((struct connection *) c, (const char *) data, data_len); -} - -void mg_printf_data(struct mg_connection *c, const char *fmt, ...) { - va_list ap; - - terminate_headers(c); - - va_start(ap, fmt); - mg_vprintf(c, fmt, ap, 1); - va_end(ap); -} - -#if !defined(NO_WEBSOCKET) || !defined(NO_AUTH) -static int is_big_endian(void) { - static const int n = 1; - return ((char *) &n)[0] == 0; -} - -static void base64_encode(const unsigned char *src, int src_len, char *dst) { - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - -#endif - -#ifndef NO_WEBSOCKET -// START OF SHA-1 code -// Copyright(c) By Steve Reid -#define SHA1HANDSOFF -#if defined(__sun) -#include "solarisfixes.h" -#endif - -union char64long16 { unsigned char c[64]; uint32_t l[16]; }; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN - if (!is_big_endian()) { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - // Erase working structures. The order of operations is important, - // used to ensure that compiler doesn't optimize those out. - memset(block, 0, sizeof(block)); - a = b = c = d = e = block[0].l[0]; -} - -static void SHA1Init(SHA1_CTX* context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -static void SHA1Update(SHA1_CTX* context, const unsigned char* data, - uint32_t len) { - uint32_t i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1]++; - context->count[1] += (len>>29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); - } - c = 0200; - SHA1Update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - SHA1Update(context, &c, 1); - } - SHA1Update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} -// END OF SHA1 CODE - -static void send_websocket_handshake(struct mg_connection *conn, - const char *key) { - static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - char buf[500], sha[20], b64_sha[sizeof(sha) * 2]; - SHA1_CTX sha_ctx; - - mg_snprintf(buf, sizeof(buf), "%s%s", key, magic); - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); - SHA1Final((unsigned char *) sha, &sha_ctx); - base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); - mg_snprintf(buf, sizeof(buf), "%s%s%s", - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); - - mg_write(conn, buf, strlen(buf)); -} - -static int deliver_websocket_frame(struct connection *conn) { - // Having buf unsigned char * is important, as it is used below in arithmetic - unsigned char *buf = (unsigned char *) conn->local_iobuf.buf; - int i, len, buf_len = conn->local_iobuf.len, frame_len = 0, - mask_len = 0, header_len = 0, data_len = 0, buffered = 0; - - if (buf_len >= 2) { - len = buf[1] & 127; - mask_len = buf[1] & 128 ? 4 : 0; - if (len < 126 && buf_len >= mask_len) { - data_len = len; - header_len = 2 + mask_len; - } else if (len == 126 && buf_len >= 4 + mask_len) { - header_len = 4 + mask_len; - data_len = ((((int) buf[2]) << 8) + buf[3]); - } else if (buf_len >= 10 + mask_len) { - header_len = 10 + mask_len; - data_len = (int) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + - htonl(* (uint32_t *) &buf[6]); - } - } - - frame_len = header_len + data_len; - buffered = frame_len > 0 && frame_len <= buf_len; - - if (buffered) { - conn->mg_conn.content_len = data_len; - conn->mg_conn.content = (char *) buf + header_len; - conn->mg_conn.wsbits = buf[0]; - - // Apply mask if necessary - if (mask_len > 0) { - for (i = 0; i < data_len; i++) { - buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4]; - } - } - - // Call the handler and remove frame from the iobuf - if (conn->endpoint.uh->handler(&conn->mg_conn)) { - conn->flags |= CONN_SPOOL_DONE; - } - memmove(buf, buf + frame_len, buf_len - frame_len); - conn->local_iobuf.len -= frame_len; - } - - return buffered; -} - -int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len) { - unsigned char *copy; - size_t copy_len = 0; - int retval = -1; - - if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) { - return -1; - } - - copy[0] = 0x80 + (opcode & 0x0f); - - // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 - if (data_len < 126) { - // Inline 7-bit length field - copy[1] = data_len; - memcpy(copy + 2, data, data_len); - copy_len = 2 + data_len; - } else if (data_len <= 0xFFFF) { - // 16-bit length field - copy[1] = 126; - * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len); - memcpy(copy + 4, data, data_len); - copy_len = 4 + data_len; - } else { - // 64-bit length field - copy[1] = 127; - * (uint32_t *) (copy + 2) = (uint32_t) - htonl((uint32_t) ((uint64_t) data_len >> 32)); - * (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff); - memcpy(copy + 10, data, data_len); - copy_len = 10 + data_len; - } - - if (copy_len > 0) { - retval = mg_write(conn, copy, copy_len); - } - free(copy); - - return retval; -} - -static void send_websocket_handshake_if_requested(struct mg_connection *conn) { - const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"), - *key = mg_get_header(conn, "Sec-WebSocket-Key"); - if (ver != NULL && key != NULL) { - conn->is_websocket = 1; - send_websocket_handshake(conn, key); - } -} - -static void ping_idle_websocket_connection(struct connection *conn, time_t t) { - if (t - conn->last_activity_time > USE_WEBSOCKET_PING_INTERVAL) { - mg_websocket_write(&conn->mg_conn, 0x9, "", 0); - } -} -#else -#define ping_idle_websocket_connection(conn, t) -#endif // !NO_WEBSOCKET - -static void write_terminating_chunk(struct connection *conn) { - mg_write(&conn->mg_conn, "0\r\n\r\n", 5); -} - -static void call_uri_handler(struct connection *conn) { - conn->mg_conn.content = conn->local_iobuf.buf; - if (conn->endpoint.uh->handler(&conn->mg_conn)) { - if (conn->flags & CONN_HEADERS_SENT) { - write_terminating_chunk(conn); - } - close_local_endpoint(conn); - } else { - conn->flags |= CONN_LONG_RUNNING; - } -} - -static void write_to_socket(struct connection *conn) { - struct iobuf *io = &conn->remote_iobuf; - int n = conn->ssl == NULL ? send(conn->client_sock, io->buf, io->len, 0) : -#ifdef USE_SSL - SSL_write(conn->ssl, io->buf, io->len); -#else - 0; -#endif - - DBG(("%p Written %d of %d(%d): [%.*s ...]", - conn, n, io->len, io->size, 40, io->buf)); - - if (is_error(n)) { - conn->flags |= CONN_CLOSE; - } else if (n > 0) { - memmove(io->buf, io->buf + n, io->len - n); - io->len -= n; - conn->num_bytes_sent += n; - } - - if (io->len == 0 && conn->flags & CONN_SPOOL_DONE) { - conn->flags |= CONN_CLOSE; - } -} - -const char *mg_get_mime_type(const char *path) { - const char *ext; - size_t i, path_len; - - path_len = strlen(path); - - for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) { - ext = path + (path_len - static_builtin_mime_types[i].ext_len); - if (path_len > static_builtin_mime_types[i].ext_len && - mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) { - return static_builtin_mime_types[i].mime_type; - } - } - - return "text/plain"; -} - -static struct uri_handler *find_uri_handler(struct mg_server *server, - const char *uri) { - struct ll *lp, *tmp; - struct uri_handler *uh; - - LINKED_LIST_FOREACH(&server->uri_handlers, lp, tmp) { - uh = LINKED_LIST_ENTRY(lp, struct uri_handler, link); - if (!strncmp(uh->uri, uri, strlen(uh->uri))) return uh; - } - - return NULL; -} - -#ifndef NO_FILESYSTEM -// Convert month to the month number. Return -1 on error, or month number -static int get_month_index(const char *s) { - static const char *month_names[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - int i; - - for (i = 0; i < (int) ARRAY_SIZE(month_names); i++) - if (!strcmp(s, month_names[i])) - return i; - - return -1; -} - -static int num_leap_years(int year) { - return year / 4 - year / 100 + year / 400; -} - -// Parse UTC date-time string, and return the corresponding time_t value. -static time_t parse_date_string(const char *datetime) { - static const unsigned short days_before_month[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 - }; - char month_str[32]; - int second, minute, hour, day, month, year, leap_days, days; - time_t result = (time_t) 0; - - if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d %3s %d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d-%3s-%d %d:%d:%d", - &day, month_str, &year, &hour, &minute, &second) == 6)) && - year > 1970 && - (month = get_month_index(month_str)) != -1) { - leap_days = num_leap_years(year) - num_leap_years(1970); - year -= 1970; - days = year * 365 + days_before_month[month] + (day - 1) + leap_days; - result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; - } - - return result; -} - -// Look at the "path" extension and figure what mime type it has. -// Store mime type in the vector. -static void get_mime_type(const struct mg_server *server, const char *path, - struct vec *vec) { - struct vec ext_vec, mime_vec; - const char *list, *ext; - size_t path_len; - - path_len = strlen(path); - - // Scan user-defined mime types first, in case user wants to - // override default mime types. - list = server->config_options[EXTRA_MIME_TYPES]; - while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { - // ext now points to the path suffix - ext = path + path_len - ext_vec.len; - if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { - *vec = mime_vec; - return; - } - } - - vec->ptr = mg_get_mime_type(path); - vec->len = strlen(vec->ptr); -} - -static const char *suggest_connection_header(const struct mg_connection *conn) { - return should_keep_alive(conn) ? "keep-alive" : "close"; -} - -static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) { - mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", - (unsigned long) st->st_mtime, (int64_t) st->st_size); -} - -// Return True if we should reply 304 Not Modified. -static int is_not_modified(const struct connection *conn, - const file_stat_t *stp) { - char etag[64]; - const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since"); - const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match"); - construct_etag(etag, sizeof(etag), stp); - return (inm != NULL && !mg_strcasecmp(etag, inm)) || - (ims != NULL && stp->st_mtime <= parse_date_string(ims)); -} - -// For given directory path, substitute it to valid index file. -// Return 0 if index file has been found, -1 if not found. -// If the file is found, it's stats is returned in stp. -static int find_index_file(struct connection *conn, char *path, - size_t path_len, file_stat_t *stp) { - const char *list = conn->server->config_options[INDEX_FILES]; - file_stat_t st; - struct vec filename_vec; - size_t n = strlen(path), found = 0; - - // The 'path' given to us points to the directory. Remove all trailing - // directory separator characters from the end of the path, and - // then append single directory separator character. - while (n > 0 && path[n - 1] == '/') { - n--; - } - path[n] = '/'; - - // Traverse index files list. For each entry, append it to the given - // path and see if the file exists. If it exists, break the loop - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - - // Ignore too long entries that may overflow path buffer - if (filename_vec.len > (int) (path_len - (n + 2))) - continue; - - // Prepare full path to the index file - strncpy(path + n + 1, filename_vec.ptr, filename_vec.len); - path[n + 1 + filename_vec.len] = '\0'; - - //DBG(("[%s]", path)); - - // Does it exist? - if (!stat(path, &st)) { - // Yes it does, break the loop - *stp = st; - found = 1; - break; - } - } - - // If no index file exists, restore directory path - if (!found) { - path[n] = '\0'; - } - - return found; -} - -static int parse_range_header(const char *header, int64_t *a, int64_t *b) { - return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); -} - -static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); -} - -static void open_file_endpoint(struct connection *conn, const char *path, - file_stat_t *st) { - char date[64], lm[64], etag[64], range[64], headers[500]; - const char *msg = "OK", *hdr; - time_t curtime = time(NULL); - int64_t r1, r2; - struct vec mime_vec; - int n; - - conn->endpoint_type = EP_FILE; - set_close_on_exec(conn->endpoint.fd); - conn->mg_conn.status_code = 200; - - get_mime_type(conn->server, path, &mime_vec); - conn->cl = st->st_size; - range[0] = '\0'; - - // If Range: header specified, act accordingly - r1 = r2 = 0; - hdr = mg_get_header(&conn->mg_conn, "Range"); - if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && - r1 >= 0 && r2 >= 0) { - conn->mg_conn.status_code = 206; - conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1; - mg_snprintf(range, sizeof(range), "Content-Range: bytes " - "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", - r1, r1 + conn->cl - 1, (int64_t) st->st_size); - msg = "Partial Content"; - lseek(conn->endpoint.fd, r1, SEEK_SET); - } - - // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 - gmt_time_string(date, sizeof(date), &curtime); - gmt_time_string(lm, sizeof(lm), &st->st_mtime); - construct_etag(etag, sizeof(etag), st); - - n = mg_snprintf(headers, sizeof(headers), - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Etag: %s\r\n" - "Content-Type: %.*s\r\n" - "Content-Length: %" INT64_FMT "\r\n" - "Connection: %s\r\n" - "Accept-Ranges: bytes\r\n" - "%s%s\r\n", - conn->mg_conn.status_code, msg, date, lm, etag, - (int) mime_vec.len, mime_vec.ptr, conn->cl, - suggest_connection_header(&conn->mg_conn), - range, USE_EXTRA_HTTP_HEADERS); - spool(&conn->remote_iobuf, headers, n); - - if (!strcmp(conn->mg_conn.request_method, "HEAD")) { - conn->flags |= CONN_SPOOL_DONE; - close(conn->endpoint.fd); - conn->endpoint_type = EP_NONE; - } -} - -#endif // NO_FILESYSTEM - -static void call_uri_handler_if_data_is_buffered(struct connection *conn) { - struct iobuf *loc = &conn->local_iobuf; - struct mg_connection *c = &conn->mg_conn; - -#ifndef NO_WEBSOCKET - if (conn->mg_conn.is_websocket) { - do { } while (deliver_websocket_frame(conn)); - } else -#endif - if (loc->len >= c->content_len) { - call_uri_handler(conn); - } -} - -#if !defined(NO_DIRECTORY_LISTING) || !defined(NO_DAV) - -#ifdef _WIN32 -struct dirent { - char d_name[MAX_PATH_SIZE]; -}; - -typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -// Implementation of POSIX opendir/closedir/readdir for Windows. -static DIR *opendir(const char *name) { - DIR *dir = NULL; - wchar_t wpath[MAX_PATH_SIZE]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_wchar(name, wpath, ARRAY_SIZE(wpath)); - attrs = GetFileAttributesW(wpath); - if (attrs != 0xFFFFFFFF && - ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { - (void) wcscat(wpath, L"\\*"); - dir->handle = FindFirstFileW(wpath, &dir->info); - dir->result.d_name[0] = '\0'; - } else { - free(dir); - dir = NULL; - } - } - - return dir; -} - -static int closedir(DIR *dir) { - int result = 0; - - if (dir != NULL) { - if (dir->handle != INVALID_HANDLE_VALUE) - result = FindClose(dir->handle) ? 0 : -1; - - free(dir); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} - -static struct dirent *readdir(DIR *dir) { - struct dirent *result = 0; - - if (dir) { - if (dir->handle != INVALID_HANDLE_VALUE) { - result = &dir->result; - (void) WideCharToMultiByte(CP_UTF8, 0, - dir->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - - if (!FindNextFileW(dir->handle, &dir->info)) { - (void) FindClose(dir->handle); - dir->handle = INVALID_HANDLE_VALUE; - } - - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return result; -} -#endif // _WIN32 POSIX opendir/closedir/readdir implementation - -static int must_hide_file(struct connection *conn, const char *path) { - const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; - const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN]; - return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 || - (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0); -} - -static int scan_directory(struct connection *conn, const char *dir, - struct dir_entry **arr) { - char path[MAX_PATH_SIZE]; - struct dir_entry *p; - struct dirent *dp; - int arr_size = 0, arr_ind = 0, inc = 100; - DIR *dirp; - - *arr = NULL; - if ((dirp = (opendir(dir))) == NULL) return 0; - - while ((dp = readdir(dirp)) != NULL) { - // Do not show current dir and hidden files - if (!strcmp(dp->d_name, ".") || - !strcmp(dp->d_name, "..") || - must_hide_file(conn, dp->d_name)) { - continue; - } - mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - - // Resize the array if nesessary - if (arr_ind >= arr_size) { - if ((p = (struct dir_entry *) - realloc(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) { - // Memset new chunk to zero, otherwize st_mtime will have garbage which - // can make strftime() segfault, see - // http://code.google.com/p/mongoose/issues/detail?id=79 - memset(p + arr_size, 0, sizeof(**arr) * inc); - - *arr = p; - arr_size += inc; - } - } - - if (arr_ind < arr_size) { - (*arr)[arr_ind].conn = conn; - (*arr)[arr_ind].file_name = strdup(dp->d_name); - stat(path, &(*arr)[arr_ind].st); - arr_ind++; - } - } - closedir(dirp); - - return arr_ind; -} - -static void mg_url_encode(const char *src, char *dst, size_t dst_len) { - static const char *dont_escape = "._-$,;~()"; - static const char *hex = "0123456789abcdef"; - const char *end = dst + dst_len - 1; - - for (; *src != '\0' && dst < end; src++, dst++) { - if (isalnum(*(const unsigned char *) src) || - strchr(dont_escape, * (const unsigned char *) src) != NULL) { - *dst = *src; - } else if (dst + 2 < end) { - dst[0] = '%'; - dst[1] = hex[(* (const unsigned char *) src) >> 4]; - dst[2] = hex[(* (const unsigned char *) src) & 0xf]; - dst += 2; - } - } - - *dst = '\0'; -} -#endif // !NO_DIRECTORY_LISTING || !NO_DAV - -#ifndef NO_DIRECTORY_LISTING - -static void print_dir_entry(const struct dir_entry *de) { - char size[64], mod[64], href[MAX_PATH_SIZE * 3], chunk[MAX_PATH_SIZE * 4]; - int64_t fsize = de->st.st_size; - int is_dir = S_ISDIR(de->st.st_mode), n; - const char *slash = is_dir ? "/" : ""; - - if (is_dir) { - mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); - } else { - // We use (signed) cast below because MSVC 6 compiler cannot - // convert unsigned __int64 to double. - if (fsize < 1024) { - mg_snprintf(size, sizeof(size), "%d", (int) fsize); - } else if (fsize < 0x100000) { - mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); - } else if (fsize < 0x40000000) { - mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); - } else { - mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); - } - } - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime)); - mg_url_encode(de->file_name, href, sizeof(href)); - n = mg_snprintf(chunk, sizeof(chunk), - "%s%s" - " %s  %s\n", - de->conn->mg_conn.uri, href, slash, de->file_name, slash, - mod, size); - write_chunk((struct connection *) de->conn, chunk, n); -} - -// Sort directory entries by size, or name, or modification time. -// On windows, __cdecl specification is needed in case if project is built -// with __stdcall convention. qsort always requires __cdels callback. -static int __cdecl compare_dir_entries(const void *p1, const void *p2) { - const struct dir_entry *a = (const struct dir_entry *) p1, - *b = (const struct dir_entry *) p2; - const char *qs = a->conn->mg_conn.query_string ? - a->conn->mg_conn.query_string : "na"; - int cmp_result = 0; - - if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) { - return -1; // Always put directories on top - } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) { - return 1; // Always put directories on top - } else if (*qs == 'n') { - cmp_result = strcmp(a->file_name, b->file_name); - } else if (*qs == 's') { - cmp_result = a->st.st_size == b->st.st_size ? 0 : - a->st.st_size > b->st.st_size ? 1 : -1; - } else if (*qs == 'd') { - cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 : - a->st.st_mtime > b->st.st_mtime ? 1 : -1; - } - - return qs[1] == 'd' ? -cmp_result : cmp_result; -} - -static void send_directory_listing(struct connection *conn, const char *dir) { - char buf[2000]; - struct dir_entry *arr = NULL; - int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL && - conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd'; - - conn->mg_conn.status_code = 200; - mg_snprintf(buf, sizeof(buf), "%s", - "HTTP/1.1 200 OK\r\n" - "Transfer-Encoding: Chunked\r\n" - "Content-Type: text/html; charset=utf-8\r\n\r\n"); - spool(&conn->remote_iobuf, buf, strlen(buf)); - - mg_snprintf(buf, sizeof(buf), - "Index of %s" - "" - "

Index of %s

"
-              ""
-              ""
-              ""
-              "",
-              conn->mg_conn.uri, conn->mg_conn.uri,
-              sort_direction, sort_direction, sort_direction);
-  write_chunk(conn, buf, strlen(buf));
-
-  num_entries = scan_directory(conn, dir, &arr);
-  qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
-  for (i = 0; i < num_entries; i++) {
-    print_dir_entry(&arr[i]);
-    free(arr[i].file_name);
-  }
-  free(arr);
-
-  write_terminating_chunk(conn);
-  close_local_endpoint(conn);
-}
-#endif  // NO_DIRECTORY_LISTING
-
-#ifndef NO_DAV
-static void print_props(struct connection *conn, const char *uri,
-                        file_stat_t *stp) {
-  char mtime[64], buf[MAX_PATH_SIZE + 200];
-
-  gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
-  mg_snprintf(buf, sizeof(buf),
-      ""
-       "%s"
-       ""
-        ""
-         "%s"
-         "%" INT64_FMT ""
-         "%s"
-        ""
-        "HTTP/1.1 200 OK"
-       ""
-      "\n",
-      uri, S_ISDIR(stp->st_mode) ? "" : "",
-      (int64_t) stp->st_size, mtime);
-  spool(&conn->remote_iobuf, buf, strlen(buf));
-}
-
-static void handle_propfind(struct connection *conn, const char *path,
-                            file_stat_t *stp) {
-  static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
-    "Connection: close\r\n"
-    "Content-Type: text/xml; charset=utf-8\r\n\r\n"
-    ""
-    "\n";
-  static const char footer[] = "";
-  const char *depth = mg_get_header(&conn->mg_conn, "Depth"),
-        *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
-
-  conn->mg_conn.status_code = 207;
-  spool(&conn->remote_iobuf, header, sizeof(header) - 1);
-
-  // Print properties for the requested resource itself
-  print_props(conn, conn->mg_conn.uri, stp);
-
-  // If it is a directory, print directory entries too if Depth is not 0
-  if (S_ISDIR(stp->st_mode) && !mg_strcasecmp(list_dir, "yes") &&
-      (depth == NULL || strcmp(depth, "0") != 0)) {
-    struct dir_entry *arr = NULL;
-    int i, num_entries = scan_directory(conn, path, &arr);
-
-    for (i = 0; i < num_entries; i++) {
-      char buf[MAX_PATH_SIZE], buf2[sizeof(buf) * 3];
-      struct dir_entry *de = &arr[i];
-
-      mg_snprintf(buf, sizeof(buf), "%s%s", de->conn->mg_conn.uri,
-                  de->file_name);
-      mg_url_encode(buf, buf2, sizeof(buf2) - 1);
-      print_props(conn, buf, &de->st);
-    }
-  }
-
-  spool(&conn->remote_iobuf, footer, sizeof(footer) - 1);
-  close_local_endpoint(conn);
-}
-
-static void handle_mkcol(struct connection *conn, const char *path) {
-  int status_code = 500;
-
-  if (conn->mg_conn.content_len > 0) {
-    status_code = 415;
-  } else if (!mkdir(path, 0755)) {
-    status_code = 201;
-  } else if (errno == EEXIST) {
-    status_code = 405;
-  } else if (errno == EACCES) {
-    status_code = 403;
-  } else if (errno == ENOENT) {
-    status_code = 409;
-  }
-  send_http_error(conn, status_code, NULL);
-}
-
-static int remove_directory(const char *dir) {
-  char path[MAX_PATH_SIZE];
-  struct dirent *dp;
-  file_stat_t st;
-  DIR *dirp;
-
-  if ((dirp = opendir(dir)) == NULL) return 0;
-
-  while ((dp = readdir(dirp)) != NULL) {
-    if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
-    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
-    stat(path, &st);
-    if (S_ISDIR(st.st_mode)) {
-      remove_directory(path);
-    } else {
-      remove(path);
-    }
-  }
-  closedir(dirp);
-  rmdir(dir);
-
-  return 1;
-}
-
-static void handle_delete(struct connection *conn, const char *path) {
-  file_stat_t st;
-
-  if (!stat(path, &st)) {
-    send_http_error(conn, 404, NULL);
-  } else if (S_ISDIR(st.st_mode)) {
-    remove_directory(path);
-    send_http_error(conn, 204, NULL);
-  } else if (!remove(path) == 0) {
-    send_http_error(conn, 204, NULL);
-  } else {
-    send_http_error(conn, 423, NULL);
-  }
-}
-
-// For a given PUT path, create all intermediate subdirectories
-// for given path. Return 0 if the path itself is a directory,
-// or -1 on error, 1 if OK.
-static int put_dir(const char *path) {
-  char buf[MAX_PATH_SIZE];
-  const char *s, *p;
-  file_stat_t st;
-
-  // Create intermediate directories if they do not exist
-  for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
-    if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
-    memcpy(buf, path, p - path);
-    buf[p - path] = '\0';
-    if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
-    if (p[1] == '\0') return 0;  // Path is a directory itself
-  }
-
-  return 1;
-}
-
-static void handle_put(struct connection *conn, const char *path) {
-  file_stat_t st;
-  const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
-  int64_t r1, r2;
-  int rc;
-
-  conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
-  if ((rc = put_dir(path)) == 0) {
-    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
-              conn->mg_conn.status_code);
-    close_local_endpoint(conn);
-  } else if (rc == -1) {
-    send_http_error(conn, 500, "put_dir: %s", strerror(errno));
-  } else if (cl_hdr == NULL) {
-    send_http_error(conn, 411, NULL);
-#ifdef _WIN32
-    //On Windows, open() is a macro with 2 params
-  } else if ((conn->endpoint.fd =
-              open(path, O_RDWR | O_CREAT | O_TRUNC)) < 0) {
-#else
-  } else if ((conn->endpoint.fd =
-              open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
-#endif
-    send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
-  } else {
-    DBG(("PUT [%s] %d", path, conn->local_iobuf.len));
-    conn->endpoint_type = EP_PUT;
-    set_close_on_exec(conn->endpoint.fd);
-    range = mg_get_header(&conn->mg_conn, "Content-Range");
-    conn->cl = to64(cl_hdr);
-    r1 = r2 = 0;
-    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
-      conn->mg_conn.status_code = 206;
-      lseek(conn->endpoint.fd, r1, SEEK_SET);
-      conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
-    }
-    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
-              conn->mg_conn.status_code);
-  }
-}
-
-static void forward_put_data(struct connection *conn) {
-  struct iobuf *io = &conn->local_iobuf;
-  int n = write(conn->endpoint.fd, io->buf, io->len);
-  if (n > 0) {
-    memmove(io->buf, io->buf + n, io->len - n);
-    io->len -= n;
-    conn->cl -= n;
-    if (conn->cl <= 0) {
-      close_local_endpoint(conn);
-    }
-  }
-}
-#endif //  NO_DAV
-
-static void send_options(struct connection *conn) {
-  static const char reply[] = "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, "
-    "CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
-  spool(&conn->remote_iobuf, reply, sizeof(reply) - 1);
-  conn->flags |= CONN_SPOOL_DONE;
-}
-
-#ifndef NO_AUTH
-void mg_send_digest_auth_request(struct mg_connection *c) {
-  struct connection *conn = (struct connection *) c;
-  c->status_code = 401;
-  mg_printf(c,
-            "HTTP/1.1 401 Unauthorized\r\n"
-            "WWW-Authenticate: Digest qop=\"auth\", "
-            "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
-            conn->server->config_options[AUTH_DOMAIN],
-            (unsigned long) time(NULL));
-}
-
-void mg_send_basic_auth_request(struct mg_connection *c) {
-  struct connection *conn = (struct connection *) c;
-  c->status_code = 401;
-  mg_printf(c,
-            "HTTP/1.1 401 Unauthorized\r\n"
-            "WWW-Authenticate: Basic realm=\"%s\"\r\n\r\n",
-            conn->server->config_options[AUTH_DOMAIN]);
-}
-
-// Use the global passwords file, if specified by auth_gpass option,
-// or search for .htpasswd in the requested directory.
-static FILE *open_auth_file(struct connection *conn, const char *path) {
-  char name[MAX_PATH_SIZE];
-  const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
-  file_stat_t st;
-  FILE *fp = NULL;
-
-  if (gpass != NULL) {
-    // Use global passwords file
-    fp = fopen(gpass, "r");
-  } else if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
-    mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
-    fp = fopen(name, "r");
-  } else {
-    // Try to find .htpasswd in requested directory.
-    if ((p = strrchr(path, '/')) == NULL) p = path;
-    mg_snprintf(name, sizeof(name), "%.*s%c%s",
-                (int) (p - path), path, '/', PASSWORDS_FILE_NAME);
-    fp = fopen(name, "r");
-  }
-
-  return fp;
-}
-
-#if !defined(HAVE_MD5) && !defined(NO_AUTH)
-typedef struct MD5Context {
-  uint32_t buf[4];
-  uint32_t bits[2];
-  unsigned char in[64];
-} MD5_CTX;
-
-static void byteReverse(unsigned char *buf, unsigned longs) {
-  uint32_t t;
-
-  // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
-  if (is_big_endian()) {
-    do {
-      t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
-        ((unsigned) buf[1] << 8 | buf[0]);
-      * (uint32_t *) buf = t;
-      buf += 4;
-    } while (--longs);
-  }
-}
-
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-#define MD5STEP(f, w, x, y, z, data, s) \
-  ( w += f(x, y, z) + data,  w = w<>(32-s),  w += x )
-
-// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
-// initialization constants.
-static void MD5Init(MD5_CTX *ctx) {
-  ctx->buf[0] = 0x67452301;
-  ctx->buf[1] = 0xefcdab89;
-  ctx->buf[2] = 0x98badcfe;
-  ctx->buf[3] = 0x10325476;
-
-  ctx->bits[0] = 0;
-  ctx->bits[1] = 0;
-}
-
-static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
-  register uint32_t a, b, c, d;
-
-  a = buf[0];
-  b = buf[1];
-  c = buf[2];
-  d = buf[3];
-
-  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
-  buf[0] += a;
-  buf[1] += b;
-  buf[2] += c;
-  buf[3] += d;
-}
-
-static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
-  uint32_t t;
-
-  t = ctx->bits[0];
-  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
-    ctx->bits[1]++;
-  ctx->bits[1] += len >> 29;
-
-  t = (t >> 3) & 0x3f;
-
-  if (t) {
-    unsigned char *p = (unsigned char *) ctx->in + t;
-
-    t = 64 - t;
-    if (len < t) {
-      memcpy(p, buf, len);
-      return;
-    }
-    memcpy(p, buf, t);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    buf += t;
-    len -= t;
-  }
-
-  while (len >= 64) {
-    memcpy(ctx->in, buf, 64);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    buf += 64;
-    len -= 64;
-  }
-
-  memcpy(ctx->in, buf, len);
-}
-
-static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
-  unsigned count;
-  unsigned char *p;
-  uint32_t *a;
-
-  count = (ctx->bits[0] >> 3) & 0x3F;
-
-  p = ctx->in + count;
-  *p++ = 0x80;
-  count = 64 - 1 - count;
-  if (count < 8) {
-    memset(p, 0, count);
-    byteReverse(ctx->in, 16);
-    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-    memset(ctx->in, 0, 56);
-  } else {
-    memset(p, 0, count - 8);
-  }
-  byteReverse(ctx->in, 14);
-
-  a = (uint32_t *)ctx->in;
-  a[14] = ctx->bits[0];
-  a[15] = ctx->bits[1];
-
-  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-  byteReverse((unsigned char *) ctx->buf, 4);
-  memcpy(digest, ctx->buf, 16);
-  memset((char *) ctx, 0, sizeof(*ctx));
-}
-#endif // !HAVE_MD5
-
-
-
-// Stringify binary data. Output buffer must be twice as big as input,
-// because each byte takes 2 bytes in string representation
-static void bin2str(char *to, const unsigned char *p, size_t len) {
-  static const char *hex = "0123456789abcdef";
-
-  for (; len--; p++) {
-    *to++ = hex[p[0] >> 4];
-    *to++ = hex[p[0] & 0x0f];
-  }
-  *to = '\0';
-}
-
-// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
-char *mg_md5(char buf[33], ...) {
-  unsigned char hash[16];
-  const char *p;
-  va_list ap;
-  MD5_CTX ctx;
-
-  MD5Init(&ctx);
-
-  va_start(ap, buf);
-  while ((p = va_arg(ap, const char *)) != NULL) {
-    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
-  }
-  va_end(ap);
-
-  MD5Final(hash, &ctx);
-  bin2str(buf, hash, sizeof(hash));
-  return buf;
-}
-
-// Check the user's password, return 1 if OK
-static int check_password(const char *method, const char *ha1, const char *uri,
-                          const char *nonce, const char *nc, const char *cnonce,
-                          const char *qop, const char *response) {
-  char ha2[32 + 1], expected_response[32 + 1];
-
-#if 0
-  // Check for authentication timeout
-  if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600) {
-    return 0;
-  }
-#endif
-
-  mg_md5(ha2, method, ":", uri, NULL);
-  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
-      ":", cnonce, ":", qop, ":", ha2, NULL);
-
-  return mg_strcasecmp(response, expected_response) == 0;
-}
-
-
-// Authorize against the opened passwords file. Return 1 if authorized.
-int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
-  struct connection *conn = (struct connection *) c;
-  const char *hdr;
-  char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
-       uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
-
-  if (c == NULL || fp == NULL) return 0;
-  if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
-      mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
-  if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
-  if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
-  if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
-  if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
-  if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
-  if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
-  if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
-
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
-        !strcmp(user, f_user) &&
-        // NOTE(lsm): due to a bug in MSIE, we do not compare URIs
-        !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
-      return check_password(c->request_method, ha1, uri,
-                            nonce, nc, cnonce, qop, resp);
-  }
-  return 0;
-}
-
-//This is the first check for a basic access to our server, even before handlers are hit.
-static int is_authorized_for_basic_access(struct mg_connection *c) {
-  struct connection *conn = (struct connection *) c;
-  const char *hdr;
-  const char *username = conn->server->config_options[BASIC_AUTH_USERNAME];
-  const char *password = conn->server->config_options[BASIC_AUTH_PASSWORD];
-
-  if (c == NULL) return 0;
-
-  //If either username/password are null, it means everyone is allowed
-  if(username == NULL || password == NULL)
-    return 1;
-
-  //Else check if the basic auth hash received is the same as the one we have/get
-  //Get a string containing username:password
-  char username_password[1024];
-  snprintf(username_password, sizeof(username_password),
-           "%s:%s",
-           username, password);
-
-  char basic_auth_hash[1024] = {0};
-  base64_encode(username_password, strlen(username_password), basic_auth_hash);
-//   printf("hash: %s (%d)\n", basic_auth_hash, strlen(basic_auth_hash));
-
-  if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
-      mg_strncasecmp(hdr, "Basic ", 6) != 0) return 0;
-
-  char tmp_buffer[1024] = {0};
-  char received_hash[1024] = {0};
-  sscanf(hdr, "%s %s", tmp_buffer, received_hash);
-//   printf("received_hash: %s (%d)\n", received_hash, strlen(received_hash));
-
-  return strcmp(basic_auth_hash, received_hash) == 0;
-}
-
-// Return 1 if request is authorised, 0 otherwise.
-static int is_authorized(struct connection *conn, const char *path) {
-  FILE *fp;
-  int authorized = 1;
-
-  if ((fp = open_auth_file(conn, path)) != NULL) {
-    authorized = mg_authorize_digest(&conn->mg_conn, fp);
-    fclose(fp);
-  }
-
-  return authorized;
-}
-
-static int is_authorized_for_dav(struct connection *conn) {
-  const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
-  FILE *fp;
-  int authorized = 0;
-
-  if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
-    authorized = mg_authorize_digest(&conn->mg_conn, fp);
-    fclose(fp);
-  }
-
-  return authorized;
-}
-
-static int is_dav_mutation(const struct connection *conn) {
-  const char *s = conn->mg_conn.request_method;
-  return s && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
-               !strcmp(s, "MKCOL"));
-}
-#endif // NO_AUTH
-
-int parse_header(const char *str, int str_len, const char *var_name, char *buf,
-                 size_t buf_size) {
-  int ch = ' ', len = 0, n = strlen(var_name);
-  const char *p, *end = str + str_len, *s = NULL;
-
-  if (buf != NULL && buf_size > 0) buf[0] = '\0';
-
-  // Find where variable starts
-  for (s = str; s != NULL && s + n < end; s++) {
-    if ((s == str || s[-1] == ' ') && s[n] == '=' &&
-        !memcmp(s, var_name, n)) break;
-  }
-
-  if (s != NULL && &s[n + 1] < end) {
-    s += n + 1;
-    if (*s == '"' || *s == '\'') ch = *s++;
-    p = s;
-    while (p < end && p[0] != ch && len < (int) buf_size) {
-      if (p[0] == '\\' && p[1] == ch) p++;
-      buf[len++] = *p++;
-    }
-    if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
-      len = 0;
-    } else {
-      if (len > 0 && s[len - 1] == ',') len--;
-      if (len > 0 && s[len - 1] == ';') len--;
-      buf[len] = '\0';
-    }
-  }
-
-  return len;
-}
-
-int mg_parse_header(const char *s, const char *var_name, char *buf,
-                    size_t buf_size) {
-  return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
-}
-
-#ifdef USE_LUA
-#include "lua_5.2.1.h"
-
-#ifdef _WIN32
-static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
-                  int offset) {
-  HANDLE fh = (HANDLE) _get_osfhandle(fd);
-  HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
-  void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
-  CloseHandle(mh);
-  return p;
-}
-#define munmap(x, y)  UnmapViewOfFile(x)
-#define MAP_FAILED NULL
-#define MAP_PRIVATE 0
-#define PROT_READ 0
-#else
-#include 
-#endif
-
-static void reg_string(struct lua_State *L, const char *name, const char *val) {
-  lua_pushstring(L, name);
-  lua_pushstring(L, val);
-  lua_rawset(L, -3);
-}
-
-static void reg_int(struct lua_State *L, const char *name, int val) {
-  lua_pushstring(L, name);
-  lua_pushinteger(L, val);
-  lua_rawset(L, -3);
-}
-
-static void reg_function(struct lua_State *L, const char *name,
-                         lua_CFunction func, struct mg_connection *conn) {
-  lua_pushstring(L, name);
-  lua_pushlightuserdata(L, conn);
-  lua_pushcclosure(L, func, 1);
-  lua_rawset(L, -3);
-}
-
-static int lua_write(lua_State *L) {
-  int i, num_args;
-  const char *str;
-  size_t size;
-  struct mg_connection *conn = (struct mg_connection *)
-    lua_touserdata(L, lua_upvalueindex(1));
-
-  num_args = lua_gettop(L);
-  for (i = 1; i <= num_args; i++) {
-    if (lua_isstring(L, i)) {
-      str = lua_tolstring(L, i, &size);
-      mg_write(conn, str, size);
-    }
-  }
-
-  return 0;
-}
-
-static int lsp_sock_close(lua_State *L) {
-  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
-    lua_getfield(L, -1, "sock");
-    closesocket((sock_t) lua_tonumber(L, -1));
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static int lsp_sock_recv(lua_State *L) {
-  char buf[2000];
-  int n;
-
-  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
-    lua_getfield(L, -1, "sock");
-    n = recv((sock_t) lua_tonumber(L, -1), buf, sizeof(buf), 0);
-    if (n <= 0) {
-      lua_pushnil(L);
-    } else {
-      lua_pushlstring(L, buf, n);
-    }
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static int lsp_sock_send(lua_State *L) {
-  const char *buf;
-  size_t len, sent = 0;
-  int n, sock;
-
-  if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
-    buf = lua_tolstring(L, -1, &len);
-    lua_getfield(L, -2, "sock");
-    sock = (int) lua_tonumber(L, -1);
-    while (sent < len) {
-      if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) break;
-      sent += n;
-    }
-    lua_pushnumber(L, sent);
-  } else {
-    return luaL_error(L, "invalid :close() call");
-  }
-  return 1;
-}
-
-static const struct luaL_Reg luasocket_methods[] = {
-  {"close", lsp_sock_close},
-  {"send", lsp_sock_send},
-  {"recv", lsp_sock_recv},
-  {NULL, NULL}
-};
-
-static sock_t conn2(const char *host, int port) {
-  struct sockaddr_in sin;
-  struct hostent *he = NULL;
-  sock_t sock = INVALID_SOCKET;
-
-  if (host != NULL &&
-      (he = gethostbyname(host)) != NULL &&
-    (sock = socket(PF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) {
-    set_close_on_exec(sock);
-    sin.sin_family = AF_INET;
-    sin.sin_port = htons((uint16_t) port);
-    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
-    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
-      closesocket(sock);
-      sock = INVALID_SOCKET;
-    }
-  }
-  return sock;
-}
-
-static int lsp_connect(lua_State *L) {
-  sock_t sock;
-
-  if (lua_isstring(L, -2) && lua_isnumber(L, -1)) {
-    sock = conn2(lua_tostring(L, -2), (int) lua_tonumber(L, -1));
-    if (sock == INVALID_SOCKET) {
-      lua_pushnil(L);
-    } else {
-      lua_newtable(L);
-      reg_int(L, "sock", sock);
-      reg_string(L, "host", lua_tostring(L, -4));
-      luaL_getmetatable(L, "luasocket");
-      lua_setmetatable(L, -2);
-    }
-  } else {
-    return luaL_error(L, "connect(host,port): invalid parameter given.");
-  }
-  return 1;
-}
-
-static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) {
-  extern void luaL_openlibs(lua_State *);
-  int i;
-
-  luaL_openlibs(L);
-#ifdef USE_LUA_SQLITE3
-  { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
-#endif
-
-  luaL_newmetatable(L, "luasocket");
-  lua_pushliteral(L, "__index");
-  luaL_newlib(L, luasocket_methods);
-  lua_rawset(L, -3);
-  lua_pop(L, 1);
-  lua_register(L, "connect", lsp_connect);
-
-  if (ri == NULL) return;
-
-  // Register mg module
-  lua_newtable(L);
-  reg_function(L, "write", lua_write, ri);
-
-  // Export request_info
-  lua_pushstring(L, "request_info");
-  lua_newtable(L);
-  reg_string(L, "request_method", ri->request_method);
-  reg_string(L, "uri", ri->uri);
-  reg_string(L, "http_version", ri->http_version);
-  reg_string(L, "query_string", ri->query_string);
-  reg_string(L, "remote_ip", ri->remote_ip);
-  reg_int(L, "remote_port", ri->remote_port);
-  lua_pushstring(L, "content");
-  lua_pushlstring(L, ri->content == NULL ? "" : ri->content, 0);
-  lua_rawset(L, -3);
-  reg_int(L, "content_len", ri->content_len);
-  reg_int(L, "num_headers", ri->num_headers);
-  lua_pushstring(L, "http_headers");
-  lua_newtable(L);
-  for (i = 0; i < ri->num_headers; i++) {
-    reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
-  }
-  lua_rawset(L, -3);
-  lua_rawset(L, -3);
-
-  lua_setglobal(L, "mg");
-
-  // Register default mg.onerror function
-  (void) luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua "
-                       "error:\\n', debug.traceback(e, 1)) end");
-}
-
-static int lua_error_handler(lua_State *L) {
-  const char *error_msg =  lua_isstring(L, -1) ?  lua_tostring(L, -1) : "?\n";
-
-  lua_getglobal(L, "mg");
-  if (!lua_isnil(L, -1)) {
-    lua_getfield(L, -1, "write");   // call mg.write()
-    lua_pushstring(L, error_msg);
-    lua_pushliteral(L, "\n");
-    lua_call(L, 2, 0);
-    (void) luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
-  } else {
-    printf("Lua error: [%s]\n", error_msg);
-    (void) luaL_dostring(L, "print(debug.traceback(), '\\n')");
-  }
-  // TODO(lsm): leave the stack balanced
-
-  return 0;
-}
-
-static void lsp(struct connection *conn, const char *p, int len, lua_State *L) {
-  int i, j, pos = 0;
-
-  for (i = 0; i < len; i++) {
-    if (p[i] == '<' && p[i + 1] == '?') {
-      for (j = i + 1; j < len ; j++) {
-        if (p[j] == '?' && p[j + 1] == '>') {
-          mg_write(&conn->mg_conn, p + pos, i - pos);
-          if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == LUA_OK) {
-            lua_pcall(L, 0, LUA_MULTRET, 0);
-          }
-          pos = j + 2;
-          i = pos - 1;
-          break;
-        }
-      }
-    }
-  }
-  if (i > pos) mg_write(&conn->mg_conn, p + pos, i - pos);
-}
-
-static void handle_lsp_request(struct connection *conn, const char *path,
-                               file_stat_t *st) {
-  void *p = NULL;
-  lua_State *L = NULL;
-  FILE *fp = NULL;
-
-  if ((fp = fopen(path, "r")) == NULL ||
-      (p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE,
-                fileno(fp), 0)) == MAP_FAILED ||
-      (L = luaL_newstate()) == NULL) {
-    send_http_error(conn, 500, "mmap(%s): %s", path, strerror(errno));
-  } else {
-    // We're not sending HTTP headers here, Lua page must do it.
-    prepare_lua_environment(&conn->mg_conn, L);
-    lua_pushcclosure(L, &lua_error_handler, 0);
-    lua_pushglobaltable(L);
-    lsp(conn, p, st->st_size, L);
-    close_local_endpoint(conn);
-  }
-
-  if (L != NULL) lua_close(L);
-  if (p != NULL) munmap(p, st->st_size);
-  if (fp != NULL) fclose(fp);
-}
-#endif // USE_LUA
-
-static void open_local_endpoint(struct connection *conn) {
-  const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
-#ifndef NO_FILESYSTEM
-  static const char lua_pat[] = LUA_SCRIPT_PATTERN;
-  file_stat_t st;
-  char path[MAX_PATH_SIZE];
-  int exists = 0, is_directory = 0;
-  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
-  const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
-#endif
-
-  conn->mg_conn.content_len = cl_hdr == NULL ? 0 : (int) to64(cl_hdr);
-
-#ifndef NO_AUTH
-  if (!is_authorized_for_basic_access(&conn->mg_conn)) {
-    mg_send_basic_auth_request(&conn->mg_conn);
-    close_local_endpoint(conn);
-    return;
-  }
-#endif
-
-  // Call URI handler if one is registered for this URI
-  if (conn->server->do_i_handle == NULL || conn->server->do_i_handle(&conn->mg_conn)) {
-      conn->endpoint.uh = find_uri_handler(conn->server, conn->mg_conn.uri);
-      if (conn->endpoint.uh != NULL) {
-        conn->endpoint_type = EP_USER;
-        conn->mg_conn.content = conn->local_iobuf.buf;
-#if USE_POST_SIZE_LIMIT > 1
-        {
-          const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
-          if (!strcmp(conn->mg_conn.request_method, "POST") &&
-              (cl == NULL || to64(cl) > USE_POST_SIZE_LIMIT)) {
-            send_http_error(conn, 500, "POST size > %zu",
-                            (size_t) USE_POST_SIZE_LIMIT);
-          }
-        }
-#endif
-        return;
-      }
-  }
-
-#ifdef NO_FILESYSTEM
-  send_http_error(conn, 404, NULL);
-#else
-  exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
-  is_directory = S_ISDIR(st.st_mode);
-
-  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
-    send_options(conn);
-  } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
-    send_http_error(conn, 404, NULL);
-#ifndef NO_AUTH
-  } else if ((!is_dav_mutation(conn) && !is_authorized(conn, path)) ||
-             (is_dav_mutation(conn) && !is_authorized_for_dav(conn))) {
-    mg_send_digest_auth_request(&conn->mg_conn);
-    close_local_endpoint(conn);
-#endif
-#ifndef NO_DAV
-  } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
-    handle_propfind(conn, path, &st);
-  } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
-    handle_mkcol(conn, path);
-  } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
-    handle_delete(conn, path);
-  } else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
-    handle_put(conn, path);
-#endif
-  } else if (!exists || must_hide_file(conn, path)) {
-    send_http_error(conn, 404, NULL);
-  } else if (is_directory &&
-             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
-    conn->mg_conn.status_code = 301;
-    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
-              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
-    close_local_endpoint(conn);
-  } else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
-    if (!mg_strcasecmp(dir_lst, "yes")) {
-#ifndef NO_DIRECTORY_LISTING
-      send_directory_listing(conn, path);
-#else
-      send_http_error(conn, 501, NULL);
-#endif
-    } else {
-      send_http_error(conn, 403, NULL);
-    }
-  } else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
-#ifdef USE_LUA
-    handle_lsp_request(conn, path, &st);
-#else
-    send_http_error(conn, 501, NULL);
-#endif
-  } else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
-#if !defined(NO_CGI)
-    open_cgi_endpoint(conn, path);
-#else
-    send_http_error(conn, 501, NULL);
-#endif // !NO_CGI
-  } else if (is_not_modified(conn, &st)) {
-    send_http_error(conn, 304, NULL);
-  } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
-    // O_BINARY is required for Windows, otherwise in default text mode
-    // two bytes \r\n will be read as one.
-    open_file_endpoint(conn, path, &st);
-  } else {
-    send_http_error(conn, 404, NULL);
-  }
-#endif  // NO_FILESYSTEM
-}
-
-static void send_continue_if_expected(struct connection *conn) {
-  static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
-  const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
-
-  if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
-    spool(&conn->remote_iobuf, expect_response, sizeof(expect_response) - 1);
-  }
-}
-
-static int is_valid_uri(const char *uri) {
-  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-  // URI can be an asterisk (*) or should start with slash.
-  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
-}
-
-static void process_request(struct connection *conn) {
-  struct iobuf *io = &conn->local_iobuf;
-
-  if (conn->request_len == 0 &&
-      (conn->request_len = get_request_len(io->buf, io->len)) > 0) {
-    // If request is buffered in, remove it from the iobuf. This is because
-    // iobuf could be reallocated, and pointers in parsed request could
-    // become invalid.
-    conn->request = (char *) malloc(conn->request_len);
-    memcpy(conn->request, io->buf, conn->request_len);
-    DBG(("%p ==> [%.*s]", conn, conn->request_len, conn->request));
-    memmove(io->buf, io->buf + conn->request_len, io->len - conn->request_len);
-    io->len -= conn->request_len;
-    conn->request_len = parse_http_message(conn->request, conn->request_len,
-                                           &conn->mg_conn);
-  }
-
-  DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len, io->len, io->buf));
-  if (conn->request_len < 0 ||
-      (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
-    send_http_error(conn, 400, NULL);
-  } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
-    send_http_error(conn, 413, NULL);
-  } else if (conn->request_len > 0 &&
-             strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
-             strcmp(conn->mg_conn.http_version, "1.1") != 0) {
-    send_http_error(conn, 505, NULL);
-  } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
-#ifndef NO_WEBSOCKET
-    send_websocket_handshake_if_requested(&conn->mg_conn);
-#endif
-    send_continue_if_expected(conn);
-    open_local_endpoint(conn);
-  }
-
-#ifndef NO_CGI
-  if (conn->endpoint_type == EP_CGI && io->len > 0) {
-    forward_post_data(conn);
-  }
-#endif
-  if (conn->endpoint_type == EP_USER) {
-    call_uri_handler_if_data_is_buffered(conn);
-  }
-#ifndef NO_DAV
-  if (conn->endpoint_type == EP_PUT && io->len > 0) {
-    forward_put_data(conn);
-  }
-#endif
-}
-
-static void read_from_socket(struct connection *conn) {
-  char buf[IOBUF_SIZE];
-  int ok, n = 0;
-  socklen_t len = sizeof(ok);
-
-  if (conn->endpoint_type == EP_CLIENT) {
-    conn->mg_conn.wsbits = 1;
-    if (!(conn->flags & CONN_CONNECTED) &&
-        getsockopt(conn->client_sock, SOL_SOCKET, SO_ERROR,
-                   (char *) &ok, &len) < 0) {
-      conn->mg_conn.wsbits = 0;
-    }
-    conn->handler(&conn->mg_conn);
-    conn->flags |= CONN_CLOSE | CONN_CONNECTED;
-  } else {
-    if (conn->ssl != NULL) {
-#ifdef USE_SSL
-      if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
-        n = SSL_read(conn->ssl, buf, sizeof(buf));
-      } else {
-        if (SSL_accept(conn->ssl) == 1) {
-          conn->flags |= CONN_SSL_HANDS_SHAKEN;
-        }
-        return;
-      }
-#endif
-    } else {
-      n = recv(conn->client_sock, buf, sizeof(buf), 0);
-    }
-
-    DBG(("%p %d", conn, n));
-    if (is_error(n)) {
-      conn->flags |= CONN_CLOSE;
-    } else if (n > 0) {
-      spool(&conn->local_iobuf, buf, n);
-      process_request(conn);
-    }
-  }
-}
-
-int mg_connect(struct mg_server *server, const char *host,
-                                 int port, mg_handler_t handler, void *param) {
-  sock_t sock = INVALID_SOCKET;
-  struct sockaddr_in sin;
-  struct hostent *he = NULL;
-  struct connection *conn = NULL;
-  int connect_ret_val;
-
-  if (host == NULL || (he = gethostbyname(host)) == NULL ||
-      (sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) return 0;
-
-  sin.sin_family = AF_INET;
-  sin.sin_port = htons((uint16_t) port);
-  sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
-  set_non_blocking_mode(sock);
-
-  connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
-  if (connect_ret_val != 0 && errno != EINPROGRESS) {
-    return 0;
-  } else if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
-    closesocket(sock);
-    return 0;
-  }
-
-  conn->client_sock = sock;
-  conn->endpoint_type = EP_CLIENT;
-  conn->handler = handler;
-  conn->mg_conn.server_param = server->server_data;
-  conn->mg_conn.connection_param = param;
-  LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link);
-
-  if (connect_ret_val == 0) {
-    conn->flags = CONN_CONNECTED;
-    handler(&conn->mg_conn);
-  }
-
-  return 1;
-}
-
-#ifndef NO_LOGGING
-static void log_header(const struct mg_connection *conn, const char *header,
-                       FILE *fp) {
-  const char *header_value;
-
-  if ((header_value = mg_get_header(conn, header)) == NULL) {
-    (void) fprintf(fp, "%s", " -");
-  } else {
-    (void) fprintf(fp, " \"%s\"", header_value);
-  }
-}
-
-static void log_access(const struct connection *conn, const char *path) {
-  const struct mg_connection *c = &conn->mg_conn;
-  FILE *fp = (path == NULL) ?  NULL : fopen(path, "a+");
-  char date[64], user[100];
-
-  if (fp == NULL) return;
-  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
-           localtime(&conn->birth_time));
-
-  flockfile(fp);
-  mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
-                  user, sizeof(user));
-  fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
-          c->remote_ip, user[0] == '\0' ? "-" : user, date,
-          c->request_method ? c->request_method : "-",
-          c->uri ? c->uri : "-", c->http_version,
-          c->status_code, conn->num_bytes_sent);
-  log_header(c, "Referer", fp);
-  log_header(c, "User-Agent", fp);
-  fputc('\n', fp);
-  fflush(fp);
-
-  funlockfile(fp);
-  fclose(fp);
-}
-#endif
-
-static void gobble_prior_post_data(struct iobuf *io, int len) {
-  if (len > 0 && len <= io->len) {
-    memmove(io->buf, io->buf + len, io->len - len);
-    io->len -= len;
-  }
-}
-
-static void close_local_endpoint(struct connection *conn) {
-  // Must be done before free()
-  int keep_alive = should_keep_alive(&conn->mg_conn) &&
-    (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
-
-  switch (conn->endpoint_type) {
-    case EP_PUT: close(conn->endpoint.fd); break;
-    case EP_FILE: close(conn->endpoint.fd); break;
-    case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
-    default: break;
-  }
-
-#ifndef NO_LOGGING
-  if (conn->mg_conn.status_code != 400) {
-    log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
-  }
-#endif
-
-  if (conn->endpoint_type == EP_USER) {
-    gobble_prior_post_data(&conn->local_iobuf, conn->mg_conn.content_len);
-  }
-
-  conn->endpoint_type = EP_NONE;
-  conn->flags = 0;
-  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
-  free(conn->request);
-  conn->request = NULL;
-
-  if (keep_alive) {
-    process_request(conn);  // Can call us recursively if pipelining is used
-  } else {
-    conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
-  }
-}
-
-static void transfer_file_data(struct connection *conn) {
-  char buf[IOBUF_SIZE];
-  int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
-               (int) conn->cl : (int) sizeof(buf));
-
-  if (is_error(n)) {
-    close_local_endpoint(conn);
-  } else if (n > 0) {
-    conn->cl -= n;
-    spool(&conn->remote_iobuf, buf, n);
-    if (conn->cl <= 0) {
-      close_local_endpoint(conn);
-    }
-  }
-}
-
-static void execute_iteration(struct mg_server *server) {
-  struct ll *lp, *tmp;
-  struct connection *conn;
-  union { mg_handler_t f; void *p; } msg[2];
-
-  recv(server->ctl[1], (void *) msg, sizeof(msg), 0);
-  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
-    conn->mg_conn.connection_param = msg[1].p;
-    msg[0].f(&conn->mg_conn);
-  }
-}
-
-void add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
-  FD_SET(sock, set);
-  if (sock > *max_fd) {
-    *max_fd = sock;
-  }
-}
-
-unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
-  struct ll *lp, *tmp;
-  struct connection *conn;
-  struct timeval tv;
-  fd_set read_set, write_set;
-  sock_t max_fd = -1;
-  time_t current_time = time(NULL), expire_time = current_time -
-    USE_IDLE_TIMEOUT_SECONDS;
-
-  if (server->listening_sock == INVALID_SOCKET) return 0;
-
-  FD_ZERO(&read_set);
-  FD_ZERO(&write_set);
-  add_to_set(server->listening_sock, &read_set, &max_fd);
-  add_to_set(server->ctl[1], &read_set, &max_fd);
-
-  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
-    add_to_set(conn->client_sock, &read_set, &max_fd);
-    if (conn->endpoint_type == EP_FILE) {
-      transfer_file_data(conn);
-    } else if (conn->endpoint_type == EP_CGI) {
-      add_to_set(conn->endpoint.cgi_sock, &read_set, &max_fd);
-    }
-    if (conn->remote_iobuf.len > 0 && !(conn->flags & CONN_BUFFER)) {
-      add_to_set(conn->client_sock, &write_set, &max_fd);
-    } else if (conn->flags & CONN_CLOSE) {
-      close_conn(conn);
-    }
-  }
-
-  tv.tv_sec = milliseconds / 1000;
-  tv.tv_usec = (milliseconds % 1000) * 1000;
-
-  if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
-    if (FD_ISSET(server->ctl[1], &read_set)) {
-      execute_iteration(server);
-    }
-
-    // Accept new connections
-    if (FD_ISSET(server->listening_sock, &read_set)) {
-      while ((conn = accept_new_connection(server)) != NULL) {
-        conn->birth_time = conn->last_activity_time = current_time;
-      }
-    }
-
-    // Read/write from clients
-    LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-      conn = LINKED_LIST_ENTRY(lp, struct connection, link);
-      if (FD_ISSET(conn->client_sock, &read_set)) {
-        conn->last_activity_time = current_time;
-        read_from_socket(conn);
-      }
-#ifndef NO_CGI
-      if (conn->endpoint_type == EP_CGI &&
-          FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
-        read_from_cgi(conn);
-      }
-#endif
-      if (FD_ISSET(conn->client_sock, &write_set) &&
-          !(conn->flags & CONN_BUFFER)) {
-        conn->last_activity_time = current_time;
-        write_to_socket(conn);
-      }
-    }
-  }
-
-  // Close expired connections and those that need to be closed
-  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
-    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
-    if (conn->mg_conn.is_websocket) {
-      ping_idle_websocket_connection(conn, current_time);
-    }
-    if (conn->flags & CONN_LONG_RUNNING) {
-      conn->mg_conn.wsbits = conn->flags & CONN_CLOSE ? 1 : 0;
-      call_uri_handler(conn);
-    }
-    if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
-      close_conn(conn);
-    }
-  }
-
-  return (unsigned int) current_time;
-}
-
-void mg_destroy_server(struct mg_server **server) {
-  int i;
-  struct ll *lp, *tmp;
-
-  if (server != NULL && *server != NULL) {
-    // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
-    mg_poll_server(*server, 0);
-    closesocket((*server)->listening_sock);
-    closesocket((*server)->ctl[0]);
-    closesocket((*server)->ctl[1]);
-    LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
-      free(LINKED_LIST_ENTRY(lp, struct connection, link));
-    }
-    LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
-      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link)->uri);
-      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
-    }
-    for (i = 0; i < (int) ARRAY_SIZE((*server)->config_options); i++) {
-      free((*server)->config_options[i]);  // It is OK to free(NULL)
-    }
-#ifdef USE_SSL
-    if ((*server)->ssl_ctx != NULL) SSL_CTX_free((*server)->ssl_ctx);
-#endif
-    free(*server);
-    *server = NULL;
-  }
-}
-
-// Apply function to all active connections.
-void mg_iterate_over_connections(struct mg_server *server, mg_handler_t handler,
-                                 void *param) {
-  // Send closure (function + parameter) to the IO thread to execute
-  union { mg_handler_t f; void *p; } msg[2];
-  msg[0].f = handler;
-  msg[1].p = param;
-  send(server->ctl[0], (void *) msg, sizeof(msg), 0);
-}
-
-void mg_add_uri_handler(struct mg_server *server, const char *uri,
-                        mg_handler_t handler) {
-  struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
-  if (p != NULL) {
-    LINKED_LIST_ADD_TO_FRONT(&server->uri_handlers, &p->link);
-    p->uri = mg_strdup(uri);
-    p->handler = handler;
-  }
-}
-
-static int get_var(const char *data, size_t data_len, const char *name,
-                   char *dst, size_t dst_len) {
-  const char *p, *e, *s;
-  size_t name_len;
-  int len;
-
-  if (dst == NULL || dst_len == 0) {
-    len = -2;
-  } else if (data == NULL || name == NULL || data_len == 0) {
-    len = -1;
-    dst[0] = '\0';
-  } else {
-    name_len = strlen(name);
-    e = data + data_len;
-    len = -1;
-    dst[0] = '\0';
-
-    // data is "var1=val1&var2=val2...". Find variable first
-    for (p = data; p + name_len < e; p++) {
-      if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
-          !mg_strncasecmp(name, p, name_len)) {
-
-        // Point p to variable value
-        p += name_len + 1;
-
-        // Point s to the end of the value
-        s = (const char *) memchr(p, '&', (size_t)(e - p));
-        if (s == NULL) {
-          s = e;
-        }
-        assert(s >= p);
-
-        // Decode variable into destination buffer
-        len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
-
-        // Redirect error code from -1 to -2 (destination buffer too small).
-        if (len == -1) {
-          len = -2;
-        }
-        break;
-      }
-    }
-  }
-
-  return len;
-}
-
-int mg_get_var(const struct mg_connection *conn, const char *name,
-               char *dst, size_t dst_len) {
-  int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
-                    strlen(conn->query_string), name, dst, dst_len);
-  if (len < 0) {
-    len = get_var(conn->content, conn->content_len, name, dst, dst_len);
-  }
-  return len;
-}
-
-static int get_line_len(const char *buf, int buf_len) {
-  int len = 0;
-  while (len < buf_len && buf[len] != '\n') len++;
-  return buf[len] == '\n' ? len + 1: -1;
-}
-
-int mg_parse_multipart(const char *buf, int buf_len,
-                       char *var_name, int var_name_len,
-                       char *file_name, int file_name_len,
-                       const char **data, int *data_len) {
-  static const char cd[] = "Content-Disposition: ";
-  //struct mg_connection c;
-  int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
-  //char *p;
-
-  if (buf == NULL || buf_len <= 0) return 0;
-  if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
-  if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
-
-  // Get boundary length
-  bl = get_line_len(buf, buf_len);
-
-  // Loop through headers, fetch variable name and file name
-  var_name[0] = file_name[0] = '\0';
-  for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
-    if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
-      parse_header(buf + n + cdl, ll - (cdl + 2), "name",
-                   var_name, var_name_len);
-      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
-                   file_name, file_name_len);
-    }
-  }
-
-  // Scan body, search for terminating boundary
-  for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
-    if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
-      if (data_len != NULL) *data_len = (pos - 2) - hl;
-      if (data != NULL) *data = buf + hl;
-      return pos;
-    }
-  }
-
-  return 0;
-}
-
-const char **mg_get_valid_option_names(void) {
-  return static_config_options;
-}
-
-static int get_option_index(const char *name) {
-  int i;
-
-  for (i = 0; static_config_options[i * 2] != NULL; i++) {
-    if (strcmp(static_config_options[i * 2], name) == 0) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-static void set_default_option_values(char **opts) {
-  const char *value, **all_opts = mg_get_valid_option_names();
-  int i;
-
-  for (i = 0; all_opts[i * 2] != NULL; i++) {
-    value = all_opts[i * 2 + 1];
-    if (opts[i] == NULL && value != NULL) {
-      opts[i] = mg_strdup(value);
-    }
-  }
-}
-
-// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
-static int parse_port_string(const char *str, union socket_address *sa) {
-  unsigned int a, b, c, d, port;
-  int len = 0;
-#ifdef USE_IPV6
-  char buf[100];
-#endif
-
-  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
-  // Also, all-zeroes in the socket address means binding to all addresses
-  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
-  memset(sa, 0, sizeof(*sa));
-  sa->sin.sin_family = AF_INET;
-
-  if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
-    // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
-    sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
-    sa->sin.sin_port = htons((uint16_t) port);
-#if defined(USE_IPV6)
-  } else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 &&
-             inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
-    // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
-    sa->sin6.sin6_family = AF_INET6;
-    sa->sin6.sin6_port = htons((uint16_t) port);
-#endif
-  } else if (sscanf(str, "%u%n", &port, &len) == 1) {
-    // If only port is specified, bind to IPv4, INADDR_ANY
-    sa->sin.sin_port = htons((uint16_t) port);
-  } else {
-    port = 0;   // Parsing failure. Make port invalid.
-  }
-
-  return port > 0 && port < 0xffff && str[len] == '\0';
-}
-
-const char *mg_set_option(struct mg_server *server, const char *name,
-                          const char *value) {
-  int ind = get_option_index(name);
-  const char *error_msg = NULL;
-
-  if (ind < 0) {
-    error_msg = "No such option";
-  } else {
-    if (server->config_options[ind] != NULL) {
-      free(server->config_options[ind]);
-    }
-    server->config_options[ind] = mg_strdup(value);
-    DBG(("%s => %s", name, value));
-
-    if (ind == LISTENING_PORT) {
-      if (server->listening_sock != INVALID_SOCKET) {
-        closesocket(server->listening_sock);
-      }
-      parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
-      server->listening_sock = open_listening_socket(&server->lsa);
-      if (server->listening_sock == INVALID_SOCKET) {
-        error_msg = "Cannot bind to port";
-      } else {
-        set_non_blocking_mode(server->listening_sock);
-      }
-#ifndef _WIN32
-    } else if (ind == RUN_AS_USER) {
-      struct passwd *pw;
-      if ((pw = getpwnam(value)) == NULL) {
-        error_msg = "Unknown user";
-      } else if (setgid(pw->pw_gid) != 0) {
-        error_msg = "setgid() failed";
-      } else if (setuid(pw->pw_uid) != 0) {
-        error_msg = "setuid() failed";
-      }
-#endif
-#ifdef USE_SSL
-    } else if (ind == SSL_CERTIFICATE) {
-      SSL_library_init();
-      if ((server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
-        error_msg = "SSL_CTX_new() failed";
-      } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, value, 1) == 0 ||
-                 SSL_CTX_use_PrivateKey_file(server->ssl_ctx, value, 1) == 0) {
-        error_msg = "Cannot load PEM file";
-      } else {
-        SSL_CTX_use_certificate_chain_file(server->ssl_ctx, value);
-      }
-#endif
-    }
-  }
-
-  return error_msg;
-}
-
-
-void mg_set_http_error_handler(struct mg_server *server, mg_handler_t handler) {
-  server->error_handler = handler;
-}
-
-void mg_set_listening_socket(struct mg_server *server, int sock) {
-  if (server->listening_sock != INVALID_SOCKET) {
-    closesocket(server->listening_sock);
-  }
-  server->listening_sock = (sock_t) sock;
-}
-
-int mg_get_listening_socket(struct mg_server *server) {
-  return server->listening_sock;
-}
-
-const char *mg_get_option(const struct mg_server *server, const char *name) {
-  const char **opts = (const char **) server->config_options;
-  int i = get_option_index(name);
-  return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
-}
-
-struct mg_server *mg_create_server(void *server_data) {
-  struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
-
-  server->do_i_handle = NULL;
-
-#ifdef _WIN32
-  WSADATA data;
-  WSAStartup(MAKEWORD(2, 2), &data);
-#else
-  // Ignore SIGPIPE signal, so if browser cancels the request, it
-  // won't kill the whole process.
-  signal(SIGPIPE, SIG_IGN);
-#endif
-
-  LINKED_LIST_INIT(&server->active_connections);
-  LINKED_LIST_INIT(&server->uri_handlers);
-
-  // Create control socket pair. Do it in a loop to protect from
-  // interrupted syscalls in mg_socketpair().
-  do {
-    mg_socketpair(server->ctl);
-  } while (server->ctl[0] == INVALID_SOCKET);
-
-  server->server_data = server_data;
-  server->listening_sock = INVALID_SOCKET;
-  set_default_option_values(server->config_options);
-
-  return server;
-}
-
-void mg_server_do_i_handle(struct mg_server *server, mg_handler_t handler)
-{
-    server->do_i_handle = handler;
-}
diff --git a/mongoose.h b/mongoose.h
deleted file mode 100644
index 014eee1b30..0000000000
--- a/mongoose.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2004-2013 Sergey Lyubka 
-// Copyright (c) 2013-2014 Cesanta Software Limited
-// All rights reserved
-//
-// This library is dual-licensed: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation. For the terms of this
-// license, see .
-//
-// You are free to use this library under the terms of the GNU General
-// Public License, but WITHOUT ANY WARRANTY; without even the implied
-// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-// See the GNU General Public License for more details.
-//
-// Alternatively, you can license this library under a commercial
-// license, as set out in .
-//
-// NOTE: Detailed API documentation is at http://cesanta.com/docs.html
-
-#ifndef MONGOOSE_HEADER_INCLUDED
-#define  MONGOOSE_HEADER_INCLUDED
-
-#define MONGOOSE_VERSION "5.2"
-
-#include       // required for FILE
-#include      // required for size_t
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-// This structure contains information about HTTP request.
-struct mg_connection {
-  const char *request_method; // "GET", "POST", etc
-  const char *uri;            // URL-decoded URI
-  const char *http_version;   // E.g. "1.0", "1.1"
-  const char *query_string;   // URL part after '?', not including '?', or NULL
-
-  char remote_ip[48];         // Max IPv6 string length is 45 characters
-  int remote_port;            // Client's port
-
-  int num_headers;            // Number of HTTP headers
-  struct mg_header {
-    const char *name;         // HTTP header name
-    const char *value;        // HTTP header value
-  } http_headers[30];
-
-  char *content;              // POST (or websocket message) data, or NULL
-  int content_len;            // content length
-
-  int is_websocket;           // Connection is a websocket connection
-  int status_code;            // HTTP status code for HTTP error handler
-  unsigned char wsbits;       // First byte of the websocket frame
-  void *server_param;         // Parameter passed to mg_add_uri_handler()
-  void *connection_param;     // Placeholder for connection-specific data
-};
-
-struct mg_server; // Opaque structure describing server instance
-typedef int (*mg_handler_t)(struct mg_connection *);
-
-// Server management functions
-struct mg_server *mg_create_server(void *server_param);
-void mg_destroy_server(struct mg_server **);
-const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
-unsigned int mg_poll_server(struct mg_server *, int milliseconds);
-void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t);
-void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
-const char **mg_get_valid_option_names(void);
-const char *mg_get_option(const struct mg_server *server, const char *name);
-void mg_set_listening_socket(struct mg_server *, int sock);
-int mg_get_listening_socket(struct mg_server *);
-void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
-
-// Connection management functions
-void mg_send_status(struct mg_connection *, int status_code);
-void mg_send_header(struct mg_connection *, const char *name, const char *val);
-void mg_send_data(struct mg_connection *, const void *data, int data_len);
-void mg_printf_data(struct mg_connection *, const char *format, ...);
-
-int mg_websocket_write(struct mg_connection *, int opcode,
-                       const char *data, size_t data_len);
-
-// Deprecated in favor of mg_send_* interface
-int mg_write(struct mg_connection *, const void *buf, int len);
-int mg_printf(struct mg_connection *conn, const char *fmt, ...);
-
-
-const char *mg_get_header(const struct mg_connection *, const char *name);
-const char *mg_get_mime_type(const char *file_name);
-int mg_get_var(const struct mg_connection *conn, const char *var_name,
-               char *buf, size_t buf_len);
-int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
-int mg_parse_multipart(const char *buf, int buf_len,
-                       char *var_name, int var_name_len,
-                       char *file_name, int file_name_len,
-                       const char **data, int *data_len);
-
-// Utility functions
-void *mg_start_thread(void *(*func)(void *), void *param);
-char *mg_md5(char buf[33], ...);
-int mg_authorize_digest(struct mg_connection *c, FILE *fp);
-void mg_send_digest_auth_request(struct mg_connection *conn);
-
-void mg_server_do_i_handle(struct mg_server *server, mg_handler_t handler);
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif // MONGOOSE_HEADER_INCLUDED
diff --git a/mongoose/Controller.cpp b/mongoose/Controller.cpp
deleted file mode 100644
index 92a2c291d5..0000000000
--- a/mongoose/Controller.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-#include 
-#include "Controller.h"
-#include "StreamResponse.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    Controller::Controller() 
-        : sessions(NULL), server(NULL), prefix("")
-    {
-    }
-
-    void Controller::setup()
-    {
-    }
-
-    void Controller::setServer(Server *server_)
-    {
-        server = server_;
-    }
-
-    void Controller::webSocketReady(WebSocket *websocket)
-    {
-    }
-
-    void Controller::webSocketData(WebSocket *websocket, string data)
-    {
-    }
-    
-    Controller::~Controller()
-    {
-        map::iterator it;
-
-        for (it=routes.begin(); it!=routes.end(); it++) {
-            delete (*it).second;
-        }
-
-        routes.clear();
-    }
-            
-    bool Controller::handles(string method, string url)
-    { 
-        string key = method + ":" + url;
-
-        return (routes.find(key) != routes.end());
-    }
-
-    Response *Controller::process(Request &request)
-    {
-        Response *response = NULL;
-
-#ifdef ENABLE_REGEX_URL
-        map::iterator it; 
-        for (it=routes.begin(); it!=routes.end(); it++) {
-            if (request.match(it->first)){
-              response = it->second->process(request);
-              break;
-            }   
-        }   
-#else
-        string key = request.getMethod() + ":" + request.getUrl();
-        if (routes.find(key) != routes.end()) {
-            response = routes[key]->process(request);
-        }
-#endif
-        
-        return response;
-    }
-            
-    void Controller::preProcess(Request &request, Response &response)
-    {
-    }
-            
-    void Controller::postProcess(Request &request, Response &response)
-    {
-    }
-
-    Response *Controller::handleRequest(Request &request)
-    {
-        Response *response = process(request);
-
-        if (response != NULL) {
-            postProcess(request, *response);
-        }
-
-        return response;
-    }
-
-    void Controller::setPrefix(string prefix_)
-    {
-        prefix = prefix_;
-    }
-            
-    void Controller::registerRoute(string httpMethod, string route, RequestHandlerBase *handler)
-    {
-        string key = httpMethod + ":" + prefix + route;
-        routes[key] = handler;
-        urls.push_back(prefix + route);
-    }
-
-    void Controller::dumpRoutes()
-    {
-        map::iterator it;
-
-        for (it=routes.begin(); it!=routes.end(); it++) {
-            cout << (*it).first << endl;
-        }
-
-    }
-
-    Response *Controller::serverInternalError(string message)
-    {
-        StreamResponse *response = new StreamResponse;
-
-        response->setCode(HTTP_SERVER_ERROR);
-        *response << "[500] Server internal error: " << message;
-
-        return response;
-    }
-
-    vector Controller::getUrls()
-    {
-        return urls;
-    }
-
-    Session &Controller::getSession(Request &request, Response &response)
-    {
-        return sessions->get(request, response);
-    }
-
-    void Controller::setSessions(Sessions *sessions_)
-    {
-        sessions = sessions_;
-    }
-}
diff --git a/mongoose/Controller.h b/mongoose/Controller.h
deleted file mode 100644
index 364c8a7f05..0000000000
--- a/mongoose/Controller.h
+++ /dev/null
@@ -1,158 +0,0 @@
-#ifndef _MONGOOSE_CONTROLLER_H
-#define _MONGOOSE_CONTROLLER_H
-
-#include 
-#include 
-#include "Request.h"
-#include "RequestHandler.h"
-#include "StreamResponse.h"
-#include "WebSocket.h"
-#include "Sessions.h"
-
-using namespace std;
-
-#define addRoute(httpMethod, url, controllerType, method) \
-    registerRoute(httpMethod, url, new RequestHandler(this, &controllerType::method ));
-
-#define addRouteResponse(httpMethod, url, controllerType, method, responseType) \
-    registerRoute(httpMethod, url, new RequestHandler(this, &controllerType::method ));
-
-/**
- * A controller is a module that respond to requests
- *
- * You can override the preProcess, process and postProcess to answer to
- * the requests
- */
-namespace Mongoose
-{
-    class Server;
-
-    class Controller
-    {
-        public:
-            Controller();
-            virtual ~Controller();
-
-            /**
-             * Sets the reference to the server hosting this controller
-             *
-             * @param Server the hosting server
-             */
-            virtual void setServer(Server *server);
-
-            /**
-             * Called before a request is processed
-             *
-             * @param Request the request
-             * @param Response the response
-             */
-            virtual void preProcess(Request &request, Response &response);
-
-            /**
-             * Called to process a request
-             *
-             * @param Request the request
-             *
-             * @return Response the created response, or NULL if the controller
-             *         does not handle this request
-             */
-            virtual Response *process(Request &request);
-
-            /**
-             * Called after a request is processed, if the controller responded
-             *
-             * @param Request the request
-             * @param Response the response
-             */
-            virtual void postProcess(Request &request, Response &response);
-
-            /**
-             * Handle a request, this will try to match the request, if this
-             * controller handles it, it will preProcess, process then postProcess it
-             *
-             * @param Request the request
-             *
-             * @return Response the created response, or NULL if the controller
-             *         does not handle this request
-             */
-            virtual Response *handleRequest(Request &request);
-
-            /**
-             * Sets the controller prefix, for instance "/api"
-             *
-             * @param string the prefix of all urls for this controller
-             */
-            void setPrefix(string prefix);
-
-            /**
-             * Called when a new websocket connection is ready
-             *
-             * @param WebSocket the instance of the connection
-             */
-            virtual void webSocketReady(WebSocket *websocket);
-
-            /**
-             * Called when data arrive in a websocket connection
-             *
-             * @param WebSocket the instance of the connection
-             * @param string the data arriving
-             */
-            virtual void webSocketData(WebSocket *websocket, string data);
-
-            /**
-             * Registers a route to the controller
-             *
-             * @param string the route path
-             * @param RequestHandlerBase the request handler for this route
-             */
-            virtual void registerRoute(string httpMethod, string route, RequestHandlerBase *handler);
-
-            /**
-             * Initializes the route and settings
-             */
-            virtual void setup();
-
-            /**
-             * Dump all routes
-             */
-            void dumpRoutes();
-
-            /**
-             * Called when an exception occur during the rendering
-             *
-             * @param string the error message
-             *
-             * @return response a response to send, 404 will occur if NULL
-             */
-            virtual Response *serverInternalError(string message);
-
-            /**
-             * Gets the session for a request/response
-             *
-             * @param Request the request
-             * @param Response the response
-             *
-             * @return Session the session for the request/response
-             */
-            Session &getSession(Request &request, Response &response);
-
-            /**
-             * Sets the sessions
-             *
-             * @param Sessions* the pointer to the sessions jar
-             */
-            void setSessions(Sessions *sessions);
-
-            virtual bool handles(string method, string url);
-            vector getUrls();
-
-        protected:
-            Sessions *sessions;
-            Server *server;
-            string prefix;
-            map routes;
-            vector urls;
-    };
-}
-
-#endif
diff --git a/mongoose/JsonController.cpp b/mongoose/JsonController.cpp
deleted file mode 100644
index d37476ccfb..0000000000
--- a/mongoose/JsonController.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "JsonController.h"
-#include "Session.h"
-
-namespace Mongoose
-{        
-    JsonController::JsonController(int gcDivisor_) :
-        WebController(gcDivisor_)
-    {
-    }
-
-    void JsonController::preProcess(Request &request, Response &response)
-    {
-        WebController::preProcess(request, response);
-
-        // RFC 4627
-        // Json content type is application/json
-        response.setHeader("Content-Type", "application/json");
-    }            
-}
diff --git a/mongoose/JsonController.h b/mongoose/JsonController.h
deleted file mode 100644
index 9b78729a59..0000000000
--- a/mongoose/JsonController.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef _MONGOOSE_JSON_CONTROLLER_H
-#define _MONGOOSE_JSON_CONTROLLER_H
-
-#include "Request.h"
-#include "Response.h"
-#include "WebController.h"
-#include "JsonResponse.h"
-
-using namespace std;
-
-/**
- * A json controller is a controller that serves JSON API
- */
-namespace Mongoose
-{
-    class JsonController : public WebController
-    {
-        public:
-            /**
-             * Creates a json controller, each gcDivisor request, the sessions will be
-             * garbage collected
-             */
-            JsonController(int gcDivisor = 100);
-
-            /**
-             * Pre process the request, this will set the content type to text/html
-             * and ping the user session
-             *
-             * @param Request the request
-             * @param Response the response
-             */
-            void preProcess(Request &request, Response &response);
-    };
-}
-
-#endif
diff --git a/mongoose/JsonResponse.cpp b/mongoose/JsonResponse.cpp
deleted file mode 100644
index d6638d5148..0000000000
--- a/mongoose/JsonResponse.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include 
-#include 
-#include "JsonResponse.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    JsonResponse::JsonResponse()
-        : humanReadable(false)
-    {
-    }
-
-    string JsonResponse::getBody()
-    {
-        if (humanReadable) {
-            Json::StyledWriter writer;
-            return writer.write(*this);
-        } else {
-            Json::FastWriter writer;
-            return writer.write(*this);
-        }
-    }
-
-    void JsonResponse::setHuman(bool human)
-    {
-        humanReadable = human;
-    }
-}
diff --git a/mongoose/JsonResponse.h b/mongoose/JsonResponse.h
deleted file mode 100644
index 189f569bdd..0000000000
--- a/mongoose/JsonResponse.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef _MONGOOSE_JSON_RESPONSE_H
-#define _MONGOOSE_JSON_RESPONSE_H
-
-#include 
-#include 
-#include 
-#include 
-
-#include "Response.h"
-
-using namespace std;
-
-/**
- * A stream response to a request
- */
-namespace Mongoose
-{
-    class JsonResponse : public Json::Value, public Response
-    {
-        public:
-            JsonResponse();
-
-            /**
-             * Gets the response body
-             *
-             * @return string the response body
-             */
-            virtual string getBody();
-
-            /**
-             * Sets the human readability of the response
-             *
-             * @param bool true for human readable
-             */
-            void setHuman(bool human);
-
-        protected:
-            bool humanReadable;
-    };
-}
-
-#endif
diff --git a/mongoose/LICENSE b/mongoose/LICENSE
deleted file mode 100644
index 3f32e05453..0000000000
--- a/mongoose/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) <2013> Grégoire Passault
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/mongoose/Mutex.cpp b/mongoose/Mutex.cpp
deleted file mode 100644
index 86fa355774..0000000000
--- a/mongoose/Mutex.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include 
-#include 
-
-#include "Mutex.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-	#ifdef _MSC_VER
-	static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
-	  (void) unused;
-	  *mutex = CreateMutex(NULL, FALSE, NULL);
-	  return *mutex == NULL ? -1 : 0;
-	}
-
-	static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
-	  return CloseHandle(*mutex) == 0 ? -1 : 0;
-	}
-
-	static int pthread_mutex_lock(pthread_mutex_t *mutex) {
-	  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
-	}
-
-	static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
-	  return ReleaseMutex(*mutex) == 0 ? -1 : 0;
-	}
-	#endif
-
-    Mutex::Mutex()
-    {
-        pthread_mutex_init(&_mutex, NULL);
-    }
-
-    Mutex::~Mutex()
-    {
-        pthread_mutex_destroy(&_mutex);
-    }
-
-    void Mutex::lock()
-    {
-        pthread_mutex_lock(&_mutex);
-    }
-
-    void Mutex::unlock()
-    {
-        pthread_mutex_unlock(&_mutex);
-    }
-}
diff --git a/mongoose/Mutex.h b/mongoose/Mutex.h
deleted file mode 100644
index adc21e46b7..0000000000
--- a/mongoose/Mutex.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef _MONGOOSE_MUTEX_H
-#define _MONGOOSE_MUTEX_H
-
-#ifndef _MSC_VER
-#include 
-#else
-#include 
-typedef HANDLE pthread_mutex_t;
-#endif
-
-/**
- * A mutex allow thread-safe operations
- */
-namespace Mongoose
-{
-    class Mutex
-    {
-        public:
-            Mutex();
-            virtual ~Mutex();
-
-            /**
-             * Locks the mutex
-             */
-            virtual void lock();
-
-            /**
-             * Unlock the mutex
-             */
-            virtual void unlock();
-
-        protected:
-            pthread_mutex_t _mutex;
-    };
-}
-
-#endif
-
diff --git a/mongoose/Request.cpp b/mongoose/Request.cpp
deleted file mode 100644
index 97f641889d..0000000000
--- a/mongoose/Request.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-#include 
-#include 
-#include 
-#include 
-#include 
-#include "Request.h"
-
-using namespace std;
-
-static int lowercase(const char *s) {
-  return tolower(* (const unsigned char *) s);
-}
-
-static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
-  int diff = 0;
-
-  if (len > 0)
-    do {
-      diff = lowercase(s1++) - lowercase(s2++);
-    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
-
-  return diff;
-}
-
-static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
-  for (; *src != '\0' && n > 1; n--) {
-    *dst++ = *src++;
-  }
-  *dst = '\0';
-}
-
-/*
-static int mg_strcasecmp(const char *s1, const char *s2) {
-  int diff;
-
-  do {
-    diff = lowercase(s1++) - lowercase(s2++);
-  } while (diff == 0 && s1[-1] != '\0');
-
-  return diff;
-}
-*/
-
-static const char *mg_strcasestr(const char *big_str, const char *small_str) {
-  int i, big_len = strlen(big_str), small_len = strlen(small_str);
-
-  for (i = 0; i <= big_len - small_len; i++) {
-    if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
-      return big_str + i;
-    }
-  }
-
-  return NULL;
-}
-
-static int mg_get_cookie(const char *cookie_header, const char *var_name,
-                  char *dst, size_t dst_size) {
-  const char *s, *p, *end;
-  int name_len, len = -1;
-
-  if (dst == NULL || dst_size == 0) {
-    len = -2;
-  } else if (var_name == NULL || (s = cookie_header) == NULL) {
-    len = -1;
-    dst[0] = '\0';
-  } else {
-    name_len = (int) strlen(var_name);
-    end = s + strlen(s);
-    dst[0] = '\0';
-
-    for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
-      if (s[name_len] == '=') {
-        s += name_len + 1;
-        if ((p = strchr(s, ' ')) == NULL)
-          p = end;
-        if (p[-1] == ';')
-          p--;
-        if (*s == '"' && p[-1] == '"' && p > s + 1) {
-          s++;
-          p--;
-        }
-        if ((size_t) (p - s) < dst_size) {
-          len = p - s;
-          mg_strlcpy(dst, s, (size_t) len + 1);
-        } else {
-          len = -3;
-        }
-        break;
-      }
-    }
-  }
-  return len;
-}
-
-namespace Mongoose
-{
-    Request::Request(struct mg_connection *connection_) :
-        connection(connection_)
-    {
-        url = string(connection->uri);
-        method = string(connection->request_method);
-    }
-
-    string Request::getUrl()
-    {
-        return url;
-    }
-
-    string Request::getMethod()
-    {
-        return method;
-    }
-
-   string Request::getData()
-   {
-       //Downloading POST data
-       ostringstream postData;
-       postData.write(connection->content, connection->content_len);
-       return postData.str();
-   }
-
-#ifdef ENABLE_REGEX_URL
-    smatch Request::getMatches()
-    {
-        return matches;
-    }
-
-    bool Request::match(string pattern)
-    {
-        key = method + ":" + url;
-        return regex_match(key, matches, regex(pattern));
-    }
-#endif
-
-    void Request::writeResponse(Response *response)
-    {
-        string data = response->getData();
-
-        mg_write(connection, data.c_str(), data.size());
-    }
-
-    bool Request::hasVariable(string key)
-    {
-        const char *dataField;
-        char dummy[10];
-
-        // Looking on the query string
-        dataField = connection->query_string;
-        if (dataField != NULL && mg_get_var(connection, key.c_str(), dummy, 1) != -1) {
-            return true;
-        }
-
-        return false;
-    }
-
-//    map Request::getAllVariable()
-//    {
-//        map mapKeyValue;
-//        stringstream ss(data);
-//        string param;
-//        while(std::getline(ss, param, '&')){ //block for '&'
-//            const string& key = param.substr(0, param.find('='));
-//            const string& value = param.substr(param.find('=')+1);
-//            mapKeyValue[key] = value; // insert map
-//        }
-//        return mapKeyValue;
-//    }
-
-    bool Request::readVariable(const char *data, string key, string &output)
-    {
-        int size = 1024, ret;
-        char *buffer = new char[size];
-
-        do {
-            ret = mg_get_var(connection, key.c_str(), buffer, size);
-
-            if (ret == -1) {
-                return false;
-            }
-
-            if (ret == -2) {
-                size *= 2;
-                delete[] buffer;
-                buffer = new char[size];
-            }
-        } while (ret == -2);
-
-        output = string(buffer);
-        delete[] buffer;
-
-        return true;
-    }
-
-
-    string Request::get(string key, string fallback)
-    {
-        const char *dataField;
-        string output;
-
-        // Looking on the query string
-        dataField = connection->query_string;
-        if (dataField != NULL && readVariable(dataField, key, output)) {
-            return output;
-        }
-
-        // Looking on the POST data
-        dataField = connection->content;
-        if (dataField != NULL && readVariable(dataField, key, output)) {
-            return output;
-        }
-
-        return fallback;
-    }
-
-    bool Request::hasCookie(string key)
-    {
-        int i;
-        char dummy[10];
-
-        for (i=0; inum_headers; i++) {
-            const struct mg_connection::mg_header *header = &connection->http_headers[i];
-
-            if (strcmp(header->name, "Cookie") == 0) {
-                if (mg_get_cookie(header->value, key.c_str(), dummy, sizeof(dummy)) != -1) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    string Request::getCookie(string key, string fallback)
-    {
-        string output;
-        int i;
-        int size = 1024;
-        int ret;
-        char *buffer = new char[size];
-        char dummy[10];
-        const char *place = NULL;
-
-        for (i=0; inum_headers; i++) {
-            const struct mg_connection::mg_header *header = &connection->http_headers[i];
-
-            if (strcmp(header->name, "Cookie") == 0) {
-                if (mg_get_cookie(header->value, key.c_str(), dummy, sizeof(dummy)) != -1) {
-                    place = header->value;
-                }
-            }
-        }
-
-        if (place == NULL) {
-            return fallback;
-        }
-
-        do {
-            ret = mg_get_cookie(place, key.c_str(), buffer, size);
-
-            if (ret == -2) {
-                size *= 2;
-                delete[] buffer;
-                buffer = new char[size];
-            }
-        } while (ret == -2);
-
-        output = string(buffer);
-        delete[] buffer;
-
-        return output;
-    }
-
-    string Request::getHeaderKeyValue(const std::string& header_key) {
-      string output;
-      for (int i=0; inum_headers; i++) {
-        const struct mg_connection::mg_header *header = &connection->http_headers[i];
-        if (strcmp(header->name, header_key.c_str()) == 0) {
-            output = header->value;
-            break;
-        }
-      }
-      return output;
-    }
-
-    std::vector Request::handleUploads(const string &upload_path)
-    {
-        std::vector result;
-        char var_name[1024];
-        char file_name[1024];
-        const char *data;
-        int data_len;
-
-        if (mg_parse_multipart(connection->content, connection->content_len,
-                    var_name, sizeof(var_name), file_name, sizeof(file_name), &data, &data_len)) {
-
-            std::string file_path = upload_path + "/" + std::string(file_name);
-            std::ofstream outfile(file_path.c_str(), std::ofstream::binary);
-            outfile.write(data, data_len);
-            result.push_back(file_path);
-        }
-
-        return result;
-    }
-}
diff --git a/mongoose/Request.h b/mongoose/Request.h
deleted file mode 100644
index 502822d1b0..0000000000
--- a/mongoose/Request.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef _MONGOOSE_REQUEST_H
-#define _MONGOOSE_REQUEST_H
-
-#include 
-#include 
-#include 
-#ifdef ENABLE_REGEX_URL
-#include 
-#endif
-#include 
-#include "UploadFile.h"
-#include "Response.h"
-
-using namespace std;
-
-/**
- * Request is a wrapper for the clients requests
- */
-namespace Mongoose
-{
-    class Request
-    {
-        public:
-            Request(struct mg_connection *connection);
-
-            /**
-             * Sends a given response to the client
-             *
-             * @param Response a response for this request
-             */
-            void writeResponse(Response *response);
-
-            /**
-             * Check if the variable given by key is present in GET or POST data
-             *
-             * @param string the name of the variable
-             *
-             * @return bool true if the param is present, false else
-             */
-            bool hasVariable(string key);
-
-            /**
-             * Get All variable present in GET or POST data
-             *
-             * @brief getAllVariable
-             * @return map with all variables
-             */
-//            map getAllVariable();
-
-            /**
-             * Get the value for a certain variable
-             *
-             * @param string the name of the variable
-             * @param string the fallback value if the variable doesn't exists
-             *
-             * @return string the value of the variable if it exists, fallback else
-             */
-            string get(string key, string fallback = "");
-
-            /**
-             * Checks if the given cookie exists
-             *
-             * @param string the name of the cookie
-             *
-             * @return bool true if the given cookie is set
-             */
-            bool hasCookie(string key);
-
-            /**
-             * Try to get the cookie value
-             *
-             * @param string the name of the cookie
-             * @param string the fallback value
-             *
-             * @retun the value of the cookie if it exists, fallback else
-             */
-            string getCookie(string key, string fallback = "");
-
-
-            string getHeaderKeyValue(const std::string& header_key);
-
-            /**
-             * Handle uploads to the target directory
-             *
-             * @param string the target directory
-             * @param path the posted file path
-             */
-            std::vector handleUploads(const std::string& upload_path);
-
-            string getUrl();
-            string getMethod();
-            string getData();
-
-#ifdef ENABLE_REGEX_URL
-            smatch getMatches();
-            bool match(string pattern);
-#endif
-            bool readVariable(const char *data, string key, string &output);
-
-        protected:
-            string method;
-            string url;
-            struct mg_connection *connection;
-    };
-}
-
-#endif
diff --git a/mongoose/RequestHandler.h b/mongoose/RequestHandler.h
deleted file mode 100644
index cad5f03a69..0000000000
--- a/mongoose/RequestHandler.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef _MONGOOSE_REQUEST_HANDLER_H
-#define _MONGOOSE_REQUEST_HANDLER_H
-
-#include "Request.h"
-#include "Response.h"
-#include 
-
-namespace Mongoose
-{
-    class RequestHandlerBase
-    {
-        public:
-            virtual Response *process(Request &request)=0;
-    };
-
-    template
-    class RequestHandler : public RequestHandlerBase
-    {
-        public:
-            typedef void (T::*fPtr)(Request &request, R &response);
-
-            RequestHandler(T *controller_, fPtr function_)
-                : controller(controller_), function(function_)
-            {
-            }
-
-            Response *process(Request &request)
-            {
-                R *response = new R;
-
-                try {
-                    controller->preProcess(request, *response);
-                    (controller->*function)(request, *response);
-                } catch (string exception) {
-                    return controller->serverInternalError(exception);
-                } catch (...) {
-                    return controller->serverInternalError("Unknown error");
-                }
-
-                return response;
-            }
-
-        protected:
-            T *controller;
-            fPtr function;
-    };
-}
-
-#endif
diff --git a/mongoose/Response.cpp b/mongoose/Response.cpp
deleted file mode 100644
index 809e621f76..0000000000
--- a/mongoose/Response.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include 
-#include "Response.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    Response::Response() : code(HTTP_OK), headers()
-    {
-    }
-            
-    Response::~Response()
-    {
-    }
-            
-    void Response::setHeader(string key, string value)
-    {
-        headers[key] = value;
-    }
-
-    bool Response::hasHeader(string key)
-    {
-        return headers.find(key) != headers.end();
-    }
-
-    string Response::getData()
-    {
-        string body = getBody();
-        ostringstream data;
-
-        data << "HTTP/1.0 " << code << "\r\n";
-
-        if (!hasHeader("Content-Length")) {
-            ostringstream length;
-            length << body.size();
-            setHeader("Content-Length", length.str());
-        }
-
-        map::iterator it;
-        for (it=headers.begin(); it!=headers.end(); it++) {
-            data << (*it).first << ": " << (*it).second << "\r\n";
-        }
-
-        data << "\r\n";
-
-        data << body;
-
-        return data.str();
-    }
-
-    void Response::setCookie(string key, string value)
-    {
-        ostringstream definition;
-        definition << key << "=" << value << "; path=/";
-
-        setHeader("Set-cookie", definition.str());
-    }
-
-    void Response::setCode(int code_)
-    {
-        code = code_;
-    }
-}
diff --git a/mongoose/Response.h b/mongoose/Response.h
deleted file mode 100644
index 48d1962c8f..0000000000
--- a/mongoose/Response.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef _MONGOOSE_RESPONSE_H
-#define _MONGOOSE_RESPONSE_H
-
-#include 
-#include 
-#include 
-
-#define HTTP_OK 200
-#define HTTP_NOT_FOUND 404
-#define HTTP_FORBIDDEN 403
-#define HTTP_SERVER_ERROR 500
-
-using namespace std;
-
-/**
- * A response to a request
- */
-namespace Mongoose
-{
-    class Response 
-    {
-        public:
-            Response();
-            virtual ~Response();
-
-            /**
-             * Test if the given header is present
-             *
-             * @param string the header key
-             *
-             * @return bool true if the header is set
-             */
-            virtual bool hasHeader(string key);
-
-            /**
-             * Sets the header
-             *
-             * @param key the header key
-             *
-             * @param value the header value
-             */
-            virtual void setHeader(string key, string value);
-
-            /**
-             * Get the data of the response, this will contain headers and
-             * body
-             *
-             * @return string the response data
-             */
-            virtual string getData();
-
-            /**
-             * Gets the response body
-             *
-             * @return string the response body
-             */
-            virtual string getBody()=0;
-
-            /**
-             * Sets the cookie, note that you can only define one cookie by request
-             * for now
-             *
-             * @param string the key of the cookie
-             * @param string value the cookie value
-             */
-            virtual void setCookie(string key, string value);
-
-            /**
-             * Sets the response code
-             */
-            virtual void setCode(int code);
-
-        protected:
-            int code;
-            map headers;
-    };
-}
-
-#endif
diff --git a/mongoose/Server.cpp b/mongoose/Server.cpp
deleted file mode 100644
index 0cdb935512..0000000000
--- a/mongoose/Server.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-#ifndef _MSC_VER
-#include 
-#else 
-#include 
-#include 
-#include 
-#endif
-#include 
-#include 
-#include 
-#include "Server.h"
-#include "Utils.h"
-
-using namespace std;
-using namespace Mongoose;
-
-static int getTime()
-{
-#ifdef _MSC_VER
-    time_t ltime;
-    time(<ime);
-    return ltime;
-#else
-    return time(NULL);
-#endif
-}
-
-static int do_i_handle(struct mg_connection *connection)
-{
-    Server *server = (Server *)connection->server_param;
-
-    return server->handles(connection->request_method, connection->uri);
-}
-
-/**
- * The handlers below are written in C to do the binding of the C mongoose with
- * the C++ API
- */
-static int event_handler(struct mg_connection *connection)
-{
-    Server *server = (Server *)connection->server_param;
-
-    if (server != NULL) {
-#ifndef NO_WEBSOCKET
-        if (connection->is_websocket) {
-            server->_webSocketReady(connection);
-        }
-#endif
-        server->_handleRequest(connection);
-    }
-
-    return 1;
-}
-
-#ifndef NO_WEBSOCKET
-static int iterate_callback(struct mg_connection *connection)
-{
-    if (connection->is_websocket && connection->content_len) {
-        Server *server = (Server *)connection->server_param;
-        server->_webSocketData(connection, string(connection->content, connection->content_len));
-    }
-
-    return 1;
-}
-#endif
-
-static void *server_poll(void *param)
-{
-    Server *server = (Server *)param;
-    server->poll();
-
-    return NULL;
-}
-
-namespace Mongoose
-{
-    Server::Server(int port, const char *documentRoot)
-        : 
-        stopped(false),
-        destroyed(false),
-        server(NULL)
-#ifndef NO_WEBSOCKET 
-        ,websockets(NULL)
-#endif
-
-    {
-        ostringstream portOss;
-        portOss << port;
-        optionsMap["listening_port"] = portOss.str();
-        optionsMap["document_root"] = string(documentRoot);
-        optionsMap["enable_keep_alive"] = "yes";
-    }
-
-    Server::~Server()
-    {
-        stop();
-    }
-
-    void Server::start()
-    {
-        if (server == NULL) {
-#ifdef ENABLE_STATS
-            requests = 0;
-            startTime = getTime();
-#endif
-            server = mg_create_server(this);
-
-            map::iterator it;
-            for (it=optionsMap.begin(); it!=optionsMap.end(); it++) {
-                mg_set_option(server, (*it).first.c_str(), (*it).second.c_str());
-            }
-
-            mg_add_uri_handler(server, "/", event_handler);
-            mg_server_do_i_handle(server, do_i_handle);
-
-            stopped = false;
-            mg_start_thread(server_poll, this);
-        } else {
-            throw string("Server is already running");
-        }
-    }
-
-    void Server::poll()
-    {
-#ifndef NO_WEBSOCKET
-        unsigned int current_timer = 0;
-#endif
-        while (!stopped) {
-            mg_poll_server(server, 1000);
-#ifndef NO_WEBSOCKET
-            mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
-#endif
-        }
-
-        mg_destroy_server(&server);
-        destroyed = true;
-    }
-
-    void Server::stop()
-    {
-        stopped = true;
-        while (!destroyed) {
-            Utils::sleep(100);
-        }
-    }
-
-    void Server::registerController(Controller *controller)
-    {
-        controller->setSessions(&sessions);
-        controller->setServer(this);
-        controller->setup();
-        controllers.push_back(controller);
-    }
-
-#ifndef NO_WEBSOCKET
-    void Server::_webSocketReady(struct mg_connection *conn)
-    {
-        WebSocket *websocket = new WebSocket(conn);
-        websockets.add(websocket);
-        websockets.clean();
-
-        vector::iterator it;
-        for (it=controllers.begin(); it!=controllers.end(); it++) {
-            (*it)->webSocketReady(websocket);
-        }
-    }
-
-    int Server::_webSocketData(struct mg_connection *conn, string data)
-    {
-        WebSocket *websocket = websockets.getWebSocket(conn);
-
-        if (websocket != NULL) {
-            websocket->appendData(data);
-
-            string fullPacket = websocket->flushData();
-            vector::iterator it;
-            for (it=controllers.begin(); it!=controllers.end(); it++) {
-                (*it)->webSocketData(websocket, fullPacket);
-            }
-
-            if (websocket->isClosed()) {
-                websockets.remove(websocket);
-                return 0;
-            } else {
-                return -1;
-            }
-        } else {
-            return 0;
-        }
-    }
-#endif
-
-    int Server::_handleRequest(struct mg_connection *conn)
-    {
-        Request request(conn);
-
-        mutex.lock();
-        currentRequests[conn] = &request;
-        mutex.unlock();
-
-        Response *response = handleRequest(request);
-
-        mutex.lock();
-        currentRequests.erase(conn);
-        mutex.unlock();
-
-        if (response == NULL) {
-            return 0;
-        } else {
-            request.writeResponse(response);
-            delete response;
-            return 1;
-        }
-    }
-
-    bool Server::handles(string method, string url)
-    {
-#ifndef NO_WEBSOCKET
-        if (url == "/websocket") {
-            return true;
-        }
-#endif
-
-        vector::iterator it;
-        for (it=controllers.begin(); it!=controllers.end(); it++) {
-            if ((*it)->handles(method, url)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    Response *Server::handleRequest(Request &request)
-    {
-        Response *response;
-        vector::iterator it;
-
-        mutex.lock();
-        requests++;
-        mutex.unlock();
-
-        for (it=controllers.begin(); it!=controllers.end(); it++) {
-            Controller *controller = *it;
-            response = controller->process(request);
-
-            if (response != NULL) {
-                return response;
-            }
-        }
-
-        return NULL;
-    }
-
-    void Server::setOption(string key, string value)
-    {
-        optionsMap[key] = value;
-    }
-
-#ifndef NO_WEBSOCKET
-    WebSockets &Server::getWebSockets()
-    {
-        return websockets;
-    }
-#endif
-
-    void Server::printStats()
-    {
-        int delta = getTime()-startTime;
-
-        if (delta) {
-            cout << "Requests: " << requests << ", Requests/s: " << (requests*1.0/delta) << endl;
-        }
-    }
-}
diff --git a/mongoose/Server.h b/mongoose/Server.h
deleted file mode 100644
index 82f876f4df..0000000000
--- a/mongoose/Server.h
+++ /dev/null
@@ -1,150 +0,0 @@
-#ifndef _MONGOOSE_SERVER_H
-#define _MONGOOSE_SERVER_H
-
-#include 
-#include 
-#include 
-#include "Request.h"
-#include "Response.h"
-#include "Controller.h"
-#ifndef NO_WEBSOCKET
-#include "WebSocket.h"
-#include "WebSockets.h"
-#endif
-#include "Mutex.h"
-#include "Sessions.h"
-
-using namespace std;
-
-/**
- * Wrapper for the Mongoose server
- */
-namespace Mongoose
-{
-    class Server
-    {
-        public:
-            /**
-             * Constructs the server
-             *
-             * @param int the number of the port to listen to
-             * @param string documentRoot the root that should be used for static files
-             */
-            Server(int port = 80, const char *documentRoot = "www");
-            virtual ~Server();
-
-            /**
-             * Runs the Mongoose server
-             */
-            void start();
-
-            /**
-             * Stops the Mongoose server
-             */
-            void stop();
-
-            /**
-             * Register a new controller on the server
-             *
-             * @param Controller* a pointer to a controller
-             */
-            void registerController(Controller *);
-
-            /**
-             * Internally used to process a request
-             *
-             * @param struct mg_connection* the mongoose connection
-             */
-            int _handleRequest(struct mg_connection *conn);
-
-            /**
-             * Internally used to process a file upload
-             *
-             * @param struct mg_connection* the mongoose conneciton
-             * @param const char * the name of the uploaded file
-             */
-            void _upload(struct mg_connection *conn, const char *fileName);
-
-            /**
-             * Handles a web socket connection
-             *
-             * @param struct mg_connection* the mongoose connection with the client
-             */
-            void _webSocketReady(struct mg_connection *conn);
-
-            /**
-             * Handles web sockets data
-             *
-             * @param struct mg_connection* the mongoose connection
-             * @param int flags the data flags
-             * @param char* the data
-             * @param size the data size
-             *
-             * @return int if we have to keep the connection opened
-             */
-            int _webSocketData(struct mg_connection *conn, string data);
-
-            /**
-             * Process the request by controllers
-             *
-             * @param Request the request
-             *
-             * @return Response the response if one of the controllers can handle it,
-             *         NULL else
-             */
-            Response *handleRequest(Request &request);
-
-            /**
-             * Sets a mongoose extra option
-             *
-             * @param string the name of the option
-             * @param string the value of the option
-             */
-            void setOption(string key, string value);
-
-#ifndef NO_WEBSOCKET
-            /**
-             * Returns the WebSockets container
-             *
-             * @return WebSockets the web sockets container
-             */
-            WebSockets &getWebSockets();
-#endif
-
-            /**
-             * Print statistics
-             */
-            void printStats();
-
-            /**
-             * Polls the server
-             */
-            void poll();
-
-            /**
-             * Does the server handles url?
-             */
-            bool handles(string method, string url);
-
-        protected:
-            volatile bool stopped;
-            volatile bool destroyed;
-            Sessions sessions;
-            Mutex mutex;
-            map optionsMap;
-            map currentRequests;
-            struct mg_server *server;
-
-#ifndef NO_WEBSOCKET
-            WebSockets websockets;
-#endif
-
-            vector controllers;
-
-            // Statistics
-            int requests;
-            int startTime;
-    };
-}
-
-#endif
diff --git a/mongoose/Session.cpp b/mongoose/Session.cpp
deleted file mode 100644
index bf546f6352..0000000000
--- a/mongoose/Session.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include 
-#include 
-#include 
-#include "Session.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    Session::Session()
-    {
-        ping();
-    }
-
-    void Session::ping()
-    {
-        mutex.lock();
-        date = time(NULL);
-        mutex.unlock();
-    }
-
-    void Session::setValue(string key, string value)
-    {
-        mutex.lock();
-        values[key] = value;
-        mutex.unlock();
-    }
-
-    void Session::unsetValue(string key)
-    {
-        mutex.lock();
-        values.erase(key);
-        mutex.unlock();
-    }
-
-    bool Session::hasValue(string key)
-    {
-        return values.find(key) != values.end();
-    }
-
-    string Session::get(string key, string fallback)
-    {
-        mutex.lock();
-        if (hasValue(key)) {
-            string value = values[key];
-            mutex.unlock();
-
-            return value;
-        } else {
-            mutex.unlock();
-            return fallback;
-        }
-    }
-
-    int Session::getAge()
-    {
-        return time(NULL)-date;
-    }
-}
diff --git a/mongoose/Session.h b/mongoose/Session.h
deleted file mode 100644
index a1b830e727..0000000000
--- a/mongoose/Session.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef _MONGOOSE_SESSION_H
-#define _MONGOOSE_SESSION_H
-
-#include 
-#include "Mutex.h"
-
-using namespace std;
-
-/**
- * A session contains the user specific values
- */ 
-namespace Mongoose
-{
-    class Session
-    {
-        public:
-            Session();
-
-            /**
-             * Sets the value of a session variable
-             *
-             * @param string the name of the variable
-             * @param string the value of the variable
-             */
-            void setValue(string key, string value);
-
-            /**
-             * Unset a session varaible
-             *
-             * @param string the variable name
-             */
-            void unsetValue(string key);
-
-            /**
-             * Check if the given variable exists
-             *
-             * @param string the name of the variable
-             */
-            bool hasValue(string key);
-
-            /**
-             * Try to get the value for the given variable
-             *
-             * @pram string the name of the variable
-             * @param string the fallback value
-             *
-             * @return string the value of the variable if it exists, fallback else
-             */
-            string get(string key, string fallback = "");
-
-            /**
-             * Pings the session, this will update the creation date to now
-             * and "keeping it alive"
-             */
-            void ping();
-
-            /**
-             * Returns the session age, in seconds
-             *
-             * @return int the number of sessions since the last activity of the session
-             */
-            int getAge();
-
-        protected:
-            map values;
-            int date;
-            Mutex mutex;
-    };
-}
-
-#endif
diff --git a/mongoose/Sessions.cpp b/mongoose/Sessions.cpp
deleted file mode 100644
index b7088ca534..0000000000
--- a/mongoose/Sessions.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include 
-#include 
-#include 
-#include "Sessions.h"
-
-using namespace std;
-
-static char charset[] = "abcdeghijklmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-#define CHARSET_SIZE (sizeof(charset)/sizeof(char))
-
-namespace Mongoose
-{
-    Sessions::Sessions(string key_) 
-        : sessions(), key(key_)
-    {
-    }
-	
-	Sessions::~Sessions()
-	{
-		map::iterator it;
-		for (it=sessions.begin(); it!=sessions.end(); it++) {
-			delete (*it).second;
-		}
-	}
-
-    string Sessions::getId(Request &request, Response &response)
-    {
-        if (request.hasCookie(key)) {
-            return request.getCookie(key);
-        } else {
-            ostringstream newCookie;
-            int i;
-
-            for (i=0; i<30; i++) {
-                newCookie << charset[rand()%CHARSET_SIZE];
-            }
-
-            response.setCookie(key, newCookie.str());
-
-            return newCookie.str();
-        }
-    }
-
-    Session &Sessions::get(Request &request, Response &response)
-    { 
-        string id = getId(request, response);
-		Session *session = NULL;
-        
-        mutex.lock();
-		if (sessions.find(id) != sessions.end()) {
-			session = sessions[id];
-		} else {
-			session = new Session();
-			sessions[id] = session;
-		}
-        mutex.unlock();
-
-        return *session;
-    }
-
-    void Sessions::garbageCollect(int oldAge)
-    {
-        vector deleteList;
-        map::iterator it;
-        vector::iterator vit;
-
-        mutex.lock();
-        for (it=sessions.begin(); it!=sessions.end(); it++) {
-            string name = (*it).first;
-            Session *session = (*it).second;
-
-            if (session->getAge() > oldAge) {
-				delete session;
-                deleteList.push_back(name);
-            }
-        }
-
-        for (vit=deleteList.begin(); vit!=deleteList.end(); vit++) {
-            sessions.erase(*vit);
-        }
-        mutex.unlock();
-    }
-}
diff --git a/mongoose/StreamResponse.cpp b/mongoose/StreamResponse.cpp
deleted file mode 100644
index fbfd34a658..0000000000
--- a/mongoose/StreamResponse.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include 
-#include "StreamResponse.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    string StreamResponse::getBody()
-    {
-        return this->str();
-    }
-}
diff --git a/mongoose/StreamResponse.h b/mongoose/StreamResponse.h
deleted file mode 100644
index 9eedd20bcd..0000000000
--- a/mongoose/StreamResponse.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _MONGOOSE_STREAM_RESPONSE_H
-#define _MONGOOSE_STREAM_RESPONSE_H
-
-#include 
-#include 
-#include 
-
-#include "Response.h"
-
-using namespace std;
-
-/**
- * A stream response to a request
- */
-namespace Mongoose
-{
-    class StreamResponse : public ostringstream, public Response
-    {
-        public:
-            /**
-             * Gets the response body
-             *
-             * @return string the response body
-             */
-            virtual string getBody();
-    };
-}
-
-#endif
diff --git a/mongoose/UploadFile.cpp b/mongoose/UploadFile.cpp
deleted file mode 100644
index 75fde016eb..0000000000
--- a/mongoose/UploadFile.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include 
-#include 
-#include 
-#include "UploadFile.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    UploadFile::UploadFile(string filename_, string data_)
-        : filename(filename_),
-        data(data_)
-    {
-    }
-
-    string UploadFile::getName()
-    {
-        return filename;
-    }
-
-    string UploadFile::getData()
-    {
-        return data;
-    }
-    
-    void UploadFile::saveTo(string directory)
-    {
-        ostringstream oss;
-        oss << directory << "/" << filename;
-        fstream file;
-        file.open(oss.str().c_str(), fstream::out);
-        file << data;
-        file.close();
-    }
-}
diff --git a/mongoose/UploadFile.h b/mongoose/UploadFile.h
deleted file mode 100644
index 3c54933f3f..0000000000
--- a/mongoose/UploadFile.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef _MONGOOSE_UPLOAD_FILE_H
-#define _MONGOOSE_UPLOAD_FILE_H
-#include 
-
-using namespace std;
-
-/**
- * A file uploaded in a request
- */
-namespace Mongoose
-{
-    class UploadFile
-    {
-        public:
-            UploadFile(string filename, string data);
-
-            string getName();
-            string getData();
-
-            void saveTo(string directory);
-
-        protected:
-            string filename;
-            string data;
-    };
-}
-
-#endif // _MONGOOSE_UPLOAD_FILE_H
diff --git a/mongoose/Utils.cpp b/mongoose/Utils.cpp
deleted file mode 100644
index d808e909e6..0000000000
--- a/mongoose/Utils.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include 
-#include 
-#include "Utils.h"
-#ifndef WIN32
-#include 
-#endif
-#ifdef WIN32
-#include 
-#endif
-
-using namespace std;
-
-namespace Mongoose
-{
-    string Utils::htmlEntities(string data)
-    {
-        string buffer;
-        buffer.reserve(data.size());
-
-        for(size_t pos = 0; pos != data.size(); ++pos) {
-            switch(data[pos]) {
-                case '&':  buffer.append("&");       break;
-                case '\"': buffer.append(""");      break;
-                case '\'': buffer.append("'");      break;
-                case '<':  buffer.append("<");        break;
-                case '>':  buffer.append(">");        break;
-                default:   buffer.append(1, data[pos]); break;
-            }
-        }
-
-        return buffer;
-    }
-
-    void Utils::sleep(int ms)
-    {
-#ifdef WIN32
-	Sleep(ms);
-#else
-    usleep(1000 * ms);
-#endif
-    }
-}
diff --git a/mongoose/Utils.h b/mongoose/Utils.h
deleted file mode 100644
index d8a36eca72..0000000000
--- a/mongoose/Utils.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _MONGOOSE_UTILS_H
-#define _MONGOOSE_UTILS_H
-
-#include 
-
-using namespace std;
-
-namespace Mongoose
-{
-    class Utils
-    {
-        public:
-            static string htmlEntities(string data);
-            static void sleep(int ms);
-    };
-}
-
-#endif
-
diff --git a/mongoose/WebController.cpp b/mongoose/WebController.cpp
deleted file mode 100644
index 6b7f86ee0a..0000000000
--- a/mongoose/WebController.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "WebController.h"
-#include "Session.h"
-
-namespace Mongoose
-{        
-    WebController::WebController(int gcDivisor_) 
-        : 
-        Controller(),
-        gcDivisor(gcDivisor_),
-        counter(0)
-    {
-    }
-
-    void WebController::preProcess(Request &request, Response &response)
-    {
-        mutex.lock();
-        counter++;
-
-        if (counter > gcDivisor) {
-            counter = 0;
-            sessions->garbageCollect();
-        }
-        mutex.unlock();
-
-        Session &session = sessions->get(request, response);
-        session.ping();
-        response.setHeader("Content-Type", "text/html");
-    }
-}
diff --git a/mongoose/WebController.h b/mongoose/WebController.h
deleted file mode 100644
index a7249482c1..0000000000
--- a/mongoose/WebController.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef _MONGOOSE_WEB_CONTROLLER_H
-#define _MONGOOSE_WEB_CONTROLLER_H
-
-#include "Request.h"
-#include "Response.h"
-#include "Controller.h"
-#include "Mutex.h"
-#include "StreamResponse.h"
-#include "Utils.h"
-
-using namespace std;
-
-/**
- * A web controller is a controller that serves HTML pages
- */
-namespace Mongoose
-{
-    class WebController : public Controller, public Utils
-    {
-        public:
-            /**
-             * Creates a web controller, each gcDivisor request, the sessions will be
-             * garbage collected
-             */
-            WebController(int gcDivisor = 100);
-
-            /**
-             * Pre process the request, this will set the content type to text/html
-             * and ping the user session
-             *
-             * @param Request the request
-             * @param Response the response
-             */
-            void preProcess(Request &request, Response &response);
-
-        protected:
-            Mutex mutex;
-            int gcDivisor;
-            int counter;
-    };
-}
-
-#endif
diff --git a/mongoose/WebSocket.cpp b/mongoose/WebSocket.cpp
deleted file mode 100644
index e8a0a78b7d..0000000000
--- a/mongoose/WebSocket.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include 
-#include 
-#include "WebSocket.h"
-#include "WebSockets.h"
-
-using namespace std;
-
-namespace Mongoose
-{
-    WebSocket::WebSocket(struct mg_connection *connection_)
-        : connection(connection_), closed(false), request(connection_), data(""), id(-1)
-    {
-    }
-
-    void WebSocket::setId(int id_)
-    {
-        id = id_;
-    }
-
-    int WebSocket::getId()
-    {
-        return id;
-    }
-
-    void WebSocket::appendData(string data_)
-    {
-        data += data_;
-    }
-
-    string WebSocket::flushData()
-    {
-        string oldData = "";
-        data.swap(oldData);
-
-        return oldData;
-    }
-
-    Request &WebSocket::getRequest()
-    {
-        return request;
-    }
-
-    void WebSocket::send(string data, int opcode)
-    {
-        if (isClosed()) {
-            return;
-        }
-
-        mutex.lock();
-        if (!mg_websocket_write(connection, opcode, data.c_str(), data.size())) {
-            closed = true;
-        }
-        mutex.unlock();
-    }
-
-    void WebSocket::notifyContainers()
-    {
-        vector::iterator it;
-
-        mutex.lock();
-        for (it=containers.begin(); it!=containers.end(); it++) {
-            (*it)->remove(this);
-        }
-        mutex.unlock();
-    }
-
-    void WebSocket::close()
-    {
-        closed = true;
-    }
-
-    bool WebSocket::isClosed()
-    {
-        return closed;
-    }
-
-    void WebSocket::addContainer(WebSockets *websockets)
-    {
-        mutex.lock();
-        containers.push_back(websockets);
-        mutex.unlock();
-    }
-
-    void WebSocket::removeContainer(WebSockets *websockets)
-    {
-        mutex.lock();
-        vector::iterator it;
-
-        for (it=containers.begin(); it!=containers.end(); it++) {
-            if (*it == websockets) {
-                containers.erase(it);
-                break;
-            }
-        }
-        mutex.unlock();
-    }
-
-    struct mg_connection *WebSocket::getConnection()
-    {
-        return connection;
-    }
-};
-
diff --git a/mongoose/WebSocket.h b/mongoose/WebSocket.h
deleted file mode 100644
index 67d5790bac..0000000000
--- a/mongoose/WebSocket.h
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef _MONGOOSE_WEBSOCKET_H
-#define _MONGOOSE_WEBSOCKET_H
-
-#include 
-#include 
-#include 
-#include "Request.h"
-#include "Mutex.h"
-
-using namespace std;
-
-#define WEBSOCKET_FIN 0x80
-
-enum {
-    WEBSOCKET_OPCODE_CONTINUATION = 0x0,
-    WEBSOCKET_OPCODE_TEXT = 0x1,
-    WEBSOCKET_OPCODE_BINARY = 0x2,
-    WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
-    WEBSOCKET_OPCODE_PING = 0x9,
-    WEBSOCKET_OPCODE_PONG = 0xa
-};
-
-namespace Mongoose
-{
-    class WebSockets;
-
-    class WebSocket
-    {
-        public:
-            WebSocket(struct mg_connection *connection_);
-
-            /**
-             * Sends data through the web socket
-             *
-             * @param string the data to send
-             */
-            void send(string data, int opcode = WEBSOCKET_OPCODE_TEXT);
-
-            /**
-             * Returns the connection request
-             *
-             * @return Request& the request
-             */
-            Request &getRequest();
-
-            /**
-             * Closes the connection
-             */
-            void close();
-
-            /**
-             * Is the connection closed ?
-             *
-             * @return bool true if the connection is closed
-             */
-            bool isClosed();
-
-            /**
-             * Append data to the internal receive buffer
-             *
-             * @param string data
-             */
-            void appendData(string data);
-
-            /**
-             * Gets the internal receive buffer and clear it
-             *
-             * @return string data
-             */
-            string flushData();
-
-            /**
-             * Gets the internal mg connection
-             *
-             * @return struct mg_connection* the connection
-             */
-            struct mg_connection *getConnection();
-
-            /**
-             * Adding this websocket in a container
-             *
-             * @param WebSockets* the container
-             */
-            void addContainer(WebSockets *websockets);
-
-            /**
-             * Removing this websocket from a container
-             */
-            void removeContainer(WebSockets *websockets);
-
-            /**
-             * Notify all the containers that the websocket is now closed and unusable
-             */
-            void notifyContainers();
-
-            /**
-             * Sets the identifier of this websocket
-             *
-             * @param int the websocket identifier
-             */
-            void setId(int id_);
-
-            /**
-             * Gets the websocket identifier
-             *
-             * @return int the identifier of this websocket
-             */
-            int getId();
-
-        protected:
-            int id;
-            Mutex mutex;
-            string data;
-            Request request;
-            struct mg_connection *connection;
-            bool closed;
-
-            vector containers;
-    };
-}
-
-#endif
diff --git a/mongoose/WebSockets.cpp b/mongoose/WebSockets.cpp
deleted file mode 100644
index f6ad72287d..0000000000
--- a/mongoose/WebSockets.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#include 
-#include "WebSockets.h"
-
-
-namespace Mongoose
-{
-    WebSockets::WebSockets(bool responsible_)
-        : responsible(responsible_), id(0)
-    {
-    }
-
-    WebSockets::~WebSockets()
-    {
-        if (responsible) {
-            vector toDelete;
-
-            map::iterator it;
-
-            for (it=websockets.begin(); it!=websockets.end(); it++) {
-                toDelete.push_back((*it).second);
-            }
-
-            vector::iterator vit;
-
-            for (vit=toDelete.begin(); vit!=toDelete.end(); vit++) {
-                remove(*vit);
-            }
-        }
-    }
-
-    void WebSockets::add(WebSocket *websocket)
-    {
-        if (websocket == NULL) {
-            return;
-        }
-
-        if (responsible) {
-            mutex.lock();
-            int newId = id++;
-            mutex.unlock();
-            websocket->setId(newId);
-        }
-
-        struct mg_connection *connection = websocket->getConnection();
-
-        mutex.lock();
-        if (websockets.find(connection) != websockets.end()) {
-            remove(websockets[connection], false);
-        }
-
-        websocketsById[websocket->getId()] = websocket;
-        websockets[connection] = websocket;
-        mutex.unlock();
-    }
-
-    WebSocket *WebSockets::getWebSocket(int id)
-    {
-        if (websocketsById.find(id) != websocketsById.end()) {
-            return websocketsById[id];
-        }
-
-        return NULL;
-    }
-
-    void WebSockets::sendAll(string data)
-    {
-        vector toClean;
-        map::iterator it;
-
-        mutex.lock();
-        for (it=websockets.begin(); it!=websockets.end(); it++) {
-            WebSocket *websocket = (*it).second;
-
-            websocket->send(data);
-        }
-        mutex.unlock();
-
-        clean();
-    }
-
-    void WebSockets::remove(WebSocket *websocket, bool lock)
-    {
-        struct mg_connection *connection = websocket->getConnection();
-
-        if (lock) {
-            mutex.lock();
-        }
-        if (websockets.find(connection) != websockets.end()) {
-            websocket->removeContainer(this);
-            websockets.erase(connection);
-            websocketsById.erase(websocket->getId());
-
-            if (responsible) {
-                websocket->close();
-                websocket->notifyContainers();
-                delete websocket;
-            }
-        }
-        if (lock) {
-            mutex.unlock();
-        }
-    }
-
-    WebSocket *WebSockets::getWebSocket(struct mg_connection *connection)
-    {
-        if (websockets.find(connection) != websockets.end()) {
-            return websockets[connection];
-        }
-
-        return NULL;
-    }
-
-    void WebSockets::clean()
-    {
-        vector toDelete;
-        map::iterator it;
-
-        mutex.lock();
-        for (it=websockets.begin(); it!=websockets.end(); it++) {
-            if ((*it).second->isClosed()) {
-                toDelete.push_back((*it).second);
-            }
-        }
-
-        vector::iterator vit;
-        for (vit=toDelete.begin(); vit!=toDelete.end(); vit++) {
-            remove(*vit, false);
-        }
-        mutex.unlock();
-    }
-};
diff --git a/mongoose/WebSockets.h b/mongoose/WebSockets.h
deleted file mode 100644
index ffbd280db3..0000000000
--- a/mongoose/WebSockets.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifndef _MONGOOSE_WEBSOCKETS_H
-#define _MONGOOSE_WEBSOCKETS_H
-
-#include 
-#include 
-#include 
-#include "WebSocket.h"
-#include "Mutex.h"
-
-using namespace std;
-
-/**
- * WebSockets is an array that contains WebSocket connections, this
- * can be used for instance to broadcast informations to them.
- *
- * The function clean() allow to remove closed connections from the
- * array.
- */
-namespace Mongoose
-{
-    class WebSockets
-    {
-        public:
-            /**
-             * Creates a websockets array, the responsible false specify whether the
-             * container will be responsible for cleaning the websocket
-             */
-            WebSockets(bool responsible = false);
-            virtual ~WebSockets();
-
-            /**
-             * Adds the given websocket to the poll. This container will not
-             * become responsible for cleaning this object
-             *
-             * @param WebSocket* the websocket object
-             */
-            void add(WebSocket *websocket);
-
-            /**
-             * Send data to all sockets in this container
-             */
-            void sendAll(string data);
-
-            /**
-             * Gets the websocket corresponding to the given connection
-             *
-             * @param strut mg_connection* the mongoose connection
-             */
-            WebSocket *getWebSocket(struct mg_connection *connection);
-
-            /**
-             * Cleans all the connections that are hold in this object and that are
-             * closed
-             */
-            void clean();
-
-            /**
-             * Removes the websocket from the container
-             *
-             * @param WebSocket* the websocket object
-             */
-            void remove(WebSocket *websocket, bool lock = true);
-    
-            /**
-             * Gets the websockets having the id 
-             *
-             * @param int id
-             */
-            WebSocket *getWebSocket(int id);
-
-        protected:
-            Mutex mutex;
-            map websockets;
-            map websocketsById;
-            bool responsible;
-
-            int id;
-    };
-}
-
-#endif
diff --git a/vendor/libyuarel b/vendor/libyuarel
new file mode 160000
index 0000000000..ffdd690698
--- /dev/null
+++ b/vendor/libyuarel
@@ -0,0 +1 @@
+Subproject commit ffdd690698077b29151ab13ce8ac1b2b2379068c
diff --git a/vendor/mongoose b/vendor/mongoose
new file mode 160000
index 0000000000..cc227526a4
--- /dev/null
+++ b/vendor/mongoose
@@ -0,0 +1 @@
+Subproject commit cc227526a4acedaf3d5937195e46de1abb3287d8
diff --git a/version.h.in b/version.h.in
new file mode 100644
index 0000000000..87618dd7d8
--- /dev/null
+++ b/version.h.in
@@ -0,0 +1,18 @@
+#pragma once
+
+#include 
+
+namespace Mongoose
+{
+    const std::string VERSION_STRING = "@mongoose_VERSION_STRING@";
+    const std::string VERSION_STRING_FULL = "@mongoose_VERSION_STRING_FULL@";
+    const std::string VERSION = "@mongoose_VERSION@";
+
+    const int VERSION_MAJOR = @mongoose_VERSION_MAJOR@;
+    const int VERSION_MINOR = @mongoose_VERSION_MINOR@;
+    const int VERSION_PATCH = @mongoose_VERSION_PATCH@;
+
+    const std::string VERSION_TWEAK = "@mongoose_VERSION_TWEAK@";
+    const std::string VERSION_AHEAD = "@mongoose_VERSION_AHEAD@";
+    const std::string VERSION_GIT_SHA = "@mongoose_VERSION_GIT_SHA@";
+}
NameModifiedSize