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
30 struct 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
67 static int
callback_client_loopback_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)68 callback_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
171 LWS_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
181 LWS_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