1/*
2 * lws-minimal-secure-streams-server
3 *
4 * Written in 2010-2020 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
10#include <libwebsockets.h>
11#include <assert.h>
12
13extern int interrupted, bad, multipart;
14
15static const char *html =
16		/* normally we serve this... */
17	"<head><meta content=\"text/html;charset=utf-8\" "
18			"http-equiv=\"Content-Type\"><script>"
19	" var ws = new WebSocket(\"wss://localhost:7681\", \"mywsprotocol\");"
20	"try { ws.onopen = function() { console.log(\"open\"); }; "
21		"ws.onmessage = function got_packet(msg) { "
22		   "var s=\"\"; s += msg.data; "
23		   "document.getElementById(\"wsd\").innerHTML = s; };"
24		"} catch(exception) {"
25		"alert(\"<p>Error\" + exception); }"
26	"</script></head><html><body>"
27	  "Hello from the web server<br>"
28	  "<div id=\"wsd\"></div>"
29	"</body></html>",
30
31*multipart_html =
32	/*
33	 * If you use -m commandline switch we send this instead, as
34	 * multipart/form-data
35	 */
36	"--aBoundaryString\r\n"
37	"Content-Disposition: form-data; name=\"myFile\"; filename=\"xxx.txt\"\r\n"
38	"Content-Type: text/plain\r\n"
39	"\r\n"
40	"The file contents\r\n"
41	"--aBoundaryString\r\n"
42	"Content-Disposition: form-data; name=\"myField\"\r\n"
43	"\r\n"
44	"(data)\r\n"
45	"--aBoundaryString--\r\n";
46
47
48typedef struct myss {
49	struct lws_ss_handle 		*ss;
50	void				*opaque_data;
51	/* ... application specific state ... */
52
53	lws_sorted_usec_list_t		sul;
54	int				count;
55	char				upgraded;
56
57} myss_srv_t;
58
59/*
60 * This is the Secure Streams Server RX and TX for HTTP(S)
61 */
62
63static lws_ss_state_return_t
64myss_srv_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
65{
66//	myss_srv_t *m = (myss_srv_t *)userobj;
67
68	lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
69	lwsl_hexdump_info(buf, len);
70
71	/*
72	 * If we received the whole message, for our example it means
73	 * we are done.
74	 */
75	if (flags & LWSSS_FLAG_EOM) {
76		bad = 0;
77		interrupted = 1;
78	}
79
80	return 0;
81}
82
83static lws_ss_state_return_t
84myss_srv_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
85	int *flags)
86{
87	myss_srv_t *m = (myss_srv_t *)userobj;
88	const char *send = html;
89
90	if (m->upgraded)
91		return LWSSSSRET_TX_DONT_SEND;
92
93	if (multipart)
94		send = multipart_html;
95
96	*flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;
97
98	lws_strncpy((char *)buf, send, *len);
99	*len = strlen(send);
100
101	return 0;
102}
103
104/*
105 * This is the Secure Streams Server RX and TX for WS(S)... when we get a
106 * state that the underlying connection upgraded protocol, we switch the stream
107 * rx and tx handlers to here.
108 */
109
110static lws_ss_state_return_t
111myss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
112{
113//	myss_srv_t *m = (myss_srv_t *)userobj;
114
115	lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
116	lwsl_hexdump_info(buf, len);
117
118	/*
119	 * If we received the whole message, for our example it means
120	 * we are done.
121	 */
122	if (flags & LWSSS_FLAG_EOM) {
123		bad = 0;
124		interrupted = 1;
125	}
126
127	return 0;
128}
129
130/* this is the callback that mediates sending the incrementing number */
131
132static void
133spam_sul_cb(struct lws_sorted_usec_list *sul)
134{
135	myss_srv_t *m = lws_container_of(sul, myss_srv_t, sul);
136
137	if (!lws_ss_request_tx(m->ss))
138		lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb,
139			 100 * LWS_US_PER_MS);
140}
141
142static lws_ss_state_return_t
143myss_ws_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
144	int *flags)
145{
146	myss_srv_t *m = (myss_srv_t *)userobj;
147
148	*flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;
149
150	*len = (unsigned int)lws_snprintf((char *)buf, *len, "hello from ws %d", m->count++);
151
152	lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb,
153			 100 * LWS_US_PER_MS);
154
155	return 0;
156}
157
158static lws_ss_state_return_t
159myss_srv_state(void *userobj, void *sh, lws_ss_constate_t state,
160	   lws_ss_tx_ordinal_t ack)
161{
162	myss_srv_t *m = (myss_srv_t *)userobj;
163
164	lwsl_user("%s: %p %s, ord 0x%x\n", __func__, m->ss,
165		  lws_ss_state_name((int)state), (unsigned int)ack);
166
167	switch (state) {
168	case LWSSSCS_DISCONNECTED:
169		lws_sul_cancel(&m->sul);
170		break;
171	case LWSSSCS_CREATING:
172		return lws_ss_request_tx(m->ss);
173
174	case LWSSSCS_ALL_RETRIES_FAILED:
175		/* if we're out of retries, we want to close the app and FAIL */
176		interrupted = 1;
177		break;
178
179	case LWSSSCS_SERVER_TXN:
180		/*
181		 * The underlying protocol started a transaction, let's
182		 * describe how we want to complete it.  We can defer this until
183		 * later, eg, after we have consumed any rx that's coming with
184		 * the client's transaction initiation phase, but in this
185		 * example we know what we want to do already.
186		 *
187		 * We do want to ack the transaction...
188		 */
189		lws_ss_server_ack(m->ss, 0);
190		/*
191		 * ... it's going to be either text/html or multipart ...
192		 */
193		if (multipart) {
194			if (lws_ss_set_metadata(m->ss, "mime",
195			   "multipart/form-data; boundary=aBoundaryString", 45))
196				return LWSSSSRET_DISCONNECT_ME;
197		} else
198			if (lws_ss_set_metadata(m->ss, "mime", "text/html", 9))
199				return LWSSSSRET_DISCONNECT_ME;
200		/*
201		 * ...it's going to be whatever size it is (and request tx)
202		 */
203		return lws_ss_request_tx_len(m->ss, (unsigned long)
204				(multipart ? strlen(multipart_html) :
205							 strlen(html)));
206
207	case LWSSSCS_SERVER_UPGRADE:
208
209		/*
210		 * This is sent when the underlying protocol has experienced
211		 * an upgrade, eg, http->ws... it's a one-way upgrade on this
212		 * stream, change the handlers to deal with the kind of
213		 * messages we send on ws
214		 */
215
216		m->upgraded = 1;
217		lws_ss_change_handlers(m->ss, myss_ws_rx, myss_ws_tx, NULL);
218		return lws_ss_request_tx(m->ss); /* we want to start sending numbers */
219
220	default:
221		break;
222	}
223
224	return 0;
225}
226
227const lws_ss_info_t ssi_server = {
228	.handle_offset			= offsetof(myss_srv_t, ss),
229	.opaque_user_data_offset	= offsetof(myss_srv_t, opaque_data),
230	.streamtype			= "myserver",
231	.rx				= myss_srv_rx,
232	.tx				= myss_srv_tx,
233	.state				= myss_srv_state,
234	.user_alloc			= sizeof(myss_srv_t),
235};
236