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