f0f00d3dcdef62c15e79b2a4885feaf5934c6e50
[platform/upstream/libwebsockets.git] / test-server / test-ping.c
1 /*
2  * libwebsockets-test-ping - libwebsockets test floodping
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 #include <sys/types.h>
27
28 #include "../lib/libwebsockets.h"
29
30 #ifndef _WIN32
31 #include <netdb.h>
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <poll.h>
36 #include <unistd.h>
37 #else
38 #include "gettimeofday.h"
39 #endif
40
41 #ifdef __ANDROID__
42 #include <termiosh>
43 #endif
44
45 #ifdef __sun
46 #include <sys/termios.h>
47 #endif
48
49 /*
50  * this is specified in the 04 standard, control frames can only have small
51  * payload length styles
52  */
53 #define MAX_PING_PAYLOAD 125
54 #define MAX_MIRROR_PAYLOAD 4096
55 #define MAX_PING_CLIENTS 256
56 #define PING_RINGBUFFER_SIZE 256
57
58 static struct lws *ping_wsi[MAX_PING_CLIENTS];
59 static unsigned int interval_us = 1000000;
60 static unsigned int size = 64;
61 static int flood;
62 static const char *address;
63 static unsigned char pingbuf[LWS_PRE + MAX_MIRROR_PAYLOAD];
64 static char peer_name[128];
65 static unsigned long started;
66 static int screen_width = 80;
67 static int use_mirror;
68 static unsigned int write_options;
69
70 static unsigned long rtt_min = 100000000;
71 static unsigned long rtt_max;
72 static unsigned long rtt_avg;
73 static unsigned long global_rx_count;
74 static unsigned long global_tx_count;
75 static int clients = 1;
76 static unsigned long interrupted_time;
77
78 struct ping {
79         unsigned long issue_timestamp;
80         unsigned long index;
81         unsigned int seen;
82 };
83
84 struct per_session_data__ping {
85         unsigned long long ping_index;
86
87         struct ping ringbuffer[PING_RINGBUFFER_SIZE];
88         int ringbuffer_head;
89         int ringbuffer_tail;
90
91         unsigned long rx_count;
92 };
93
94 /*
95  * uses the ping pong protocol features to provide an equivalent for the
96  * ping utility for 04+ websockets
97  */
98
99 enum demo_protocols {
100
101         PROTOCOL_LWS_MIRROR,
102
103         /* always last */
104         DEMO_PROTOCOL_COUNT
105 };
106
107
108 static int
109 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
110                     void *user, void *in, size_t len)
111 {
112         struct per_session_data__ping *psd = user;
113         struct timeval tv;
114         unsigned char *p;
115         unsigned long iv;
116         int match = 0;
117         unsigned long long l;
118         int shift;
119         int n;
120
121         switch (reason) {
122         case LWS_CALLBACK_CLOSED:
123                 fprintf(stderr, "LWS_CALLBACK_CLOSED on %p\n", (void *)wsi);
124
125                 /* remove closed guy */
126
127                 for (n = 0; n < clients; n++)
128                         if (ping_wsi[n] == wsi) {
129                                 clients--;
130                                 while (n < clients) {
131                                         ping_wsi[n] = ping_wsi[n + 1];
132                                         n++;
133                                 }
134                         }
135
136                 break;
137
138         case LWS_CALLBACK_CLIENT_ESTABLISHED:
139
140                 psd->rx_count = 0;
141                 psd->ping_index = 1;
142                 psd->ringbuffer_head = 0;
143                 psd->ringbuffer_tail = 0;
144
145                 /*
146                  * start the ball rolling,
147                  * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
148                  */
149
150                 lws_callback_on_writable(wsi);
151                 break;
152
153         case LWS_CALLBACK_CLIENT_RECEIVE:
154         case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
155                 gettimeofday(&tv, NULL);
156                 iv = (tv.tv_sec * 1000000) + tv.tv_usec;
157
158                 psd->rx_count++;
159
160                 shift = 56;
161                 p = in;
162                 l = 0;
163
164                 while (shift >= 0) {
165                         l |= ((lws_intptr_t)*p++) << shift;
166                         shift -= 8;
167                 }
168
169                 /* find it in the ringbuffer, look backwards from head */
170                 n = psd->ringbuffer_head;
171                 while (!match) {
172
173                         if (psd->ringbuffer[n].index == l) {
174                                 psd->ringbuffer[n].seen++;
175                                 match = 1;
176                                 continue;
177                         }
178
179                         if (n == psd->ringbuffer_tail) {
180                                 match = -1;
181                                 continue;
182                         }
183
184                         if (n == 0)
185                                 n = PING_RINGBUFFER_SIZE - 1;
186                         else
187                                 n--;
188                 }
189
190                 if (match < 1) {
191
192                         if (!flood)
193                                 fprintf(stderr, "%d bytes from %s: req=%ld "
194                                       "time=(unknown)\n", (int)len, address,
195                                         (long)l);
196                         else
197                                 fprintf(stderr, "\b \b");
198
199                         break;
200                 }
201
202                 if (psd->ringbuffer[n].seen > 1)
203                         fprintf(stderr, "DUP! ");
204
205                 if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min)
206                         rtt_min = iv - psd->ringbuffer[n].issue_timestamp;
207
208                 if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max)
209                         rtt_max = iv - psd->ringbuffer[n].issue_timestamp;
210
211                 rtt_avg += iv - psd->ringbuffer[n].issue_timestamp;
212                 global_rx_count++;
213
214                 if (!flood)
215                         fprintf(stderr, "%d bytes from %s: req=%ld "
216                                 "time=%lu.%lums\n", (int)len, address, (long)l,
217                                (iv - psd->ringbuffer[n].issue_timestamp) / 1000,
218                         ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10);
219                 else
220                         fprintf(stderr, "\b \b");
221                 break;
222
223         case LWS_CALLBACK_CLIENT_WRITEABLE:
224
225                 shift = 56;
226                 p = &pingbuf[LWS_PRE];
227
228                 /* 64-bit ping index in network byte order */
229
230                 while (shift >= 0) {
231                         *p++ = (unsigned char)(psd->ping_index >> shift);
232                         shift -= 8;
233                 }
234
235                 while ((unsigned int)(p - &pingbuf[LWS_PRE]) < size)
236                         *p++ = 0;
237
238                 gettimeofday(&tv, NULL);
239
240                 psd->ringbuffer[psd->ringbuffer_head].issue_timestamp =
241                                              (tv.tv_sec * 1000000) + tv.tv_usec;
242                 psd->ringbuffer[psd->ringbuffer_head].index = (unsigned long)psd->ping_index++;
243                 psd->ringbuffer[psd->ringbuffer_head].seen = 0;
244
245                 if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
246                         psd->ringbuffer_head = 0;
247                 else
248                         psd->ringbuffer_head++;
249
250                 /* snip any re-used tail so we keep to the ring length */
251
252                 if (psd->ringbuffer_tail == psd->ringbuffer_head) {
253                         if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
254                                 psd->ringbuffer_tail = 0;
255                         else
256                                 psd->ringbuffer_tail++;
257                 }
258
259                 global_tx_count++;
260
261                 if (use_mirror)
262                         n = lws_write(wsi,
263                                 &pingbuf[LWS_PRE],
264                                         size, write_options | LWS_WRITE_BINARY);
265                 else
266                         n = lws_write(wsi,
267                                 &pingbuf[LWS_PRE],
268                                         size, write_options | LWS_WRITE_PING);
269
270                 if (n < 0)
271                         return -1;
272                 if (n < (int)size) {
273                         lwsl_err("Partial write\n");
274                         return -1;
275                 }
276
277                 if (flood &&
278                          (psd->ping_index - psd->rx_count) < (screen_width - 1))
279                         fprintf(stderr, ".");
280                 break;
281
282         default:
283                 break;
284         }
285
286         return 0;
287 }
288
289
290 /* list of supported protocols and callbacks */
291
292 static struct lws_protocols protocols[] = {
293
294         {
295                 "lws-mirror-protocol",
296                 callback_lws_mirror,
297                 sizeof (struct per_session_data__ping),
298         },
299         {
300                 NULL, NULL, 0/* end of list */
301         }
302 };
303
304 static const struct lws_extension exts[] = {
305         {
306                 "permessage-deflate",
307                 lws_extension_callback_pm_deflate,
308                 "permessage-deflate; client_no_context_takeover; client_max_window_bits"
309         },
310         {
311                 "deflate-frame",
312                 lws_extension_callback_pm_deflate,
313                 "deflate_frame"
314         },
315         { NULL, NULL, NULL /* terminator */ }
316 };
317
318 static struct option options[] = {
319         { "help",       no_argument,            NULL, 'h' },
320         { "debug",      required_argument,      NULL, 'd' },
321         { "port",       required_argument,      NULL, 'p' },
322         { "ssl",        no_argument,            NULL, 't' },
323         { "interval",   required_argument,      NULL, 'i' },
324         { "size",       required_argument,      NULL, 's' },
325         { "protocol",   required_argument,      NULL, 'n' },
326         { "flood",      no_argument,            NULL, 'f' },
327         { "mirror",     no_argument,            NULL, 'm' },
328         { "replicate",  required_argument,      NULL, 'r' },
329         { "killmask",   no_argument,            NULL, 'k' },
330         { "version",    required_argument,      NULL, 'v' },
331         { NULL, 0, 0, 0 }
332 };
333
334 #ifndef _WIN32
335 static void
336 signal_handler(int sig, siginfo_t *si, void *v)
337 {
338         struct timeval tv;
339
340         gettimeofday(&tv, NULL);
341         interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec;
342 }
343 #endif
344
345 int main(int argc, char **argv)
346 {
347         int n = 0;
348         int port = 7681;
349         int use_ssl = 0;
350         struct lws_context *context;
351         char protocol_name[256], ads_port[300];
352         char ip[30];
353 #ifndef _WIN32
354         struct sigaction sa;
355         struct winsize w;
356 #endif
357         struct timeval tv;
358         unsigned long oldus = 0;
359         unsigned long l;
360         int ietf_version = -1;
361         struct lws_context_creation_info info;
362         struct lws_client_connect_info i;
363
364         memset(&info, 0, sizeof info);
365
366         if (argc < 2)
367                 goto usage;
368
369         while (n >= 0) {
370                 n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:d:", options, NULL);
371                 if (n < 0)
372                         continue;
373                 switch (n) {
374                 case 'd':
375                         lws_set_log_level(atoi(optarg), NULL);
376                         break;
377                 case 'm':
378                         use_mirror = 1;
379                         break;
380                 case 't':
381                         use_ssl = 2; /* 2 = allow selfsigned */
382                         break;
383                 case 'p':
384                         port = atoi(optarg);
385                         break;
386                 case 'n':
387                         strncpy(protocol_name, optarg, sizeof protocol_name);
388                         protocol_name[(sizeof protocol_name) - 1] = '\0';
389                         protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
390                         break;
391                 case 'i':
392                         interval_us = (unsigned int)(1000000.0 * atof(optarg));
393                         break;
394                 case 's':
395                         size = atoi(optarg);
396                         break;
397                 case 'f':
398                         flood = 1;
399                         break;
400                 case 'r':
401                         clients = atoi(optarg);
402                         if (clients > MAX_PING_CLIENTS || clients < 1) {
403                                 fprintf(stderr, "Max clients supported = %d\n",
404                                                               MAX_PING_CLIENTS);
405                                 return 1;
406                         }
407                         break;
408                 case 'k':
409                         write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
410                         break;
411                 case 'v':
412                         ietf_version = atoi(optarg);
413                         break;
414
415                 case 'h':
416                         goto usage;
417                 }
418         }
419
420         if (!use_mirror) {
421                 if (size > MAX_PING_PAYLOAD) {
422                         fprintf(stderr, "Max ping opcode payload size %d\n",
423                                                               MAX_PING_PAYLOAD);
424                         return 1;
425                 }
426         } else {
427                 if (size > MAX_MIRROR_PAYLOAD) {
428                         fprintf(stderr, "Max mirror payload size %d\n",
429                                                             MAX_MIRROR_PAYLOAD);
430                         return 1;
431                 }
432         }
433
434 #ifndef _WIN32
435         if (isatty(STDOUT_FILENO))
436                 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
437                         if (w.ws_col > 0)
438                                 screen_width = w.ws_col;
439 #endif
440
441         info.port = CONTEXT_PORT_NO_LISTEN;
442         info.protocols = protocols;
443         info.extensions = exts;
444
445         info.gid = -1;
446         info.uid = -1;
447
448         if (use_ssl)
449                 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
450
451         context = lws_create_context(&info);
452         if (context == NULL) {
453                 fprintf(stderr, "Creating libwebsocket context failed\n");
454                 return 1;
455         }
456
457         /* create client websockets using dumb increment protocol */
458
459         address = argv[optind];
460         lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
461                  address, port & 65535);
462         lwsl_notice("Connecting to %s...\n", ads_port);
463         memset(&i, 0, sizeof(i));
464         i.context = context;
465         i.address = address;
466         i.port = port;
467         i.ssl_connection = use_ssl;
468         i.path = "/";
469         i.host = ads_port;
470         i.origin = ads_port;
471         i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
472         i.ietf_version_or_minus_one = ietf_version;
473
474         for (n = 0; n < clients; n++) {
475                 ping_wsi[n] = lws_client_connect_via_info(&i);
476                 if (ping_wsi[n] == NULL) {
477                         lwsl_err("client %d failed to connect\n", n);
478                         return 1;
479                 }
480         }
481
482         lws_get_peer_addresses(ping_wsi[0], lws_get_socket_fd(ping_wsi[0]),
483                                     peer_name, sizeof peer_name, ip, sizeof ip);
484
485         lwsl_notice("libwebsockets test server ping - license LGPL2.1+SLE\n");
486         lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
487         fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
488                                                            peer_name, ip, size);
489
490 #ifndef _WIN32
491         /* set the ^C handler */
492         sa.sa_sigaction = signal_handler;
493         sa.sa_flags = SA_SIGINFO;
494         sigemptyset(&sa.sa_mask);
495         sigaction(SIGINT, &sa, NULL);
496 #endif
497
498         gettimeofday(&tv, NULL);
499         started = (tv.tv_sec * 1000000) + tv.tv_usec;
500
501         /* service loop */
502
503         n = 0;
504         while (n >= 0) {
505
506                 gettimeofday(&tv, NULL);
507                 l = (tv.tv_sec * 1000000) + tv.tv_usec;
508
509                 /* servers can hang up on us */
510
511                 if (clients == 0) {
512                         n = -1;
513                         continue;
514                 }
515
516                 if (!interrupted_time) {
517                         if ((l - oldus) > interval_us) {
518                                 for (n = 0; n < clients; n++)
519                                         lws_callback_on_writable(ping_wsi[n]);
520                                 oldus = l;
521                         }
522                 } else
523
524                         /* allow time for in-flight pongs to come */
525
526                         if ((l - interrupted_time) > 250000) {
527                                 n = -1;
528                                 continue;
529                         }
530
531                 if (!interval_us)
532                         n = lws_service(context, 0);
533                 else
534                         n = lws_service(context, 1);
535         }
536
537         /* stats */
538
539         fprintf(stderr, "\n--- %s websocket ping statistics "
540                 "using %d connections ---\n"
541                 "%lu packets transmitted, %lu received, "
542                 "%lu%% packet loss, time %ldms\n"
543                 "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n"
544                 "payload bandwidth average %0.3f KiBytes/sec\n",
545                 peer_name, clients, global_tx_count, global_rx_count,
546                 ((global_tx_count - global_rx_count) * 100) / global_tx_count,
547                 (l - started) / 1000,
548                 ((double)rtt_min) / 1000.0,
549                 ((double)rtt_avg / global_rx_count) / 1000.0,
550                 ((double)rtt_max) / 1000.0,
551                 ((double)global_rx_count * (double)size) /
552                                   ((double)(l - started) / 1000000.0) / 1024.0);
553
554         lws_context_destroy(context);
555
556         return 0;
557
558 usage:
559         fprintf(stderr, "Usage: libwebsockets-test-ping "
560                                              "<server address> [--port=<p>] "
561                                              "[--ssl] [--interval=<float sec>] "
562                                              "[--size=<bytes>] "
563                                              "[--protocol=<protocolname>] "
564                                              "[--mirror] "
565                                              "[--replicate=clients>] "
566                                              "[--version <version>] "
567                                              "[-d <log bitfield> ]"
568                                              "\n");
569         return 1;
570 }