test-server-v2.0: disable setting default protocol
[platform/upstream/libwebsockets.git] / test-server / test-echo.c
1 /*
2  * libwebsockets-test-echo
3  *
4  * Copyright (C) 2010-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 <assert.h>
26 #include <signal.h>
27
28 #include "../lib/libwebsockets.h"
29
30 #ifndef _WIN32
31 #include <syslog.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34 #else
35 #include "gettimeofday.h"
36 #include <process.h>
37 #endif
38
39 static volatile int force_exit = 0;
40 static int versa, state;
41 static int times = -1;
42
43 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
44
45 #define MAX_ECHO_PAYLOAD 1024
46
47 struct per_session_data__echo {
48         size_t rx, tx;
49         unsigned char buf[LWS_PRE + MAX_ECHO_PAYLOAD];
50         unsigned int len;
51         unsigned int index;
52         int final;
53         int continuation;
54         int binary;
55 };
56
57 static int
58 callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user,
59               void *in, size_t len)
60 {
61         struct per_session_data__echo *pss =
62                         (struct per_session_data__echo *)user;
63         int n;
64
65         switch (reason) {
66
67 #ifndef LWS_NO_SERVER
68
69         case LWS_CALLBACK_ESTABLISHED:
70                 pss->index = 0;
71                 pss->len = -1;
72                 break;
73
74         case LWS_CALLBACK_SERVER_WRITEABLE:
75 do_tx:
76
77                 n = LWS_WRITE_CONTINUATION;
78                 if (!pss->continuation) {
79                         if (pss->binary)
80                                 n = LWS_WRITE_BINARY;
81                         else
82                                 n = LWS_WRITE_TEXT;
83                         pss->continuation = 1;
84                 }
85                 if (!pss->final)
86                         n |= LWS_WRITE_NO_FIN;
87                 lwsl_info("+++ test-echo: writing %d, with final %d\n",
88                           pss->len, pss->final);
89
90                 pss->tx += pss->len;
91                 n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, n);
92                 if (n < 0) {
93                         lwsl_err("ERROR %d writing to socket, hanging up\n", n);
94                         return 1;
95                 }
96                 if (n < (int)pss->len) {
97                         lwsl_err("Partial write\n");
98                         return -1;
99                 }
100                 pss->len = -1;
101                 if (pss->final)
102                         pss->continuation = 0;
103                 lws_rx_flow_control(wsi, 1);
104                 break;
105
106         case LWS_CALLBACK_RECEIVE:
107 do_rx:
108                 pss->final = lws_is_final_fragment(wsi);
109                 pss->binary = lws_frame_is_binary(wsi);
110                 lwsl_info("+++ test-echo: RX len %ld final %ld, pss->len=%ld\n",
111                           (long)len, (long)pss->final, (long)pss->len);
112
113                 memcpy(&pss->buf[LWS_PRE], in, len);
114                 assert((int)pss->len == -1);
115                 pss->len = (unsigned int)len;
116                 pss->rx += len;
117
118                 lws_rx_flow_control(wsi, 0);
119                 lws_callback_on_writable(wsi);
120                 break;
121 #endif
122
123 #ifndef LWS_NO_CLIENT
124         /* when the callback is used for client operations --> */
125
126         case LWS_CALLBACK_CLOSED:
127         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
128                 lwsl_debug("closed\n");
129                 state = 0;
130                 break;
131
132         case LWS_CALLBACK_CLIENT_ESTABLISHED:
133                 lwsl_debug("Client has connected\n");
134                 pss->index = 0;
135                 pss->len = -1;
136                 state = 2;
137                 break;
138
139         case LWS_CALLBACK_CLIENT_RECEIVE:
140 #ifndef LWS_NO_SERVER
141                 if (versa)
142                         goto do_rx;
143 #endif
144                 lwsl_notice("Client RX: %s", (char *)in);
145                 break;
146
147         case LWS_CALLBACK_CLIENT_WRITEABLE:
148 #ifndef LWS_NO_SERVER
149                 if (versa) {
150                         if (pss->len != (unsigned int)-1)
151                                 goto do_tx;
152                         break;
153                 }
154 #endif
155                 /* we will send our packet... */
156                 pss->len = sprintf((char *)&pss->buf[LWS_PRE],
157                                    "hello from libwebsockets-test-echo client pid %d index %d\n",
158                                    getpid(), pss->index++);
159                 lwsl_notice("Client TX: %s", &pss->buf[LWS_PRE]);
160                 n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, LWS_WRITE_TEXT);
161                 if (n < 0) {
162                         lwsl_err("ERROR %d writing to socket, hanging up\n", n);
163                         return -1;
164                 }
165                 if (n < (int)pss->len) {
166                         lwsl_err("Partial write\n");
167                         return -1;
168                 }
169                 break;
170 #endif
171         case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
172                 /* reject everything else except permessage-deflate */
173                 if (strcmp(in, "permessage-deflate"))
174                         return 1;
175                 break;
176
177         default:
178                 break;
179         }
180
181         return 0;
182 }
183
184
185
186 static struct lws_protocols protocols[] = {
187         /* first protocol must always be HTTP handler */
188
189         {
190                 "",             /* name - can be overridden with -e */
191                 callback_echo,
192                 sizeof(struct per_session_data__echo),  /* per_session_data_size */
193                 MAX_ECHO_PAYLOAD,
194         },
195         {
196                 NULL, NULL, 0           /* End of list */
197         }
198 };
199
200 static const struct lws_extension exts[] = {
201         {
202                 "permessage-deflate",
203                 lws_extension_callback_pm_deflate,
204                 "permessage-deflate; client_no_context_takeover; client_max_window_bits"
205         },
206         {
207                 "deflate-frame",
208                 lws_extension_callback_pm_deflate,
209                 "deflate_frame"
210         },
211         { NULL, NULL, NULL /* terminator */ }
212 };
213
214
215 void sighandler(int sig)
216 {
217         force_exit = 1;
218 }
219
220 static struct option options[] = {
221         { "help",       no_argument,            NULL, 'h' },
222         { "debug",      required_argument,      NULL, 'd' },
223         { "port",       required_argument,      NULL, 'p' },
224         { "ssl-cert",   required_argument,      NULL, 'C' },
225         { "ssl-key",    required_argument,      NULL, 'k' },
226 #ifndef LWS_NO_CLIENT
227         { "client",     required_argument,      NULL, 'c' },
228         { "ratems",     required_argument,      NULL, 'r' },
229 #endif
230         { "ssl",        no_argument,            NULL, 's' },
231         { "versa",      no_argument,            NULL, 'v' },
232         { "uri",        required_argument,      NULL, 'u' },
233         { "passphrase", required_argument,      NULL, 'P' },
234         { "interface",  required_argument,      NULL, 'i' },
235         { "times",      required_argument,      NULL, 'n' },
236         { "echogen",    no_argument,            NULL, 'e' },
237 #ifndef LWS_NO_DAEMONIZE
238         { "daemonize",  no_argument,            NULL, 'D' },
239 #endif
240         { NULL, 0, 0, 0 }
241 };
242
243 int main(int argc, char **argv)
244 {
245         int n = 0;
246         int port = 7681;
247         int use_ssl = 0;
248         struct lws_context *context;
249         int opts = 0;
250         char interface_name[128] = "";
251         const char *_interface = NULL;
252         char ssl_cert[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
253         char ssl_key[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
254 #ifndef _WIN32
255 /* LOG_PERROR is not POSIX standard, and may not be portable */
256 #ifdef __sun
257         int syslog_options = LOG_PID;
258 #else
259         int syslog_options = LOG_PID | LOG_PERROR;
260 #endif
261 #endif
262         int client = 0;
263         int listen_port = 80;
264         struct lws_context_creation_info info;
265         char passphrase[256];
266         char uri[256] = "/";
267 #ifndef LWS_NO_CLIENT
268         char address[256], ads_port[256 + 30];
269         int rate_us = 250000;
270         unsigned long long oldus;
271         struct lws *wsi;
272         int disallow_selfsigned = 0;
273         struct timeval tv;
274         const char *connect_protocol = NULL;
275         struct lws_client_connect_info i;
276 #endif
277
278         int debug_level = 7;
279 #ifndef LWS_NO_DAEMONIZE
280         int daemonize = 0;
281 #endif
282
283         memset(&info, 0, sizeof info);
284
285 #ifndef LWS_NO_CLIENT
286         lwsl_notice("Built to support client operations\n");
287 #endif
288 #ifndef LWS_NO_SERVER
289         lwsl_notice("Built to support server operations\n");
290 #endif
291
292         while (n >= 0) {
293                 n = getopt_long(argc, argv, "i:hsp:d:DC:k:P:vu:n:e"
294 #ifndef LWS_NO_CLIENT
295                         "c:r:"
296 #endif
297                                 , options, NULL);
298                 if (n < 0)
299                         continue;
300                 switch (n) {
301                 case 'P':
302                         strncpy(passphrase, optarg, sizeof(passphrase));
303                         passphrase[sizeof(passphrase) - 1] = '\0';
304                         info.ssl_private_key_password = passphrase;
305                         break;
306                 case 'C':
307                         strncpy(ssl_cert, optarg, sizeof(ssl_cert));
308                         ssl_cert[sizeof(ssl_cert) - 1] = '\0';
309                         disallow_selfsigned = 1;
310                         break;
311                 case 'k':
312                         strncpy(ssl_key, optarg, sizeof(ssl_key));
313                         ssl_key[sizeof(ssl_key) - 1] = '\0';
314                         break;
315                 case 'u':
316                         strncpy(uri, optarg, sizeof(uri));
317                         uri[sizeof(uri) - 1] = '\0';
318                         break;
319
320 #ifndef LWS_NO_DAEMONIZE
321                 case 'D':
322                         daemonize = 1;
323 #if !defined(_WIN32) && !defined(__sun)
324                         syslog_options &= ~LOG_PERROR;
325 #endif
326                         break;
327 #endif
328 #ifndef LWS_NO_CLIENT
329                 case 'c':
330                         client = 1;
331                         strncpy(address, optarg, sizeof(address) - 1);
332                         address[sizeof(address) - 1] = '\0';
333                         port = 80;
334                         break;
335                 case 'r':
336                         rate_us = atoi(optarg) * 1000;
337                         break;
338 #endif
339                 case 'd':
340                         debug_level = atoi(optarg);
341                         break;
342                 case 's':
343                         use_ssl = 1; /* 1 = take care about cert verification, 2 = allow anything */
344                         break;
345                 case 'p':
346                         port = atoi(optarg);
347                         break;
348                 case 'v':
349                         versa = 1;
350                         break;
351                 case 'e':
352                         protocols[0].name = "lws-echogen";
353                         connect_protocol = protocols[0].name;
354                         lwsl_err("using lws-echogen\n");
355                         break;
356                 case 'i':
357                         strncpy(interface_name, optarg, sizeof interface_name);
358                         interface_name[(sizeof interface_name) - 1] = '\0';
359                         _interface = interface_name;
360                         break;
361                 case 'n':
362                         times = atoi(optarg);
363                         break;
364                 case '?':
365                 case 'h':
366                         fprintf(stderr, "Usage: libwebsockets-test-echo\n"
367                                 "  --debug      / -d <debug bitfield>\n"
368                                 "  --port       / -p <port>\n"
369                                 "  --ssl-cert   / -C <cert path>\n"
370                                 "  --ssl-key    / -k <key path>\n"
371 #ifndef LWS_NO_CLIENT
372                                 "  --client     / -c <server IP>\n"
373                                 "  --ratems     / -r <rate in ms>\n"
374 #endif
375                                 "  --ssl        / -s\n"
376                                 "  --passphrase / -P <passphrase>\n"
377                                 "  --interface  / -i <interface>\n"
378                                 "  --uri        / -u <uri path>\n"
379                                 "  --times      / -n <-1 unlimited or times to echo>\n"
380 #ifndef LWS_NO_DAEMONIZE
381                                 "  --daemonize  / -D\n"
382 #endif
383                         );
384                         exit(1);
385                 }
386         }
387
388 #ifndef LWS_NO_DAEMONIZE
389         /*
390          * normally lock path would be /var/lock/lwsts or similar, to
391          * simplify getting started without having to take care about
392          * permissions or running as root, set to /tmp/.lwsts-lock
393          */
394 #if defined(WIN32) || defined(_WIN32)
395 #else
396         if (!client && daemonize && lws_daemonize("/tmp/.lwstecho-lock")) {
397                 fprintf(stderr, "Failed to daemonize\n");
398                 return 1;
399         }
400 #endif
401 #endif
402
403 #ifndef _WIN32
404         /* we will only try to log things according to our debug_level */
405         setlogmask(LOG_UPTO (LOG_DEBUG));
406         openlog("lwsts", syslog_options, LOG_DAEMON);
407 #endif
408
409         /* tell the library what debug level to emit and to send it to syslog */
410         lws_set_log_level(debug_level, lwsl_emit_syslog);
411
412         lwsl_notice("libwebsockets test server echo - license LGPL2.1+SLE\n");
413         lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
414
415 #ifndef LWS_NO_CLIENT
416         if (client) {
417                 lwsl_notice("Running in client mode\n");
418                 listen_port = CONTEXT_PORT_NO_LISTEN;
419                 if (use_ssl && !disallow_selfsigned) {
420                         lwsl_info("allowing selfsigned\n");
421                         use_ssl = 2;
422                 } else {
423                         lwsl_info("requiring server cert validation against %s\n",
424                                   ssl_cert);
425                         info.ssl_ca_filepath = ssl_cert;
426                 }
427         } else {
428 #endif
429 #ifndef LWS_NO_SERVER
430                 lwsl_notice("Running in server mode\n");
431                 listen_port = port;
432 #endif
433 #ifndef LWS_NO_CLIENT
434         }
435 #endif
436
437         info.port = listen_port;
438         info.iface = _interface;
439         info.protocols = protocols;
440         if (use_ssl && !client) {
441                 info.ssl_cert_filepath = ssl_cert;
442                 info.ssl_private_key_filepath = ssl_key;
443         } else
444                 if (use_ssl && client) {
445                         info.ssl_cert_filepath = NULL;
446                         info.ssl_private_key_filepath = NULL;
447                 }
448         info.gid = -1;
449         info.uid = -1;
450         info.extensions = exts;
451         info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
452
453         if (use_ssl)
454                 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
455 #ifndef LWS_NO_EXTENSIONS
456         info.extensions = exts;
457 #endif
458
459         context = lws_create_context(&info);
460         if (context == NULL) {
461                 lwsl_err("libwebsocket init failed\n");
462                 return -1;
463         }
464
465
466         signal(SIGINT, sighandler);
467
468 #ifndef LWS_NO_CLIENT
469         gettimeofday(&tv, NULL);
470         oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
471 #endif
472
473         n = 0;
474         while (n >= 0 && !force_exit) {
475 #ifndef LWS_NO_CLIENT
476                 if (client && !state && times) {
477                         state = 1;
478                         lwsl_notice("Client connecting to %s:%u....\n",
479                                     address, port);
480                         /* we are in client mode */
481
482                         address[sizeof(address) - 1] = '\0';
483                         sprintf(ads_port, "%s:%u", address, port & 65535);
484                         if (times > 0)
485                                 times--;
486
487                         memset(&i, 0, sizeof(i));
488
489                         i.context = context;
490                         i.address = address;
491                         i.port = port;
492                         i.ssl_connection = use_ssl;
493                         i.path = uri;
494                         i.host = ads_port;
495                         i.origin = ads_port;
496                         i.protocol = connect_protocol;
497
498                         wsi = lws_client_connect_via_info(&i);
499                         if (!wsi) {
500                                 lwsl_err("Client failed to connect to %s:%u\n",
501                                          address, port);
502                                 goto bail;
503                         }
504                 }
505
506                 if (client && !versa && times) {
507                         gettimeofday(&tv, NULL);
508
509                         if (((((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec) - oldus) > rate_us) {
510                                 lws_callback_on_writable_all_protocol(context,
511                                                 &protocols[0]);
512                                 oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
513                                 if (times > 0)
514                                         times--;
515                         }
516                 }
517
518                 if (client && !state && !times)
519                         break;
520 #endif
521                 n = lws_service(context, 10);
522         }
523 #ifndef LWS_NO_CLIENT
524 bail:
525 #endif
526         lws_context_destroy(context);
527
528         lwsl_notice("libwebsockets-test-echo exited cleanly\n");
529 #ifndef _WIN32
530         closelog();
531 #endif
532
533         return 0;
534 }