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