1/* 2 * ws protocol handler plugin for "client_loopback_test" 3 * 4 * Written in 2010-2019 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * The person who associated a work with this deed has dedicated 10 * the work to the public domain by waiving all of his or her rights 11 * to the work worldwide under copyright law, including all related 12 * and neighboring rights, to the extent allowed by law. You can copy, 13 * modify, distribute and perform the work, even for commercial purposes, 14 * all without asking permission. 15 * 16 * These test plugins are intended to be adapted for use in your code, which 17 * may be proprietary. So unlike the library itself, they are licensed 18 * Public Domain. 19 */ 20 21#if !defined(LWS_DLL) 22#define LWS_DLL 23#endif 24#if !defined(LWS_INTERNAL) 25#define LWS_INTERNAL 26#endif 27#include <libwebsockets.h> 28#include <string.h> 29 30struct per_session_data__client_loopback_test { 31 struct lws *wsi; 32}; 33 34/* 35 * This is a bit fiddly... 36 * 37 * 0) If you want the wss:// test to work, make sure the vhost is marked with 38 * enable-client-ssl if using lwsws, or call lws_init_vhost_client_ssl() on 39 * the vhost if you're doing it by hand. 40 * 41 * 1) enable the protocol on a vhost 42 * 43 * "ws-protocols": [{ 44 * "client-loopback-test": { 45 * "status": "ok" 46 * }, ... 47 * 48 * the vhost should listen on 80 (ws://) or 443 (wss://) 49 * 50 * 2) mount the http part of the test one level down on the same vhost, eg 51 * { 52 * "mountpoint": "/c", 53 * "origin": "callback://client-loopback-test" 54 * } 55 * 56 * 3) Use a browser to visit the mountpoint with a URI attached for looping 57 * back, eg, if testing on localhost 58 * 59 * http://localhost/c/ws://localhost 60 * https://localhost/c/wss://localhost 61 * 62 * 4) The HTTP part of this test protocol will try to do the requested 63 * ws client connection, to the same test protocol on the same 64 * server. 65 */ 66 67static int 68callback_client_loopback_test(struct lws *wsi, enum lws_callback_reasons reason, 69 void *user, void *in, size_t len) 70{ 71 struct lws_client_connect_info i; 72 struct per_session_data__client_loopback_test *pss = 73 (struct per_session_data__client_loopback_test *)user; 74 const char *p = (const char *)in; 75 char buf[100]; 76 int n; 77 78 switch (reason) { 79 80 /* HTTP part */ 81 82 case LWS_CALLBACK_HTTP: 83 if (len < 10) 84 return -1; 85 86 p++; 87 while (*p && *p != '/') 88 p++; 89 if (!*p) { 90 lws_return_http_status(wsi, 400, "Arg needs to be in format ws://xxx or wss://xxx"); 91 return -1; 92 } 93 p++; 94 95 memset(&i, 0, sizeof(i)); 96 i.context = lws_get_context(wsi); 97 98 // stacked /// get resolved to / 99 100 if (strncmp(p, "ws:/", 4) == 0) { 101 i.ssl_connection = 0; 102 i.port = 80; 103 p += 4; 104 } else 105 if (strncmp(p, "wss:/", 5) == 0) { 106 i.port = 443; 107 i.ssl_connection = 1; 108 p += 5; 109 } else { 110 sprintf(buf, "Arg %s is not in format ws://xxx or wss://xxx\n", p); 111 lws_return_http_status(wsi, 400, buf); 112 return -1; 113 } 114 115 i.address = p; 116 i.path = ""; 117 i.host = p; 118 i.origin = p; 119 i.ietf_version_or_minus_one = -1; 120 i.protocol = "client-loopback-test"; 121 122 pss->wsi = lws_client_connect_via_info(&i); 123 if (!pss->wsi) 124 lws_return_http_status(wsi, 401, "client-loopback-test: connect failed\n"); 125 else { 126 lwsl_notice("client connection to %s:%d with ssl: %d started\n", 127 i.address, i.port, i.ssl_connection); 128 lws_return_http_status(wsi, 200, "OK"); 129 } 130 131 /* either way, close the triggering http link */ 132 133 return -1; 134 135 case LWS_CALLBACK_CLOSED_HTTP: 136 lwsl_notice("Http part closed\n"); 137 break; 138 139 /* server part */ 140 141 case LWS_CALLBACK_ESTABLISHED: 142 lwsl_notice("server part: LWS_CALLBACK_ESTABLISHED\n"); 143 strcpy(buf + LWS_PRE, "Made it"); 144 n = lws_write(wsi, (unsigned char *)buf + LWS_PRE, 145 7, LWS_WRITE_TEXT); 146 if (n < 7) 147 return -1; 148 break; 149 150 /* client part */ 151 152 case LWS_CALLBACK_CLIENT_ESTABLISHED: 153 lwsl_notice("Client connection established\n"); 154 break; 155 156 case LWS_CALLBACK_CLIENT_RECEIVE: 157 lws_strncpy(buf, in, sizeof(buf)); 158 lwsl_notice("Client connection received %ld from server '%s'\n", 159 (long)len, buf); 160 161 /* OK we are done with the client connection */ 162 return -1; 163 164 default: 165 break; 166 } 167 168 return 0; 169} 170 171LWS_VISIBLE const struct lws_protocols client_loopback_test_protocols[] = { 172 { 173 "client-loopback-test", 174 callback_client_loopback_test, 175 sizeof(struct per_session_data__client_loopback_test), 176 1024, /* rx buf size must be >= permessage-deflate rx size */ 177 0, NULL, 0 178 }, 179}; 180 181LWS_VISIBLE const lws_plugin_protocol_t client_loopback_test = { 182 .hdr = { 183 "client loopback test", 184 "lws_protocol_plugin", 185 LWS_BUILD_HASH, 186 LWS_PLUGIN_API_MAGIC 187 }, 188 189 .protocols = client_loopback_test_protocols, 190 .count_protocols = LWS_ARRAY_SIZE(client_loopback_test_protocols), 191 .extensions = NULL, 192 .count_extensions = 0, 193}; 194