From 65e2247b176be2354b88ece73623b91965aadec1 Mon Sep 17 00:00:00 2001
From: NADAL Jean-Baptiste
Date: Wed, 13 Nov 2019 18:26:18 +0100
Subject: [PATCH] Update civetweb
---
CMakeLists.txt | 1 +
build.local.sh | 17 +-
src/CMakeLists.txt | 8 +-
src/domo-iot.cpp | 445 ++++++++++++++++++++++++++++
src/{domo-iot.c => domo-iot.nats.c} | 85 ++++--
5 files changed, 527 insertions(+), 29 deletions(-)
create mode 100644 src/domo-iot.cpp
rename src/{domo-iot.c => domo-iot.nats.c} (52%)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e06bf1..dacaa40 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,4 +10,5 @@ set(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build/lib")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake-modules/")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../nats.c ${CMAKE_CURRENT_BINARY_DIR}/nats.c)
+add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../civetweb ${CMAKE_CURRENT_BINARY_DIR}/civetweb)
add_subdirectory (src)
diff --git a/build.local.sh b/build.local.sh
index 66539c7..4324035 100755
--- a/build.local.sh
+++ b/build.local.sh
@@ -4,6 +4,21 @@ echo "Clean"
rm -rf build/*
fi
+CMAKE_OPTS=""
+
+# Options for CIVETWEB
+CMAKE_OPTS="$CMAKE_OPTS -DCIVETWEB_ENABLE_SERVER_EXECUTABLE=OFF"
+CMAKE_OPTS="$CMAKE_OPTS -DCIVETWEB_ENABLE_CXX=ON"
+CMAKE_OPTS="$CMAKE_OPTS -DCIVETWEB_ENABLE_WEBSOCKETS=ON"
+CMAKE_OPTS="$CMAKE_OPTS -DCIVETWEB_BUILD_TESTING=OFF"
+
+# Options for NATS.c
+CMAKE_OPTS="$CMAKE_OPTS -DNATS_BUILD_EXAMPLES=OFF"
+CMAKE_OPTS="$CMAKE_OPTS -DNATS_BUILD_STREAMING=OFF"
+CMAKE_OPTS="$CMAKE_OPTS -DNATS_BUILD_TLS_USE_OPENSSL_1_1_API=ON"
+CMAKE_OPTS="$CMAKE_OPTS -DNATS_BUILD_WITH_TLS=OFF"
+CMAKE_OPTS="$CMAKE_OPTS -DNATS_BUILD_LIB_SHARED=OFF"
+
cd build
-cmake .. -DNATS_BUILD_WITH_TLS=OFF -DNATS_BUILD_TLS_USE_OPENSSL_1_1_API=ON -DNATS_BUILD_STREAMING=OFF ..
+cmake .. $CMAKE_OPTS ..
make
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cb78cb8..d7e1469 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,14 +12,18 @@ include_directories(${CMAKE_SOURCE_DIR}/../nats.c/src)
file(
GLOB_RECURSE
source_files
- domo-iot.c
+ domo-iot.cpp
)
add_executable (domo-iot ${source_files})
target_link_libraries (domo-iot
LINK_PUBLIC
- nats
+ nats_static
+ event
+ pthread
+ event_pthreads
+ civetweb-cpp
rt
)
diff --git a/src/domo-iot.cpp b/src/domo-iot.cpp
new file mode 100644
index 0000000..a830c5d
--- /dev/null
+++ b/src/domo-iot.cpp
@@ -0,0 +1,445 @@
+/* Copyright (c) 2013-2018 the Civetweb developers
+ * Copyright (c) 2013 No Face Press, LLC
+ * License http://opensource.org/licenses/mit-license.php MIT License
+ */
+
+// Simple example program on how to use Embedded C++ interface.
+
+#include "CivetServer.h"
+#include
+
+#ifdef _WIN32
+#include
+#else
+#include
+#endif
+
+#define DOCUMENT_ROOT "."
+#define PORT "8081"
+#define EXAMPLE_URI "/example"
+#define EXIT_URI "/exit"
+
+
+#define USE_WEBSOCKET 1
+
+/* Exit flag for main loop */
+volatile 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\nConnection: close\r\n\r\n");
+ mg_printf(conn, "\r\n");
+ mg_printf(conn,
+ "This is an example text from a C++ handler
\r\n");
+ mg_printf(conn,
+ "To see a page from the A handler click here
\r\n");
+ mg_printf(conn,
+ "\r\n");
+ mg_printf(conn,
+ "\r\n");
+ mg_printf(conn,
+ "To see a page from the A/B handler click here
\r\n");
+ mg_printf(conn,
+ "To see a page from the *.foo handler click here
\r\n");
+ mg_printf(conn,
+ "To see a page from the WebSocket handler click here
\r\n");
+ mg_printf(conn,
+ "To exit click here
\r\n",
+ EXIT_URI);
+ mg_printf(conn, "\r\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\nConnection: close\r\n\r\n");
+ mg_printf(conn, "Bye!\n");
+ exitNow = true;
+ return true;
+ }
+};
+
+class AHandler : public CivetHandler
+{
+ private:
+ bool
+ handleAll(const char *method,
+ CivetServer *server,
+ struct mg_connection *conn)
+ {
+ std::string s = "";
+ mg_printf(conn,
+ "HTTP/1.1 200 OK\r\nContent-Type: "
+ "text/html\r\nConnection: close\r\n\r\n");
+ mg_printf(conn, "");
+ mg_printf(conn, "This is the A handler for \"%s\" !
", method);
+ if (CivetServer::getParam(conn, "param", s)) {
+ mg_printf(conn, "param set to %s
", s.c_str());
+ } else {
+ mg_printf(conn, "param not set
");
+ }
+ mg_printf(conn, "\n");
+ return true;
+ }
+
+ public:
+ bool
+ handleGet(CivetServer *server, struct mg_connection *conn)
+ {
+ return handleAll("GET", server, conn);
+ }
+ bool
+ handlePost(CivetServer *server, struct mg_connection *conn)
+ {
+ return handleAll("POST", server, conn);
+ }
+};
+
+class ABHandler : 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\nConnection: close\r\n\r\n");
+ mg_printf(conn, "");
+ mg_printf(conn, "This is the AB handler!!!
");
+ mg_printf(conn, "\n");
+ return true;
+ }
+};
+
+class FooHandler : public CivetHandler
+{
+ public:
+ bool
+ handleGet(CivetServer *server, struct mg_connection *conn)
+ {
+ /* Handler may access the request info using mg_get_request_info */
+ const struct mg_request_info *req_info = mg_get_request_info(conn);
+
+ mg_printf(conn,
+ "HTTP/1.1 200 OK\r\nContent-Type: "
+ "text/html\r\nConnection: close\r\n\r\n");
+
+ mg_printf(conn, "\n");
+ mg_printf(conn, "This is the Foo GET handler!!!
\n");
+ mg_printf(conn,
+ "The request was:
%s %s HTTP/%s
\n",
+ req_info->request_method,
+ req_info->request_uri,
+ req_info->http_version);
+ mg_printf(conn, "\n");
+
+ return true;
+ }
+ bool
+ handlePost(CivetServer *server, struct mg_connection *conn)
+ {
+ /* Handler may access the request info using mg_get_request_info */
+ const struct mg_request_info *req_info = mg_get_request_info(conn);
+ long long rlen, wlen;
+ long long nlen = 0;
+ long long tlen = req_info->content_length;
+ char buf[1024];
+
+ mg_printf(conn,
+ "HTTP/1.1 200 OK\r\nContent-Type: "
+ "text/html\r\nConnection: close\r\n\r\n");
+
+ mg_printf(conn, "\n");
+ mg_printf(conn, "This is the Foo POST handler!!!
\n");
+ mg_printf(conn,
+ "The request was:
%s %s HTTP/%s
\n",
+ req_info->request_method,
+ req_info->request_uri,
+ req_info->http_version);
+ mg_printf(conn, "Content Length: %li
\n", (long)tlen);
+ mg_printf(conn, "\n");
+
+ while (nlen < tlen) {
+ rlen = tlen - nlen;
+ if (rlen > sizeof(buf)) {
+ rlen = sizeof(buf);
+ }
+ rlen = mg_read(conn, buf, (size_t)rlen);
+ if (rlen <= 0) {
+ break;
+ }
+ wlen = mg_write(conn, buf, (size_t)rlen);
+ if (wlen != rlen) {
+ break;
+ }
+ nlen += wlen;
+ }
+
+ mg_printf(conn, "\n\n");
+ mg_printf(conn, "\n");
+
+ return true;
+ }
+
+ #define fopen_recursive fopen
+
+ bool
+ handlePut(CivetServer *server, struct mg_connection *conn)
+ {
+ /* Handler may access the request info using mg_get_request_info */
+ const struct mg_request_info *req_info = mg_get_request_info(conn);
+ long long rlen, wlen;
+ long long nlen = 0;
+ long long tlen = req_info->content_length;
+ FILE * f;
+ char buf[1024];
+ int fail = 0;
+
+#ifdef _WIN32
+ _snprintf(buf, sizeof(buf), "D:\\somewhere\\%s\\%s", req_info->remote_user, req_info->local_uri);
+ buf[sizeof(buf)-1] = 0;
+ if (strlen(buf)>255) {
+ /* Windows will not work with path > 260 (MAX_PATH), unless we use
+ * the unicode API. However, this is just an example code: A real
+ * code will probably never store anything to D:\\somewhere and
+ * must be adapted to the specific needs anyhow. */
+ fail = 1;
+ f = NULL;
+ } else {
+ f = fopen_recursive(buf, "wb");
+ }
+#else
+ snprintf(buf, sizeof(buf), "~/somewhere/%s/%s", req_info->remote_user, req_info->local_uri);
+ buf[sizeof(buf)-1] = 0;
+ if (strlen(buf)>1020) {
+ /* The string is too long and probably truncated. Make sure an
+ * UTF-8 string is never truncated between the UTF-8 code bytes.
+ * This example code must be adapted to the specific needs. */
+ fail = 1;
+ f = NULL;
+ } else {
+ f = fopen_recursive(buf, "w");
+ }
+#endif
+
+ if (!f) {
+ fail = 1;
+ } else {
+ while (nlen < tlen) {
+ rlen = tlen - nlen;
+ if (rlen > sizeof(buf)) {
+ rlen = sizeof(buf);
+ }
+ rlen = mg_read(conn, buf, (size_t)rlen);
+ if (rlen <= 0) {
+ fail = 1;
+ break;
+ }
+ wlen = fwrite(buf, 1, (size_t)rlen, f);
+ if (wlen != rlen) {
+ fail = 1;
+ break;
+ }
+ nlen += wlen;
+ }
+ fclose(f);
+ }
+
+ if (fail) {
+ mg_printf(conn,
+ "HTTP/1.1 409 Conflict\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n\r\n");
+ } else {
+ mg_printf(conn,
+ "HTTP/1.1 201 Created\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n\r\n");
+ }
+
+ return true;
+ }
+};
+
+class WsStartHandler : 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\nConnection: "
+ "close\r\n\r\n");
+
+ mg_printf(conn, "\n");
+ mg_printf(conn, "\n\n");
+ mg_printf(conn, "\n");
+ mg_printf(conn, "Embedded websocket example\n");
+
+#ifdef USE_WEBSOCKET
+ /* mg_printf(conn, "\n"); ... xhtml style */
+ mg_printf(conn, "\n");
+ mg_printf(conn, "\n\n");
+ mg_printf(
+ conn,
+ "No websocket connection yet
\n");
+#else
+ mg_printf(conn, "\n\n");
+ mg_printf(conn, "Example not compiled with USE_WEBSOCKET\n");
+#endif
+ mg_printf(conn, "\n\n");
+
+ return 1;
+}
+};
+
+
+#ifdef USE_WEBSOCKET
+class WebSocketHandler : public CivetWebSocketHandler {
+
+ virtual bool handleConnection(CivetServer *server,
+ const struct mg_connection *conn) {
+ printf("WS connected\n");
+ return true;
+ }
+
+ virtual void handleReadyState(CivetServer *server,
+ struct mg_connection *conn) {
+ printf("WS ready\n");
+
+ const char *text = "Hello from the websocket ready handler";
+ mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, text, strlen(text));
+ }
+
+ virtual bool handleData(CivetServer *server,
+ struct mg_connection *conn,
+ int bits,
+ char *data,
+ size_t data_len) {
+ printf("WS got %lu bytes: ", (long unsigned)data_len);
+ fwrite(data, 1, data_len, stdout);
+ printf("\n");
+
+ mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, data, data_len);
+ return (data_len<4);
+ }
+
+ virtual void handleClose(CivetServer *server,
+ const struct mg_connection *conn) {
+ printf("WS closed\n");
+ }
+};
+#endif
+
+
+int
+main(int argc, char *argv[])
+{
+ const char *options[] = {
+ "document_root", DOCUMENT_ROOT, "listening_ports", PORT, 0};
+
+ std::vector cpp_options;
+ for (int i=0; i<(sizeof(options)/sizeof(options[0])-1); i++) {
+ cpp_options.push_back(options[i]);
+ }
+
+ // CivetServer server(options); // <-- C style start
+ CivetServer server(cpp_options); // <-- C++ style start
+
+ ExampleHandler h_ex;
+ server.addHandler(EXAMPLE_URI, h_ex);
+
+ ExitHandler h_exit;
+ server.addHandler(EXIT_URI, h_exit);
+
+ AHandler h_a;
+ server.addHandler("/a", h_a);
+
+ ABHandler h_ab;
+ server.addHandler("/a/b", h_ab);
+
+ WsStartHandler h_ws;
+ server.addHandler("/ws", h_ws);
+
+#ifdef NO_FILES
+ /* This handler will handle "everything else", including
+ * requests to files. If this handler is installed,
+ * NO_FILES should be set. */
+ FooHandler h_foo;
+ server.addHandler("", h_foo);
+
+ printf("See a page from the \"all\" handler at http://localhost:%s/\n", PORT);
+#else
+ FooHandler h_foo;
+ server.addHandler("**.foo", h_foo);
+ printf("Browse files at http://localhost:%s/\n", PORT);
+#endif
+
+#ifdef USE_WEBSOCKET
+ WebSocketHandler h_websocket;
+ server.addWebSocketHandler("/websocket", h_websocket);
+ printf("Run websocket example at http://localhost:%s/ws\n", PORT);
+#endif
+
+ 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) {
+#ifdef _WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+
+ printf("Bye!\n");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/domo-iot.c b/src/domo-iot.nats.c
similarity index 52%
rename from src/domo-iot.c
rename to src/domo-iot.nats.c
index 7b5e353..d191642 100644
--- a/src/domo-iot.c
+++ b/src/domo-iot.nats.c
@@ -25,10 +25,12 @@
/*-------------------------------- INCLUDES ---------------------------------*/
+#include
+
+#include
#include
-static void
-onMsg(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure)
+static void onMsg(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure)
{
printf("Received msg: %s - %.*s\n",
natsMsg_GetSubject(msg),
@@ -37,9 +39,6 @@ onMsg(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure)
// Need to destroy the message!
natsMsg_Destroy(msg);
-
- // Notify the main thread that we are done.
- *(bool *)(closure) = true;
}
/*! ----------------------------------------------------------------------------
@@ -50,33 +49,54 @@ onMsg(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure)
int main(int argc, char *argv[])
{
natsConnection *conn = NULL;
+ natsOptions *opts = NULL;
natsSubscription *sub = NULL;
natsStatus s;
- volatile bool done = false;
+ struct event_base *evLoop = NULL;
+ nats_Open(-1);
printf("Listening on subject 'foo'\n");
- // Creates a connection to the default NATS URL
- s = natsConnection_ConnectTo(&conn, "nats.nadal-fr.com");
- if (s == NATS_OK)
- {
- // Creates an asynchronous subscription on subject "foo".
- // When a message is sent on subject "foo", the callback
- // onMsg() will be invoked by the client library.
- // You can pass a closure as the last argument.
- s = natsConnection_Subscribe(&sub, conn, "foo", onMsg, (void *)&done);
- }
- if (s == NATS_OK)
- {
- for (; !done;)
- {
- nats_Sleep(100);
- }
- }
+ // One time initialization of things that we need.
+ natsLibevent_Init();
- // Anything that is created need to be destroyed
- natsSubscription_Destroy(sub);
- natsConnection_Destroy(conn);
+ // Create a loop.
+ evLoop = event_base_new();
+ if (evLoop == NULL)
+ s = NATS_ERR;
+
+ if (natsOptions_Create(&opts) != NATS_OK)
+ s = NATS_NO_MEMORY;
+
+ s = natsOptions_UseGlobalMessageDelivery(opts, true);
+
+ // Indicate which loop and callbacks to use once connected.
+ if (s == NATS_OK)
+ s = natsOptions_SetEventLoop(opts, (void *)evLoop,
+ natsLibevent_Attach,
+ natsLibevent_Read,
+ natsLibevent_Write,
+ natsLibevent_Detach);
+
+ s = natsOptions_SetURL(opts, "nats.nadal-fr.com");
+
+ if (s == NATS_OK)
+ s = natsConnection_Connect(&conn, opts);
+
+ if (s == NATS_OK)
+ s = natsConnection_Subscribe(&sub, conn, "foo", onMsg, NULL);
+
+ // For maximum performance, set no limit on the number of pending messages.
+ if (s == NATS_OK)
+ s = natsSubscription_SetPendingLimits(sub, -1, -1);
+
+ // Run the event loop.
+ // This call will return when the connection is closed (either after
+ // receiving all messages, or disconnected and unable to reconnect).
+ if (s == NATS_OK)
+ {
+ event_base_dispatch(evLoop);
+ }
// If there was an error, print a stack trace and exit
if (s != NATS_OK)
@@ -85,5 +105,18 @@ int main(int argc, char *argv[])
exit(2);
}
+ // Anything that is created need to be destroyed
+ natsSubscription_Destroy(sub);
+ natsConnection_Destroy(conn);
+ natsOptions_Destroy(opts);
+
+ if (evLoop != NULL)
+ event_base_free(evLoop);
+
+ // To silence reports of memory still in used with valgrind
+ nats_Close();
+ libevent_global_shutdown();
+
+ printf("done\n");
return 0;
}