2 * libwebsockets-test-client - libwebsockets test implementation
4 * Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
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.
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
21 #include "lws_config.h"
31 #include "gettimeofday.h"
38 #include "../lib/libwebsockets.h"
40 static int deny_deflate, deny_mux, longlived, mirror_lifetime;
41 static struct lws *wsi_dumb, *wsi_mirror;
42 static volatile int force_exit;
43 static unsigned int opts;
44 #if defined(LWS_USE_POLARSSL)
46 #if defined(LWS_USE_MBEDTLS)
48 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
49 char crl_path[1024] = "";
55 * This demo shows how to connect multiple websockets simultaneously to a
56 * websocket server (there is no restriction on their having to be the same
57 * server just it simplifies the demo).
59 * dumb-increment-protocol: we connect to the server and print the number
62 * lws-mirror-protocol: draws random circles, which are mirrored on to every
63 * client (see them being drawn in every browser
64 * session also using the test server)
69 PROTOCOL_DUMB_INCREMENT,
78 * dumb_increment protocol
80 * since this also happens to be protocols[0], some callbacks that are not
81 * bound to a specific protocol also turn up here.
85 callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
86 void *user, void *in, size_t len)
90 case LWS_CALLBACK_CLIENT_ESTABLISHED:
91 lwsl_info("dumb: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
94 case LWS_CALLBACK_CLOSED:
95 lwsl_notice("dumb: LWS_CALLBACK_CLOSED\n");
99 case LWS_CALLBACK_CLIENT_RECEIVE:
100 ((char *)in)[len] = '\0';
101 lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
104 /* because we are protocols[0] ... */
106 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
107 if (wsi == wsi_dumb) {
108 lwsl_err("dumb: LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
111 if (wsi == wsi_mirror) {
112 lwsl_err("mirror: LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
117 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
118 if ((strcmp(in, "deflate-stream") == 0) && deny_deflate) {
119 lwsl_notice("denied deflate-stream extension\n");
122 if ((strcmp(in, "x-webkit-deflate-frame") == 0))
124 if ((strcmp(in, "deflate-frame") == 0))
128 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
130 char buffer[1024 + LWS_PRE];
131 char *px = buffer + LWS_PRE;
132 int lenx = sizeof(buffer) - LWS_PRE;
134 lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP\n");
137 * Often you need to flow control this by something
138 * else being writable. In that case call the api
139 * to get a callback when writable here, and do the
140 * pending client read in the writeable callback of
143 if (lws_http_client_read(wsi, &px, &lenx) < 0)
150 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
155 #if defined(LWS_USE_POLARSSL)
157 #if defined(LWS_USE_MBEDTLS)
159 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
160 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
162 /* Enable CRL checking of the server certificate */
163 X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
164 X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
165 SSL_CTX_set1_param((SSL_CTX*)user, param);
166 X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX*)user);
167 X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
168 int n = X509_load_cert_crl_file(lookup, crl_path, X509_FILETYPE_PEM);
169 X509_VERIFY_PARAM_free(param);
173 lwsl_err("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: SSL error: %s (%d)\n", ERR_error_string(n, errbuf), n);
190 /* lws-mirror_protocol */
194 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
195 void *user, void *in, size_t len)
197 unsigned char buf[LWS_PRE + 4096];
198 unsigned int rands[4];
203 case LWS_CALLBACK_CLIENT_ESTABLISHED:
205 lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
207 lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
208 mirror_lifetime = 16384 + (rands[0] & 65535);
209 /* useful to test single connection stability */
211 mirror_lifetime += 500000;
213 lwsl_info("opened mirror connection with "
214 "%d lifetime\n", mirror_lifetime);
217 * mirror_lifetime is decremented each send, when it reaches
218 * zero the connection is closed in the send callback.
219 * When the close callback comes, wsi_mirror is set to NULL
220 * so a new connection will be opened
222 * start the ball rolling,
223 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
225 lws_callback_on_writable(wsi);
228 case LWS_CALLBACK_CLOSED:
229 lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime);
233 case LWS_CALLBACK_CLIENT_WRITEABLE:
234 for (n = 0; n < 1; n++) {
235 lws_get_random(lws_get_context(wsi), rands, sizeof(rands));
236 l += sprintf((char *)&buf[LWS_PRE + l],
238 rands[0] & 0xffffff, /* colour */
239 rands[1] & 511, /* x */
240 rands[2] & 255, /* y */
241 (rands[3] & 31) + 1); /* radius */
244 n = lws_write(wsi, &buf[LWS_PRE], l,
245 opts | LWS_WRITE_TEXT);
249 lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
254 if (!mirror_lifetime) {
255 lwsl_info("closing mirror session\n");
258 /* get notified as soon as we can write again */
259 lws_callback_on_writable(wsi);
270 /* list of supported protocols and callbacks */
272 static struct lws_protocols protocols[] = {
274 "dumb-increment-protocol,fake-nonexistant-protocol",
275 callback_dumb_increment,
280 "fake-nonexistant-protocol,lws-mirror-protocol",
285 { NULL, NULL, 0, 0 } /* end */
288 static const struct lws_extension exts[] = {
290 "permessage-deflate",
291 lws_extension_callback_pm_deflate,
292 "permessage-deflate; client_max_window_bits"
296 lws_extension_callback_pm_deflate,
299 { NULL, NULL, NULL /* terminator */ }
304 void sighandler(int sig)
309 static struct option options[] = {
310 { "help", no_argument, NULL, 'h' },
311 { "debug", required_argument, NULL, 'd' },
312 { "port", required_argument, NULL, 'p' },
313 { "ssl", no_argument, NULL, 's' },
314 { "version", required_argument, NULL, 'v' },
315 { "undeflated", no_argument, NULL, 'u' },
316 { "nomux", no_argument, NULL, 'n' },
317 { "longlived", no_argument, NULL, 'l' },
318 { "ssl-cert", required_argument, NULL, 'C' },
319 { "ssl-key", required_argument, NULL, 'K' },
320 { "ssl-ca", required_argument, NULL, 'A' },
321 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
322 { "ssl-crl", required_argument, NULL, 'R' },
327 static int ratelimit_connects(unsigned int *last, unsigned int secs)
331 gettimeofday(&tv, NULL);
332 if (tv.tv_sec - (*last) < secs)
340 int main(int argc, char **argv)
342 int n = 0, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
343 unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1;
344 struct lws_context_creation_info info;
345 struct lws_client_connect_info i;
346 struct lws_context *context;
347 const char *prot, *p;
349 char cert_path[1024] = "";
350 char key_path[1024] = "";
351 char ca_path[1024] = "";
353 memset(&info, 0, sizeof info);
355 lwsl_notice("libwebsockets test client - license LGPL2.1+SLE\n");
356 lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
362 n = getopt_long(argc, argv, "nuv:hsp:d:lC:K:A:", options, NULL);
367 lws_set_log_level(atoi(optarg), NULL);
370 use_ssl = 2; /* 2 = allow selfsigned */
379 ietf_version = atoi(optarg);
388 strncpy(cert_path, optarg, sizeof(cert_path) - 1);
389 cert_path[sizeof(cert_path) - 1] = '\0';
392 strncpy(key_path, optarg, sizeof(key_path) - 1);
393 key_path[sizeof(key_path) - 1] = '\0';
396 strncpy(ca_path, optarg, sizeof(ca_path) - 1);
397 ca_path[sizeof(ca_path) - 1] = '\0';
399 #if defined(LWS_USE_POLARSSL)
401 #if defined(LWS_USE_MBEDTLS)
403 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
405 strncpy(crl_path, optarg, sizeof(crl_path) - 1);
406 crl_path[sizeof(crl_path) - 1] = '\0';
419 signal(SIGINT, sighandler);
421 memset(&i, 0, sizeof(i));
424 if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
427 /* add back the leading / on path */
429 strncpy(path + 1, p, sizeof(path) - 2);
430 path[sizeof(path) - 1] = '\0';
433 if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
435 if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
440 * create the websockets context. This tracks open connections and
441 * knows how to route any traffic and which protocol version to use,
442 * and if each connection is client or server side.
444 * For this client-only demo, we tell it to not listen on any port.
447 info.port = CONTEXT_PORT_NO_LISTEN;
448 info.protocols = protocols;
453 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
456 * If the server wants us to present a valid SSL client certificate
457 * then we can set it up here.
461 info.ssl_cert_filepath = cert_path;
463 info.ssl_private_key_filepath = key_path;
466 * A CA cert and CRL can be used to validate the cert send by the server
469 info.ssl_ca_filepath = ca_path;
470 #if defined(LWS_USE_POLARSSL)
472 #if defined(LWS_USE_MBEDTLS)
474 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
475 else if (crl_path[0])
476 lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
482 lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
484 lwsl_notice(" Selfsigned certs allowed\n");
487 context = lws_create_context(&info);
488 if (context == NULL) {
489 fprintf(stderr, "Creating libwebsocket context failed\n");
494 i.ssl_connection = use_ssl;
496 i.origin = i.address;
497 i.ietf_version_or_minus_one = ietf_version;
498 i.client_exts = exts;
500 if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
501 lwsl_notice("using %s mode (non-ws)\n", prot);
505 lwsl_notice("using %s mode (ws)\n", prot);
508 * sit there servicing the websocket context to handle incoming
509 * packets, and drawing random circles on the mirror protocol websocket
511 * nothing happens until the client websocket connection is
512 * asynchronously established... calling lws_client_connect() only
513 * instantiates the connection logically, lws_service() progresses it
517 while (!force_exit) {
520 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
521 lwsl_notice("dumb: connecting\n");
522 i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
523 wsi_dumb = lws_client_connect_via_info(&i);
526 if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
527 lwsl_notice("mirror: connecting\n");
528 i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
529 wsi_mirror = lws_client_connect_via_info(&i);
532 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
533 lwsl_notice("http: connecting\n");
534 wsi_dumb = lws_client_connect_via_info(&i);
537 lws_service(context, 500);
540 lwsl_err("Exiting\n");
541 lws_context_destroy(context);
546 fprintf(stderr, "Usage: libwebsockets-test-client "
547 "<server address> [--port=<p>] "
548 "[--ssl] [-k] [-v <ver>] "
549 "[-d <log bitfield>] [-l]\n");