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

Reply via email to