From af000e03c77e70839919448b9d58e608830b1c68 Mon Sep 17 00:00:00 2001 From: Thomas Davis Date: Sun, 1 Sep 2013 00:43:10 -0400 Subject: [PATCH] Added new URI to handler mapping for C code. --- RELEASE_NOTES.md | 5 ++ examples/embedded_cpp/embedded_cpp.cpp | 2 +- include/CivetServer.h | 50 +++---------- include/civetweb.h | 30 ++++++++ src/CivetServer.cpp | 90 ++++------------------ src/civetweb.c | 100 +++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 119 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 4a986250..ffc4eda1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,7 +1,12 @@ Release Notes v1.4 (UNDER DEVELOPMENT) === ### Objectives: *???* +The ma +- Added mg_set_request_handler() which provides a URI mapping for callbacks. + This is a new alternative to overriding callbacks.begin_request. +- Externalized mg_url_encode() +- Externalized mg_strncasecmp() for utiliy - Added CivetServer::getParam methods - Added CivetServer::urlDecode methods - Added CivetServer::urlEncode methods diff --git a/examples/embedded_cpp/embedded_cpp.cpp b/examples/embedded_cpp/embedded_cpp.cpp index fbd6d116..60744a10 100644 --- a/examples/embedded_cpp/embedded_cpp.cpp +++ b/examples/embedded_cpp/embedded_cpp.cpp @@ -51,7 +51,7 @@ int main(int argc, char *argv[]) { server.addHandler(EXIT_URI, new ExitHandler()); printf("Browse files at http://localhost:%s/\n", PORT); - printf("Run example at http://localhost:%s%s\n", PORT, EXIT_URI); + printf("Run example at http://localhost:%s%s\n", PORT, EXAMPLE_URI); printf("Exit at http://localhost:%s%s\n", PORT, EXIT_URI); while (!exitNow) { diff --git a/include/CivetServer.h b/include/CivetServer.h index 31fc4220..37f44353 100644 --- a/include/CivetServer.h +++ b/include/CivetServer.h @@ -81,7 +81,6 @@ public: * * @param options - the web server options. * @param callbacks - optional web server callback methods. - * Note that this class overrides begin_request callback. */ CivetServer(const char **options, const struct mg_callbacks *callbacks = 0); @@ -110,8 +109,7 @@ public: * addHandler(const std::string &, CivetHandler *) * * Adds a URI handler. If there is existing URI handler, it will - * be replaced with this one. The handler is "owned" by this server - * and will be deallocated with it. + * be replaced with this one. * * URI's are ordered and partcial URI's are supported. For example, * consider two URIs in order: /a/b and /a; /a matches @@ -127,31 +125,12 @@ public: /** * removeHandler(const std::string &) * - * Removes a handler, deleting it if found. + * Removes a handler. * * @param - the exact URL used in addHandler(). */ void removeHandler(const std::string &uri); - /** - * getHandler(const std::string &uri) - * - * @param uri - the URI - * @returns the handler that matches the requested URI or 0 if none were found. - */ - CivetHandler *getHandler(const std::string &uri) const { - return getHandler(uri.data(), (unsigned int)uri.length()); - } - - /** - * getHandler(const char *uri, unsigned urilen) - * - * @param uri - the URI - * @param urilen - the length of the URI - * @returns the handler that matches the requested URI or 0 if none were found. - */ - CivetHandler *getHandler(const char *uri, unsigned urilen) const; - /** * getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue) * @param conn - the connection information @@ -293,30 +272,19 @@ public: protected: + struct mg_context *context; + +private: /** - * handleRequest(struct mg_connection *) + * requestHandler(struct mg_connection *, void *cbdata) * * Handles the incomming request. * * @param conn - the connection information - * @returns true if implemented, false otherwise + * @param cbdata - pointer to the CivetHandler instance. + * @returns 0 if implemented, false otherwise */ - virtual bool handleRequest(struct mg_connection *conn); - - /** - * Returns the index of the handler that matches the - * URI exactly. - * - * @param uri - the url to match - */ - int getIndex(const std::string &uri) const; - - std::vector uris; - std::vector handlers; - struct mg_context *context; - -private: - static int begin_request_callback(struct mg_connection *conn); + static int requestHandler(struct mg_connection *conn, void *cbdata); }; diff --git a/include/civetweb.h b/include/civetweb.h index 0ffd5457..033ae54e 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -168,6 +168,36 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, // threads are stopped. Context pointer becomes invalid. void mg_stop(struct mg_context *); +// mg_request_handler +// +// Called when a new request comes in. This callback is URI based +// and configured with mg_sethandler(). +// +// Parameters: +// conn: current connection information. +// cbdata: the callback data configured with mg_sethandler(). +// Returns: +// 0: the handler could not handle the request, so fall through. +// 1: the handler processed the request. +typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata); + +// mg_set_request_handler +// +// Sets or removes a URI mapping for a request handler. +// +// URI's are ordered and partcial URI's are supported. For example, +// consider two URIs in order: /a/b and /a; /a matches +// /a, /a/b matches /a/b, /a/c matches /a. Reversing the order to +// /a and /a/b; /a matches /a/b, /a/b matches /a. /a/c matches /a. +// +// Parameters: +// ctx: server context +// uri: the URI to configure +// handler: the callback handler to use when the URI is requested. +// If NULL, the URI will be removed. +// cbdata: the callback data to give to the handler when it s requested. +void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata); + // Get the value of particular configuration parameter. // The value returned is read-only. Civetweb does not allow changing diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp index fd394578..2b1d2034 100644 --- a/src/CivetServer.cpp +++ b/src/CivetServer.cpp @@ -37,109 +37,51 @@ bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn) return false; } -int CivetServer::begin_request_callback(struct mg_connection *conn) { +int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) { struct mg_request_info *request_info = mg_get_request_info(conn); - - if (!request_info->user_data) - return 0; - CivetServer *me = (CivetServer*) (request_info->user_data); + CivetHandler *handler = (CivetHandler *)cbdata; - if (me->handleRequest(conn)) { - return 1; // Mark as processed - } - - return 0; -} - -bool CivetServer::handleRequest(struct mg_connection *conn) { - struct mg_request_info *request_info = mg_get_request_info(conn); - - CivetHandler *handler = getHandler(request_info->uri); if (handler) { if (strcmp(request_info->request_method, "GET") == 0) { - return handler->handleGet(this, conn); + return handler->handleGet(me, conn) ? 1 : 0; } else if (strcmp(request_info->request_method, "POST") == 0) { - return handler->handlePost(this, conn); + return handler->handlePost(me, conn) ? 1 : 0; } else if (strcmp(request_info->request_method, "PUT") == 0) { - return !handler->handlePost(this, conn); + return !handler->handlePut(me, conn) ? 1 : 0; } else if (strcmp(request_info->request_method, "DELETE") == 0) { - return !handler->handlePost(this, conn); + return !handler->handleDelete(me, conn) ? 1 : 0; } } - return false; // No handler found + return 0; // No handler found + } CivetServer::CivetServer(const char **options, const struct mg_callbacks *_callbacks) : context(0) { - struct mg_callbacks callbacks; if (_callbacks) { - memcpy(&callbacks, _callbacks, sizeof(callbacks)); + context = mg_start(_callbacks, this, options); } else { + struct mg_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); + context = mg_start(&callbacks, this, options); } - callbacks.begin_request = &begin_request_callback; - - context = mg_start(&callbacks, this, options); } CivetServer::~CivetServer() { close(); } -CivetHandler *CivetServer::getHandler(const char *uri, unsigned urilen) const { - - for (unsigned index = 0; index < uris.size(); index++) { - const std::string &handlerURI = uris[index]; - - // first try for an exact match - if (handlerURI == uri) { - return handlers[index]; - } - - // next try for a partial match - // we will accept uri/something - if (handlerURI.length() < urilen - && uri[handlerURI.length()] == '/' - && handlerURI.compare(0, handlerURI.length(), uri, handlerURI.length()) == 0) { - - return handlers[index]; - } - } - - return 0; // none found - -} - void CivetServer::addHandler(const std::string &uri, CivetHandler *handler) { - int index = getIndex(uri); - if (index < 0) { - uris.push_back(uri); - handlers.push_back(handler); - } else if (handlers[index] != handler) { - delete handlers[index]; - handlers[index] = handler; - } + mg_set_request_handler(context, uri.c_str(), requestHandler, handler); } void CivetServer::removeHandler(const std::string &uri) { - int index = getIndex(uri); - if (index >= 0) { - uris.erase(uris.begin() + index, uris.begin() + index + 1); - handlers.erase(handlers.begin() + index, handlers.begin() + index + 1); - } -} - -int CivetServer::getIndex(const std::string &uri) const { - for (unsigned index = 0; index < uris.size(); index++) { - if (uris[index].compare(uri) == 0) - return index; - } - return -1; + mg_set_request_handler(context, uri.c_str(), NULL, NULL); } void CivetServer::close() { @@ -147,12 +89,6 @@ void CivetServer::close() { mg_stop (context); context = 0; } - for (int i = (int) handlers.size() - 1; i >= 0; i--) { - delete handlers[i]; - } - handlers.clear(); - uris.clear(); - } int CivetServer::getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue) diff --git a/src/civetweb.c b/src/civetweb.c index 21cbc8d6..71735d3f 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -494,6 +494,14 @@ static const char *config_options[] = { NULL }; +struct mg_request_handler_info { + char *uri; + size_t uri_len; + mg_request_handler handler; + void *cbdata; + struct mg_request_handler_info *next; +}; + struct mg_context { volatile int stop_flag; // Should we stop event loop SSL_CTX *ssl_ctx; // SSL context @@ -513,6 +521,9 @@ struct mg_context { volatile int sq_tail; // Tail of the socket queue pthread_cond_t sq_full; // Signaled when socket is produced pthread_cond_t sq_empty; // Signaled when socket is consumed + + // linked list of uri handlers + struct mg_request_handler_info *request_handlers; }; struct mg_connection { @@ -4265,6 +4276,81 @@ static void redirect_to_https_port(struct mg_connection *conn, int ssl_index) { lsa.sin.sin_port), conn->request_info.uri); } + +void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata) { + struct mg_request_handler_info *tmp_rh, *lastref = 0; + + // first see it the uri exists + for (tmp_rh = ctx->request_handlers; + tmp_rh != NULL && strcmp(uri, tmp_rh->uri); + lastref = tmp_rh, tmp_rh = tmp_rh->next) + ; + + if (tmp_rh != NULL) { + // already there... + + if (handler != NULL) { + // change this entry + tmp_rh->handler = handler; + tmp_rh->cbdata = cbdata; + } else { + // remove this entry + if (lastref != NULL) + lastref->next = tmp_rh->next; + else + ctx->request_handlers = tmp_rh->next; + free(tmp_rh->uri); + free(tmp_rh); + } + return; + } + + if (handler == NULL) { + // no handler to set, this was a remove request + return; + } + + tmp_rh = (struct mg_request_handler_info *)malloc(sizeof(struct mg_request_handler_info)); + tmp_rh->uri = mg_strdup(uri); + tmp_rh->uri_len = strlen(uri); + tmp_rh->handler = handler; + tmp_rh->cbdata = cbdata; + tmp_rh->next = NULL; + + if (lastref == NULL) + ctx->request_handlers = tmp_rh; + else + lastref->next = tmp_rh; + +} + +static int use_request_handler(struct mg_connection *conn) { + struct mg_request_info *request_info = mg_get_request_info(conn); + const char *uri = request_info->uri; + size_t urilen = strlen(uri); + struct mg_request_handler_info *tmp_rh = conn->ctx->request_handlers; + + for (; tmp_rh != NULL; tmp_rh = tmp_rh->next) { + + // first try for an exact match + if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) { + return tmp_rh->handler(conn, tmp_rh->cbdata); + } + + // next try for a partial match + // we will accept uri/something + if (tmp_rh->uri_len < urilen + && uri[tmp_rh->uri_len] == '/' + && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) { + + return tmp_rh->handler(conn, tmp_rh->cbdata); + } + + } + + return 0; // none found +} + // This is the heart of the Civetweb's logic. // This function is called when the request is read, parsed and validated, // and Civetweb must decide what action to take: serve a file, or @@ -4297,6 +4383,9 @@ static void handle_request(struct mg_connection *conn) { } else if (conn->ctx->callbacks.begin_request != NULL && conn->ctx->callbacks.begin_request(conn)) { // Do nothing, callback has served the request + } else if (conn->ctx->request_handlers != NULL && + use_request_handler(conn)) { + // Do nothing, callback has served the request #if defined(USE_WEBSOCKET) } else if (is_websocket_request(conn)) { handle_websocket_request(conn); @@ -5182,6 +5271,8 @@ static void *master_thread(void *thread_func_param) { static void free_context(struct mg_context *ctx) { int i; + struct mg_request_handler_info *tmp_rh; + if (ctx == NULL) return; @@ -5194,6 +5285,14 @@ static void free_context(struct mg_context *ctx) { free(ctx->config[i]); } + // Deallocate request handlers + while (ctx->request_handlers) { + tmp_rh = ctx->request_handlers; + ctx->request_handlers = tmp_rh->next; + free(tmp_rh->uri); + free(tmp_rh); + } + #ifndef NO_SSL // Deallocate SSL context if (ctx->ssl_ctx != NULL) { @@ -5244,6 +5343,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, } ctx->callbacks = *callbacks; ctx->user_data = user_data; + ctx->request_handlers = 0; while (options && (name = *options++) != NULL) { if ((i = get_option_index(name)) == -1) {