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