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