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