From b3b554367cb649be13e672a0d4d40533b11364ed Mon Sep 17 00:00:00 2001 From: Thomas Davis Date: Mon, 19 Aug 2013 22:05:40 -0400 Subject: [PATCH] Added C++ abstraction and example code --- cpp/CivetServer.cpp | 142 ++++++++++++++++++++++++++++++ cpp/CivetServer.h | 208 ++++++++++++++++++++++++++++++++++++++++++++ cpp/Makefile | 38 ++++++++ cpp/example.cpp | 75 ++++++++++++++++ 4 files changed, 463 insertions(+) create mode 100644 cpp/CivetServer.cpp create mode 100644 cpp/CivetServer.h create mode 100644 cpp/Makefile create mode 100644 cpp/example.cpp diff --git a/cpp/CivetServer.cpp b/cpp/CivetServer.cpp new file mode 100644 index 00000000..54f3e655 --- /dev/null +++ b/cpp/CivetServer.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2013 Thomas Davis +// +// 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 + +#include + +#include +#include + +int CivetServer::begin_request_callback(struct mg_connection *conn) { + 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); + + 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); + } else if (strcmp(request_info->request_method, "POST") == 0) { + return handler->handlePost(this, conn); + } else if (strcmp(request_info->request_method, "PUT") == 0) { + return !handler->handlePost(this, conn); + } else if (strcmp(request_info->request_method, "DELETE") == 0) { + return !handler->handlePost(this, conn); + } + } + + return false; // 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)); + } else { + memset(&callbacks, 0, sizeof(callbacks)); + } + 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; + } +} + +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; +} + +void CivetServer::close() { + if (context) { + mg_stop (context); + context = 0; + } + for (int i = handlers.size() - 1; i >= 0; i--) { + delete handlers[i]; + } + handlers.clear(); + uris.clear(); + +} diff --git a/cpp/CivetServer.h b/cpp/CivetServer.h new file mode 100644 index 00000000..abaa2692 --- /dev/null +++ b/cpp/CivetServer.h @@ -0,0 +1,208 @@ +// Copyright (c) 2013 Thomas Davis +// +// 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 + +#ifndef _CIVETWEB_SERVER_H_ +#define _CIVETWEB_SERVER_H_ +#ifdef __cplusplus + +#include +#include +#include + +class CivetServer; // forward declaration + +/** + * Basic interface for a URI request handler. Handlers implementations + * must be reentrant. + */ +class CivetHandler { +public: + + /** + * Destructor + */ + virtual ~CivetHandler() { + } + + /** + * Callback method for GET request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handleGet(CivetServer *server, struct mg_connection *conn) { + return false; + } + + /** + * Callback method for POST request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handlePost(CivetServer *server, struct mg_connection *conn) { + return false; + } + + /** + * Callback method for PUT request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handlePut(CivetServer *server, struct mg_connection *conn) { + return false; + } + + /** + * Callback method for DELETE request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handleDelete(CivetServer *server, struct mg_connection *conn) { + return false; + } + +}; + +/** + * CivetServer + * + * Basic class for embedded web server. This has a URL mapping built-in. + */ +class CivetServer { +public: + + /** + * Constructor + * + * This automatically starts the sever. + * It is good practice to call getContext() after this in case there + * were errors starting the server. + * + * @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); + + /** + * Destructor + */ + virtual ~CivetServer(); + + /** + * close() + * + * Stops server and frees resources. + */ + void close(); + + /** + * getContext() + * + * @return the context or 0 if not running. + */ + const struct mg_context *getContext() const { + return context; + } + + /** + * 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. + * + * 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. + * + * @param uri - URI to match. + * @param handler - handler instance to use. This will be free'ed + * when the server closes and instances cannot be reused. + */ + void addHandler(const std::string &uri, CivetHandler *handler); + + /** + * removeHandler(const std::string &) + * + * Removes a handler, deleting it if found. + * + * @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(), 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; + +protected: + + /** + * handleRequest(struct mg_connection *) + * + * Handles the incomming request. + * + * @param conn - the connection information + * @returns true 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); + +}; + +#endif /* __cplusplus */ +#endif /* _CIVETWEB_SERVER_H_ */ diff --git a/cpp/Makefile b/cpp/Makefile new file mode 100644 index 00000000..b3cf18c3 --- /dev/null +++ b/cpp/Makefile @@ -0,0 +1,38 @@ +# Copyright (c) 2013 Thomas Davis +# +# 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 + +CFLAGS= -W -Wall -Wno-unused-parameter -I.. -I. -g +LIB_SOURCES = CivetServer.cpp ../civetweb.c ../build/md5.c +LIBS = -lpthread + +all: + $(CXX) $(CFLAGS) example.cpp $(LIB_SOURCES) $(LIBS) -o example; + +MSVC = e:/vc6 +CL = $(MSVC)/bin/cl +CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \ + /I$(MSVC)/include /I.. /I. /Dsnprintf=_snprintf \ + /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 + +windows: + $(CL) example.cpp $(LIB_SOURCES) $(CLFLAGS) + +clean: + rm -rf example *.exe *.dSYM *.obj diff --git a/cpp/example.cpp b/cpp/example.cpp new file mode 100644 index 00000000..53e92180 --- /dev/null +++ b/cpp/example.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2013 Thomas Davis +// +// 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 + +// Simple example program on how to use Embedded C++ interface. + +#include "CivetServer.h" + +#define DOCUMENT_ROOT "." +#define PORT "8888" +#define EXAMPLE_URI "/example" +#define EXIT_URI "/exit" +bool exitNow = false; + +class ExampleHandler: public CivetHandler { +public: + bool handleGet(CivetServer *server, struct mg_connection *conn) { + mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); + mg_printf(conn, ""); + mg_printf(conn, "

This is example text!!!

"); + mg_printf(conn, "

To exit click here

", + EXIT_URI); + mg_printf(conn, "\n"); + return true; + } +}; + +class ExitHandler: public CivetHandler { +public: + bool handleGet(CivetServer *server, struct mg_connection *conn) { + mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"); + mg_printf(conn, "Bye!\n"); + exitNow = true; + return true; + } +}; + +int main(int argc, char *argv[]) { + + const char * options[] = { "document_root", DOCUMENT_ROOT, + "listening_ports", PORT, 0 }; + + CivetServer server(options); + + server.addHandler(EXAMPLE_URI, new ExampleHandler()); + 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("Exit at http://localhost:%s%s\n", PORT, EXIT_URI); + + while (!exitNow) { + sleep(1); + } + + printf("Bye!\n"); + + return 0; +}