license clarification and test apps CC zero
[platform/upstream/libwebsockets.git] / test-server / test-client.c
1 /*
2  * libwebsockets-test-client - libwebsockets test implementation
3  *
4  * Copyright (C) 2011-2016 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  * The test apps 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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <getopt.h>
24 #include <string.h>
25 #include <signal.h>
26
27 #ifdef _WIN32
28 #define random rand
29 #include "gettimeofday.h"
30 #else
31 #include <syslog.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34 #endif
35
36 #include "../lib/libwebsockets.h"
37
38 static int deny_deflate, deny_mux, longlived, mirror_lifetime;
39 static struct lws *wsi_dumb, *wsi_mirror;
40 static volatile int force_exit;
41 static unsigned int opts;
42
43 /*
44  * This demo shows how to connect multiple websockets simultaneously to a
45  * websocket server (there is no restriction on their having to be the same
46  * server just it simplifies the demo).
47  *
48  *  dumb-increment-protocol:  we connect to the server and print the number
49  *                              we are given
50  *
51  *  lws-mirror-protocol: draws random circles, which are mirrored on to every
52  *                              client (see them being drawn in every browser
53  *                              session also using the test server)
54  */
55
56 enum demo_protocols {
57
58         PROTOCOL_DUMB_INCREMENT,
59         PROTOCOL_LWS_MIRROR,
60
61         /* always last */
62         DEMO_PROTOCOL_COUNT
63 };
64
65
66 /*
67  * dumb_increment protocol
68  *
69  * since this also happens to be protocols[0], some callbacks that are not
70  * bound to a specific protocol also turn up here.
71  */
72
73 static int
74 callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
75                         void *user, void *in, size_t len)
76 {
77         switch (reason) {
78
79         case LWS_CALLBACK_CLIENT_ESTABLISHED:
80                 lwsl_info("dumb: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
81                 break;
82
83         case LWS_CALLBACK_CLOSED:
84                 lwsl_notice("dumb: LWS_CALLBACK_CLOSED\n");
85                 wsi_dumb = NULL;
86                 break;
87
88         case LWS_CALLBACK_CLIENT_RECEIVE:
89                 ((char *)in)[len] = '\0';
90                 lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
91                 break;
92
93         /* because we are protocols[0] ... */
94
95         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
96                 if (wsi == wsi_dumb) {
97                         lwsl_err("dumb: LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
98                         wsi_dumb = NULL;
99                 }
100                 if (wsi == wsi_mirror) {
101                         lwsl_err("mirror: LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
102                         wsi_mirror = NULL;
103                 }
104                 break;
105
106         case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
107                 if ((strcmp(in, "deflate-stream") == 0) && deny_deflate) {
108                         lwsl_notice("denied deflate-stream extension\n");
109                         return 1;
110                 }
111                 if ((strcmp(in, "x-webkit-deflate-frame") == 0))
112                         return 1;
113                 if ((strcmp(in, "deflate-frame") == 0))
114                         return 1;
115                 break;
116
117         default:
118                 break;
119         }
120
121         return 0;
122 }
123
124
125 /* lws-mirror_protocol */
126
127
128 static int
129 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
130                     void *user, void *in, size_t len)
131 {
132         unsigned char buf[LWS_PRE + 4096];
133         unsigned int rands[4];
134         int l = 0;
135         int n;
136
137         switch (reason) {
138         case LWS_CALLBACK_CLIENT_ESTABLISHED:
139
140                 lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
141
142                 lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
143                 mirror_lifetime = 16384 + (rands[0] & 65535);
144                 /* useful to test single connection stability */
145                 if (longlived)
146                         mirror_lifetime += 500000;
147
148                 lwsl_info("opened mirror connection with "
149                           "%d lifetime\n", mirror_lifetime);
150
151                 /*
152                  * mirror_lifetime is decremented each send, when it reaches
153                  * zero the connection is closed in the send callback.
154                  * When the close callback comes, wsi_mirror is set to NULL
155                  * so a new connection will be opened
156                  *
157                  * start the ball rolling,
158                  * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
159                  */
160                 lws_callback_on_writable(wsi);
161                 break;
162
163         case LWS_CALLBACK_CLOSED:
164                 lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime);
165                 wsi_mirror = NULL;
166                 break;
167
168         case LWS_CALLBACK_CLIENT_WRITEABLE:
169                 for (n = 0; n < 1; n++) {
170                         lws_get_random(lws_get_context(wsi), rands, sizeof(rands));
171                         l += sprintf((char *)&buf[LWS_PRE + l],
172                                         "c #%06X %u %u %u;",
173                                         rands[0] & 0xffffff,    /* colour */
174                                         rands[1] & 511,         /* x */
175                                         rands[2] & 255,         /* y */
176                                         (rands[3] & 31) + 1);   /* radius */
177                 }
178
179                 n = lws_write(wsi, &buf[LWS_PRE], l,
180                               opts | LWS_WRITE_TEXT);
181                 if (n < 0)
182                         return -1;
183                 if (n < l) {
184                         lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
185                         return -1;
186                 }
187
188                 mirror_lifetime--;
189                 if (!mirror_lifetime) {
190                         lwsl_info("closing mirror session\n");
191                         return -1;
192                 }
193                 /* get notified as soon as we can write again */
194                 lws_callback_on_writable(wsi);
195                 break;
196
197         default:
198                 break;
199         }
200
201         return 0;
202 }
203
204
205 /* list of supported protocols and callbacks */
206
207 static struct lws_protocols protocols[] = {
208         {
209                 "dumb-increment-protocol,fake-nonexistant-protocol",
210                 callback_dumb_increment,
211                 0,
212                 20,
213         },
214         {
215                 "fake-nonexistant-protocol,lws-mirror-protocol",
216                 callback_lws_mirror,
217                 0,
218                 128,
219         },
220         { NULL, NULL, 0, 0 } /* end */
221 };
222
223 static const struct lws_extension exts[] = {
224         {
225                 "permessage-deflate",
226                 lws_extension_callback_pm_deflate,
227                 "permessage-deflate; client_max_window_bits"
228         },
229         {
230                 "deflate-frame",
231                 lws_extension_callback_pm_deflate,
232                 "deflate_frame"
233         },
234         { NULL, NULL, NULL /* terminator */ }
235 };
236
237
238
239 void sighandler(int sig)
240 {
241         force_exit = 1;
242 }
243
244 static struct option options[] = {
245         { "help",       no_argument,            NULL, 'h' },
246         { "debug",      required_argument,      NULL, 'd' },
247         { "port",       required_argument,      NULL, 'p' },
248         { "ssl",        no_argument,            NULL, 's' },
249         { "version",    required_argument,      NULL, 'v' },
250         { "undeflated", no_argument,            NULL, 'u' },
251         { "nomux",      no_argument,            NULL, 'n' },
252         { "longlived",  no_argument,            NULL, 'l' },
253         { NULL, 0, 0, 0 }
254 };
255
256 static int ratelimit_connects(unsigned int *last, unsigned int secs)
257 {
258         struct timeval tv;
259
260         gettimeofday(&tv, NULL);
261         if (tv.tv_sec - (*last) < secs)
262                 return 0;
263
264         *last = tv.tv_sec;
265
266         return 1;
267 }
268
269 int main(int argc, char **argv)
270 {
271         int n = 0, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
272         unsigned int rl_dumb = 0, rl_mirror = 0;
273         struct lws_context_creation_info info;
274         struct lws_client_connect_info i;
275         struct lws_context *context;
276         const char *prot, *p;
277         char path[300];
278
279         memset(&info, 0, sizeof info);
280
281         lwsl_notice("libwebsockets test client - license LGPL2.1+SLE\n");
282         lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
283
284         if (argc < 2)
285                 goto usage;
286
287         while (n >= 0) {
288                 n = getopt_long(argc, argv, "nuv:hsp:d:l", options, NULL);
289                 if (n < 0)
290                         continue;
291                 switch (n) {
292                 case 'd':
293                         lws_set_log_level(atoi(optarg), NULL);
294                         break;
295                 case 's':
296                         use_ssl = 2; /* 2 = allow selfsigned */
297                         break;
298                 case 'p':
299                         port = atoi(optarg);
300                         break;
301                 case 'l':
302                         longlived = 1;
303                         break;
304                 case 'v':
305                         ietf_version = atoi(optarg);
306                         break;
307                 case 'u':
308                         deny_deflate = 1;
309                         break;
310                 case 'n':
311                         deny_mux = 1;
312                         break;
313                 case 'h':
314                         goto usage;
315                 }
316         }
317
318         if (optind >= argc)
319                 goto usage;
320
321         signal(SIGINT, sighandler);
322
323         memset(&i, 0, sizeof(i));
324
325         i.port = port;
326         if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
327                 goto usage;
328
329         /* add back the leading / on path */
330         path[0] = '/';
331         strncpy(path + 1, p, sizeof(path) - 2);
332         path[sizeof(path) - 1] = '\0';
333         i.path = path;
334
335         if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
336                 use_ssl = 0;
337         if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
338                 use_ssl = 1;
339
340         /*
341          * create the websockets context.  This tracks open connections and
342          * knows how to route any traffic and which protocol version to use,
343          * and if each connection is client or server side.
344          *
345          * For this client-only demo, we tell it to not listen on any port.
346          */
347
348         info.port = CONTEXT_PORT_NO_LISTEN;
349         info.protocols = protocols;
350         info.gid = -1;
351         info.uid = -1;
352
353         context = lws_create_context(&info);
354         if (context == NULL) {
355                 fprintf(stderr, "Creating libwebsocket context failed\n");
356                 return 1;
357         }
358
359         i.context = context;
360         i.ssl_connection = use_ssl;
361         i.host = i.address;
362         i.origin = i.address;
363         i.ietf_version_or_minus_one = ietf_version;
364         i.client_exts = exts;
365         /*
366          * sit there servicing the websocket context to handle incoming
367          * packets, and drawing random circles on the mirror protocol websocket
368          *
369          * nothing happens until the client websocket connection is
370          * asynchronously established... calling lws_client_connect() only
371          * instantiates the connection logically, lws_service() progresses it
372          * asynchronously.
373          */
374
375         while (!force_exit) {
376
377                 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
378                         lwsl_notice("dumb: connecting\n");
379                         i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
380                         wsi_dumb = lws_client_connect_via_info(&i);
381                 }
382
383                 if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
384                         lwsl_notice("mirror: connecting\n");
385                         i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
386                         wsi_mirror = lws_client_connect_via_info(&i);
387                 }
388
389                 lws_service(context, 500);
390         }
391
392         lwsl_err("Exiting\n");
393         lws_context_destroy(context);
394
395         return ret;
396
397 usage:
398         fprintf(stderr, "Usage: libwebsockets-test-client "
399                                 "<server address> [--port=<p>] "
400                                 "[--ssl] [-k] [-v <ver>] "
401                                 "[-d <log bitfield>] [-l]\n");
402         return 1;
403 }