mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00

To get a clean bill of health from valgrind, we have to have a way to inform the user code that we're going down and it should free everything it is holding that was malloc'd. This patch introduces LWS_CALLBACK_PROTOCOL_DESTROY which each protocol gets when the context is being destroyed and no more activity will come after that call. They can get rid of everything there. To match it, LWS_CALLBACK_PROTOCOL_INIT is introduced which would allow one-time init per protocol too. Signed-off-by: Andy Green <andy.green@linaro.org>
690 lines
17 KiB
C
690 lines
17 KiB
C
/*
|
|
* libwebsockets-test-server - libwebsockets test implementation
|
|
*
|
|
* Copyright (C) 2010-2011 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 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.
|
|
*
|
|
* 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
|
|
*/
|
|
#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 <assert.h>
|
|
#ifndef WIN32
|
|
#include <syslog.h>
|
|
#endif
|
|
#include <signal.h>
|
|
|
|
#include "../lib/libwebsockets.h"
|
|
|
|
static int close_testing;
|
|
int max_poll_elements;
|
|
|
|
struct pollfd *pollfds;
|
|
int *fd_lookup;
|
|
int count_pollfds;
|
|
int force_exit = 0;
|
|
|
|
/*
|
|
* This demo server shows how to use libwebsockets for one or more
|
|
* websocket protocols in the same server
|
|
*
|
|
* It defines the following websocket protocols:
|
|
*
|
|
* dumb-increment-protocol: once the socket is opened, an incrementing
|
|
* ascii string is sent down it every 50ms.
|
|
* If you send "reset\n" on the websocket, then
|
|
* the incrementing number is reset to 0.
|
|
*
|
|
* lws-mirror-protocol: copies any received packet to every connection also
|
|
* using this protocol, including the sender
|
|
*/
|
|
|
|
enum demo_protocols {
|
|
/* always first */
|
|
PROTOCOL_HTTP = 0,
|
|
|
|
PROTOCOL_DUMB_INCREMENT,
|
|
PROTOCOL_LWS_MIRROR,
|
|
|
|
/* always last */
|
|
DEMO_PROTOCOL_COUNT
|
|
};
|
|
|
|
|
|
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
|
|
|
/*
|
|
* 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" },
|
|
};
|
|
|
|
/* 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;
|
|
#ifdef EXTERNAL_POLL
|
|
int m;
|
|
int fd = (int)(long)user;
|
|
#endif
|
|
|
|
switch (reason) {
|
|
case LWS_CALLBACK_HTTP:
|
|
|
|
for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
|
|
if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
|
|
break;
|
|
|
|
sprintf(buf, LOCAL_RESOURCE_PATH"%s", 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;
|
|
|
|
/*
|
|
* 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)user, 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;
|
|
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++);
|
|
n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
|
|
if (n < 0) {
|
|
lwsl_err("ERROR %d writing to socket\n", n);
|
|
return 1;
|
|
}
|
|
if (close_testing && pss->number == 50) {
|
|
lwsl_info("close tesing limit, closing\n");
|
|
libwebsocket_close_and_free_session(context, wsi,
|
|
LWS_CLOSE_STATUS_NORMAL);
|
|
}
|
|
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 128
|
|
|
|
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 struct libwebsocket *wsi_choked[20];
|
|
static int num_wsi_choked;
|
|
|
|
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 < 0) {
|
|
lwsl_err("ERROR %d writing to socket\n", n);
|
|
return 1;
|
|
}
|
|
|
|
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)) {
|
|
for (n = 0; n < num_wsi_choked; n++)
|
|
libwebsocket_rx_flow_control(wsi_choked[n], 1);
|
|
num_wsi_choked = 0;
|
|
}
|
|
// 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);
|
|
return 0;
|
|
}
|
|
}
|
|
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:
|
|
if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) {
|
|
libwebsocket_rx_flow_control(wsi, 0);
|
|
wsi_choked[num_wsi_choked++] = wsi;
|
|
}
|
|
|
|
// 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[] = {
|
|
/* first protocol must always be HTTP handler */
|
|
|
|
{
|
|
"http-only", /* name */
|
|
callback_http, /* callback */
|
|
0, /* per_session_data_size */
|
|
0, /* max frame size / rx buffer */
|
|
},
|
|
{
|
|
"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,
|
|
},
|
|
{ NULL, NULL, 0, 0 } /* terminator */
|
|
};
|
|
|
|
void sighandler(int sig)
|
|
{
|
|
force_exit = 1;
|
|
}
|
|
|
|
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' },
|
|
#ifndef LWS_NO_DAEMONIZE
|
|
{ "daemonize", no_argument, NULL, 'D' },
|
|
#endif
|
|
{ NULL, 0, 0, 0 }
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int n = 0;
|
|
int use_ssl = 0;
|
|
struct libwebsocket_context *context;
|
|
int opts = 0;
|
|
char interface_name[128] = "";
|
|
const char *interface = NULL;
|
|
#ifndef WIN32
|
|
int syslog_options = LOG_PID | LOG_PERROR;
|
|
#endif
|
|
unsigned int oldus = 0;
|
|
struct lws_context_creation_info info;
|
|
|
|
int debug_level = 7;
|
|
#ifndef LWS_NO_DAEMONIZE
|
|
int daemonize = 0;
|
|
#endif
|
|
|
|
memset(&info, 0, sizeof info);
|
|
info.port = 7681;
|
|
|
|
while (n >= 0) {
|
|
n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL);
|
|
if (n < 0)
|
|
continue;
|
|
switch (n) {
|
|
#ifndef LWS_NO_DAEMONIZE
|
|
case 'D':
|
|
daemonize = 1;
|
|
#ifndef WIN32
|
|
syslog_options &= ~LOG_PERROR;
|
|
#endif
|
|
break;
|
|
#endif
|
|
case 'd':
|
|
debug_level = atoi(optarg);
|
|
break;
|
|
case 's':
|
|
use_ssl = 1;
|
|
break;
|
|
case 'p':
|
|
info.port = atoi(optarg);
|
|
break;
|
|
case 'i':
|
|
strncpy(interface_name, optarg, sizeof interface_name);
|
|
interface_name[(sizeof interface_name) - 1] = '\0';
|
|
interface = 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 'h':
|
|
fprintf(stderr, "Usage: test-server "
|
|
"[--port=<p>] [--ssl] "
|
|
"[-d <log bitfield>]\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 1;
|
|
}
|
|
#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 - "
|
|
"(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
|
|
"licensed under LGPL2.1\n");
|
|
#ifdef EXTERNAL_POLL
|
|
max_poll_elements = getdtablesize();
|
|
pollfds = malloc(max_poll_elements * sizeof (struct 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);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
info.interface = interface;
|
|
info.protocols = protocols;
|
|
#ifndef LWS_NO_EXTENSIONS
|
|
info.extensions = libwebsocket_internal_extensions;
|
|
#endif
|
|
if (!use_ssl) {
|
|
info.ssl_cert_filepath = NULL;
|
|
info.ssl_private_key_filepath = NULL;
|
|
} else {
|
|
info.ssl_cert_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
|
|
info.ssl_private_key_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
|
|
}
|
|
info.gid = -1;
|
|
info.uid = -1;
|
|
info.options = opts;
|
|
|
|
context = libwebsocket_create_context(&info);
|
|
if (context == NULL) {
|
|
lwsl_err("libwebsocket init failed\n");
|
|
return -1;
|
|
}
|
|
|
|
n = 0;
|
|
while (n >= 0 && !force_exit) {
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
/*
|
|
* This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
|
|
* live websocket connection using the DUMB_INCREMENT protocol,
|
|
* 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;
|
|
}
|
|
|
|
#ifdef EXTERNAL_POLL
|
|
|
|
/*
|
|
* this represents an existing server's single poll action
|
|
* which also includes libwebsocket sockets
|
|
*/
|
|
|
|
n = poll(pollfds, count_pollfds, 50);
|
|
if (n < 0)
|
|
continue;
|
|
|
|
|
|
if (n)
|
|
for (n = 0; n < count_pollfds; n++)
|
|
if (pollfds[n].revents)
|
|
/*
|
|
* returns immediately if the fd does not
|
|
* match anything under libwebsockets
|
|
* control
|
|
*/
|
|
if (libwebsocket_service_fd(context,
|
|
&pollfds[n]) < 0)
|
|
goto done;
|
|
#else
|
|
/*
|
|
* If libwebsockets sockets are all we care about,
|
|
* you can use this api which takes care of the poll()
|
|
* and looping through finding who needed service.
|
|
*
|
|
* If no socket needs service, it'll return anyway after
|
|
* the number of ms in the second argument.
|
|
*/
|
|
|
|
n = libwebsocket_service(context, 50);
|
|
#endif
|
|
}
|
|
|
|
#ifdef EXTERNAL_POLL
|
|
done:
|
|
#endif
|
|
|
|
libwebsocket_context_destroy(context);
|
|
|
|
lwsl_notice("libwebsockets-test-server exited cleanly\n");
|
|
|
|
#ifndef WIN32
|
|
closelog();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|