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 #define LWS_DLL
22 #define LWS_INTERNAL
23 #include <libwebsockets.h>
24 #include <string.h>
25
26 struct per_session_data__client_loopback_test {
27 struct lws *wsi;
28 };
29
30 /*
31 * This is a bit fiddly...
32 *
33 * 0) If you want the wss:// test to work, make sure the vhost is marked with
34 * enable-client-ssl if using lwsws, or call lws_init_vhost_client_ssl() on
35 * the vhost if you're doing it by hand.
36 *
37 * 1) enable the protocol on a vhost
38 *
39 * "ws-protocols": [{
40 * "client-loopback-test": {
41 * "status": "ok"
42 * }, ...
43 *
44 * the vhost should listen on 80 (ws://) or 443 (wss://)
45 *
46 * 2) mount the http part of the test one level down on the same vhost, eg
47 * {
48 * "mountpoint": "/c",
49 * "origin": "callback://client-loopback-test"
50 * }
51 *
52 * 3) Use a browser to visit the mountpoint with a URI attached for looping
53 * back, eg, if testing on localhost
54 *
55 * http://localhost/c/ws://localhost
56 * https://localhost/c/wss://localhost
57 *
58 * 4) The HTTP part of this test protocol will try to do the requested
59 * ws client connection, to the same test protocol on the same
60 * server.
61 */
62
63 static int
callback_client_loopback_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)64 callback_client_loopback_test(struct lws *wsi, enum lws_callback_reasons reason,
65 void *user, void *in, size_t len)
66 {
67 struct lws_client_connect_info i;
68 struct per_session_data__client_loopback_test *pss =
69 (struct per_session_data__client_loopback_test *)user;
70 const char *p = (const char *)in;
71 char buf[100];
72 int n;
73
74 switch (reason) {
75
76 /* HTTP part */
77
78 case LWS_CALLBACK_HTTP:
79 if (len < 10)
80 return -1;
81
82 p++;
83 while (*p && *p != '/')
84 p++;
85 if (!*p) {
86 lws_return_http_status(wsi, 400, "Arg needs to be in format ws://xxx or wss://xxx");
87 return -1;
88 }
89 p++;
90
91 memset(&i, 0, sizeof(i));
92 i.context = lws_get_context(wsi);
93
94 // stacked /// get resolved to /
95
96 if (strncmp(p, "ws:/", 4) == 0) {
97 i.ssl_connection = 0;
98 i.port = 80;
99 p += 4;
100 } else
101 if (strncmp(p, "wss:/", 5) == 0) {
102 i.port = 443;
103 i.ssl_connection = 1;
104 p += 5;
105 } else {
106 sprintf(buf, "Arg %s is not in format ws://xxx or wss://xxx\n", p);
107 lws_return_http_status(wsi, 400, buf);
108 return -1;
109 }
110
111 i.address = p;
112 i.path = "";
113 i.host = p;
114 i.origin = p;
115 i.ietf_version_or_minus_one = -1;
116 i.protocol = "client-loopback-test";
117
118 pss->wsi = lws_client_connect_via_info(&i);
119 if (!pss->wsi)
120 lws_return_http_status(wsi, 401, "client-loopback-test: connect failed\n");
121 else {
122 lwsl_notice("client connection to %s:%d with ssl: %d started\n",
123 i.address, i.port, i.ssl_connection);
124 lws_return_http_status(wsi, 200, "OK");
125 }
126
127 /* either way, close the triggering http link */
128
129 return -1;
130
131 case LWS_CALLBACK_CLOSED_HTTP:
132 lwsl_notice("Http part closed\n");
133 break;
134
135 /* server part */
136
137 case LWS_CALLBACK_ESTABLISHED:
138 lwsl_notice("server part: LWS_CALLBACK_ESTABLISHED\n");
139 strcpy(buf + LWS_PRE, "Made it");
140 n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
141 7, LWS_WRITE_TEXT);
142 if (n < 7)
143 return -1;
144 break;
145
146 /* client part */
147
148 case LWS_CALLBACK_CLIENT_ESTABLISHED:
149 lwsl_notice("Client connection established\n");
150 break;
151
152 case LWS_CALLBACK_CLIENT_RECEIVE:
153 lws_strncpy(buf, in, sizeof(buf));
154 lwsl_notice("Client connection received %ld from server '%s'\n",
155 (long)len, buf);
156
157 /* OK we are done with the client connection */
158 return -1;
159
160 default:
161 break;
162 }
163
164 return 0;
165 }
166
167 static const struct lws_protocols protocols[] = {
168 {
169 "client-loopback-test",
170 callback_client_loopback_test,
171 sizeof(struct per_session_data__client_loopback_test),
172 1024, /* rx buf size must be >= permessage-deflate rx size */
173 },
174 };
175
176 LWS_VISIBLE int
init_protocol_client_loopback_test(struct lws_context * context,struct lws_plugin_capability * c)177 init_protocol_client_loopback_test(struct lws_context *context,
178 struct lws_plugin_capability *c)
179 {
180 if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
181 lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
182 c->api_magic);
183 return 1;
184 }
185
186 c->protocols = protocols;
187 c->count_protocols = LWS_ARRAY_SIZE(protocols);
188 c->extensions = NULL;
189 c->count_extensions = 0;
190
191 return 0;
192 }
193
194 LWS_VISIBLE int
destroy_protocol_client_loopback_test(struct lws_context * context)195 destroy_protocol_client_loopback_test(struct lws_context *context)
196 {
197 return 0;
198 }
199