Path: blob/trunk/third_party/cpp/civetweb/CivetServer.cpp
2868 views
/* Copyright (c) 2013-2017 the Civetweb developers1* Copyright (c) 2013 No Face Press, LLC2*3* License http://opensource.org/licenses/mit-license.php MIT License4*/56#include "CivetServer.h"78#include <assert.h>9#include <stdexcept>10#include <stdlib.h>11#include <string.h>1213#ifndef UNUSED_PARAMETER14#define UNUSED_PARAMETER(x) (void)(x)15#endif1617#ifndef MAX_PARAM_BODY_LENGTH18// Set a default limit for parameters in a form body: 2 MB19#define MAX_PARAM_BODY_LENGTH (1024 * 1024 * 2)20#endif2122bool23CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)24{25UNUSED_PARAMETER(server);26UNUSED_PARAMETER(conn);27return false;28}2930bool31CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)32{33UNUSED_PARAMETER(server);34UNUSED_PARAMETER(conn);35return false;36}3738bool39CivetHandler::handleHead(CivetServer *server, struct mg_connection *conn)40{41UNUSED_PARAMETER(server);42UNUSED_PARAMETER(conn);43return false;44}4546bool47CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)48{49UNUSED_PARAMETER(server);50UNUSED_PARAMETER(conn);51return false;52}5354bool55CivetHandler::handlePatch(CivetServer *server, struct mg_connection *conn)56{57UNUSED_PARAMETER(server);58UNUSED_PARAMETER(conn);59return false;60}6162bool63CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)64{65UNUSED_PARAMETER(server);66UNUSED_PARAMETER(conn);67return false;68}6970bool71CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)72{73UNUSED_PARAMETER(server);74UNUSED_PARAMETER(conn);75return false;76}7778bool79CivetWebSocketHandler::handleConnection(CivetServer *server,80const struct mg_connection *conn)81{82UNUSED_PARAMETER(server);83UNUSED_PARAMETER(conn);84return true;85}8687void88CivetWebSocketHandler::handleReadyState(CivetServer *server,89struct mg_connection *conn)90{91UNUSED_PARAMETER(server);92UNUSED_PARAMETER(conn);93return;94}9596bool97CivetWebSocketHandler::handleData(CivetServer *server,98struct mg_connection *conn,99int bits,100char *data,101size_t data_len)102{103UNUSED_PARAMETER(server);104UNUSED_PARAMETER(conn);105UNUSED_PARAMETER(bits);106UNUSED_PARAMETER(data);107UNUSED_PARAMETER(data_len);108return true;109}110111void112CivetWebSocketHandler::handleClose(CivetServer *server,113const struct mg_connection *conn)114{115UNUSED_PARAMETER(server);116UNUSED_PARAMETER(conn);117return;118}119120int121CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)122{123const struct mg_request_info *request_info = mg_get_request_info(conn);124assert(request_info != NULL);125CivetServer *me = (CivetServer *)(request_info->user_data);126assert(me != NULL);127128// Happens when a request hits the server before the context is saved129if (me->context == NULL)130return 0;131132mg_lock_context(me->context);133me->connections[conn] = CivetConnection();134mg_unlock_context(me->context);135136CivetHandler *handler = (CivetHandler *)cbdata;137138if (handler) {139if (strcmp(request_info->request_method, "GET") == 0) {140return handler->handleGet(me, conn) ? 1 : 0;141} else if (strcmp(request_info->request_method, "POST") == 0) {142return handler->handlePost(me, conn) ? 1 : 0;143} else if (strcmp(request_info->request_method, "HEAD") == 0) {144return handler->handleHead(me, conn) ? 1 : 0;145} else if (strcmp(request_info->request_method, "PUT") == 0) {146return handler->handlePut(me, conn) ? 1 : 0;147} else if (strcmp(request_info->request_method, "DELETE") == 0) {148return handler->handleDelete(me, conn) ? 1 : 0;149} else if (strcmp(request_info->request_method, "OPTIONS") == 0) {150return handler->handleOptions(me, conn) ? 1 : 0;151} else if (strcmp(request_info->request_method, "PATCH") == 0) {152return handler->handlePatch(me, conn) ? 1 : 0;153}154}155156return 0; // No handler found157}158159int160CivetServer::authHandler(struct mg_connection *conn, void *cbdata)161{162const struct mg_request_info *request_info = mg_get_request_info(conn);163assert(request_info != NULL);164CivetServer *me = (CivetServer *)(request_info->user_data);165assert(me != NULL);166167// Happens when a request hits the server before the context is saved168if (me->context == NULL)169return 0;170171mg_lock_context(me->context);172me->connections[conn] = CivetConnection();173mg_unlock_context(me->context);174175CivetAuthHandler *handler = (CivetAuthHandler *)cbdata;176177if (handler) {178return handler->authorize(me, conn) ? 1 : 0;179}180181return 0; // No handler found182}183184int185CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,186void *cbdata)187{188const struct mg_request_info *request_info = mg_get_request_info(conn);189assert(request_info != NULL);190CivetServer *me = (CivetServer *)(request_info->user_data);191assert(me != NULL);192193// Happens when a request hits the server before the context is saved194if (me->context == NULL)195return 0;196197CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;198199if (handler) {200return handler->handleConnection(me, conn) ? 0 : 1;201}202203return 1; // No handler found, close connection204}205206void207CivetServer::webSocketReadyHandler(struct mg_connection *conn, void *cbdata)208{209const struct mg_request_info *request_info = mg_get_request_info(conn);210assert(request_info != NULL);211CivetServer *me = (CivetServer *)(request_info->user_data);212assert(me != NULL);213214// Happens when a request hits the server before the context is saved215if (me->context == NULL)216return;217218CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;219220if (handler) {221handler->handleReadyState(me, conn);222}223}224225int226CivetServer::webSocketDataHandler(struct mg_connection *conn,227int bits,228char *data,229size_t data_len,230void *cbdata)231{232const struct mg_request_info *request_info = mg_get_request_info(conn);233assert(request_info != NULL);234CivetServer *me = (CivetServer *)(request_info->user_data);235assert(me != NULL);236237// Happens when a request hits the server before the context is saved238if (me->context == NULL)239return 0;240241CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;242243if (handler) {244return handler->handleData(me, conn, bits, data, data_len) ? 1 : 0;245}246247return 1; // No handler found248}249250void251CivetServer::webSocketCloseHandler(const struct mg_connection *conn,252void *cbdata)253{254const struct mg_request_info *request_info = mg_get_request_info(conn);255assert(request_info != NULL);256CivetServer *me = (CivetServer *)(request_info->user_data);257assert(me != NULL);258259// Happens when a request hits the server before the context is saved260if (me->context == NULL)261return;262263CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;264265if (handler) {266handler->handleClose(me, conn);267}268}269270CivetCallbacks::CivetCallbacks()271{272memset(this, 0, sizeof(*this));273}274275CivetServer::CivetServer(const char **options,276const struct CivetCallbacks *_callbacks,277const void *UserContextIn)278: context(0)279{280struct CivetCallbacks callbacks;281282UserContext = UserContextIn;283284if (_callbacks) {285callbacks = *_callbacks;286userCloseHandler = _callbacks->connection_close;287} else {288userCloseHandler = NULL;289}290callbacks.connection_close = closeHandler;291context = mg_start(&callbacks, this, options);292if (context == NULL)293throw CivetException("null context when constructing CivetServer. "294"Possible problem binding to port.");295}296297CivetServer::CivetServer(std::vector<std::string> options,298const struct CivetCallbacks *_callbacks,299const void *UserContextIn)300: context(0)301{302struct CivetCallbacks callbacks;303304UserContext = UserContextIn;305306if (_callbacks) {307callbacks = *_callbacks;308userCloseHandler = _callbacks->connection_close;309} else {310userCloseHandler = NULL;311}312callbacks.connection_close = closeHandler;313314std::vector<const char *> pointers(options.size());315for (size_t i = 0; i < options.size(); i++) {316pointers[i] = (options[i].c_str());317}318pointers.push_back(0);319320context = mg_start(&callbacks, this, &pointers[0]);321if (context == NULL)322throw CivetException("null context when constructing CivetServer. "323"Possible problem binding to port.");324}325326CivetServer::~CivetServer()327{328close();329}330331void332CivetServer::closeHandler(const struct mg_connection *conn)333{334CivetServer *me = (CivetServer *)mg_get_user_data(mg_get_context(conn));335assert(me != NULL);336337// Happens when a request hits the server before the context is saved338if (me->context == NULL)339return;340341if (me->userCloseHandler) {342me->userCloseHandler(conn);343}344mg_lock_context(me->context);345me->connections.erase(const_cast<struct mg_connection *>(conn));346mg_unlock_context(me->context);347}348349void350CivetServer::addHandler(const std::string &uri, CivetHandler *handler)351{352mg_set_request_handler(context, uri.c_str(), requestHandler, handler);353}354355void356CivetServer::addWebSocketHandler(const std::string &uri,357CivetWebSocketHandler *handler)358{359mg_set_websocket_handler(context,360uri.c_str(),361webSocketConnectionHandler,362webSocketReadyHandler,363webSocketDataHandler,364webSocketCloseHandler,365handler);366}367368void369CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler)370{371mg_set_auth_handler(context, uri.c_str(), authHandler, handler);372}373374void375CivetServer::removeHandler(const std::string &uri)376{377mg_set_request_handler(context, uri.c_str(), NULL, NULL);378}379380void381CivetServer::removeWebSocketHandler(const std::string &uri)382{383mg_set_websocket_handler(384context, uri.c_str(), NULL, NULL, NULL, NULL, NULL);385}386387void388CivetServer::removeAuthHandler(const std::string &uri)389{390mg_set_auth_handler(context, uri.c_str(), NULL, NULL);391}392393void394CivetServer::close()395{396if (context) {397mg_stop(context);398context = 0;399}400}401402int403CivetServer::getCookie(struct mg_connection *conn,404const std::string &cookieName,405std::string &cookieValue)406{407// Maximum cookie length as per microsoft is 4096.408// http://msdn.microsoft.com/en-us/library/ms178194.aspx409char _cookieValue[4096];410const char *cookie = mg_get_header(conn, "Cookie");411int lRead = mg_get_cookie(cookie,412cookieName.c_str(),413_cookieValue,414sizeof(_cookieValue));415cookieValue.clear();416cookieValue.append(_cookieValue);417return lRead;418}419420const char *421CivetServer::getHeader(struct mg_connection *conn,422const std::string &headerName)423{424return mg_get_header(conn, headerName.c_str());425}426427void428CivetServer::urlDecode(const char *src,429std::string &dst,430bool is_form_url_encoded)431{432urlDecode(src, strlen(src), dst, is_form_url_encoded);433}434435void436CivetServer::urlDecode(const char *src,437size_t src_len,438std::string &dst,439bool is_form_url_encoded)440{441int i, j, a, b;442#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')443444dst.clear();445for (i = j = 0; i < (int)src_len; i++, j++) {446if (i < (int)src_len - 2 && src[i] == '%'447&& isxdigit(*(const unsigned char *)(src + i + 1))448&& isxdigit(*(const unsigned char *)(src + i + 2))) {449a = tolower(*(const unsigned char *)(src + i + 1));450b = tolower(*(const unsigned char *)(src + i + 2));451dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));452i += 2;453} else if (is_form_url_encoded && src[i] == '+') {454dst.push_back(' ');455} else {456dst.push_back(src[i]);457}458}459}460461bool462CivetServer::getParam(struct mg_connection *conn,463const char *name,464std::string &dst,465size_t occurrence)466{467const char *formParams = NULL;468const char *queryString = NULL;469const struct mg_request_info *ri = mg_get_request_info(conn);470assert(ri != NULL);471CivetServer *me = (CivetServer *)(ri->user_data);472assert(me != NULL);473mg_lock_context(me->context);474CivetConnection &conobj = me->connections[conn];475mg_lock_connection(conn);476mg_unlock_context(me->context);477478if (conobj.postData != NULL) {479// check if form parameter are already stored480formParams = conobj.postData;481} else {482// otherwise, check if there is a request body483const char *con_len_str = mg_get_header(conn, "Content-Length");484if (con_len_str) {485char *end = 0;486unsigned long con_len = strtoul(con_len_str, &end, 10);487if ((end == NULL) || (*end != 0)) {488// malformed header489return false;490}491if ((con_len > 0) && (con_len <= MAX_PARAM_BODY_LENGTH)) {492// Body is within a reasonable range493494// Allocate memory:495// Add one extra character: in case the post-data is a text, it496// is required as 0-termination.497// Do not increment con_len, since the 0 terminating is not part498// of the content (text or binary).499conobj.postData = (char *)malloc(con_len + 1);500if (conobj.postData != NULL) {501// malloc may fail for huge requests502mg_read(conn, conobj.postData, con_len);503conobj.postData[con_len] = 0;504formParams = conobj.postData;505conobj.postDataLen = con_len;506}507}508if (conobj.postData == NULL) {509// we cannot store the body510return false;511}512}513}514515if (ri->query_string != NULL) {516// get requests do store html <form> field values in the http517// query_string518queryString = ri->query_string;519}520521mg_unlock_connection(conn);522523bool get_param_success = false;524if (!get_param_success && formParams != NULL) {525get_param_success =526getParam(formParams, strlen(formParams), name, dst, occurrence);527}528if (!get_param_success && queryString != NULL) {529get_param_success =530getParam(queryString, strlen(queryString), name, dst, occurrence);531}532533return get_param_success;534}535536bool537CivetServer::getParam(const char *data,538size_t data_len,539const char *name,540std::string &dst,541size_t occurrence)542{543const char *p, *e, *s;544size_t name_len;545546dst.clear();547if (data == NULL || name == NULL || data_len == 0) {548return false;549}550name_len = strlen(name);551e = data + data_len;552553// data is "var1=val1&var2=val2...". Find variable first554for (p = data; p + name_len < e; p++) {555if ((p == data || p[-1] == '&') && p[name_len] == '='556&& !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {557558// Point p to variable value559p += name_len + 1;560561// Point s to the end of the value562s = (const char *)memchr(p, '&', (size_t)(e - p));563if (s == NULL) {564s = e;565}566assert(s >= p);567568// Decode variable into destination buffer569urlDecode(p, (int)(s - p), dst, true);570return true;571}572}573return false;574}575576std::string577CivetServer::getPostData(struct mg_connection *conn)578{579mg_lock_connection(conn);580std::string postdata;581char buf[2048];582int r = mg_read(conn, buf, sizeof(buf));583while (r > 0) {584std::string p = std::string(buf);585p.resize(r);586postdata += p;587r = mg_read(conn, buf, sizeof(buf));588}589mg_unlock_connection(conn);590return postdata;591}592593void594CivetServer::urlEncode(const char *src, std::string &dst, bool append)595{596urlEncode(src, strlen(src), dst, append);597}598599void600CivetServer::urlEncode(const char *src,601size_t src_len,602std::string &dst,603bool append)604{605static const char *dont_escape = "._-$,;~()";606static const char *hex = "0123456789abcdef";607608if (!append)609dst.clear();610611for (; src_len > 0; src++, src_len--) {612if (isalnum(*(const unsigned char *)src)613|| strchr(dont_escape, *(const unsigned char *)src) != NULL) {614dst.push_back(*src);615} else {616dst.push_back('%');617dst.push_back(hex[(*(const unsigned char *)src) >> 4]);618dst.push_back(hex[(*(const unsigned char *)src) & 0xf]);619}620}621}622623std::vector<int>624CivetServer::getListeningPorts()625{626std::vector<int> ports(50);627std::vector<struct mg_server_ports> server_ports(50);628int size = mg_get_server_ports(context,629(int)server_ports.size(),630&server_ports[0]);631if (size <= 0) {632ports.resize(0);633return ports;634}635ports.resize(size);636server_ports.resize(size);637for (int i = 0; i < size; i++) {638ports[i] = server_ports[i].port;639}640641return ports;642}643644CivetServer::CivetConnection::CivetConnection()645{646postData = NULL;647postDataLen = 0;648}649650CivetServer::CivetConnection::~CivetConnection()651{652free(postData);653}654655656