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