/*
* libwebsockets-test-server - libwebsockets test implementation
*
- * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
*
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
+ * 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.
*
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
+ * 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.
*/
-#ifdef CMAKE_BUILD
-#include "lws_config.h"
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <assert.h>
-#ifdef WIN32
+#include "test-server.h"
+
+int close_testing;
+int max_poll_elements;
+int debug_level = 7;
#ifdef EXTERNAL_POLL
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #endif
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <stddef.h>
-
- #include "websock-w32.h"
+struct lws_pollfd *pollfds;
+int *fd_lookup;
+int count_pollfds;
#endif
+volatile int force_exit = 0;
+struct lws_context *context;
+struct lws_plat_file_ops fops_plat;
-#else // NOT WIN32
-#include <syslog.h>
+/* 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_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
+char crl_path[1024] = "";
#endif
-#include <signal.h>
+/*
+ * This demonstrates how to use the clean protocol service separation of
+ * plugins, but with static inclusion instead of runtime dynamic loading
+ * (which requires libuv).
+ *
+ * dumb-increment doesn't use the plugin, both to demonstrate how to
+ * do the protocols directly, and because it wants libuv for a timer.
+ *
+ * Please consider using test-server-v2.0.c instead of this: it has the
+ * same functionality but
+ *
+ * 1) uses lws built-in http handling so you don't need to deal with it in
+ * your callback
+ *
+ * 2) Links with libuv and uses the plugins at runtime
+ *
+ * 3) Uses advanced lws features like mounts to bind parts of the filesystem
+ * to the served URL space
+ *
+ * Another option is lwsws, this operates like test-server-v2,0.c but is
+ * configured using JSON, do you do not need to provide any code for the
+ * serving action at all, just implement your protocols in plugins.
+ */
-#include "../lib/libwebsockets.h"
+#define LWS_PLUGIN_STATIC
+#include "../plugins/protocol_lws_mirror.c"
+#include "../plugins/protocol_lws_status.c"
-static int close_testing;
-int max_poll_elements;
+/* singlethreaded version --> no locks */
-struct pollfd *pollfds;
-int *fd_lookup;
-int count_pollfds;
-int force_exit = 0;
+void test_server_lock(int care)
+{
+}
+void test_server_unlock(int care)
+{
+}
/*
* This demo server shows how to use libwebsockets for one or more
*
* lws-mirror-protocol: copies any received packet to every connection also
* using this protocol, including the sender
+ *
+ * lws-status: informs connected browsers of who else is
+ * connected.
*/
enum demo_protocols {
PROTOCOL_DUMB_INCREMENT,
PROTOCOL_LWS_MIRROR,
+ PROTOCOL_LWS_STATUS,
/* always last */
DEMO_PROTOCOL_COUNT
};
-
-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
-char *resource_path = LOCAL_RESOURCE_PATH;
-
-/*
- * We take a strict whitelist approach to stop ../ attacks
- */
-
-struct serveable {
- const char *urlpath;
- const char *mimetype;
-};
-
-static const struct serveable whitelist[] = {
- { "/favicon.ico", "image/x-icon" },
- { "/libwebsockets.org-logo.png", "image/png" },
-
- /* last one is the default served if no match */
- { "/test.html", "text/html" },
-};
-
-struct per_session_data__http {
- int fd;
-};
-
-/* this protocol server (always the first one) just knows how to do HTTP */
-
-static int callback_http(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason, void *user,
- void *in, size_t len)
-{
-#if 0
- char client_name[128];
- char client_ip[128];
-#endif
- char buf[256];
- int n, m;
- unsigned char *p;
- static unsigned char buffer[4096];
- struct stat stat_buf;
- struct per_session_data__http *pss = (struct per_session_data__http *)user;
-#ifdef EXTERNAL_POLL
- int fd = (int)(long)in;
-#endif
-
- switch (reason) {
- case LWS_CALLBACK_HTTP:
-
- /* check for the "send a big file by hand" example case */
-
- if (!strcmp((const char *)in, "/leaf.jpg")) {
- char leaf_path[1024];
- snprintf(leaf_path, sizeof(leaf_path), "%s/leaf.jpg", resource_path);
-
- /* well, let's demonstrate how to send the hard way */
-
- p = buffer;
-
-#ifdef WIN32
- pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
-#else
- pss->fd = open(leaf_path, O_RDONLY);
-#endif
-
- if (pss->fd < 0)
- return -1;
-
- fstat(pss->fd, &stat_buf);
-
- /*
- * we will send a big jpeg file, but it could be
- * anything. Set the Content-Type: appropriately
- * so the browser knows what to do with it.
- */
-
- p += sprintf((char *)p,
- "HTTP/1.0 200 OK\x0d\x0a"
- "Server: libwebsockets\x0d\x0a"
- "Content-Type: image/jpeg\x0d\x0a"
- "Content-Length: %u\x0d\x0a\x0d\x0a",
- (unsigned int)stat_buf.st_size);
-
- /*
- * send the http headers...
- * this won't block since it's the first payload sent
- * on the connection since it was established
- * (too small for partial)
- */
-
- n = libwebsocket_write(wsi, buffer,
- p - buffer, LWS_WRITE_HTTP);
-
- if (n < 0) {
- close(pss->fd);
- return -1;
- }
- /*
- * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
- */
- libwebsocket_callback_on_writable(context, wsi);
- break;
- }
-
- /* if not, send a file the easy way */
-
- for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
- if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
- break;
-
- sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
-
- if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
- return -1; /* through completion or error, close the socket */
-
- /*
- * notice that the sending of the file completes asynchronously,
- * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done
- */
-
- break;
-
- case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
- /* kill the connection after we sent one file */
- return -1;
-
- case LWS_CALLBACK_HTTP_WRITEABLE:
- /*
- * we can send more of whatever it is we were sending
- */
-
- do {
- n = read(pss->fd, buffer, sizeof buffer);
- /* problem reading, close conn */
- if (n < 0)
- goto bail;
- /* sent it all, close conn */
- if (n == 0)
- goto bail;
- /*
- * because it's HTTP and not websocket, don't need to take
- * care about pre and postamble
- */
- m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
- if (m < 0)
- /* write failed, close conn */
- goto bail;
- if (m != n)
- /* partial write, adjust */
- lseek(pss->fd, m - n, SEEK_CUR);
-
- } while (!lws_send_pipe_choked(wsi));
- libwebsocket_callback_on_writable(context, wsi);
- break;
-
-bail:
- close(pss->fd);
- return -1;
-
- /*
- * callback for confirming to continue with client IP appear in
- * protocol 0 callback since no websocket protocol has been agreed
- * yet. You can just ignore this if you won't filter on client IP
- * since the default uhandled callback return is 0 meaning let the
- * connection continue.
- */
-
- case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-#if 0
- libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
- sizeof(client_name), client_ip, sizeof(client_ip));
-
- fprintf(stderr, "Received network connect from %s (%s)\n",
- client_name, client_ip);
-#endif
- /* if we returned non-zero from here, we kill the connection */
- break;
-
-#ifdef EXTERNAL_POLL
- /*
- * callbacks for managing the external poll() array appear in
- * protocol 0 callback
- */
-
- case LWS_CALLBACK_ADD_POLL_FD:
-
- if (count_pollfds >= max_poll_elements) {
- lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
- return 1;
- }
-
- fd_lookup[fd] = count_pollfds;
- pollfds[count_pollfds].fd = fd;
- pollfds[count_pollfds].events = (int)(long)len;
- pollfds[count_pollfds++].revents = 0;
- break;
-
- case LWS_CALLBACK_DEL_POLL_FD:
- if (!--count_pollfds)
- break;
- m = fd_lookup[fd];
- /* have the last guy take up the vacant slot */
- pollfds[m] = pollfds[count_pollfds];
- fd_lookup[pollfds[count_pollfds].fd] = m;
- break;
-
- case LWS_CALLBACK_SET_MODE_POLL_FD:
- pollfds[fd_lookup[fd]].events |= (int)(long)len;
- break;
-
- case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
- pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
- break;
-#endif
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * this is just an example of parsing handshake headers, you don't need this
- * in your code unless you will filter allowing connections by the header
- * content
- */
-
-static void
-dump_handshake_info(struct libwebsocket *wsi)
-{
- int n;
- static const char *token_names[WSI_TOKEN_COUNT] = {
- /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
- /*[WSI_TOKEN_HOST] =*/ "Host",
- /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
- /*[WSI_TOKEN_KEY1] =*/ "key 1",
- /*[WSI_TOKEN_KEY2] =*/ "key 2",
- /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
- /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
- /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
- /*[WSI_TOKEN_DRAFT] =*/ "Draft",
- /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
-
- /* new for 04 */
- /*[WSI_TOKEN_KEY] =*/ "Key",
- /*[WSI_TOKEN_VERSION] =*/ "Version",
- /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
-
- /* new for 05 */
- /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
-
- /* client receives these */
- /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
- /*[WSI_TOKEN_NONCE] =*/ "Nonce",
- /*[WSI_TOKEN_HTTP] =*/ "Http",
- /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
- };
- char buf[256];
-
- for (n = 0; n < WSI_TOKEN_COUNT; n++) {
- if (!lws_hdr_total_length(wsi, n))
- continue;
-
- lws_hdr_copy(wsi, buf, sizeof buf, n);
-
- fprintf(stderr, " %s = %s\n", token_names[n], buf);
- }
-}
-
-/* dumb_increment protocol */
-
-/*
- * one of these is auto-created for each connection and a pointer to the
- * appropriate instance is passed to the callback in the user parameter
- *
- * for this example protocol we use it to individualize the count for each
- * connection.
- */
-
-struct per_session_data__dumb_increment {
- int number;
-};
-
-static int
-callback_dumb_increment(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- int n, m;
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
- LWS_SEND_BUFFER_POST_PADDING];
- unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
- struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
-
- switch (reason) {
-
- case LWS_CALLBACK_ESTABLISHED:
- lwsl_info("callback_dumb_increment: "
- "LWS_CALLBACK_ESTABLISHED\n");
- pss->number = 0;
- break;
-
- case LWS_CALLBACK_SERVER_WRITEABLE:
- n = sprintf((char *)p, "%d", pss->number++);
- m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
- if (m < n) {
- lwsl_err("ERROR %d writing to di socket\n", n);
- return -1;
- }
- if (close_testing && pss->number == 50) {
- lwsl_info("close tesing limit, closing\n");
- return -1;
- }
- break;
-
- case LWS_CALLBACK_RECEIVE:
-// fprintf(stderr, "rx %d\n", (int)len);
- if (len < 6)
- break;
- if (strcmp((const char *)in, "reset\n") == 0)
- pss->number = 0;
- break;
- /*
- * this just demonstrates how to use the protocol filter. If you won't
- * study and reject connections based on header content, you don't need
- * to handle this callback
- */
-
- case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info(wsi);
- /* you could return non-zero here and kill the connection */
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-
-/* lws-mirror_protocol */
-
-#define MAX_MESSAGE_QUEUE 32
-
-struct per_session_data__lws_mirror {
- struct libwebsocket *wsi;
- int ringbuffer_tail;
-};
-
-struct a_message {
- void *payload;
- size_t len;
-};
-
-static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
-static int ringbuffer_head;
-
-static int
-callback_lws_mirror(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- int n;
- struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
-
- switch (reason) {
-
- case LWS_CALLBACK_ESTABLISHED:
- lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
- pss->ringbuffer_tail = ringbuffer_head;
- pss->wsi = wsi;
- break;
-
- case LWS_CALLBACK_PROTOCOL_DESTROY:
- lwsl_notice("mirror protocol cleaning up\n");
- for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
- if (ringbuffer[n].payload)
- free(ringbuffer[n].payload);
- break;
-
- case LWS_CALLBACK_SERVER_WRITEABLE:
- if (close_testing)
- break;
- while (pss->ringbuffer_tail != ringbuffer_head) {
-
- n = libwebsocket_write(wsi, (unsigned char *)
- ringbuffer[pss->ringbuffer_tail].payload +
- LWS_SEND_BUFFER_PRE_PADDING,
- ringbuffer[pss->ringbuffer_tail].len,
- LWS_WRITE_TEXT);
- if (n < ringbuffer[pss->ringbuffer_tail].len) {
- lwsl_err("ERROR %d writing to mirror socket\n", n);
- return -1;
- }
- if (n < ringbuffer[pss->ringbuffer_tail].len)
- lwsl_err("mirror partial write %d vs %d\n",
- n, ringbuffer[pss->ringbuffer_tail].len);
-
- if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
- pss->ringbuffer_tail = 0;
- else
- pss->ringbuffer_tail++;
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
- libwebsocket_rx_flow_allow_all_protocol(
- libwebsockets_get_protocol(wsi));
-
- // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-
- if (lws_send_pipe_choked(wsi)) {
- libwebsocket_callback_on_writable(context, wsi);
- break;
- }
- /*
- * for tests with chrome on same machine as client and
- * server, this is needed to stop chrome choking
- */
- usleep(1);
- }
- break;
-
- case LWS_CALLBACK_RECEIVE:
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
- lwsl_err("dropping!\n");
- goto choke;
- }
-
- if (ringbuffer[ringbuffer_head].payload)
- free(ringbuffer[ringbuffer_head].payload);
-
- ringbuffer[ringbuffer_head].payload =
- malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
- LWS_SEND_BUFFER_POST_PADDING);
- ringbuffer[ringbuffer_head].len = len;
- memcpy((char *)ringbuffer[ringbuffer_head].payload +
- LWS_SEND_BUFFER_PRE_PADDING, in, len);
- if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
- ringbuffer_head = 0;
- else
- ringbuffer_head++;
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
- goto done;
-
-choke:
- lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
- libwebsocket_rx_flow_control(wsi, 0);
-
-// lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-done:
- libwebsocket_callback_on_writable_all_protocol(
- libwebsockets_get_protocol(wsi));
- break;
-
- /*
- * this just demonstrates how to use the protocol filter. If you won't
- * study and reject connections based on header content, you don't need
- * to handle this callback
- */
-
- case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info(wsi);
- /* you could return non-zero here and kill the connection */
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-
/* list of supported protocols and callbacks */
-static struct libwebsocket_protocols protocols[] = {
+static struct lws_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"dumb-increment-protocol",
callback_dumb_increment,
sizeof(struct per_session_data__dumb_increment),
- 10,
- },
- {
- "lws-mirror-protocol",
- callback_lws_mirror,
- sizeof(struct per_session_data__lws_mirror),
- 128,
+ 10, /* rx buf size must be >= permessage-deflate rx size
+ * dumb-increment only sends very small packets, so we set
+ * this accordingly. If your protocol will send bigger
+ * things, adjust this to match */
},
+ LWS_PLUGIN_PROTOCOL_MIRROR,
+ LWS_PLUGIN_PROTOCOL_LWS_STATUS,
{ NULL, NULL, 0, 0 } /* terminator */
};
+
+/* this shows how to override the lws file operations. You don't need
+ * to do any of this unless you have a reason (eg, want to serve
+ * compressed files without decompressing the whole archive)
+ */
+static lws_fop_fd_t
+test_server_fops_open(const struct lws_plat_file_ops *fops,
+ const char *vfs_path, const char *vpath,
+ lws_fop_flags_t *flags)
+{
+ lws_fop_fd_t fop_fd;
+
+ /* call through to original platform implementation */
+ fop_fd = fops_plat.open(fops, vfs_path, vpath, flags);
+
+ if (fop_fd)
+ lwsl_info("%s: opening %s, ret %p, len %lu\n", __func__,
+ vfs_path, fop_fd,
+ (long)lws_vfs_get_length(fop_fd));
+ else
+ lwsl_info("%s: open %s failed\n", __func__, vfs_path);
+
+ return fop_fd;
+}
+
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 */ }
+};
+
+
+
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", required_argument, NULL, 'd' },
{ "port", required_argument, NULL, 'p' },
{ "ssl", no_argument, NULL, 's' },
- { "interface", required_argument, NULL, 'i' },
- { "closetest", no_argument, NULL, 'c' },
+ { "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' },
+ { "daemonize", no_argument, NULL, 'D' },
#endif
- { "resource_path", required_argument, NULL, 'r' },
+ { "resource_path", required_argument, NULL, 'r' },
+ { "pingpong-secs", required_argument, NULL, 'P' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
- char cert_path[1024];
- char key_path[1024];
- int n = 0;
- int use_ssl = 0;
- struct libwebsocket_context *context;
- int opts = 0;
+ struct lws_context_creation_info info;
+ struct lws_vhost *vhost;
char interface_name[128] = "";
+ unsigned int ms, oldms = 0;
const char *iface = NULL;
-#ifndef WIN32
+ char cert_path[1024] = "";
+ char key_path[1024] = "";
+ char ca_path[1024] = "";
+ int uid = -1, gid = -1;
+ int use_ssl = 0;
+ int pp_secs = 0;
+ int opts = 0;
+ int n = 0;
+#ifndef _WIN32
+/* LOG_PERROR is not POSIX standard, and may not be portable */
+#ifdef __sun
+ int syslog_options = LOG_PID;
+#else
int syslog_options = LOG_PID | LOG_PERROR;
#endif
- unsigned int oldus = 0;
- struct lws_context_creation_info info;
-
- int debug_level = 7;
+#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, "ci:hsp:d:Dr:", options, NULL);
+ n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:k", 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
+ #if !defined(_WIN32) && !defined(__sun)
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;
+ opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+ break;
+ case 'a':
+ opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
break;
case 'p':
info.port = atoi(optarg);
interface_name[(sizeof interface_name) - 1] = '\0';
iface = interface_name;
break;
+ case 'k':
+ info.bind_iface = 1;
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+ info.caps[0] = CAP_NET_RAW;
+ info.count_caps = 1;
+#endif
+ break;
case 'c':
close_testing = 1;
fprintf(stderr, " Close testing mode -- closes on "
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;
+ case 'P':
+ pp_secs = atoi(optarg);
+ lwsl_notice("Setting pingpong interval to %d\n", pp_secs);
+ break;
+#if defined(LWS_OPENSSL_SUPPORT)
+ case 'v':
+ use_ssl = 1;
+ opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
+ break;
+
+#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
case 'h':
fprintf(stderr, "Usage: test-server "
"[--port=<p>] [--ssl] "
}
#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 1;
+ return 10;
}
#endif
signal(SIGINT, sighandler);
-#ifndef WIN32
+#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);
/* 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 - "
- "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
- "licensed under LGPL2.1\n");
+ 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);
#ifdef EXTERNAL_POLL
max_poll_elements = getdtablesize();
- pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
+ pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd));
fd_lookup = malloc(max_poll_elements * sizeof (int));
if (pollfds == NULL || fd_lookup == NULL) {
lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
info.iface = iface;
info.protocols = protocols;
-#ifndef LWS_NO_EXTENSIONS
- info.extensions = libwebsocket_get_internal_extensions();
-#endif
- if (!use_ssl) {
- info.ssl_cert_filepath = NULL;
- info.ssl_private_key_filepath = NULL;
- } else {
- snprintf(cert_path, sizeof(cert_path), "%s/libwebsockets-test-server.pem", resource_path);
- snprintf(key_path, sizeof(cert_path), "%s/libwebsockets-test-server.key.pem", resource_path);
+ info.ssl_cert_filepath = NULL;
+ info.ssl_private_key_filepath = NULL;
+ info.ws_ping_pong_interval = pp_secs;
+
+ 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 = -1;
- info.uid = -1;
- info.options = opts;
-
- context = libwebsocket_create_context(&info);
+ info.gid = gid;
+ info.uid = uid;
+ info.max_http_header_pool = 16;
+ info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
+ 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";
+
+ if (use_ssl)
+ /* redirect guys coming on http */
+ info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
+
+ context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
return -1;
}
+ vhost = lws_create_vhost(context, &info);
+ if (!vhost) {
+ lwsl_err("vhost creation failed\n");
+ return -1;
+ }
+
+#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
+ lws_init_vhost_client_ssl(&info, vhost);
+#endif
+
+ /* this shows how to override the lws file operations. You don't need
+ * to do any of this unless you have a reason (eg, want to serve
+ * compressed files without decompressing the whole archive)
+ */
+ /* stash original platform fops */
+ fops_plat = *(lws_get_fops(context));
+ /* override the active fops */
+ lws_get_fops(context)->open = test_server_fops_open;
+
n = 0;
+#ifdef EXTERNAL_POLL
+ int ms_1sec = 0;
+#endif
while (n >= 0 && !force_exit) {
struct timeval tv;
* as soon as it can take more packets (usually immediately)
*/
- if (((unsigned int)tv.tv_usec - oldus) > 50000) {
- libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
- oldus = tv.tv_usec;
+ ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if ((ms - oldms) > 50) {
+ lws_callback_on_writable_all_protocol(context,
+ &protocols[PROTOCOL_DUMB_INCREMENT]);
+ oldms = ms;
}
#ifdef EXTERNAL_POLL
-
/*
* this represents an existing server's single poll action
* which also includes libwebsocket sockets
if (n < 0)
continue;
-
- if (n)
+ if (n) {
for (n = 0; n < count_pollfds; n++)
if (pollfds[n].revents)
/*
* match anything under libwebsockets
* control
*/
- if (libwebsocket_service_fd(context,
+ if (lws_service_fd(context,
&pollfds[n]) < 0)
goto done;
+
+ /* if needed, force-service wsis that may not have read all input */
+ while (!lws_service_adjust_timeout(context, 1, 0)) {
+ lwsl_notice("extpoll doing forced service!\n");
+ lws_service_tsi(context, -1, 0);
+ }
+ } else {
+ /* no revents, but before polling again, make lws check for any timeouts */
+ if (ms - ms_1sec > 1000) {
+ lwsl_notice("1 per sec\n");
+ lws_service_fd(context, NULL);
+ ms_1sec = ms;
+ }
+ }
#else
/*
* If libwebsockets sockets are all we care about,
* the number of ms in the second argument.
*/
- n = libwebsocket_service(context, 50);
+ n = lws_service(context, 50);
#endif
}
done:
#endif
- libwebsocket_context_destroy(context);
+ lws_context_destroy(context);
lwsl_notice("libwebsockets-test-server exited cleanly\n");
-#ifndef WIN32
+#ifndef _WIN32
closelog();
#endif