diff --git a/CMakeLists.txt b/CMakeLists.txt index 56688c34..3d3fa986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 00000000..d03aae6d --- /dev/null +++ b/plugins/protocol_client_loopback_test.c @@ -0,0 +1,194 @@ +/* + * ws protocol handler plugin for "client_loopback_test" + * + * Copyright (C) 2010-2016 Andy Green + * + * 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 + +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; +}