libwebsockets-test-server-v2.0 showing how to use mounts and plugins

No dependency on test-server.h (just libwebsockets.h and getopt.h / syslog.h)

No need for any user callback code and all in one short file.

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2016-05-02 05:58:07 +08:00
parent 952fcdede1
commit 2f72f67a65
3 changed files with 456 additions and 0 deletions

View file

@ -1041,6 +1041,20 @@ if (NOT LWS_WITHOUT_TESTAPPS)
endif(WIN32)
endif()
#
# test-server-v2.0
#
if (LWS_WITH_PLUGINS)
create_test_app(
test-server-v2.0
"test-server/test-server-v2.0.c"
""
""
""
""
"")
endif()
# Data files for running the test server.
set(TEST_SERVER_DATA
"${PROJECT_SOURCE_DIR}/test-server/favicon.ico"

View file

@ -79,6 +79,21 @@ if you connect to the test server using a browser at the
same time you will be able to see the circles being drawn.
Choosing between test server variations
---------------------------------------
If you will be doing standalone serving with lws, ideally you should avoid
making your own server at all, and use lwsws with your own protocol plugins.
The second best option is follow test-server-v2.0.c, which uses a mount to
autoserve a directory, and lws protocol plugins for ws, without needing any
user callback code (other than what's needed in the protocol plugin).
For those two options libuv is needed to support the protocol plugins, if
that's not possible then the other variations with their own protocol code
should be considered.
Testing simple echo
-------------------

View file

@ -0,0 +1,427 @@
/*
* libwebsockets-test-server-v2.0 - libwebsockets test implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
#include <libwebsockets.h>
#include <getopt.h>
#include <syslog.h>
int close_testing;
int debug_level = 7;
volatile int force_exit = 0;
struct lws_context *context;
/* http server gets files from this path */
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
char *resource_path = LOCAL_RESOURCE_PATH;
#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
char crl_path[1024] = "";
#endif
#endif
#endif
/*
* This test server is ONLY this .c file, it's radically simpler than the
* pre-v2.0 test servers. For example it has no user callback content.
*
* To achieve that, it uses the LWS protocol plugins. Those in turn
* use libuv. So you must configure with LWS_WITH_PLUGINS (which implies
* libuv) to get this to build.
*
* You can find the individual protocol plugin sources in ../plugins
*/
/*
* the mount we attach later will autoserve everything we need, so we
* don't need to actually do anything in our http callback.
*/
int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
void *in, size_t len)
{
return 0;
}
/* list of supported protocols and callbacks */
static struct lws_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", /* name */
callback_http, /* callback */
0, /* per_session_data_size */
0, /* max frame size / rx buffer */
},
/*
* the other protocols are provided by lws plugins
*/
{ NULL, NULL, 0, 0 } /* terminator */
};
void sighandler(int sig)
{
force_exit = 1;
lws_cancel_service(context);
}
static const struct lws_extension exts[] = {
{
"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
},
{
"deflate-frame",
lws_extension_callback_pm_deflate,
"deflate_frame"
},
{ NULL, NULL, NULL /* terminator */ }
};
/*
* mount a filesystem directory into the URL space at /
* point it to our /usr/share directory with our assets in
* stuff from here is autoserved by the library
*/
static const struct lws_http_mount mount = {
NULL, /* linked-list pointer to next, but we only have one */
"/", /* mountpoint in URL namespace on this vhost */
LOCAL_RESOURCE_PATH, /* where to go on the filesystem for that */
"test.html", /* default filename if none given */
NULL,
0,
0,
0,
0,
0,
LWSMPRO_FILE, /* mount type is a directory in a filesystem */
1, /* strlen("/"), ie length of the mountpoint */
};
/*
* We must enable the plugin protocols we want into our vhost with a
* linked-list. We can also give the plugin per-vhost options here.
*/
static const struct lws_protocol_vhost_options pvo_2 = {
NULL,
NULL,
"lws-status",
"" /* ignored, just matches the protocol name above */
};
static const struct lws_protocol_vhost_options pvo_1 = {
&pvo_2,
NULL,
"lws-mirror-protocol",
""
};
static const struct lws_protocol_vhost_options pvo = {
&pvo_1,
NULL,
"dumb-increment-protocol",
""
};
static void signal_cb(uv_signal_t *watcher, int signum)
{
lwsl_err("Signal %d caught, exiting...\n", watcher->signum);
switch (watcher->signum) {
case SIGTERM:
case SIGINT:
break;
default:
signal(SIGABRT, SIG_DFL);
abort();
break;
}
lws_libuv_stop(context);
}
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", required_argument, NULL, 'd' },
{ "port", required_argument, NULL, 'p' },
{ "ssl", no_argument, NULL, 's' },
{ "allow-non-ssl", no_argument, NULL, 'a' },
{ "interface", required_argument, NULL, 'i' },
{ "closetest", no_argument, NULL, 'c' },
{ "ssl-cert", required_argument, NULL, 'C' },
{ "ssl-key", required_argument, NULL, 'K' },
{ "ssl-ca", required_argument, NULL, 'A' },
#if defined(LWS_OPENSSL_SUPPORT)
{ "ssl-verify-client", no_argument, NULL, 'v' },
#if defined(LWS_HAVE_SSL_CTX_set1_param)
{ "ssl-crl", required_argument, NULL, 'R' },
#endif
#endif
{ "libev", no_argument, NULL, 'e' },
#ifndef LWS_NO_DAEMONIZE
{ "daemonize", no_argument, NULL, 'D' },
#endif
{ "resource_path", required_argument, NULL, 'r' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
char interface_name[128] = "";
const char *iface = NULL;
char cert_path[1024] = "";
char key_path[1024] = "";
char ca_path[1024] = "";
int uid = -1, gid = -1;
int use_ssl = 0;
int opts = 0;
int n = 0;
#ifndef _WIN32
int syslog_options = LOG_PID | LOG_PERROR;
#endif
#ifndef LWS_NO_DAEMONIZE
int daemonize = 0;
#endif
/*
* take care to zero down the info struct, he contains random garbaage
* from the stack otherwise
*/
memset(&info, 0, sizeof info);
info.port = 7681;
while (n >= 0) {
n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:", options, NULL);
if (n < 0)
continue;
switch (n) {
case 'e':
opts |= LWS_SERVER_OPTION_LIBEV;
break;
#ifndef LWS_NO_DAEMONIZE
case 'D':
daemonize = 1;
#ifndef _WIN32
syslog_options &= ~LOG_PERROR;
#endif
break;
#endif
case 'u':
uid = atoi(optarg);
break;
case 'g':
gid = atoi(optarg);
break;
case 'd':
debug_level = atoi(optarg);
break;
case 's':
use_ssl = 1;
break;
case 'a':
opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
break;
case 'p':
info.port = atoi(optarg);
break;
case 'i':
strncpy(interface_name, optarg, sizeof interface_name);
interface_name[(sizeof interface_name) - 1] = '\0';
iface = interface_name;
break;
case 'c':
close_testing = 1;
fprintf(stderr, " Close testing mode -- closes on "
"client after 50 dumb increments"
"and suppresses lws_mirror spam\n");
break;
case 'r':
resource_path = optarg;
printf("Setting resource path to \"%s\"\n", resource_path);
break;
case 'C':
strncpy(cert_path, optarg, sizeof(cert_path) - 1);
cert_path[sizeof(cert_path) - 1] = '\0';
break;
case 'K':
strncpy(key_path, optarg, sizeof(key_path) - 1);
key_path[sizeof(key_path) - 1] = '\0';
break;
case 'A':
strncpy(ca_path, optarg, sizeof(ca_path) - 1);
ca_path[sizeof(ca_path) - 1] = '\0';
break;
#if defined(LWS_OPENSSL_SUPPORT)
case 'v':
use_ssl = 1;
opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
break;
#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
#if defined(LWS_HAVE_SSL_CTX_set1_param)
case 'R':
strncpy(crl_path, optarg, sizeof(crl_path) - 1);
crl_path[sizeof(crl_path) - 1] = '\0';
break;
#endif
#endif
#endif
#endif
case 'h':
fprintf(stderr, "Usage: test-server "
"[--port=<p>] [--ssl] "
"[-d <log bitfield>] "
"[--resource_path <path>]\n");
exit(1);
}
}
#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
/*
* normally lock path would be /var/lock/lwsts or similar, to
* simplify getting started without having to take care about
* permissions or running as root, set to /tmp/.lwsts-lock
*/
if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
fprintf(stderr, "Failed to daemonize\n");
return 10;
}
#endif
signal(SIGINT, sighandler);
#ifndef _WIN32
/* we will only try to log things according to our debug_level */
setlogmask(LOG_UPTO (LOG_DEBUG));
openlog("lwsts", syslog_options, LOG_DAEMON);
#endif
/* tell the library what debug level to emit and to send it to syslog */
lws_set_log_level(debug_level, lwsl_emit_syslog);
lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n");
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
printf("Using resource path \"%s\"\n", resource_path);
info.iface = iface;
info.protocols = protocols;
info.ssl_cert_filepath = NULL;
info.ssl_private_key_filepath = NULL;
if (use_ssl) {
if (strlen(resource_path) > sizeof(cert_path) - 32) {
lwsl_err("resource path too long\n");
return -1;
}
if (!cert_path[0])
sprintf(cert_path, "%s/libwebsockets-test-server.pem",
resource_path);
if (strlen(resource_path) > sizeof(key_path) - 32) {
lwsl_err("resource path too long\n");
return -1;
}
if (!key_path[0])
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
resource_path);
info.ssl_cert_filepath = cert_path;
info.ssl_private_key_filepath = key_path;
if (ca_path[0])
info.ssl_ca_filepath = ca_path;
}
info.gid = gid;
info.uid = uid;
info.max_http_header_pool = 16;
info.options = opts |
LWS_SERVER_OPTION_VALIDATE_UTF8 |
LWS_SERVER_OPTION_LIBUV; /* plugins require this */
info.extensions = exts;
info.timeout_secs = 5;
info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
/* tell lws to look for protocol plugins here */
info.plugins_dir = INSTALL_DATADIR"/libwebsockets-test-server/plugins/";
/* tell lws about our mount we want */
info.mounts = &mount;
/*
* give it our linked-list of Per-Vhost Options, these control
* which protocols (from plugins) are allowed to be enabled on
* our vhost
*/
info.pvo = &pvo;
if (use_ssl)
/* redirect guys coming on http */
info.options |=
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
/*
* As it is, this creates the context and a single Vhost at the same
* time. You can use LWS_SERVER_OPTION_EXPLICIT_VHOSTS option above
* to just create the context, and call lws_create_vhost() afterwards
* multiple times with different info to get multiple listening vhosts.
*/
context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
return -1;
}
/* libuv event loop */
lws_uv_sigint_cfg(context, 1, signal_cb);
if (lws_uv_initloop(context, NULL, 0))
lwsl_err("lws_uv_initloop failed\n");
else
lws_libuv_run(context, 0);
lws_context_destroy(context);
lwsl_notice("libwebsockets-test-server exited cleanly\n");
#ifndef _WIN32
closelog();
#endif
return 0;
}