ソースを参照

Added C++ abstraction and example code

Thomas Davis 12 年 前
コミット
b3b554367c
4 ファイル変更463 行追加0 行削除
  1. 142 0
      cpp/CivetServer.cpp
  2. 208 0
      cpp/CivetServer.h
  3. 38 0
      cpp/Makefile
  4. 75 0
      cpp/example.cpp

+ 142 - 0
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 <CivetServer.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+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();
+
+}

+ 208 - 0
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 <civetweb.h>
+#include <vector>
+#include <string>
+
+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<std::string> uris;
+	std::vector<CivetHandler *> handlers;
+	struct mg_context *context;
+
+private:
+	static int begin_request_callback(struct mg_connection *conn);
+
+};
+
+#endif /*  __cplusplus */
+#endif /* _CIVETWEB_SERVER_H_ */

+ 38 - 0
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

+ 75 - 0
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, "<html><body>");
+		mg_printf(conn, "<h2>This is example text!!!</h2>");
+		mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
+				EXIT_URI);
+		mg_printf(conn, "</body></html>\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;
+}