On Wed, 2014-12-03 at 08:12 +0100, Sebastian Huber wrote: > On 03/12/14 08:05, Nick Withers wrote: > > On Wed, 2014-12-03 at 07:48 +0100, Sebastian Huber wrote: > >> >Hello Nick, > >> > > >> >what is the benefit of providing this WebSocket stuff? > > I personally use it to serve dynamic content to web clients. The > > callback mechanism that Mongoose provides allows me to hook these > > requests into my application and reply with whatever I like. > > > > Without processes, CGI's out of the question (well, that's my > > understanding - haven't looked at it for ages) and I'm not aware of > > other viable alternatives...? Having the embedded-side constantly write > > data to files so they could be statically served just wouldn't be > > workable with this application. > > > > Does that make sense? Perhaps I should shoot you off some examples of > > where I use it? > > Ok, I think then we should first enable this unconditionally. If > someone has code size problems, then we can start with a configure > option, but this is only a future optimization. > > This new feature should have a test case in the existing test.
How's the attached? -- Nick Withers Embedded Systems Programmer Department of Nuclear Physics, Research School of Physics and Engineering The Australian National University (CRICOS: 00120C)
>From a14a7ca89503165537bfbdecde2dacfaa8573736 Mon Sep 17 00:00:00 2001 From: Nick Withers <nick.with...@anu.edu.au> Date: Wed, 10 Dec 2014 16:02:52 +1100 Subject: [PATCH] Enable WebSocket support in the Mongoose HTTP server --- cpukit/mghttpd/Makefile.am | 2 +- testsuites/libtests/mghttpd01/init.c | 54 +++++++++- testsuites/libtests/mghttpd01/test-http-client.c | 122 ++++++++++++++++++++++- testsuites/libtests/mghttpd01/test-http-client.h | 11 ++ 4 files changed, 182 insertions(+), 7 deletions(-) diff --git a/cpukit/mghttpd/Makefile.am b/cpukit/mghttpd/Makefile.am index 78af78d..bb5f84b 100644 --- a/cpukit/mghttpd/Makefile.am +++ b/cpukit/mghttpd/Makefile.am @@ -7,7 +7,7 @@ include_mghttpddir = $(includedir)/mghttpd project_lib_LIBRARIES = libmghttpd.a libmghttpd_a_CPPFLAGS = $(AM_CPPFLAGS) # libmghttpd_a_CPPFLAGS += -DHAVE_MD5 -libmghttpd_a_CPPFLAGS += -DNO_SSL -DNO_POPEN -DNO_CGI +libmghttpd_a_CPPFLAGS += -DNO_SSL -DNO_POPEN -DNO_CGI -DUSE_WEBSOCKET libmghttpd_a_SOURCES = mongoose.c mongoose.h include_mghttpd_HEADERS = mongoose.h diff --git a/testsuites/libtests/mghttpd01/init.c b/testsuites/libtests/mghttpd01/init.c index a5eee8c..9f28570 100644 --- a/testsuites/libtests/mghttpd01/init.c +++ b/testsuites/libtests/mghttpd01/init.c @@ -23,6 +23,7 @@ #include <rtems/rtems_bsdnet.h> #include <stdio.h> +#include <string.h> #include <mghttpd/mongoose.h> #include <rtems/imfs.h> @@ -43,6 +44,9 @@ const char rtems_test_name[] = "MGHTTPD 1"; "\r\n" \ "This is a message from the callback function.\r\n" +#define WSTEST_REQ "Test request" +#define WSTEST_RESP "This is a message from the WebSocket callback function." + #define INDEX_HTML "HTTP/1.1 200 OK\r\n" \ "Date: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \ "Last-Modified: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \ @@ -96,6 +100,22 @@ static int callback(struct mg_connection *conn) return 0; } +static int callback_websocket(struct mg_connection *connection, + int bits, + char *data, + size_t data_len) +{ + if (data_len == strlen(WSTEST_REQ) && strncmp(data, WSTEST_REQ, data_len) == 0) + { + mg_websocket_write(connection, WEBSOCKET_OPCODE_TEXT, WSTEST_RESP, strlen(WSTEST_RESP)); + + /* Don't close the WebSocket */ + return 1; + } + + return 0; +} + static void test_mg_index_html(void) { httpc_context httpc_ctx; @@ -164,10 +184,41 @@ static void test_mg_callback(void) free(buffer); } +static void test_mg_websocket(void) +{ + httpc_context httpc_ctx; + char *buffer = malloc(BUFFERSIZE); + bool brv = false; + int rv = 0; + + rtems_test_assert(buffer != NULL); + + puts("=== Get a WebSocket response generated from a callback function" \ + " from first Mongoose instance:"); + + httpc_init_context(&httpc_ctx); + brv = httpc_open_connection(&httpc_ctx, "127.0.0.1", 80); + rtems_test_assert(brv); + brv = httpc_ws_open_connection(&httpc_ctx); + rtems_test_assert(brv); + brv = httpc_ws_send_request(&httpc_ctx, WSTEST_REQ, buffer, BUFFERSIZE); + rtems_test_assert(brv); + brv = httpc_close_connection(&httpc_ctx); + rtems_test_assert(brv); + puts(buffer); + rv = strcmp(buffer, WSTEST_RESP); + rtems_test_assert(rv == 0); + + puts("=== OK"); + + free(buffer); +} + static void test_mongoose(void) { const struct mg_callbacks callbacks = { - .begin_request = callback + .begin_request = callback, + .websocket_data = callback_websocket }; const char *options[] = { "listening_ports", "80", @@ -192,6 +243,7 @@ static void test_mongoose(void) test_mg_index_html(); test_mg_callback(); + test_mg_websocket(); mg_stop(mg1); mg_stop(mg2); diff --git a/testsuites/libtests/mghttpd01/test-http-client.c b/testsuites/libtests/mghttpd01/test-http-client.c index 4902db4..829f986 100644 --- a/testsuites/libtests/mghttpd01/test-http-client.c +++ b/testsuites/libtests/mghttpd01/test-http-client.c @@ -19,10 +19,26 @@ #include <netinet/tcp.h> #include <netdb.h> #include <unistd.h> +#include <stdint.h> #include <string.h> +#include <tmacros.h> + #include "test-http-client.h" +#define HTTPC_WS_CONN_REQ "GET / HTTP/1.1\r\n" \ + "Host: localhost\r\n" \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" \ + "Sec-WebSocket-Version: 13\r\n" \ + "\r\n" +#define HTTPC_WS_CONN_RESP "HTTP/1.1 101 Switching Protocols\r\n" \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" \ + "\r\n" + void httpc_init_context( httpc_context *ctx ) @@ -82,15 +98,111 @@ bool httpc_send_request( int responsesize ) { + rtems_test_assert(ctx != NULL); + rtems_test_assert(ctx->socket >= 0); + rtems_test_assert(request != NULL); + rtems_test_assert(response != NULL); + rtems_test_assert(responsesize > 0); + + static const char *lineend = " HTTP/1.1\r\n\r\n"; + + write(ctx->socket, request, strlen(request)); + write(ctx->socket, lineend, strlen(lineend)); + + char *pos; + int size; + + for (pos = response; response + responsesize - 1 - pos && (size = read(ctx->socket, pos, response + responsesize - 1 - pos)) > 0; pos += size) + ; + + if (size == -1) + return false; + + *pos = '\0'; + + return true; +} + +bool httpc_ws_open_connection( + httpc_context *ctx +) +{ + rtems_test_assert(ctx != NULL); + rtems_test_assert(ctx->socket >= 0); + + write(ctx->socket, HTTPC_WS_CONN_REQ, strlen(HTTPC_WS_CONN_REQ)); + + char response[strlen(HTTPC_WS_CONN_RESP)]; + char *pos; + int size; + + for (pos = response; response + sizeof (response) - pos && (size = read(ctx->socket, pos, response + sizeof (response) - pos)) > 0; pos += size) + ; + + if (size <= 0) + return false; + + if (strncmp(response, HTTPC_WS_CONN_RESP, sizeof (response)) != 0) + return (false); + + return true; +} + +bool httpc_ws_send_request( + httpc_context *ctx, + char *request, + char *response, + int responsesize +) +{ + rtems_test_assert(ctx != NULL); + rtems_test_assert(ctx->socket >= 0); + rtems_test_assert(request != NULL); + rtems_test_assert(response != NULL); + rtems_test_assert(responsesize > 0); + + static const uint16_t ws_header_fin = 1U << 15; + static const uint16_t ws_header_text = 1U << 8; + static const uint16_t ws_header_size = 0x7FU; + int size = strlen(request); - char lineend[] = " HTTP/1.1\r\n\r\n"; + if (size > UINT16_MAX || (uint16_t) size & ~ws_header_size) + return false; + + uint16_t header = htons(ws_header_fin | ws_header_text | (size & ws_header_size)); + + write(ctx->socket, &header, sizeof(header)); write(ctx->socket, request, size); - write(ctx->socket, lineend, sizeof(lineend)); - size = read(ctx->socket, response, responsesize-1); - response[size] = '\0'; + char *pos; + + for (pos = (char *) &header; (char *) &header + sizeof (header) - pos && (size = read(ctx->socket, pos, (char *) &header + sizeof (header) - pos)) > 0; pos += size) + ; + + if (size <= 0) + return false; + + header = ntohs(header); + + if (!(header & ws_header_fin)) + return false; + + if (!(header & ws_header_text)) + return false; + + if (responsesize < (header & ws_header_size) + 1) + return false; + + responsesize = header & ws_header_size; + + for (pos = response; response + responsesize - pos && (size = read(ctx->socket, pos, response + responsesize - pos)) > 0; pos += size) + ; + + if (size <= 0) + return false; + + *pos = '\0'; return true; } - diff --git a/testsuites/libtests/mghttpd01/test-http-client.h b/testsuites/libtests/mghttpd01/test-http-client.h index d3e7cdb..bf51c70 100644 --- a/testsuites/libtests/mghttpd01/test-http-client.h +++ b/testsuites/libtests/mghttpd01/test-http-client.h @@ -51,6 +51,17 @@ bool httpc_send_request( int responsesize ); +bool httpc_ws_open_connection( + httpc_context *ctx +); + +bool httpc_ws_send_request( + httpc_context *ctx, + char *request, + char *response, + int responsesize +); + #ifdef __cplusplus } #endif /* __cplusplus */ -- 2.1.2
_______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel