* 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 <getopt.h>
#include <string.h>
#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <assert.h>
+#ifdef WIN32
-#include "../lib/libwebsockets.h"
+#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"
+#endif
-static int close_testing;
+#else // NOT WIN32
+#include <syslog.h>
+#endif
-#ifdef EXTERNAL_POLL
-#define LWS_NO_FORK
+#include <signal.h>
-#define MAX_POLL_ELEMENTS 32000
+#include "../lib/libwebsockets.h"
-struct pollfd pollfds[MAX_POLL_ELEMENTS];
-int fd_lookup[MAX_POLL_ELEMENTS];
-int count_pollfds;
+static int close_testing;
+int max_poll_elements;
-#endif /* EXTERNAL_POLL */
+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
{ "/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,
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;
+ 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 m;
- int fd = (int)(long)user;
+ 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")) {
+
+ /* well, let's demonstrate how to send the hard way */
+
+ p = buffer;
+
+ pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY);
+ 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(in, whitelist[n].urlpath) == 0)
+ 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))
- fprintf(stderr, "Failed to send HTTP file\n");
+ return -1; /* through completion or error, close the socket */
/*
* notice that the sending of the file completes asynchronously,
break;
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-// fprintf(stderr, "LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
+// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
/* kill the connection after we sent one file */
- return 1;
+ 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
*/
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-
- libwebsockets_get_peer_addresses((int)(long)user, client_name,
+#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);
-
+ 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;
case LWS_CALLBACK_ADD_POLL_FD:
- if (count_pollfds >= MAX_POLL_ELEMENTS) {
- fprintf(stderr, "LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
+ if (count_pollfds >= max_poll_elements) {
+ lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
return 1;
}
*/
static void
-dump_handshake_info(struct lws_tokens *lwst)
+dump_handshake_info(struct libwebsocket *wsi)
{
int n;
static const char *token_names[WSI_TOKEN_COUNT] = {
/*[WSI_TOKEN_HTTP] =*/ "Http",
/*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
};
+ char buf[256];
for (n = 0; n < WSI_TOKEN_COUNT; n++) {
- if (lwst[n].token == NULL)
+ if (!lws_hdr_total_length(wsi, n))
continue;
- fprintf(stderr, " %s = %s\n", token_names[n], lwst[n].token);
+ lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+ fprintf(stderr, " %s = %s\n", token_names[n], buf);
}
}
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 = user;
+ struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
- fprintf(stderr, "callback_dumb_increment: "
+ lwsl_info("callback_dumb_increment: "
"LWS_CALLBACK_ESTABLISHED\n");
pss->number = 0;
break;
- /*
- * in this protocol, we just use the broadcast action as the chance to
- * send our own connection-specific data and ignore the broadcast info
- * that is available in the 'in' parameter
- */
-
- case LWS_CALLBACK_BROADCAST:
+ case LWS_CALLBACK_SERVER_WRITEABLE:
n = sprintf((char *)p, "%d", pss->number++);
+ /* too small for partial */
n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
if (n < 0) {
- fprintf(stderr, "ERROR %d writing to socket\n", n);
- return 1;
+ lwsl_err("ERROR %d writing to di socket\n", n);
+ return -1;
}
if (close_testing && pss->number == 50) {
- fprintf(stderr, "close tesing limit, closing\n");
- libwebsocket_close_and_free_session(context, wsi,
- LWS_CLOSE_STATUS_NORMAL);
+ lwsl_info("close tesing limit, closing\n");
+ return -1;
}
break;
case LWS_CALLBACK_RECEIVE:
- fprintf(stderr, "rx %d\n", (int)len);
+// fprintf(stderr, "rx %d\n", (int)len);
if (len < 6)
break;
- if (strcmp(in, "reset\n") == 0)
+ if (strcmp((const char *)in, "reset\n") == 0)
pss->number = 0;
break;
/*
*/
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info((struct lws_tokens *)(long)user);
+ dump_handshake_info(wsi);
/* you could return non-zero here and kill the connection */
break;
/* lws-mirror_protocol */
-#define MAX_MESSAGE_QUEUE 64
+#define MAX_MESSAGE_QUEUE 128
struct per_session_data__lws_mirror {
struct libwebsocket *wsi;
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,
void *user, void *in, size_t len)
{
int n;
- struct per_session_data__lws_mirror *pss = user;
+ struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
- fprintf(stderr, "callback_lws_mirror: "
+ 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;
- if (pss->ringbuffer_tail != ringbuffer_head) {
+ while (pss->ringbuffer_tail != ringbuffer_head) {
n = libwebsocket_write(wsi, (unsigned char *)
ringbuffer[pss->ringbuffer_tail].payload +
ringbuffer[pss->ringbuffer_tail].len,
LWS_WRITE_TEXT);
if (n < 0) {
- fprintf(stderr, "ERROR %d writing to socket\n", n);
- exit(1);
+ 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))
else
pss->ringbuffer_tail++;
- if (((ringbuffer_head - pss->ringbuffer_tail) %
- MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
- libwebsocket_rx_flow_control(wsi, 1);
-
- libwebsocket_callback_on_writable(context, wsi);
+ 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_BROADCAST:
- n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
- if (n < 0)
- fprintf(stderr, "mirror write failed\n");
- 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);
else
ringbuffer_head++;
- if (((ringbuffer_head - pss->ringbuffer_tail) %
- MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
+ 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
*/
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info((struct lws_tokens *)(long)user);
+ dump_handshake_info(wsi);
/* you could return non-zero here and kill the connection */
break;
{
"http-only", /* name */
callback_http, /* callback */
- 0 /* per_session_data_size */
+ sizeof (struct per_session_data__http), /* 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)
+ sizeof(struct per_session_data__lws_mirror),
+ 128,
},
- {
- NULL, NULL, 0 /* End of list */
- }
+ { 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' },
- { "killmask", no_argument, NULL, 'k' },
{ "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;
- const char *cert_path =
- LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
- const char *key_path =
- LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
- LWS_SEND_BUFFER_POST_PADDING];
- int port = 7681;
int use_ssl = 0;
struct libwebsocket_context *context;
int opts = 0;
char interface_name[128] = "";
- const char *interface = NULL;
-#ifdef LWS_NO_FORK
+ const char *iface = 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
- fprintf(stderr, "libwebsockets test server\n"
- "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> "
- "licensed under LGPL2.1\n");
+ memset(&info, 0, sizeof info);
+ info.port = 7681;
while (n >= 0) {
- n = getopt_long(argc, argv, "ci:khsp:d:", options, NULL);
+ 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':
- lws_set_log_level(atoi(optarg), NULL);
+ debug_level = atoi(optarg);
break;
case 's':
use_ssl = 1;
break;
- case 'k':
- opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
- break;
case 'p':
- port = atoi(optarg);
+ info.port = atoi(optarg);
break;
case 'i':
strncpy(interface_name, optarg, sizeof interface_name);
interface_name[(sizeof interface_name) - 1] = '\0';
- interface = interface_name;
+ iface = interface_name;
break;
case 'c':
close_testing = 1;
}
}
- if (!use_ssl)
- cert_path = key_path = NULL;
-
- context = libwebsocket_create_context(port, interface, protocols,
- libwebsocket_internal_extensions,
- cert_path, key_path, NULL, -1, -1, opts, NULL);
- if (context == NULL) {
- fprintf(stderr, "libwebsocket init failed\n");
- return -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
- buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
+ signal(SIGINT, sighandler);
-#ifdef LWS_NO_FORK
+#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
- /*
- * This example shows how to work with no forked service loop
- */
+ /* 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.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 {
+ 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;
- fprintf(stderr, " Using no-fork service loop\n");
+ context = libwebsocket_create_context(&info);
+ if (context == NULL) {
+ lwsl_err("libwebsocket init failed\n");
+ return -1;
+ }
n = 0;
- while (n >= 0) {
+ while (n >= 0 && !force_exit) {
struct timeval tv;
gettimeofday(&tv, NULL);
/*
- * This broadcasts to all dumb-increment-protocol connections
- * at 20Hz.
- *
- * We're just sending a character 'x', in these examples the
- * callbacks send their own per-connection content.
- *
- * You have to send something with nonzero length to get the
- * callback actions delivered.
- *
- * We take care of pre-and-post padding allocation.
+ * 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) {
- libwebsockets_broadcast(
- &protocols[PROTOCOL_DUMB_INCREMENT],
- &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+ libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
oldus = tv.tv_usec;
}
- /*
- * This example server does not fork or create a thread for
- * websocket service, it all runs in this single loop. So,
- * we have to give the websockets an opportunity to service
- * "manually".
- *
- * If no socket is needing service, the call below returns
- * immediately and quickly. Negative return means we are
- * in process of closing
- */
#ifdef EXTERNAL_POLL
/*
&pollfds[n]) < 0)
goto done;
#else
- n = libwebsocket_service(context, 50);
-#endif
- }
-
-#else /* !LWS_NO_FORK */
-
- /*
- * This example shows how to work with the forked websocket service loop
- */
-
- fprintf(stderr, " Using forked service loop\n");
-
- /*
- * This forks the websocket service action into a subprocess so we
- * don't have to take care about it.
- */
-
- n = libwebsockets_fork_service_loop(context);
- if (n < 0) {
- fprintf(stderr, "Unable to fork service loop %d\n", n);
- return 1;
- }
-
- while (1) {
-
- usleep(50000);
-
/*
- * This broadcasts to all dumb-increment-protocol connections
- * at 20Hz.
+ * 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.
*
- * We're just sending a character 'x', in these examples the
- * callbacks send their own per-connection content.
- *
- * You have to send something with nonzero length to get the
- * callback actions delivered.
- *
- * We take care of pre-and-post padding allocation.
+ * If no socket needs service, it'll return anyway after
+ * the number of ms in the second argument.
*/
- libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
- &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+ n = libwebsocket_service(context, 50);
+#endif
}
-#endif
#ifdef EXTERNAL_POLL
done:
#endif
libwebsocket_context_destroy(context);
+ lwsl_notice("libwebsockets-test-server exited cleanly\n");
+
+#ifndef WIN32
+ closelog();
+#endif
+
return 0;
}