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