prepare for v1.4
[platform/upstream/libwebsockets.git] / test-server / test-client.c
1 /*
2  * libwebsockets-test-client - libwebsockets test implementation
3  *
4  * Copyright (C) 2011 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <getopt.h>
25 #include <string.h>
26 #include <signal.h>
27
28 #ifdef _WIN32
29 #define random rand
30 #else
31 #include <unistd.h>
32 #endif
33
34 #ifdef CMAKE_BUILD
35 #include "lws_config.h"
36 #endif
37
38 #include "../lib/libwebsockets.h"
39
40 static unsigned int opts;
41 static int was_closed;
42 static int deny_deflate;
43 static int deny_mux;
44 static struct libwebsocket *wsi_mirror;
45 static int mirror_lifetime = 0;
46 static volatile int force_exit = 0;
47 static int longlived = 0;
48
49 /*
50  * This demo shows how to connect multiple websockets simultaneously to a
51  * websocket server (there is no restriction on their having to be the same
52  * server just it simplifies the demo).
53  *
54  *  dumb-increment-protocol:  we connect to the server and print the number
55  *                              we are given
56  *
57  *  lws-mirror-protocol: draws random circles, which are mirrored on to every
58  *                              client (see them being drawn in every browser
59  *                              session also using the test server)
60  */
61
62 enum demo_protocols {
63
64         PROTOCOL_DUMB_INCREMENT,
65         PROTOCOL_LWS_MIRROR,
66
67         /* always last */
68         DEMO_PROTOCOL_COUNT
69 };
70
71
72 /* dumb_increment protocol */
73
74 static int
75 callback_dumb_increment(struct libwebsocket_context *this,
76                         struct libwebsocket *wsi,
77                         enum libwebsocket_callback_reasons reason,
78                                                void *user, void *in, size_t len)
79 {
80         switch (reason) {
81
82         case LWS_CALLBACK_CLIENT_ESTABLISHED:
83                 fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
84                 break;
85
86         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
87                 fprintf(stderr, "LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
88                 was_closed = 1;
89                 break;
90
91         case LWS_CALLBACK_CLOSED:
92                 fprintf(stderr, "LWS_CALLBACK_CLOSED\n");
93                 was_closed = 1;
94                 break;
95
96         case LWS_CALLBACK_CLIENT_RECEIVE:
97                 ((char *)in)[len] = '\0';
98                 fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in);
99                 break;
100
101         /* because we are protocols[0] ... */
102
103         case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
104                 if ((strcmp(in, "deflate-stream") == 0) && deny_deflate) {
105                         fprintf(stderr, "denied deflate-stream extension\n");
106                         return 1;
107                 }
108                 if ((strcmp(in, "deflate-frame") == 0) && deny_deflate) {
109                         fprintf(stderr, "denied deflate-frame extension\n");
110                         return 1;
111                 }
112                 if ((strcmp(in, "x-google-mux") == 0) && deny_mux) {
113                         fprintf(stderr, "denied x-google-mux extension\n");
114                         return 1;
115                 }
116
117                 break;
118
119         default:
120                 break;
121         }
122
123         return 0;
124 }
125
126
127 /* lws-mirror_protocol */
128
129
130 static int
131 callback_lws_mirror(struct libwebsocket_context *context,
132                         struct libwebsocket *wsi,
133                         enum libwebsocket_callback_reasons reason,
134                                                void *user, void *in, size_t len)
135 {
136         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
137                                                   LWS_SEND_BUFFER_POST_PADDING];
138         int l = 0;
139         int n;
140         unsigned int rands[4];
141
142         switch (reason) {
143
144         case LWS_CALLBACK_CLIENT_ESTABLISHED:
145
146                 fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
147
148                 libwebsockets_get_random(context, rands, sizeof(rands[0]));
149                 mirror_lifetime = 10 + (rands[0] & 1023);
150                 /* useful to test single connection stability */
151                 if (longlived)
152                         mirror_lifetime += 50000;
153
154                 fprintf(stderr, "opened mirror connection with "
155                                      "%d lifetime\n", mirror_lifetime);
156
157                 /*
158                  * mirror_lifetime is decremented each send, when it reaches
159                  * zero the connection is closed in the send callback.
160                  * When the close callback comes, wsi_mirror is set to NULL
161                  * so a new connection will be opened
162                  */
163
164                 /*
165                  * start the ball rolling,
166                  * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
167                  */
168
169                 libwebsocket_callback_on_writable(context, wsi);
170                 break;
171
172         case LWS_CALLBACK_CLOSED:
173                 fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime);
174                 wsi_mirror = NULL;
175                 break;
176
177         case LWS_CALLBACK_CLIENT_RECEIVE:
178 /*              fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */
179                 break;
180
181         case LWS_CALLBACK_CLIENT_WRITEABLE:
182
183                 for (n = 0; n < 1; n++) {
184                         libwebsockets_get_random(context, rands, sizeof(rands));
185                         l += sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING + l],
186                                         "c #%06X %d %d %d;",
187                                         (int)rands[0] & 0xffffff,
188                                         (int)rands[1] % 500,
189                                         (int)rands[2] % 250,
190                                         (int)rands[3] % 24);
191                 }
192
193                 n = libwebsocket_write(wsi,
194                    &buf[LWS_SEND_BUFFER_PRE_PADDING], l, opts | LWS_WRITE_TEXT);
195
196                 if (n < 0)
197                         return -1;
198                 if (n < l) {
199                         lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
200                         return -1;
201                 }
202
203                 mirror_lifetime--;
204                 if (!mirror_lifetime) {
205                         fprintf(stderr, "closing mirror session\n");
206                         return -1;
207                 } else
208                         /* get notified as soon as we can write again */
209                         libwebsocket_callback_on_writable(context, wsi);
210                 break;
211
212         default:
213                 break;
214         }
215
216         return 0;
217 }
218
219
220 /* list of supported protocols and callbacks */
221
222 static struct libwebsocket_protocols protocols[] = {
223         {
224                 "dumb-increment-protocol,fake-nonexistant-protocol",
225                 callback_dumb_increment,
226                 0,
227                 20,
228         },
229         {
230                 "fake-nonexistant-protocol,lws-mirror-protocol",
231                 callback_lws_mirror,
232                 0,
233                 128,
234         },
235         { NULL, NULL, 0, 0 } /* end */
236 };
237
238 void sighandler(int sig)
239 {
240         force_exit = 1;
241 }
242
243 static struct option options[] = {
244         { "help",       no_argument,            NULL, 'h' },
245         { "debug",      required_argument,      NULL, 'd' },
246         { "port",       required_argument,      NULL, 'p' },
247         { "ssl",        no_argument,            NULL, 's' },
248         { "version",    required_argument,      NULL, 'v' },
249         { "undeflated", no_argument,            NULL, 'u' },
250         { "nomux",      no_argument,            NULL, 'n' },
251         { "longlived",  no_argument,            NULL, 'l' },
252         { NULL, 0, 0, 0 }
253 };
254
255
256 int main(int argc, char **argv)
257 {
258         int n = 0;
259         int ret = 0;
260         int port = 7681;
261         int use_ssl = 0;
262         struct libwebsocket_context *context;
263         const char *address;
264         struct libwebsocket *wsi_dumb;
265         int ietf_version = -1; /* latest */
266         struct lws_context_creation_info info;
267
268         memset(&info, 0, sizeof info);
269
270         fprintf(stderr, "libwebsockets test client\n"
271                         "(C) Copyright 2010-2015 Andy Green <andy@warmcat.com> "
272                                                     "licensed under LGPL2.1\n");
273
274         if (argc < 2)
275                 goto usage;
276
277         while (n >= 0) {
278                 n = getopt_long(argc, argv, "nuv:hsp:d:l", options, NULL);
279                 if (n < 0)
280                         continue;
281                 switch (n) {
282                 case 'd':
283                         lws_set_log_level(atoi(optarg), NULL);
284                         break;
285                 case 's':
286                         use_ssl = 2; /* 2 = allow selfsigned */
287                         break;
288                 case 'p':
289                         port = atoi(optarg);
290                         break;
291                 case 'l':
292                         longlived = 1;
293                         break;
294                 case 'v':
295                         ietf_version = atoi(optarg);
296                         break;
297                 case 'u':
298                         deny_deflate = 1;
299                         break;
300                 case 'n':
301                         deny_mux = 1;
302                         break;
303                 case 'h':
304                         goto usage;
305                 }
306         }
307
308         if (optind >= argc)
309                 goto usage;
310
311         signal(SIGINT, sighandler);
312
313         address = argv[optind];
314
315         /*
316          * create the websockets context.  This tracks open connections and
317          * knows how to route any traffic and which protocol version to use,
318          * and if each connection is client or server side.
319          *
320          * For this client-only demo, we tell it to not listen on any port.
321          */
322
323         info.port = CONTEXT_PORT_NO_LISTEN;
324         info.protocols = protocols;
325 #ifndef LWS_NO_EXTENSIONS
326         info.extensions = libwebsocket_get_internal_extensions();
327 #endif
328         info.gid = -1;
329         info.uid = -1;
330
331         context = libwebsocket_create_context(&info);
332         if (context == NULL) {
333                 fprintf(stderr, "Creating libwebsocket context failed\n");
334                 return 1;
335         }
336
337         /* create a client websocket using dumb increment protocol */
338
339         wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl,
340                         "/", argv[optind], argv[optind],
341                          protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version);
342
343         if (wsi_dumb == NULL) {
344                 fprintf(stderr, "libwebsocket connect failed\n");
345                 ret = 1;
346                 goto bail;
347         }
348
349         fprintf(stderr, "Waiting for connect...\n");
350
351         /*
352          * sit there servicing the websocket context to handle incoming
353          * packets, and drawing random circles on the mirror protocol websocket
354          * nothing happens until the client websocket connection is
355          * asynchronously established
356          */
357
358         n = 0;
359         while (n >= 0 && !was_closed && !force_exit) {
360                 n = libwebsocket_service(context, 10);
361
362                 if (n < 0)
363                         continue;
364
365                 if (wsi_mirror)
366                         continue;
367
368                 /* create a client websocket using mirror protocol */
369
370                 wsi_mirror = libwebsocket_client_connect(context,
371                         address, port, use_ssl,  "/",
372                         argv[optind], argv[optind],
373                         protocols[PROTOCOL_LWS_MIRROR].name, ietf_version);
374
375                 if (wsi_mirror == NULL) {
376                         fprintf(stderr, "libwebsocket "
377                                               "mirror connect failed\n");
378                         ret = 1;
379                         goto bail;
380                 }
381         }
382
383 bail:
384         fprintf(stderr, "Exiting\n");
385
386         libwebsocket_context_destroy(context);
387
388         return ret;
389
390 usage:
391         fprintf(stderr, "Usage: libwebsockets-test-client "
392                                 "<server address> [--port=<p>] "
393                                 "[--ssl] [-k] [-v <ver>] "
394                                 "[-d <log bitfield>] [-l]\n");
395         return 1;
396 }