refactor test server
authorAndy Green <andy.green@linaro.org>
Thu, 19 Nov 2015 05:55:47 +0000 (13:55 +0800)
committerAndy Green <andy.green@linaro.org>
Thu, 19 Nov 2015 05:55:47 +0000 (13:55 +0800)
Split test-server into four C files and a header

Signed-off-by: Andy Green <andy.green@linaro.org>
CMakeLists.txt
test-server/test-server-dumb-increment.c [new file with mode: 0644]
test-server/test-server-http.c [new file with mode: 0644]
test-server/test-server-mirror.c [new file with mode: 0644]
test-server/test-server.c
test-server/test-server.h [new file with mode: 0644]

index 75691e5..721f17e 100644 (file)
@@ -681,11 +681,22 @@ if (NOT LWS_WITHOUT_TESTAPPS)
        #
        # Helper function for adding a test app.
        #
-       macro(create_test_app TEST_NAME MAIN_SRC)
+       macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4)
 
                set(TEST_SRCS ${MAIN_SRC})
                set(TEST_HDR)
-
+               if ("${S2}" STREQUAL "")
+               else()
+                       list(APPEND TEST_SRCS ${S2})
+               endif()
+               if ("${S3}" STREQUAL "")
+               else()
+                       list(APPEND TEST_SRCS ${S3})
+               endif()
+               if ("${S4}" STREQUAL "")
+               else()
+                       list(APPEND TEST_SRCS ${S4})
+               endif()
                if (WIN32)
                        list(APPEND TEST_SRCS
                                ${WIN32_HELPERS_PATH}/getopt.c
@@ -743,14 +754,14 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                # test-server
                #
                if (NOT LWS_WITHOUT_TEST_SERVER)
-                       create_test_app(test-server "test-server/test-server.c")
+                       create_test_app(test-server "test-server/test-server.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
                endif()
 
                #
                # test-server-extpoll
                #
                if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL)
-                       create_test_app(test-server-extpoll "test-server/test-server.c")
+                       create_test_app(test-server-extpoll "test-server/test-server.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
                        # Set defines for this executable only.
                        set_property(
                                TARGET test-server-extpoll
@@ -855,27 +866,27 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                # test-client
                #
                if (NOT LWS_WITHOUT_TEST_CLIENT)
-                       create_test_app(test-client "test-server/test-client.c")
+                       create_test_app(test-client "test-server/test-client.c" "" "" "")
                endif()
 
                #
                # test-fraggle
                #
                if (NOT LWS_WITHOUT_TEST_FRAGGLE)
-                       create_test_app(test-fraggle "test-server/test-fraggle.c")
+                       create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "")
                endif()
 
                #
                # test-ping
                #
                if (NOT LWS_WITHOUT_TEST_PING)
-                       create_test_app(test-ping "test-server/test-ping.c")
+                       create_test_app(test-ping "test-server/test-ping.c" "" "" "")
                endif()
                #
                # test-echo
                #
                if (NOT WITHOUT_TEST_ECHO)
-                       create_test_app(test-echo "test-server/test-echo.c")
+                       create_test_app(test-echo "test-server/test-echo.c" "" "" "")
                endif()
 
        endif(NOT LWS_WITHOUT_CLIENT)
diff --git a/test-server/test-server-dumb-increment.c b/test-server/test-server-dumb-increment.c
new file mode 100644 (file)
index 0000000..81af06d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+#include "test-server.h"
+
+/* dumb_increment protocol */
+
+int
+callback_dumb_increment(struct libwebsocket_context *context,
+                       struct libwebsocket *wsi,
+                       enum libwebsocket_callback_reasons reason,
+                       void *user, void *in, size_t len)
+{
+       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
+                                                 LWS_SEND_BUFFER_POST_PADDING];
+       struct per_session_data__dumb_increment *pss =
+                       (struct per_session_data__dumb_increment *)user;
+       unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
+       int n, m;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_ESTABLISHED:
+               pss->number = 0;
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               n = sprintf((char *)p, "%d", pss->number++);
+               m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
+               if (m < n) {
+                       lwsl_err("ERROR %d writing to di socket\n", n);
+                       return -1;
+               }
+               if (close_testing && pss->number == 50) {
+                       lwsl_info("close tesing limit, closing\n");
+                       return -1;
+               }
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               if (len < 6)
+                       break;
+               if (strcmp((const char *)in, "reset\n") == 0)
+                       pss->number = 0;
+               break;
+       /*
+        * this just demonstrates how to use the protocol filter. If you won't
+        * study and reject connections based on header content, you don't need
+        * to handle this callback
+        */
+       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+               dump_handshake_info(wsi);
+               /* you could return non-zero here and kill the connection */
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
diff --git a/test-server/test-server-http.c b/test-server/test-server-http.c
new file mode 100644 (file)
index 0000000..dacd8e8
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+#include "test-server.h"
+
+/*
+ * This demo server shows how to use libwebsockets for one or more
+ * websocket protocols in the same server
+ *
+ * It defines the following websocket protocols:
+ *
+ *  dumb-increment-protocol:  once the socket is opened, an incrementing
+ *                             ascii string is sent down it every 50ms.
+ *                             If you send "reset\n" on the websocket, then
+ *                             the incrementing number is reset to 0.
+ *
+ *  lws-mirror-protocol: copies any received packet to every connection also
+ *                             using this protocol, including the sender
+ */
+
+enum demo_protocols {
+       /* always first */
+       PROTOCOL_HTTP = 0,
+
+       PROTOCOL_DUMB_INCREMENT,
+       PROTOCOL_LWS_MIRROR,
+
+       /* always last */
+       DEMO_PROTOCOL_COUNT
+};
+
+/*
+ * We take a strict whitelist approach to stop ../ attacks
+ */
+struct serveable {
+       const char *urlpath;
+       const char *mimetype;
+}; 
+
+/*
+ * this is just an example of parsing handshake headers, you don't need this
+ * in your code unless you will filter allowing connections by the header
+ * content
+ */
+void
+dump_handshake_info(struct libwebsocket *wsi)
+{
+       int n = 0;
+       char buf[256];
+       const unsigned char *c;
+
+       do {
+               c = lws_token_to_string(n);
+               if (!c) {
+                       n++;
+                       continue;
+               }
+
+               if (!lws_hdr_total_length(wsi, n)) {
+                       n++;
+                       continue;
+               }
+
+               lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+               fprintf(stderr, "    %s = %s\n", (char *)c, buf);
+               n++;
+       } while (c);
+}
+
+const char * get_mimetype(const char *file)
+{
+       int n = strlen(file);
+
+       if (n < 5)
+               return NULL;
+
+       if (!strcmp(&file[n - 4], ".ico"))
+               return "image/x-icon";
+
+       if (!strcmp(&file[n - 4], ".png"))
+               return "image/png";
+
+       if (!strcmp(&file[n - 5], ".html"))
+               return "text/html";
+
+       return NULL;
+}
+
+/* this protocol server (always the first one) handles HTTP,
+ * 
+ * Some misc callbacks that aren't associated with a protocol also turn up only
+ * here on the first protocol server.
+ */
+
+int callback_http(struct libwebsocket_context *context,
+               struct libwebsocket *wsi,
+               enum libwebsocket_callback_reasons reason, void *user,
+                                                          void *in, size_t len)
+{
+       struct per_session_data__http *pss =
+                       (struct per_session_data__http *)user;
+       static unsigned char buffer[4096];
+       struct stat stat_buf;
+       char leaf_path[1024];
+       const char *mimetype;
+       char *other_headers;
+       unsigned char *end;
+       struct timeval tv;
+       unsigned char *p;
+       char buf[256];
+       char b64[64];
+       int n, m;
+
+#ifdef EXTERNAL_POLL
+       struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+#endif
+
+       switch (reason) {
+       case LWS_CALLBACK_HTTP:
+
+               dump_handshake_info(wsi);
+
+               if (len < 1) {
+                       libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_BAD_REQUEST, NULL);
+                       goto try_to_reuse;
+               }
+
+               /* this example server has no concept of directories */
+               if (strchr((const char *)in + 1, '/')) {
+                       libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_FORBIDDEN, NULL);
+                       goto try_to_reuse;
+               }
+
+               /* if a legal POST URL, let it continue and accept data */
+               if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+                       return 0;
+
+               /* check for the "send a big file by hand" example case */
+
+               if (!strcmp((const char *)in, "/leaf.jpg")) {
+                       if (strlen(resource_path) > sizeof(leaf_path) - 10)
+                               return -1;
+                       sprintf(leaf_path, "%s/leaf.jpg", resource_path);
+
+                       /* well, let's demonstrate how to send the hard way */
+
+                       p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
+                       end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
+#ifdef _WIN32
+                       pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
+#else
+                       pss->fd = open(leaf_path, O_RDONLY);
+#endif
+
+                       if (pss->fd < 0)
+                               return -1;
+
+                       if (fstat(pss->fd, &stat_buf) < 0)
+                               return -1;
+
+                       /*
+                        * we will send a big jpeg file, but it could be
+                        * anything.  Set the Content-Type: appropriately
+                        * so the browser knows what to do with it.
+                        * 
+                        * Notice we use the APIs to build the header, which
+                        * will do the right thing for HTTP 1/1.1 and HTTP2
+                        * depending on what connection it happens to be working
+                        * on
+                        */
+                       if (lws_add_http_header_status(context, wsi, 200, &p, end))
+                               return 1;
+                       if (lws_add_http_header_by_token(context, wsi,
+                                       WSI_TOKEN_HTTP_SERVER,
+                                       (unsigned char *)"libwebsockets",
+                                       13, &p, end))
+                               return 1;
+                       if (lws_add_http_header_by_token(context, wsi,
+                                       WSI_TOKEN_HTTP_CONTENT_TYPE,
+                                       (unsigned char *)"image/jpeg",
+                                       10, &p, end))
+                               return 1;
+                       if (lws_add_http_header_content_length(context, wsi,
+                                               stat_buf.st_size, &p, end))
+                               return 1;
+                       if (lws_finalize_http_header(context, wsi, &p, end))
+                               return 1;
+
+                       /*
+                        * send the http headers...
+                        * this won't block since it's the first payload sent
+                        * on the connection since it was established
+                        * (too small for partial)
+                        * 
+                        * Notice they are sent using LWS_WRITE_HTTP_HEADERS
+                        * which also means you can't send body too in one step,
+                        * this is mandated by changes in HTTP2
+                        */
+
+                       n = libwebsocket_write(wsi,
+                                       buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                       p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
+                                       LWS_WRITE_HTTP_HEADERS);
+
+                       if (n < 0) {
+                               close(pss->fd);
+                               return -1;
+                       }
+                       /*
+                        * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
+                        */
+                       libwebsocket_callback_on_writable(context, wsi);
+                       break;
+               }
+
+               /* if not, send a file the easy way */
+               strcpy(buf, resource_path);
+               if (strcmp(in, "/")) {
+                       if (*((const char *)in) != '/')
+                               strcat(buf, "/");
+                       strncat(buf, in, sizeof(buf) - strlen(resource_path));
+               } else /* default file to serve */
+                       strcat(buf, "/test.html");
+               buf[sizeof(buf) - 1] = '\0';
+
+               /* refuse to serve files we don't understand */
+               mimetype = get_mimetype(buf);
+               if (!mimetype) {
+                       lwsl_err("Unknown mimetype for %s\n", buf);
+                       libwebsockets_return_http_status(context, wsi,
+                                     HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
+                       return -1;
+               }
+
+               /* demostrates how to set a cookie on / */
+
+               other_headers = NULL;
+               n = 0;
+               if (!strcmp((const char *)in, "/") &&
+                          !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
+                       /* this isn't very unguessable but it'll do for us */
+                       gettimeofday(&tv, NULL);
+                       n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
+                               (unsigned int)tv.tv_sec,
+                               (unsigned int)tv.tv_usec);
+
+                       p = (unsigned char *)leaf_path;
+
+                       if (lws_add_http_header_by_name(context, wsi, 
+                               (unsigned char *)"set-cookie:", 
+                               (unsigned char *)b64, n, &p,
+                               (unsigned char *)leaf_path + sizeof(leaf_path)))
+                               return 1;
+                       n = (char *)p - leaf_path;
+                       other_headers = leaf_path;
+               }
+
+               n = libwebsockets_serve_http_file(context, wsi, buf,
+                                               mimetype, other_headers, n);
+               if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
+                       return -1; /* error or can't reuse connection: close the socket */
+
+               /*
+                * notice that the sending of the file completes asynchronously,
+                * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+                * it's done
+                */
+
+               break;
+
+       case LWS_CALLBACK_HTTP_BODY:
+               strncpy(buf, in, 20);
+               buf[20] = '\0';
+               if (len < 20)
+                       buf[len] = '\0';
+
+               lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
+                               (const char *)buf, (int)len);
+
+               break;
+
+       case LWS_CALLBACK_HTTP_BODY_COMPLETION:
+               lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
+               /* the whole of the sent body arrived, close or reuse the connection */
+               libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_OK, NULL);
+               goto try_to_reuse;
+
+       case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+               goto try_to_reuse;
+
+       case LWS_CALLBACK_HTTP_WRITEABLE:
+               /*
+                * we can send more of whatever it is we were sending
+                */
+               do {
+                       /* we'd like the send this much */
+                       n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
+                       
+                       /* but if the peer told us he wants less, we can adapt */
+                       m = lws_get_peer_write_allowance(wsi);
+
+                       /* -1 means not using a protocol that has this info */
+                       if (m == 0)
+                               /* right now, peer can't handle anything */
+                               goto later;
+
+                       if (m != -1 && m < n)
+                               /* he couldn't handle that much */
+                               n = m;
+                       
+                       n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                                                       n);
+                       /* problem reading, close conn */
+                       if (n < 0)
+                               goto bail;
+                       /* sent it all, close conn */
+                       if (n == 0)
+                               goto flush_bail;
+                       /*
+                        * To support HTTP2, must take care about preamble space
+                        * 
+                        * identification of when we send the last payload frame
+                        * is handled by the library itself if you sent a
+                        * content-length header
+                        */
+                       m = libwebsocket_write(wsi,
+                                              buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                              n, LWS_WRITE_HTTP);
+                       if (m < 0)
+                               /* write failed, close conn */
+                               goto bail;
+
+                       /*
+                        * http2 won't do this
+                        */
+                       if (m != n)
+                               /* partial write, adjust */
+                               if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
+                                       goto bail;
+
+                       if (m) /* while still active, extend timeout */
+                               libwebsocket_set_timeout(wsi,
+                                       PENDING_TIMEOUT_HTTP_CONTENT, 5);
+                       
+                       /* if we have indigestion, let him clear it before eating more */
+                       if (lws_partial_buffered(wsi))
+                               break;
+
+               } while (!lws_send_pipe_choked(wsi));
+
+later:
+               libwebsocket_callback_on_writable(context, wsi);
+               break;
+flush_bail:
+               /* true if still partial pending */
+               if (lws_partial_buffered(wsi)) {
+                       libwebsocket_callback_on_writable(context, wsi);
+                       break;
+               }
+               close(pss->fd);
+               goto try_to_reuse;
+
+bail:
+               close(pss->fd);
+               return -1;
+
+       /*
+        * callback for confirming to continue with client IP appear in
+        * protocol 0 callback since no websocket protocol has been agreed
+        * yet.  You can just ignore this if you won't filter on client IP
+        * since the default uhandled callback return is 0 meaning let the
+        * connection continue.
+        */
+       case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+
+               /* if we returned non-zero from here, we kill the connection */
+               break;
+
+#ifdef EXTERNAL_POLL
+       /*
+        * callbacks for managing the external poll() array appear in
+        * protocol 0 callback
+        */
+
+       case LWS_CALLBACK_LOCK_POLL:
+               /*
+                * lock mutex to protect pollfd state
+                * called before any other POLL related callback
+                */
+               break;
+
+       case LWS_CALLBACK_UNLOCK_POLL:
+               /*
+                * unlock mutex to protect pollfd state when
+                * called after any other POLL related callback
+                */
+               break;
+
+       case LWS_CALLBACK_ADD_POLL_FD:
+
+               if (count_pollfds >= max_poll_elements) {
+                       lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
+                       return 1;
+               }
+
+               fd_lookup[pa->fd] = count_pollfds;
+               pollfds[count_pollfds].fd = pa->fd;
+               pollfds[count_pollfds].events = pa->events;
+               pollfds[count_pollfds++].revents = 0;
+               break;
+
+       case LWS_CALLBACK_DEL_POLL_FD:
+               if (!--count_pollfds)
+                       break;
+               m = fd_lookup[pa->fd];
+               /* have the last guy take up the vacant slot */
+               pollfds[m] = pollfds[count_pollfds];
+               fd_lookup[pollfds[count_pollfds].fd] = m;
+               break;
+
+       case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
+               pollfds[fd_lookup[pa->fd]].events = pa->events;
+               break;
+#endif
+
+       case LWS_CALLBACK_GET_THREAD_ID:
+               /*
+                * if you will call "libwebsocket_callback_on_writable"
+                * from a different thread, return the caller thread ID
+                * here so lws can use this information to work out if it
+                * should signal the poll() loop to exit and restart early
+                */
+
+               /* return pthread_getthreadid_np(); */
+
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+
+       /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */
+try_to_reuse:
+       if (lws_http_transaction_completed(wsi))
+               return -1;
+
+       return 0;
+}
diff --git a/test-server/test-server-mirror.c b/test-server/test-server-mirror.c
new file mode 100644 (file)
index 0000000..ced499d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+#include "test-server.h"
+
+/* lws-mirror_protocol */
+
+#define MAX_MESSAGE_QUEUE 32
+
+struct a_message {
+       void *payload;
+       size_t len;
+};
+
+static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
+static int ringbuffer_head;
+
+int
+callback_lws_mirror(struct libwebsocket_context *context,
+                   struct libwebsocket *wsi,
+                   enum libwebsocket_callback_reasons reason,
+                   void *user, void *in, size_t len)
+{
+       struct per_session_data__lws_mirror *pss =
+                       (struct per_session_data__lws_mirror *)user;
+       int n;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
+               pss->ringbuffer_tail = ringbuffer_head;
+               pss->wsi = wsi;
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_DESTROY:
+               lwsl_notice("%s: mirror protocol cleaning up\n", __func__);
+               for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
+                       if (ringbuffer[n].payload)
+                               free(ringbuffer[n].payload);
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               if (close_testing)
+                       break;
+               while (pss->ringbuffer_tail != ringbuffer_head) {
+
+                       n = libwebsocket_write(wsi, (unsigned char *)
+                                  ringbuffer[pss->ringbuffer_tail].payload +
+                                  LWS_SEND_BUFFER_PRE_PADDING,
+                                  ringbuffer[pss->ringbuffer_tail].len,
+                                                               LWS_WRITE_TEXT);
+                       if (n < 0) {
+                               lwsl_err("ERROR %d writing to mirror socket\n", n);
+                               return -1;
+                       }
+                       if (n < ringbuffer[pss->ringbuffer_tail].len)
+                               lwsl_err("mirror partial write %d vs %d\n",
+                                      n, ringbuffer[pss->ringbuffer_tail].len);
+
+                       if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
+                               pss->ringbuffer_tail = 0;
+                       else
+                               pss->ringbuffer_tail++;
+
+                       if (((ringbuffer_head - pss->ringbuffer_tail) &
+                                 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
+                               libwebsocket_rx_flow_allow_all_protocol(
+                                              libwebsockets_get_protocol(wsi));
+
+                       if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
+                               libwebsocket_callback_on_writable(context, wsi);
+                               break;
+                       }
+                       /*
+                        * for tests with chrome on same machine as client and
+                        * server, this is needed to stop chrome choking
+                        */
+#ifdef _WIN32
+                       Sleep(1);
+#else
+                       usleep(1);
+#endif
+               }
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               if (((ringbuffer_head - pss->ringbuffer_tail) &
+                   (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
+                       lwsl_err("dropping!\n");
+                       goto choke;
+               }
+
+               if (ringbuffer[ringbuffer_head].payload)
+                       free(ringbuffer[ringbuffer_head].payload);
+
+               ringbuffer[ringbuffer_head].payload =
+                               malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
+                                                 LWS_SEND_BUFFER_POST_PADDING);
+               ringbuffer[ringbuffer_head].len = len;
+               memcpy((char *)ringbuffer[ringbuffer_head].payload +
+                                         LWS_SEND_BUFFER_PRE_PADDING, in, len);
+               if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
+                       ringbuffer_head = 0;
+               else
+                       ringbuffer_head++;
+
+               if (((ringbuffer_head - pss->ringbuffer_tail) &
+                   (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
+                       goto done;
+
+choke:
+               lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
+               libwebsocket_rx_flow_control(wsi, 0);
+
+done:
+               libwebsocket_callback_on_writable_all_protocol(
+                                              libwebsockets_get_protocol(wsi));
+               break;
+
+       /*
+        * this just demonstrates how to use the protocol filter. If you won't
+        * study and reject connections based on header content, you don't need
+        * to handle this callback
+        */
+
+       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+               dump_handshake_info(wsi);
+               /* you could return non-zero here and kill the connection */
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
index 40b627c..8949dfd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * libwebsockets-test-server - libwebsockets test implementation
  *
- * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA  02110-1301  USA
  */
-#include "lws_config.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <assert.h>
+#include "test-server.h"
 
-#include "../lib/libwebsockets.h"
-
-#ifdef _WIN32
-#include <io.h>
-#ifdef EXTERNAL_POLL
-#define poll WSAPoll
-#endif
-#include "gettimeofday.h"
-#else
-#include <syslog.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-static int close_testing;
+int close_testing;
 int max_poll_elements;
 
 #ifdef EXTERNAL_POLL
@@ -51,8 +29,12 @@ struct libwebsocket_pollfd *pollfds;
 int *fd_lookup;
 int count_pollfds;
 #endif
-static volatile int force_exit = 0;
-static struct libwebsocket_context *context;
+volatile int force_exit = 0;
+struct libwebsocket_context *context;
+
+/* http server gets files from this path */
+#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
+char *resource_path = LOCAL_RESOURCE_PATH;
 
 /*
  * This demo server shows how to use libwebsockets for one or more
@@ -80,653 +62,6 @@ enum demo_protocols {
        DEMO_PROTOCOL_COUNT
 };
 
-
-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
-char *resource_path = LOCAL_RESOURCE_PATH;
-
-/*
- * We take a strict whitelist approach to stop ../ attacks
- */
-
-struct serveable {
-       const char *urlpath;
-       const char *mimetype;
-}; 
-
-struct per_session_data__http {
-       int fd;
-};
-
-/*
- * this is just an example of parsing handshake headers, you don't need this
- * in your code unless you will filter allowing connections by the header
- * content
- */
-
-static void
-dump_handshake_info(struct libwebsocket *wsi)
-{
-       int n = 0;
-       char buf[256];
-       const unsigned char *c;
-
-       do {
-               c = lws_token_to_string(n);
-               if (!c) {
-                       n++;
-                       continue;
-               }
-
-               if (!lws_hdr_total_length(wsi, n)) {
-                       n++;
-                       continue;
-               }
-
-               lws_hdr_copy(wsi, buf, sizeof buf, n);
-
-               fprintf(stderr, "    %s = %s\n", (char *)c, buf);
-               n++;
-       } while (c);
-}
-
-const char * get_mimetype(const char *file)
-{
-       int n = strlen(file);
-
-       if (n < 5)
-               return NULL;
-
-       if (!strcmp(&file[n - 4], ".ico"))
-               return "image/x-icon";
-
-       if (!strcmp(&file[n - 4], ".png"))
-               return "image/png";
-
-       if (!strcmp(&file[n - 5], ".html"))
-               return "text/html";
-
-       return NULL;
-}
-
-/* this protocol server (always the first one) just knows how to do HTTP */
-
-static int callback_http(struct libwebsocket_context *context,
-               struct libwebsocket *wsi,
-               enum libwebsocket_callback_reasons reason, void *user,
-                                                          void *in, size_t len)
-{
-       char buf[256];
-       char leaf_path[1024];
-       char b64[64];
-       struct timeval tv;
-       int n, m;
-       unsigned char *p;
-       char *other_headers;
-       static unsigned char buffer[4096];
-       struct stat stat_buf;
-       struct per_session_data__http *pss =
-                       (struct per_session_data__http *)user;
-       const char *mimetype;
-#ifdef EXTERNAL_POLL
-       struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
-#endif
-       unsigned char *end;
-       switch (reason) {
-       case LWS_CALLBACK_HTTP:
-
-               dump_handshake_info(wsi);
-
-               if (len < 1) {
-                       libwebsockets_return_http_status(context, wsi,
-                                               HTTP_STATUS_BAD_REQUEST, NULL);
-                       goto try_to_reuse;
-               }
-
-               /* this example server has no concept of directories */
-               if (strchr((const char *)in + 1, '/')) {
-                       libwebsockets_return_http_status(context, wsi,
-                                               HTTP_STATUS_FORBIDDEN, NULL);
-                       goto try_to_reuse;
-               }
-
-               /* if a legal POST URL, let it continue and accept data */
-               if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
-                       return 0;
-
-               /* check for the "send a big file by hand" example case */
-
-               if (!strcmp((const char *)in, "/leaf.jpg")) {
-                       if (strlen(resource_path) > sizeof(leaf_path) - 10)
-                               return -1;
-                       sprintf(leaf_path, "%s/leaf.jpg", resource_path);
-
-                       /* well, let's demonstrate how to send the hard way */
-
-                       p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
-                       end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
-#ifdef _WIN32
-                       pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
-#else
-                       pss->fd = open(leaf_path, O_RDONLY);
-#endif
-
-                       if (pss->fd < 0)
-                               return -1;
-
-                       if (fstat(pss->fd, &stat_buf) < 0)
-                               return -1;
-
-                       /*
-                        * we will send a big jpeg file, but it could be
-                        * anything.  Set the Content-Type: appropriately
-                        * so the browser knows what to do with it.
-                        * 
-                        * Notice we use the APIs to build the header, which
-                        * will do the right thing for HTTP 1/1.1 and HTTP2
-                        * depending on what connection it happens to be working
-                        * on
-                        */
-                       if (lws_add_http_header_status(context, wsi, 200, &p, end))
-                               return 1;
-                       if (lws_add_http_header_by_token(context, wsi,
-                                       WSI_TOKEN_HTTP_SERVER,
-                                       (unsigned char *)"libwebsockets",
-                                       13, &p, end))
-                               return 1;
-                       if (lws_add_http_header_by_token(context, wsi,
-                                       WSI_TOKEN_HTTP_CONTENT_TYPE,
-                                       (unsigned char *)"image/jpeg",
-                                       10, &p, end))
-                               return 1;
-                       if (lws_add_http_header_content_length(context, wsi,
-                                               stat_buf.st_size, &p, end))
-                               return 1;
-                       if (lws_finalize_http_header(context, wsi, &p, end))
-                               return 1;
-
-                       /*
-                        * send the http headers...
-                        * this won't block since it's the first payload sent
-                        * on the connection since it was established
-                        * (too small for partial)
-                        * 
-                        * Notice they are sent using LWS_WRITE_HTTP_HEADERS
-                        * which also means you can't send body too in one step,
-                        * this is mandated by changes in HTTP2
-                        */
-
-                       n = libwebsocket_write(wsi,
-                                       buffer + LWS_SEND_BUFFER_PRE_PADDING,
-                                       p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
-                                       LWS_WRITE_HTTP_HEADERS);
-
-                       if (n < 0) {
-                               close(pss->fd);
-                               return -1;
-                       }
-                       /*
-                        * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
-                        */
-                       libwebsocket_callback_on_writable(context, wsi);
-                       break;
-               }
-
-               /* if not, send a file the easy way */
-               strcpy(buf, resource_path);
-               if (strcmp(in, "/")) {
-                       if (*((const char *)in) != '/')
-                               strcat(buf, "/");
-                       strncat(buf, in, sizeof(buf) - strlen(resource_path));
-               } else /* default file to serve */
-                       strcat(buf, "/test.html");
-               buf[sizeof(buf) - 1] = '\0';
-
-               /* refuse to serve files we don't understand */
-               mimetype = get_mimetype(buf);
-               if (!mimetype) {
-                       lwsl_err("Unknown mimetype for %s\n", buf);
-                       libwebsockets_return_http_status(context, wsi,
-                                     HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
-                       return -1;
-               }
-
-               /* demostrates how to set a cookie on / */
-
-               other_headers = NULL;
-               n = 0;
-               if (!strcmp((const char *)in, "/") &&
-                          !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
-                       /* this isn't very unguessable but it'll do for us */
-                       gettimeofday(&tv, NULL);
-                       n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
-                               (unsigned int)tv.tv_sec,
-                               (unsigned int)tv.tv_usec);
-
-                       p = (unsigned char *)leaf_path;
-
-                       if (lws_add_http_header_by_name(context, wsi, 
-                               (unsigned char *)"set-cookie:", 
-                               (unsigned char *)b64, n, &p,
-                               (unsigned char *)leaf_path + sizeof(leaf_path)))
-                               return 1;
-                       n = (char *)p - leaf_path;
-                       other_headers = leaf_path;
-               }
-
-               n = libwebsockets_serve_http_file(context, wsi, buf,
-                                               mimetype, other_headers, n);
-               if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
-                       return -1; /* error or can't reuse connection: close the socket */
-
-               /*
-                * notice that the sending of the file completes asynchronously,
-                * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
-                * it's done
-                */
-
-               break;
-
-       case LWS_CALLBACK_HTTP_BODY:
-               strncpy(buf, in, 20);
-               buf[20] = '\0';
-               if (len < 20)
-                       buf[len] = '\0';
-
-               lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
-                               (const char *)buf, (int)len);
-
-               break;
-
-       case LWS_CALLBACK_HTTP_BODY_COMPLETION:
-               lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
-               /* the whole of the sent body arrived, close or reuse the connection */
-               libwebsockets_return_http_status(context, wsi,
-                                               HTTP_STATUS_OK, NULL);
-               goto try_to_reuse;
-
-       case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-//             lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
-               /* kill the connection after we sent one file */
-               goto try_to_reuse;
-
-       case LWS_CALLBACK_HTTP_WRITEABLE:
-               /*
-                * we can send more of whatever it is we were sending
-                */
-               do {
-                       /* we'd like the send this much */
-                       n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
-                       
-                       /* but if the peer told us he wants less, we can adapt */
-                       m = lws_get_peer_write_allowance(wsi);
-
-                       /* -1 means not using a protocol that has this info */
-                       if (m == 0)
-                               /* right now, peer can't handle anything */
-                               goto later;
-
-                       if (m != -1 && m < n)
-                               /* he couldn't handle that much */
-                               n = m;
-                       
-                       n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
-                                                                       n);
-                       /* problem reading, close conn */
-                       if (n < 0)
-                               goto bail;
-                       /* sent it all, close conn */
-                       if (n == 0)
-                               goto flush_bail;
-                       /*
-                        * To support HTTP2, must take care about preamble space
-                        * 
-                        * identification of when we send the last payload frame
-                        * is handled by the library itself if you sent a
-                        * content-length header
-                        */
-                       m = libwebsocket_write(wsi,
-                                              buffer + LWS_SEND_BUFFER_PRE_PADDING,
-                                              n, LWS_WRITE_HTTP);
-                       if (m < 0)
-                               /* write failed, close conn */
-                               goto bail;
-
-                       /*
-                        * http2 won't do this
-                        */
-                       if (m != n)
-                               /* partial write, adjust */
-                               if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
-                                       goto bail;
-
-                       if (m) /* while still active, extend timeout */
-                               libwebsocket_set_timeout(wsi,
-                                       PENDING_TIMEOUT_HTTP_CONTENT, 5);
-                       
-                       /* if we have indigestion, let him clear it before eating more */
-                       if (lws_partial_buffered(wsi))
-                               break;
-
-               } while (!lws_send_pipe_choked(wsi));
-
-later:
-               libwebsocket_callback_on_writable(context, wsi);
-               break;
-flush_bail:
-               /* true if still partial pending */
-               if (lws_partial_buffered(wsi)) {
-                       libwebsocket_callback_on_writable(context, wsi);
-                       break;
-               }
-               close(pss->fd);
-               goto try_to_reuse;
-
-bail:
-               close(pss->fd);
-               return -1;
-
-       /*
-        * callback for confirming to continue with client IP appear in
-        * protocol 0 callback since no websocket protocol has been agreed
-        * yet.  You can just ignore this if you won't filter on client IP
-        * since the default uhandled callback return is 0 meaning let the
-        * connection continue.
-        */
-
-       case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-
-               /* if we returned non-zero from here, we kill the connection */
-               break;
-
-#ifdef EXTERNAL_POLL
-       /*
-        * callbacks for managing the external poll() array appear in
-        * protocol 0 callback
-        */
-
-       case LWS_CALLBACK_LOCK_POLL:
-               /*
-                * lock mutex to protect pollfd state
-                * called before any other POLL related callback
-                */
-               break;
-
-       case LWS_CALLBACK_UNLOCK_POLL:
-               /*
-                * unlock mutex to protect pollfd state when
-                * called after any other POLL related callback
-                */
-               break;
-
-       case LWS_CALLBACK_ADD_POLL_FD:
-
-               if (count_pollfds >= max_poll_elements) {
-                       lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
-                       return 1;
-               }
-
-               fd_lookup[pa->fd] = count_pollfds;
-               pollfds[count_pollfds].fd = pa->fd;
-               pollfds[count_pollfds].events = pa->events;
-               pollfds[count_pollfds++].revents = 0;
-               break;
-
-       case LWS_CALLBACK_DEL_POLL_FD:
-               if (!--count_pollfds)
-                       break;
-               m = fd_lookup[pa->fd];
-               /* have the last guy take up the vacant slot */
-               pollfds[m] = pollfds[count_pollfds];
-               fd_lookup[pollfds[count_pollfds].fd] = m;
-               break;
-
-       case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
-               pollfds[fd_lookup[pa->fd]].events = pa->events;
-               break;
-
-#endif
-
-       case LWS_CALLBACK_GET_THREAD_ID:
-               /*
-                * if you will call "libwebsocket_callback_on_writable"
-                * from a different thread, return the caller thread ID
-                * here so lws can use this information to work out if it
-                * should signal the poll() loop to exit and restart early
-                */
-
-               /* return pthread_getthreadid_np(); */
-
-               break;
-
-       default:
-               break;
-       }
-
-       return 0;
-       
-try_to_reuse:
-       if (lws_http_transaction_completed(wsi))
-               return -1;
-
-       return 0;
-}
-
-
-/* dumb_increment protocol */
-
-/*
- * one of these is auto-created for each connection and a pointer to the
- * appropriate instance is passed to the callback in the user parameter
- *
- * for this example protocol we use it to individualize the count for each
- * connection.
- */
-
-struct per_session_data__dumb_increment {
-       int number;
-};
-
-static int
-callback_dumb_increment(struct libwebsocket_context *context,
-                       struct libwebsocket *wsi,
-                       enum libwebsocket_callback_reasons reason,
-                                              void *user, void *in, size_t len)
-{
-       int n, m;
-       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
-                                                 LWS_SEND_BUFFER_POST_PADDING];
-       unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
-       struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
-
-       switch (reason) {
-
-       case LWS_CALLBACK_ESTABLISHED:
-               lwsl_info("callback_dumb_increment: "
-                                                "LWS_CALLBACK_ESTABLISHED\n");
-               pss->number = 0;
-               break;
-
-       case LWS_CALLBACK_SERVER_WRITEABLE:
-               n = sprintf((char *)p, "%d", pss->number++);
-               m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
-               if (m < n) {
-                       lwsl_err("ERROR %d writing to di socket\n", n);
-                       return -1;
-               }
-               if (close_testing && pss->number == 50) {
-                       lwsl_info("close tesing limit, closing\n");
-                       return -1;
-               }
-               break;
-
-       case LWS_CALLBACK_RECEIVE:
-//             fprintf(stderr, "rx %d\n", (int)len);
-               if (len < 6)
-                       break;
-               if (strcmp((const char *)in, "reset\n") == 0)
-                       pss->number = 0;
-               break;
-       /*
-        * this just demonstrates how to use the protocol filter. If you won't
-        * study and reject connections based on header content, you don't need
-        * to handle this callback
-        */
-
-       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info(wsi);
-               /* you could return non-zero here and kill the connection */
-               break;
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-
-/* lws-mirror_protocol */
-
-#define MAX_MESSAGE_QUEUE 32
-
-struct per_session_data__lws_mirror {
-       struct libwebsocket *wsi;
-       int ringbuffer_tail;
-};
-
-struct a_message {
-       void *payload;
-       size_t len;
-};
-
-static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
-static int ringbuffer_head;
-
-static int
-callback_lws_mirror(struct libwebsocket_context *context,
-                       struct libwebsocket *wsi,
-                       enum libwebsocket_callback_reasons reason,
-                                              void *user, void *in, size_t len)
-{
-       int n;
-       struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
-
-       switch (reason) {
-
-       case LWS_CALLBACK_ESTABLISHED:
-               lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
-               pss->ringbuffer_tail = ringbuffer_head;
-               pss->wsi = wsi;
-               break;
-
-       case LWS_CALLBACK_PROTOCOL_DESTROY:
-               lwsl_notice("mirror protocol cleaning up\n");
-               for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
-                       if (ringbuffer[n].payload)
-                               free(ringbuffer[n].payload);
-               break;
-
-       case LWS_CALLBACK_SERVER_WRITEABLE:
-               if (close_testing)
-                       break;
-               while (pss->ringbuffer_tail != ringbuffer_head) {
-
-                       n = libwebsocket_write(wsi, (unsigned char *)
-                                  ringbuffer[pss->ringbuffer_tail].payload +
-                                  LWS_SEND_BUFFER_PRE_PADDING,
-                                  ringbuffer[pss->ringbuffer_tail].len,
-                                                               LWS_WRITE_TEXT);
-                       if (n < 0) {
-                               lwsl_err("ERROR %d writing to mirror socket\n", n);
-                               return -1;
-                       }
-                       if (n < ringbuffer[pss->ringbuffer_tail].len)
-                               lwsl_err("mirror partial write %d vs %d\n",
-                                      n, ringbuffer[pss->ringbuffer_tail].len);
-
-                       if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
-                               pss->ringbuffer_tail = 0;
-                       else
-                               pss->ringbuffer_tail++;
-
-                       if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
-                               libwebsocket_rx_flow_allow_all_protocol(
-                                              libwebsockets_get_protocol(wsi));
-
-                       // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-
-                       if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
-                               libwebsocket_callback_on_writable(context, wsi);
-                               break;
-                       }
-                       /*
-                        * for tests with chrome on same machine as client and
-                        * server, this is needed to stop chrome choking
-                        */
-#ifdef _WIN32
-                       Sleep(1);
-#else
-                       usleep(1);
-#endif
-               }
-               break;
-
-       case LWS_CALLBACK_RECEIVE:
-
-               if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
-                       lwsl_err("dropping!\n");
-                       goto choke;
-               }
-
-               if (ringbuffer[ringbuffer_head].payload)
-                       free(ringbuffer[ringbuffer_head].payload);
-
-               ringbuffer[ringbuffer_head].payload =
-                               malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
-                                                 LWS_SEND_BUFFER_POST_PADDING);
-               ringbuffer[ringbuffer_head].len = len;
-               memcpy((char *)ringbuffer[ringbuffer_head].payload +
-                                         LWS_SEND_BUFFER_PRE_PADDING, in, len);
-               if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
-                       ringbuffer_head = 0;
-               else
-                       ringbuffer_head++;
-
-               if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
-                       goto done;
-
-choke:
-               lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
-               libwebsocket_rx_flow_control(wsi, 0);
-
-//             lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-done:
-               libwebsocket_callback_on_writable_all_protocol(
-                                              libwebsockets_get_protocol(wsi));
-               break;
-
-       /*
-        * this just demonstrates how to use the protocol filter. If you won't
-        * study and reject connections based on header content, you don't need
-        * to handle this callback
-        */
-
-       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info(wsi);
-               /* you could return non-zero here and kill the connection */
-               break;
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-
 /* list of supported protocols and callbacks */
 
 static struct libwebsocket_protocols protocols[] = {
@@ -764,37 +99,40 @@ static struct option options[] = {
        { "debug",      required_argument,      NULL, 'd' },
        { "port",       required_argument,      NULL, 'p' },
        { "ssl",        no_argument,            NULL, 's' },
-       { "allow-non-ssl",      no_argument,            NULL, 'a' },
+       { "allow-non-ssl",      no_argument,    NULL, 'a' },
        { "interface",  required_argument,      NULL, 'i' },
        { "closetest",  no_argument,            NULL, 'c' },
        { "libev",  no_argument,                NULL, 'e' },
-       #ifndef LWS_NO_DAEMONIZE
+#ifndef LWS_NO_DAEMONIZE
        { "daemonize",  no_argument,            NULL, 'D' },
 #endif
-       { "resource_path", required_argument,           NULL, 'r' },
+       { "resource_path", required_argument,   NULL, 'r' },
        { NULL, 0, 0, 0 }
 };
 
 int main(int argc, char **argv)
 {
+       struct lws_context_creation_info info;
+       char interface_name[128] = "";
+       unsigned int ms, oldms = 0;
+       const char *iface = NULL;
        char cert_path[1024];
        char key_path[1024];
-       int n = 0;
+       int debug_level = 7;
        int use_ssl = 0;
        int opts = 0;
-       char interface_name[128] = "";
-       const char *iface = NULL;
+       int n = 0;
 #ifndef _WIN32
        int syslog_options = LOG_PID | LOG_PERROR;
 #endif
-       unsigned int ms, oldms = 0;
-       struct lws_context_creation_info info;
-
-       int debug_level = 7;
 #ifndef LWS_NO_DAEMONIZE
-       int daemonize = 0;
+//     int daemonize = 0;
 #endif
 
+       /* 
+        * take care to zero down the info struct, he contains random garbaage
+        * from the stack otherwise
+        */
        memset(&info, 0, sizeof info);
        info.port = 7681;
 
@@ -893,10 +231,11 @@ int main(int argc, char **argv)
 #ifndef LWS_NO_EXTENSIONS
        info.extensions = libwebsocket_get_internal_extensions();
 #endif
-       if (!use_ssl) {
-               info.ssl_cert_filepath = NULL;
-               info.ssl_private_key_filepath = NULL;
-       } else {
+       
+       info.ssl_cert_filepath = NULL;
+       info.ssl_private_key_filepath = NULL;
+
+       if (use_ssl) {
                if (strlen(resource_path) > sizeof(cert_path) - 32) {
                        lwsl_err("resource path too long\n");
                        return -1;
@@ -937,12 +276,12 @@ int main(int argc, char **argv)
 
                ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
                if ((ms - oldms) > 50) {
-                       libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
+                       libwebsocket_callback_on_writable_all_protocol(
+                               &protocols[PROTOCOL_DUMB_INCREMENT]);
                        oldms = ms;
                }
 
 #ifdef EXTERNAL_POLL
-
                /*
                 * this represents an existing server's single poll action
                 * which also includes libwebsocket sockets
@@ -952,7 +291,6 @@ int main(int argc, char **argv)
                if (n < 0)
                        continue;
 
-
                if (n)
                        for (n = 0; n < count_pollfds; n++)
                                if (pollfds[n].revents)
diff --git a/test-server/test-server.h b/test-server/test-server.h
new file mode 100644 (file)
index 0000000..a2e0fa5
--- /dev/null
@@ -0,0 +1,73 @@
+#include "lws_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "../lib/libwebsockets.h"
+
+#ifdef _WIN32
+#include <io.h>
+#ifdef EXTERNAL_POLL
+#define poll WSAPoll
+#endif
+#include "gettimeofday.h"
+#else
+#include <syslog.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+extern int close_testing;
+extern int max_poll_elements;
+
+#ifdef EXTERNAL_POLL
+extern struct libwebsocket_pollfd *pollfds;
+extern int *fd_lookup;
+extern int count_pollfds;
+#endif
+extern volatile int force_exit;
+extern struct libwebsocket_context *context;
+extern char *resource_path;
+
+struct per_session_data__http {
+       int fd;
+};
+
+/*
+ * one of these is auto-created for each connection and a pointer to the
+ * appropriate instance is passed to the callback in the user parameter
+ *
+ * for this example protocol we use it to individualize the count for each
+ * connection.
+ */
+
+struct per_session_data__dumb_increment {
+       int number;
+};
+
+struct per_session_data__lws_mirror {
+       struct libwebsocket *wsi;
+       int ringbuffer_tail;
+};
+
+extern int callback_http(struct libwebsocket_context *context,
+                        struct libwebsocket *wsi,
+                        enum libwebsocket_callback_reasons reason,
+                        void *user, void *in, size_t len);
+extern int callback_lws_mirror(struct libwebsocket_context *context,
+                              struct libwebsocket *wsi,
+                              enum libwebsocket_callback_reasons reason,
+                              void *user, void *in, size_t len);
+extern int callback_dumb_increment(struct libwebsocket_context *context,
+                              struct libwebsocket *wsi,
+                              enum libwebsocket_callback_reasons reason,
+                              void *user, void *in, size_t len);
+
+extern void
+dump_handshake_info(struct libwebsocket *wsi);
\ No newline at end of file