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