• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ws protocol handler plugin for "lws-minimal-pmd-bulk"
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 protocol shows how to send and receive bulk messages over a ws connection
10  * that optionally may have the permessage-deflate extension negotiated on it.
11  */
12 
13 #if !defined (LWS_PLUGIN_STATIC)
14 #define LWS_DLL
15 #define LWS_INTERNAL
16 #include <libwebsockets.h>
17 #endif
18 
19 #include <string.h>
20 
21 /*
22  * We will produce a large ws message either from this text repeated many times,
23  * or from 0x40 + a 6-bit pseudorandom number
24  */
25 
26 static const char * const redundant_string =
27 	"No one would have believed in the last years of the nineteenth "
28 	"century that this world was being watched keenly and closely by "
29 	"intelligences greater than man's and yet as mortal as his own; that as "
30 	"men busied themselves about their various concerns they were "
31 	"scrutinised and studied, perhaps almost as narrowly as a man with a "
32 	"microscope might scrutinise the transient creatures that swarm and "
33 	"multiply in a drop of water.  With infinite complacency men went to "
34 	"and fro over this globe about their little affairs, serene in their "
35 	"assurance of their empire over matter. It is possible that the "
36 	"infusoria under the microscope do the same.  No one gave a thought to "
37 	"the older worlds of space as sources of human danger, or thought of "
38 	"them only to dismiss the idea of life upon them as impossible or "
39 	"improbable.  It is curious to recall some of the mental habits of "
40 	"those departed days.  At most terrestrial men fancied there might be "
41 	"other men upon Mars, perhaps inferior to themselves and ready to "
42 	"welcome a missionary enterprise. Yet across the gulf of space, minds "
43 	"that are to our minds as ours are to those of the beasts that perish, "
44 	"intellects vast and cool and unsympathetic, regarded this earth with "
45 	"envious eyes, and slowly and surely drew their plans against us.  And "
46 	"early in the twentieth century came the great disillusionment. "
47 ;
48 
49 /* this reflects the length of the string above */
50 #define REPEAT_STRING_LEN 1337
51 /* this is the total size of the ws message we will send */
52 #define MESSAGE_SIZE (100 * REPEAT_STRING_LEN)
53 /* this is how much we will send each time the connection is writable */
54 #define MESSAGE_CHUNK_SIZE (1 * 1024)
55 
56 /* one of these is created for each client connecting to us */
57 
58 struct per_session_data__minimal_pmd_bulk {
59 	int position_tx, position_rx;
60 	uint64_t rng_rx, rng_tx;
61 };
62 
63 struct vhd_minimal_pmd_bulk {
64 	struct lws_context *context;
65 	struct lws_vhost *vhost;
66 	struct lws *client_wsi;
67 
68 	int *interrupted;
69 	int *options;
70 };
71 
rng(uint64_t * r)72 static uint64_t rng(uint64_t *r)
73 {
74         *r ^= *r << 21;
75         *r ^= *r >> 35;
76         *r ^= *r << 4;
77 
78         return *r;
79 }
80 
81 static int
connect_client(struct vhd_minimal_pmd_bulk * vhd)82 connect_client(struct vhd_minimal_pmd_bulk *vhd)
83 {
84 	struct lws_client_connect_info i;
85 
86 	memset(&i, 0, sizeof(i));
87 
88 	i.context = vhd->context;
89 	i.port = 7681;
90 	i.address = "localhost";
91 	i.path = "/";
92 	i.host = i.address;
93 	i.origin = i.address;
94 	i.ssl_connection = 0;
95 	i.vhost = vhd->vhost;
96 	i.protocol = "lws-minimal-pmd-bulk";
97 	i.pwsi = &vhd->client_wsi;
98 
99 	return !lws_client_connect_via_info(&i);
100 }
101 
102 static void
schedule_callback(struct lws * wsi,int reason,int secs)103 schedule_callback(struct lws *wsi, int reason, int secs)
104 {
105 	lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
106 		lws_get_protocol(wsi), reason, secs);
107 }
108 
109 static int
callback_minimal_pmd_bulk(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)110 callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason,
111 			  void *user, void *in, size_t len)
112 {
113 	struct per_session_data__minimal_pmd_bulk *pss =
114 			(struct per_session_data__minimal_pmd_bulk *)user;
115 	struct vhd_minimal_pmd_bulk *vhd = (struct vhd_minimal_pmd_bulk *)
116 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
117 				lws_get_protocol(wsi));
118 	uint8_t buf[LWS_PRE + MESSAGE_CHUNK_SIZE], *start = &buf[LWS_PRE], *p;
119 	int n, m, flags;
120 
121 	switch (reason) {
122 
123 	case LWS_CALLBACK_PROTOCOL_INIT:
124 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
125 				lws_get_protocol(wsi),
126 				sizeof(struct vhd_minimal_pmd_bulk));
127 		if (!vhd)
128 			return -1;
129 
130 		vhd->context = lws_get_context(wsi);
131 		vhd->vhost = lws_get_vhost(wsi);
132 
133 		/* get the pointer to "interrupted" we were passed in pvo */
134 		vhd->interrupted = (int *)lws_pvo_search(
135 			(const struct lws_protocol_vhost_options *)in,
136 			"interrupted")->value;
137 		vhd->options = (int *)lws_pvo_search(
138 			(const struct lws_protocol_vhost_options *)in,
139 			"options")->value;
140 
141 		if (connect_client(vhd))
142 			schedule_callback(wsi, LWS_CALLBACK_USER, 1);
143 		break;
144 
145 	case LWS_CALLBACK_CLIENT_ESTABLISHED:
146 		pss->rng_tx = 4;
147 		pss->rng_rx = 4;
148 		lws_callback_on_writable(wsi);
149 		break;
150 
151 	case LWS_CALLBACK_CLIENT_WRITEABLE:
152 
153 		/*
154 		 * when we connect, we will send the server a message
155 		 */
156 
157 		if (pss->position_tx == MESSAGE_SIZE)
158 			break;
159 
160 		/* fill up one chunk's worth of message content */
161 
162 		p = start;
163 		n = MESSAGE_CHUNK_SIZE;
164 		if (n > MESSAGE_SIZE - pss->position_tx)
165 			n = MESSAGE_SIZE - pss->position_tx;
166 
167 		flags = lws_write_ws_flags(LWS_WRITE_BINARY, !pss->position_tx,
168 					   pss->position_tx + n == MESSAGE_SIZE);
169 
170 		/*
171 		 * select between producing compressible repeated text,
172 		 * or uncompressible PRNG output
173 		 */
174 
175 		if (*vhd->options & 1) {
176 			while (n) {
177 				size_t s;
178 
179 				m = pss->position_tx % REPEAT_STRING_LEN;
180 				s = REPEAT_STRING_LEN - m;
181 				if (s > (size_t)n)
182 					s = n;
183 				memcpy(p, &redundant_string[m], s);
184 				pss->position_tx += s;
185 				p += s;
186 				n -= s;
187 			}
188 		} else {
189 			pss->position_tx += n;
190 			while (n--)
191 				*p++ = rng(&pss->rng_tx);
192 		}
193 
194 		n = lws_ptr_diff(p, start);
195 		m = lws_write(wsi, start, n, flags);
196 		if (m < n) {
197 			lwsl_err("ERROR %d writing ws\n", m);
198 			return -1;
199 		}
200 		if (pss->position_tx != MESSAGE_SIZE) /* if more to do... */
201 			lws_callback_on_writable(wsi);
202 		else
203 			/* if we sent and received everything */
204 			if (pss->position_rx == MESSAGE_SIZE)
205 				*vhd->interrupted = 2;
206 		break;
207 
208 	case LWS_CALLBACK_CLIENT_RECEIVE:
209 
210 		/*
211 		 * When we connect, the server will send us a message too
212 		 */
213 
214 		lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, last %d)\n",
215 			(int)len, (int)lws_remaining_packet_payload(wsi),
216 			lws_is_final_fragment(wsi));
217 
218 		if (*vhd->options & 1) {
219 			while (len) {
220 				size_t s;
221 
222 				m = pss->position_rx % REPEAT_STRING_LEN;
223 				s = REPEAT_STRING_LEN - m;
224 				if (s > len)
225 					s = len;
226 				if (memcmp(in, &redundant_string[m], s)) {
227 					lwsl_user("echo'd data doesn't match\n");
228 					return -1;
229 				}
230 				pss->position_rx += s;
231 				in = ((unsigned char *)in) + s;
232 				len -= s;
233 			}
234 		} else {
235 			p = (uint8_t *)in;
236 			pss->position_rx += len;
237 			while (len--)
238 				if (*p++ != (uint8_t)rng(&pss->rng_rx)) {
239 					lwsl_user("echo'd data doesn't match\n");
240 					return -1;
241 				}
242 		}
243 
244 		/* if we sent and received everything */
245 
246 		if (pss->position_rx == MESSAGE_SIZE &&
247 		    pss->position_tx == MESSAGE_SIZE)
248 			*vhd->interrupted = 2;
249 
250 		break;
251 
252 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
253 		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
254 			 in ? (char *)in : "(null)");
255 		vhd->client_wsi = NULL;
256 		schedule_callback(wsi, LWS_CALLBACK_USER, 1);
257 		break;
258 
259 	case LWS_CALLBACK_CLIENT_CLOSED:
260 		vhd->client_wsi = NULL;
261 		schedule_callback(wsi, LWS_CALLBACK_USER, 1);
262 		break;
263 
264 	/* rate-limited client connect retries */
265 
266 	case LWS_CALLBACK_USER:
267 		lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);
268 		if (connect_client(vhd))
269 			schedule_callback(wsi, LWS_CALLBACK_USER, 1);
270 		break;
271 
272 	default:
273 		break;
274 	}
275 
276 	return 0;
277 }
278 
279 #define LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK \
280 	{ \
281 		"lws-minimal-pmd-bulk", \
282 		callback_minimal_pmd_bulk, \
283 		sizeof(struct per_session_data__minimal_pmd_bulk), \
284 		4096, \
285 		0, NULL, 0 \
286 	}
287 
288 #if !defined (LWS_PLUGIN_STATIC)
289 
290 /* boilerplate needed if we are built as a dynamic plugin */
291 
292 static const struct lws_protocols protocols[] = {
293 	LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK
294 };
295 
296 int
init_protocol_minimal_pmd_bulk(struct lws_context * context,struct lws_plugin_capability * c)297 init_protocol_minimal_pmd_bulk(struct lws_context *context,
298 			       struct lws_plugin_capability *c)
299 {
300 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
301 		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
302 			 c->api_magic);
303 		return 1;
304 	}
305 
306 	c->protocols = protocols;
307 	c->count_protocols = LWS_ARRAY_SIZE(protocols);
308 	c->extensions = NULL;
309 	c->count_extensions = 0;
310 
311 	return 0;
312 }
313 
314 int
destroy_protocol_minimal_pmd_bulk(struct lws_context * context)315 destroy_protocol_minimal_pmd_bulk(struct lws_context *context)
316 {
317 	return 0;
318 }
319 #endif
320