protocol_client_loopback_test
authorAndy Green <andy@warmcat.com>
Thu, 12 May 2016 11:45:04 +0000 (19:45 +0800)
committerAndy Green <andy@warmcat.com>
Thu, 12 May 2016 13:14:13 +0000 (21:14 +0800)
This is used to confirm that SSL client connections can coexist with
a vhost doing serving.

To set it up,

/*
 * This is a bit fiddly...
 *
 * 0) If you want the wss:// test to work, make sure the vhost is marked with
 *    enable-client-ssl if using lwsws, or call lws_init_vhost_client_ssl() on
 *    the vhost if you're doing it by hand.
 *
 * 1) enable the protocol on a vhost
 *
 *      "ws-protocols": [{
 *     "client-loopback-test": {
 *      "status": "ok"
 *     },  ...
 *
 *     the vhost should listen on 80 (ws://) or 443 (wss://)
 *
 * 2) mount the http part of the test one level down on the same vhost, eg
 *   {
 *      "mountpoint": "/c",
 *      "origin": "callback://client-loopback-test"
 *   }
 *
 * 3) Use a browser to visit the mountpoint with a URI attached for looping
 *    back, eg, if testing on localhost
 *
 *    http://localhost/c/ws://localhost
 *    https://localhost/c/wss://localhost
 *
 * 4) The HTTP part of this test protocol will try to do the requested
 *    ws client connection, to the same test protocol on the same
 *    server.
 */

Results should look like this

lwsws[29938]: client connection to localhost:443 with ssl: 1 started
lwsws[29938]: server part: LWS_CALLBACK_ESTABLISHED
lwsws[29938]: checking client ext permessage-deflate
lwsws[29938]: instantiating client ext permessage-deflate
lwsws[29938]: Client connection established
lwsws[29938]: Client connection received 7 from server 'Made it'

Signed-off-by: Andy Green <andy@warmcat.com>
CMakeLists.txt
plugins/protocol_client_loopback_test.c [new file with mode: 0644]

index 56688c3..3d3fa98 100644 (file)
@@ -1231,6 +1231,12 @@ if (LWS_WITH_SERVER_STATUS)
                create_plugin(protocol_lws_server_status
                              "plugins/protocol_lws_server_status.c")
 endif()
+
+if (NOT LWS_WITHOUT_CLIENT)
+               create_plugin(protocol_client_loopback_test
+                              "plugins/protocol_client_loopback_test.c")
+endif(NOT LWS_WITHOUT_CLIENT)
+
        endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
 
        #
diff --git a/plugins/protocol_client_loopback_test.c b/plugins/protocol_client_loopback_test.c
new file mode 100644 (file)
index 0000000..d03aae6
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * ws protocol handler plugin for "client_loopback_test"
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * These test plugins are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ */
+#include "../lib/libwebsockets.h"
+#include <string.h>
+
+struct per_session_data__client_loopback_test {
+       struct lws *wsi;
+};
+
+/*
+ * This is a bit fiddly...
+ *
+ * 0) If you want the wss:// test to work, make sure the vhost is marked with
+ *    enable-client-ssl if using lwsws, or call lws_init_vhost_client_ssl() on
+ *    the vhost if you're doing it by hand.
+ *
+ * 1) enable the protocol on a vhost
+ *
+ *      "ws-protocols": [{
+ *     "client-loopback-test": {
+ *      "status": "ok"
+ *     },  ...
+ *
+ *     the vhost should listen on 80 (ws://) or 443 (wss://)
+ *
+ * 2) mount the http part of the test one level down on the same vhost, eg
+ *   {
+ *      "mountpoint": "/c",
+ *      "origin": "callback://client-loopback-test"
+ *   }
+ *
+ * 3) Use a browser to visit the mountpoint with a URI attached for looping
+ *    back, eg, if testing on localhost
+ *
+ *    http://localhost/c/ws://localhost
+ *    https://localhost/c/wss://localhost
+ *
+ * 4) The HTTP part of this test protocol will try to do the requested
+ *    ws client connection, to the same test protocol on the same
+ *    server.
+ */
+
+static int
+callback_client_loopback_test(struct lws *wsi, enum lws_callback_reasons reason,
+                       void *user, void *in, size_t len)
+{
+       struct lws_client_connect_info i;
+       struct per_session_data__client_loopback_test *pss =
+                       (struct per_session_data__client_loopback_test *)user;
+       const char *p = (const char *)in;
+       char buf[100];
+       int n;
+
+       switch (reason) {
+
+       /* HTTP part */
+
+       case LWS_CALLBACK_HTTP:
+               if (len < 10)
+                       return -1;
+
+               p++;
+               while (*p && *p != '/')
+                       p++;
+               if (!*p) {
+                       lws_return_http_status(wsi, 400, "Arg needs to be in format ws://xxx or wss://xxx");
+                       return -1;
+               }
+               p++;
+
+               memset(&i, 0, sizeof(i));
+               i.context = lws_get_context(wsi);
+
+               // stacked /// get resolved to /
+
+               if (strncmp(p, "ws:/", 4) == 0) {
+                       i.ssl_connection = 0;
+                       i.port = 80;
+                       p += 4;
+               } else
+                       if (strncmp(p, "wss:/", 5) == 0) {
+                               i.port = 443;
+                               i.ssl_connection = 1;
+                               p += 5;
+                       } else {
+                               sprintf(buf, "Arg %s is not in format ws://xxx or wss://xxx\n", p);
+                               lws_return_http_status(wsi, 400, buf);
+                               return -1;
+                       }
+
+               i.address = p;
+               i.path = "";
+               i.host = p;
+               i.origin = p;
+               i.ietf_version_or_minus_one = -1;
+               i.protocol = "client-loopback-test";
+
+               pss->wsi = lws_client_connect_via_info(&i);
+               if (!pss->wsi)
+                       lws_return_http_status(wsi, 401, "client-loopback-test: connect failed\n");
+               else {
+                       lwsl_notice("client connection to %s:%d with ssl: %d started\n",
+                                   i.address, i.port, i.ssl_connection);
+                       lws_return_http_status(wsi, 200, "OK");
+               }
+
+               /* either way, close the triggering http link */
+
+               return -1;
+
+       case LWS_CALLBACK_CLOSED_HTTP:
+               lwsl_notice("Http part closed\n");
+               break;
+
+       /* server part */
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_notice("server part: LWS_CALLBACK_ESTABLISHED\n");
+               strcpy(buf + LWS_PRE, "Made it");
+               n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
+                             7, LWS_WRITE_TEXT);
+               if (n < 7)
+                       return -1;
+               break;
+
+       /* client part */
+
+       case LWS_CALLBACK_CLIENT_ESTABLISHED:
+               lwsl_notice("Client connection established\n");
+               break;
+
+       case LWS_CALLBACK_CLIENT_RECEIVE:
+               strncpy(buf, in, sizeof(buf) - 1);
+               lwsl_notice("Client connection received %d from server '%s'\n", len, buf);
+
+               /* OK we are done with the client connection */
+               return -1;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+       {
+               "client-loopback-test",
+               callback_client_loopback_test,
+               sizeof(struct per_session_data__client_loopback_test),
+               1024, /* rx buf size must be >= permessage-deflate rx size */
+       },
+};
+
+LWS_VISIBLE int
+init_protocol_client_loopback_test(struct lws_context *context,
+                                  struct lws_plugin_capability *c)
+{
+       if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
+               lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
+                        c->api_magic);
+               return 1;
+       }
+
+       c->protocols = protocols;
+       c->count_protocols = ARRAY_SIZE(protocols);
+       c->extensions = NULL;
+       c->count_extensions = 0;
+
+       return 0;
+}
+
+LWS_VISIBLE int
+destroy_protocol_client_loopback_test(struct lws_context *context)
+{
+       return 0;
+}