2010-11-08 17:12:19 +00:00
|
|
|
/*
|
|
|
|
* libwebsockets-test-server - libwebsockets test implementation
|
2010-11-13 10:03:47 +00:00
|
|
|
*
|
2011-01-23 16:50:33 +00:00
|
|
|
* Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
|
2010-11-08 17:12:19 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2013-02-06 15:27:27 +09:00
|
|
|
#include "lws_config.h"
|
2010-11-08 17:12:19 +00:00
|
|
|
|
2010-10-29 14:15:22 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2010-10-31 07:40:33 +00:00
|
|
|
#include <getopt.h>
|
2014-03-29 07:43:38 +01:00
|
|
|
#include <signal.h>
|
2010-10-31 11:57:17 +00:00
|
|
|
#include <string.h>
|
2013-02-14 23:06:37 +08:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2013-01-15 19:44:33 +08:00
|
|
|
#include <assert.h>
|
2013-02-13 09:29:26 +08:00
|
|
|
|
2014-03-29 07:43:38 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <io.h>
|
2013-02-13 09:29:26 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
2014-03-29 07:15:40 +01:00
|
|
|
#define poll WSAPoll
|
2013-02-13 09:29:26 +08:00
|
|
|
#endif
|
2014-03-29 07:43:38 +01:00
|
|
|
#else
|
2013-01-19 11:32:18 +08:00
|
|
|
#include <syslog.h>
|
2014-03-29 07:43:38 +01:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <unistd.h>
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-02-13 09:29:26 +08:00
|
|
|
|
2010-11-01 09:12:17 +00:00
|
|
|
#include "../lib/libwebsockets.h"
|
2010-10-29 14:15:22 +01:00
|
|
|
|
2011-03-07 07:08:07 +00:00
|
|
|
static int close_testing;
|
2013-01-20 20:51:14 +08:00
|
|
|
int max_poll_elements;
|
2013-01-15 19:44:33 +08:00
|
|
|
|
2015-03-05 17:06:16 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
2013-01-20 20:51:14 +08:00
|
|
|
struct pollfd *pollfds;
|
|
|
|
int *fd_lookup;
|
2013-01-17 11:16:15 +08:00
|
|
|
int count_pollfds;
|
2015-03-05 17:06:16 +08:00
|
|
|
#endif
|
2014-01-27 12:37:47 +01:00
|
|
|
static volatile int force_exit = 0;
|
2014-03-06 11:57:50 +01:00
|
|
|
static struct libwebsocket_context *context;
|
2013-01-19 11:11:42 +08:00
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
2011-01-27 06:26:52 +00:00
|
|
|
* If you send "reset\n" on the websocket, then
|
|
|
|
* the incrementing number is reset to 0.
|
2010-11-12 14:12:13 +00:00
|
|
|
*
|
|
|
|
* lws-mirror-protocol: copies any received packet to every connection also
|
2011-01-27 06:26:52 +00:00
|
|
|
* using this protocol, including the sender
|
2010-11-12 13:10:40 +00:00
|
|
|
*/
|
|
|
|
|
2011-01-18 15:29:04 +00:00
|
|
|
enum demo_protocols {
|
|
|
|
/* always first */
|
|
|
|
PROTOCOL_HTTP = 0,
|
|
|
|
|
|
|
|
PROTOCOL_DUMB_INCREMENT,
|
|
|
|
PROTOCOL_LWS_MIRROR,
|
|
|
|
|
|
|
|
/* always last */
|
|
|
|
DEMO_PROTOCOL_COUNT
|
|
|
|
};
|
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
|
2012-04-12 11:06:05 +08:00
|
|
|
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
2013-03-18 17:08:25 +00:00
|
|
|
char *resource_path = LOCAL_RESOURCE_PATH;
|
2010-10-29 14:15:22 +01:00
|
|
|
|
2013-01-16 10:06:28 +08:00
|
|
|
/*
|
|
|
|
* We take a strict whitelist approach to stop ../ attacks
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct serveable {
|
|
|
|
const char *urlpath;
|
|
|
|
const char *mimetype;
|
|
|
|
};
|
|
|
|
|
2013-02-14 23:06:37 +08:00
|
|
|
struct per_session_data__http {
|
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
|
2013-11-13 07:45:17 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2016-01-20 08:55:31 +08:00
|
|
|
int n = 0, len;
|
2013-11-13 07:45:17 +08:00
|
|
|
char buf[256];
|
2014-10-12 14:31:47 +08:00
|
|
|
const unsigned char *c;
|
2013-11-13 07:45:17 +08:00
|
|
|
|
2014-10-12 14:31:47 +08:00
|
|
|
do {
|
|
|
|
c = lws_token_to_string(n);
|
|
|
|
if (!c) {
|
|
|
|
n++;
|
2013-11-13 07:45:17 +08:00
|
|
|
continue;
|
2014-10-12 14:31:47 +08:00
|
|
|
}
|
|
|
|
|
2016-01-20 08:55:31 +08:00
|
|
|
len = lws_hdr_total_length(wsi, n);
|
|
|
|
if (!len || len > sizeof(buf) - 1) {
|
2014-10-12 14:31:47 +08:00
|
|
|
n++;
|
|
|
|
continue;
|
|
|
|
}
|
2013-11-13 07:45:17 +08:00
|
|
|
|
|
|
|
lws_hdr_copy(wsi, buf, sizeof buf, n);
|
2016-01-20 08:55:31 +08:00
|
|
|
buf[sizeof(buf) - 1] = '\0';
|
2013-11-13 07:45:17 +08:00
|
|
|
|
2014-10-12 14:31:47 +08:00
|
|
|
fprintf(stderr, " %s = %s\n", (char *)c, buf);
|
|
|
|
n++;
|
|
|
|
} while (c);
|
2013-11-13 07:45:17 +08:00
|
|
|
}
|
|
|
|
|
2013-11-10 15:15:21 +08:00
|
|
|
const char * get_mimetype(const char *file)
|
|
|
|
{
|
|
|
|
int n = strlen(file);
|
|
|
|
|
|
|
|
if (n < 5)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".ico"))
|
|
|
|
return "image/x-icon";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".png"))
|
|
|
|
return "image/png";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 5], ".html"))
|
|
|
|
return "text/html";
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
/* this protocol server (always the first one) just knows how to do HTTP */
|
2010-10-31 12:42:52 +00:00
|
|
|
|
2012-04-09 15:09:01 +08:00
|
|
|
static int callback_http(struct libwebsocket_context *context,
|
2011-02-14 09:14:25 +00:00
|
|
|
struct libwebsocket *wsi,
|
2010-11-13 10:03:47 +00:00
|
|
|
enum libwebsocket_callback_reasons reason, void *user,
|
2010-11-03 11:13:06 +00:00
|
|
|
void *in, size_t len)
|
2010-10-29 14:15:22 +01:00
|
|
|
{
|
2013-01-16 10:06:28 +08:00
|
|
|
char buf[256];
|
2013-04-30 06:51:10 +08:00
|
|
|
char leaf_path[1024];
|
2013-11-09 11:06:29 +08:00
|
|
|
char b64[64];
|
|
|
|
struct timeval tv;
|
2013-02-23 10:50:10 +08:00
|
|
|
int n, m;
|
2013-02-14 23:06:37 +08:00
|
|
|
unsigned char *p;
|
2013-11-09 11:06:29 +08:00
|
|
|
char *other_headers;
|
2013-02-18 11:32:49 +08:00
|
|
|
static unsigned char buffer[4096];
|
2013-02-14 23:06:37 +08:00
|
|
|
struct stat stat_buf;
|
2013-04-30 06:51:10 +08:00
|
|
|
struct per_session_data__http *pss =
|
|
|
|
(struct per_session_data__http *)user;
|
2013-11-10 15:15:21 +08:00
|
|
|
const char *mimetype;
|
2013-01-15 12:39:48 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
2014-02-15 20:21:02 +08:00
|
|
|
struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
|
2013-01-15 12:39:48 +08:00
|
|
|
#endif
|
2014-10-12 14:31:47 +08:00
|
|
|
unsigned char *end;
|
2010-10-29 14:15:22 +01:00
|
|
|
switch (reason) {
|
2010-10-31 11:57:17 +00:00
|
|
|
case LWS_CALLBACK_HTTP:
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2013-11-13 07:45:17 +08:00
|
|
|
dump_handshake_info(wsi);
|
|
|
|
|
2013-11-11 07:30:33 +08:00
|
|
|
if (len < 1) {
|
|
|
|
libwebsockets_return_http_status(context, wsi,
|
|
|
|
HTTP_STATUS_BAD_REQUEST, NULL);
|
2014-10-17 08:38:44 +08:00
|
|
|
goto try_to_reuse;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* this example server has no concept of directories */
|
2013-11-11 07:30:33 +08:00
|
|
|
if (strchr((const char *)in + 1, '/')) {
|
|
|
|
libwebsockets_return_http_status(context, wsi,
|
|
|
|
HTTP_STATUS_FORBIDDEN, NULL);
|
2014-10-17 08:38:44 +08:00
|
|
|
goto try_to_reuse;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2013-11-19 13:38:16 +01:00
|
|
|
/* if a legal POST URL, let it continue and accept data */
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
|
|
|
|
return 0;
|
|
|
|
|
2013-02-14 23:06:37 +08:00
|
|
|
/* check for the "send a big file by hand" example case */
|
|
|
|
|
|
|
|
if (!strcmp((const char *)in, "/leaf.jpg")) {
|
2013-04-30 06:51:10 +08:00
|
|
|
if (strlen(resource_path) > sizeof(leaf_path) - 10)
|
|
|
|
return -1;
|
|
|
|
sprintf(leaf_path, "%s/leaf.jpg", resource_path);
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
/* well, let's demonstrate how to send the hard way */
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
|
2014-10-12 14:31:47 +08:00
|
|
|
end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
|
2013-03-14 11:45:49 -04:00
|
|
|
#ifdef WIN32
|
2013-03-18 17:08:25 +00:00
|
|
|
pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
|
2013-03-14 11:45:49 -04:00
|
|
|
#else
|
2013-03-18 17:08:25 +00:00
|
|
|
pss->fd = open(leaf_path, O_RDONLY);
|
2013-03-14 11:45:49 -04:00
|
|
|
#endif
|
|
|
|
|
2013-02-14 23:06:37 +08:00
|
|
|
if (pss->fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-30 13:04:46 +08:00
|
|
|
if (fstat(pss->fd, &stat_buf) < 0)
|
|
|
|
return -1;
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
2014-10-12 14:31:47 +08:00
|
|
|
*
|
|
|
|
* Notice we use the APIs to build the header, which
|
|
|
|
* will do the right thing for HTTP 1/1.1 and HTTP2
|
|
|
|
* depending on what connection it happens to be working
|
|
|
|
* on
|
2013-02-14 23:06:37 +08:00
|
|
|
*/
|
2014-10-12 14:31:47 +08:00
|
|
|
if (lws_add_http_header_status(context, wsi, 200, &p, end))
|
|
|
|
return 1;
|
2014-11-26 12:04:29 +08:00
|
|
|
if (lws_add_http_header_by_token(context, wsi,
|
|
|
|
WSI_TOKEN_HTTP_SERVER,
|
|
|
|
(unsigned char *)"libwebsockets",
|
|
|
|
13, &p, end))
|
2014-10-12 14:31:47 +08:00
|
|
|
return 1;
|
2014-11-26 12:04:29 +08:00
|
|
|
if (lws_add_http_header_by_token(context, wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
|
|
(unsigned char *)"image/jpeg",
|
|
|
|
10, &p, end))
|
2014-10-12 14:31:47 +08:00
|
|
|
return 1;
|
2014-11-26 12:04:29 +08:00
|
|
|
if (lws_add_http_header_content_length(context, wsi,
|
|
|
|
stat_buf.st_size, &p, end))
|
2014-10-12 14:31:47 +08:00
|
|
|
return 1;
|
|
|
|
if (lws_finalize_http_header(context, wsi, &p, end))
|
|
|
|
return 1;
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* send the http headers...
|
|
|
|
* this won't block since it's the first payload sent
|
|
|
|
* on the connection since it was established
|
2013-02-23 10:50:10 +08:00
|
|
|
* (too small for partial)
|
2014-10-12 14:31:47 +08:00
|
|
|
*
|
|
|
|
* Notice they are sent using LWS_WRITE_HTTP_HEADERS
|
|
|
|
* which also means you can't send body too in one step,
|
|
|
|
* this is mandated by changes in HTTP2
|
2013-02-14 23:06:37 +08:00
|
|
|
*/
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
n = libwebsocket_write(wsi,
|
2014-11-26 12:04:29 +08:00
|
|
|
buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
|
|
|
p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
|
|
|
|
LWS_WRITE_HTTP_HEADERS);
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
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 */
|
2013-11-10 15:15:21 +08:00
|
|
|
strcpy(buf, resource_path);
|
2013-11-11 06:14:52 +08:00
|
|
|
if (strcmp(in, "/")) {
|
|
|
|
if (*((const char *)in) != '/')
|
|
|
|
strcat(buf, "/");
|
2013-11-10 15:15:21 +08:00
|
|
|
strncat(buf, in, sizeof(buf) - strlen(resource_path));
|
2013-11-11 06:14:52 +08:00
|
|
|
} else /* default file to serve */
|
2013-11-10 15:15:21 +08:00
|
|
|
strcat(buf, "/test.html");
|
|
|
|
buf[sizeof(buf) - 1] = '\0';
|
2013-11-11 07:30:33 +08:00
|
|
|
|
|
|
|
/* refuse to serve files we don't understand */
|
2013-11-10 15:15:21 +08:00
|
|
|
mimetype = get_mimetype(buf);
|
|
|
|
if (!mimetype) {
|
|
|
|
lwsl_err("Unknown mimetype for %s\n", buf);
|
2013-11-11 07:30:33 +08:00
|
|
|
libwebsockets_return_http_status(context, wsi,
|
|
|
|
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
|
2013-11-10 15:15:21 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2010-10-31 11:57:17 +00:00
|
|
|
|
2013-11-09 11:06:29 +08:00
|
|
|
/* demostrates how to set a cookie on / */
|
|
|
|
|
|
|
|
other_headers = NULL;
|
2014-10-12 14:31:47 +08:00
|
|
|
n = 0;
|
2013-11-09 11:06:29 +08:00
|
|
|
if (!strcmp((const char *)in, "/") &&
|
|
|
|
!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
|
|
|
|
/* this isn't very unguessable but it'll do for us */
|
|
|
|
gettimeofday(&tv, NULL);
|
2014-10-12 14:31:47 +08:00
|
|
|
n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
|
2013-11-09 11:06:29 +08:00
|
|
|
(unsigned int)tv.tv_sec,
|
|
|
|
(unsigned int)tv.tv_usec);
|
|
|
|
|
2014-10-12 14:31:47 +08:00
|
|
|
p = (unsigned char *)leaf_path;
|
|
|
|
|
2014-11-05 15:32:58 +08:00
|
|
|
if (lws_add_http_header_by_name(context, wsi,
|
|
|
|
(unsigned char *)"set-cookie:",
|
|
|
|
(unsigned char *)b64, n, &p,
|
|
|
|
(unsigned char *)leaf_path + sizeof(leaf_path)))
|
2014-10-12 14:31:47 +08:00
|
|
|
return 1;
|
|
|
|
n = (char *)p - leaf_path;
|
2013-11-09 11:06:29 +08:00
|
|
|
other_headers = leaf_path;
|
|
|
|
}
|
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
n = libwebsockets_serve_http_file(context, wsi, buf,
|
|
|
|
mimetype, other_headers, n);
|
|
|
|
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
|
|
|
return -1; /* error or can't reuse connection: close the socket */
|
2013-01-13 09:53:18 +08:00
|
|
|
|
2013-01-15 13:40:23 +08:00
|
|
|
/*
|
|
|
|
* notice that the sending of the file completes asynchronously,
|
|
|
|
* we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
|
|
|
|
* it's done
|
|
|
|
*/
|
|
|
|
|
2013-01-16 10:06:28 +08:00
|
|
|
break;
|
2013-01-15 13:40:23 +08:00
|
|
|
|
2013-11-19 13:38:16 +01:00
|
|
|
case LWS_CALLBACK_HTTP_BODY:
|
|
|
|
strncpy(buf, in, 20);
|
|
|
|
buf[20] = '\0';
|
|
|
|
if (len < 20)
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
|
|
|
lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
|
|
|
|
(const char *)buf, (int)len);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
|
|
|
|
lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
|
2014-10-17 08:38:44 +08:00
|
|
|
/* the whole of the sent body arrived, close or reuse the connection */
|
2013-11-19 13:38:16 +01:00
|
|
|
libwebsockets_return_http_status(context, wsi,
|
|
|
|
HTTP_STATUS_OK, NULL);
|
2014-10-17 08:38:44 +08:00
|
|
|
goto try_to_reuse;
|
2013-11-19 13:38:16 +01:00
|
|
|
|
2013-01-15 13:40:23 +08:00
|
|
|
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
2013-01-19 11:32:18 +08:00
|
|
|
// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
|
2013-01-15 13:40:23 +08:00
|
|
|
/* kill the connection after we sent one file */
|
2014-10-17 08:38:44 +08:00
|
|
|
goto try_to_reuse;
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
case LWS_CALLBACK_HTTP_WRITEABLE:
|
|
|
|
/*
|
|
|
|
* we can send more of whatever it is we were sending
|
|
|
|
*/
|
|
|
|
do {
|
2014-11-05 15:32:58 +08:00
|
|
|
/* we'd like the send this much */
|
|
|
|
n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
|
|
|
|
|
|
|
|
/* but if the peer told us he wants less, we can adapt */
|
|
|
|
m = lws_get_peer_write_allowance(wsi);
|
|
|
|
|
|
|
|
/* -1 means not using a protocol that has this info */
|
|
|
|
if (m == 0)
|
|
|
|
/* right now, peer can't handle anything */
|
|
|
|
goto later;
|
|
|
|
|
|
|
|
if (m != -1 && m < n)
|
|
|
|
/* he couldn't handle that much */
|
|
|
|
n = m;
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
2014-11-05 15:32:58 +08:00
|
|
|
n);
|
2013-02-14 23:06:37 +08:00
|
|
|
/* problem reading, close conn */
|
|
|
|
if (n < 0)
|
|
|
|
goto bail;
|
|
|
|
/* sent it all, close conn */
|
|
|
|
if (n == 0)
|
2013-12-09 14:16:17 +08:00
|
|
|
goto flush_bail;
|
2013-02-14 23:06:37 +08:00
|
|
|
/*
|
2014-10-08 12:00:53 +08:00
|
|
|
* To support HTTP2, must take care about preamble space
|
2014-11-05 15:32:58 +08:00
|
|
|
*
|
|
|
|
* identification of when we send the last payload frame
|
|
|
|
* is handled by the library itself if you sent a
|
|
|
|
* content-length header
|
2013-02-14 23:06:37 +08:00
|
|
|
*/
|
2014-10-08 12:00:53 +08:00
|
|
|
m = libwebsocket_write(wsi,
|
|
|
|
buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
|
|
|
n, LWS_WRITE_HTTP);
|
2013-02-23 10:50:10 +08:00
|
|
|
if (m < 0)
|
2013-02-14 23:06:37 +08:00
|
|
|
/* write failed, close conn */
|
|
|
|
goto bail;
|
2014-11-05 15:32:58 +08:00
|
|
|
|
2014-10-18 12:23:05 +08:00
|
|
|
/*
|
|
|
|
* http2 won't do this
|
|
|
|
*/
|
2013-02-23 10:50:10 +08:00
|
|
|
if (m != n)
|
|
|
|
/* partial write, adjust */
|
2014-11-30 12:57:57 +08:00
|
|
|
if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
|
|
|
|
goto bail;
|
2014-11-05 15:32:58 +08:00
|
|
|
|
2014-04-10 11:11:06 +08:00
|
|
|
if (m) /* while still active, extend timeout */
|
|
|
|
libwebsocket_set_timeout(wsi,
|
|
|
|
PENDING_TIMEOUT_HTTP_CONTENT, 5);
|
2014-08-18 22:49:39 +08:00
|
|
|
|
2014-11-05 15:32:58 +08:00
|
|
|
/* if we have indigestion, let him clear it before eating more */
|
2014-08-18 22:49:39 +08:00
|
|
|
if (lws_partial_buffered(wsi))
|
|
|
|
break;
|
2014-04-10 11:11:06 +08:00
|
|
|
|
2013-02-14 23:06:37 +08:00
|
|
|
} while (!lws_send_pipe_choked(wsi));
|
2014-11-05 15:32:58 +08:00
|
|
|
|
|
|
|
later:
|
2013-02-14 23:06:37 +08:00
|
|
|
libwebsocket_callback_on_writable(context, wsi);
|
|
|
|
break;
|
2013-12-09 14:16:17 +08:00
|
|
|
flush_bail:
|
|
|
|
/* true if still partial pending */
|
2014-08-18 22:49:39 +08:00
|
|
|
if (lws_partial_buffered(wsi)) {
|
2013-12-09 14:16:17 +08:00
|
|
|
libwebsocket_callback_on_writable(context, wsi);
|
|
|
|
break;
|
|
|
|
}
|
2014-10-17 08:38:44 +08:00
|
|
|
close(pss->fd);
|
|
|
|
goto try_to_reuse;
|
2013-02-14 23:06:37 +08:00
|
|
|
|
|
|
|
bail:
|
|
|
|
close(pss->fd);
|
|
|
|
return -1;
|
2010-11-11 12:28:29 +00:00
|
|
|
|
2011-02-13 08:40:37 +00:00
|
|
|
/*
|
|
|
|
* 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 we returned non-zero from here, we kill the connection */
|
|
|
|
break;
|
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
|
|
|
/*
|
|
|
|
* callbacks for managing the external poll() array appear in
|
|
|
|
* protocol 0 callback
|
|
|
|
*/
|
|
|
|
|
2013-12-18 09:48:26 +08:00
|
|
|
case LWS_CALLBACK_LOCK_POLL:
|
|
|
|
/*
|
|
|
|
* lock mutex to protect pollfd state
|
|
|
|
* called before any other POLL related callback
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_UNLOCK_POLL:
|
|
|
|
/*
|
|
|
|
* unlock mutex to protect pollfd state when
|
|
|
|
* called after any other POLL related callback
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
case LWS_CALLBACK_ADD_POLL_FD:
|
2013-01-17 11:16:15 +08:00
|
|
|
|
2013-01-20 20:51:14 +08:00
|
|
|
if (count_pollfds >= max_poll_elements) {
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
|
2013-01-15 12:39:48 +08:00
|
|
|
return 1;
|
2013-01-15 19:44:33 +08:00
|
|
|
}
|
|
|
|
|
2014-02-15 20:21:02 +08:00
|
|
|
fd_lookup[pa->fd] = count_pollfds;
|
|
|
|
pollfds[count_pollfds].fd = pa->fd;
|
|
|
|
pollfds[count_pollfds].events = pa->events;
|
2013-01-15 12:39:48 +08:00
|
|
|
pollfds[count_pollfds++].revents = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWS_CALLBACK_DEL_POLL_FD:
|
2013-01-17 11:16:15 +08:00
|
|
|
if (!--count_pollfds)
|
|
|
|
break;
|
2014-02-15 20:21:02 +08:00
|
|
|
m = fd_lookup[pa->fd];
|
2013-01-17 11:16:15 +08:00
|
|
|
/* have the last guy take up the vacant slot */
|
|
|
|
pollfds[m] = pollfds[count_pollfds];
|
|
|
|
fd_lookup[pollfds[count_pollfds].fd] = m;
|
2013-01-15 12:39:48 +08:00
|
|
|
break;
|
|
|
|
|
2014-02-15 20:21:02 +08:00
|
|
|
case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
|
|
|
|
pollfds[fd_lookup[pa->fd]].events = pa->events;
|
2013-01-15 12:39:48 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
#endif
|
2013-01-17 11:16:15 +08:00
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
case LWS_CALLBACK_GET_THREAD_ID:
|
|
|
|
/*
|
|
|
|
* if you will call "libwebsocket_callback_on_writable"
|
|
|
|
* from a different thread, return the caller thread ID
|
|
|
|
* here so lws can use this information to work out if it
|
2014-03-06 11:57:50 +01:00
|
|
|
* should signal the poll() loop to exit and restart early
|
2013-12-25 16:34:37 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* return pthread_getthreadid_np(); */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-10-17 08:38:44 +08:00
|
|
|
|
|
|
|
try_to_reuse:
|
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2010-11-12 10:44:16 +00:00
|
|
|
}
|
|
|
|
|
2011-02-13 08:54:05 +00:00
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
/* dumb_increment protocol */
|
|
|
|
|
2010-12-19 20:50:01 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
struct per_session_data__dumb_increment {
|
|
|
|
int number;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2012-04-09 15:09:01 +08:00
|
|
|
callback_dumb_increment(struct libwebsocket_context *context,
|
2011-02-14 09:14:25 +00:00
|
|
|
struct libwebsocket *wsi,
|
2010-11-12 10:44:16 +00:00
|
|
|
enum libwebsocket_callback_reasons reason,
|
2010-11-13 10:03:47 +00:00
|
|
|
void *user, void *in, size_t len)
|
2010-11-12 10:44:16 +00:00
|
|
|
{
|
2013-03-23 09:46:18 +08:00
|
|
|
int n, m;
|
2011-01-27 06:26:52 +00:00
|
|
|
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
|
2010-11-12 10:44:16 +00:00
|
|
|
LWS_SEND_BUFFER_POST_PADDING];
|
2011-01-27 06:26:52 +00:00
|
|
|
unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
|
2013-02-01 08:42:15 +08:00
|
|
|
struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
switch (reason) {
|
2010-11-11 12:28:29 +00:00
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
case LWS_CALLBACK_ESTABLISHED:
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_info("callback_dumb_increment: "
|
2012-04-09 15:09:01 +08:00
|
|
|
"LWS_CALLBACK_ESTABLISHED\n");
|
2010-11-12 10:44:16 +00:00
|
|
|
pss->number = 0;
|
|
|
|
break;
|
|
|
|
|
2013-01-29 17:57:39 +08:00
|
|
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
2011-01-27 06:26:52 +00:00
|
|
|
n = sprintf((char *)p, "%d", pss->number++);
|
2013-03-23 09:46:18 +08:00
|
|
|
m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
|
|
|
|
if (m < n) {
|
2013-02-17 09:14:08 +08:00
|
|
|
lwsl_err("ERROR %d writing to di socket\n", n);
|
|
|
|
return -1;
|
2010-11-11 12:28:29 +00:00
|
|
|
}
|
2011-03-07 07:08:07 +00:00
|
|
|
if (close_testing && pss->number == 50) {
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_info("close tesing limit, closing\n");
|
2013-02-12 10:19:08 +08:00
|
|
|
return -1;
|
2011-03-07 07:08:07 +00:00
|
|
|
}
|
2010-11-12 10:44:16 +00:00
|
|
|
break;
|
2010-11-11 12:52:28 +00:00
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
case LWS_CALLBACK_RECEIVE:
|
2013-01-19 11:32:18 +08:00
|
|
|
// fprintf(stderr, "rx %d\n", (int)len);
|
2010-11-12 10:44:16 +00:00
|
|
|
if (len < 6)
|
|
|
|
break;
|
2013-02-01 08:42:15 +08:00
|
|
|
if (strcmp((const char *)in, "reset\n") == 0)
|
2010-11-12 10:44:16 +00:00
|
|
|
pss->number = 0;
|
|
|
|
break;
|
2011-02-13 08:54:05 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
replace per header mallocs with single malloc 3 level struct
This big patch replaces the malloc / realloc per header
approach used until now with a single three-level struct
that gets malloc'd during the header union phase and freed
in one go when we transition to a different union phase.
It's more expensive in that we malloc a bit more than 4Kbytes,
but it's a lot cheaper in terms of malloc, frees, heap fragmentation,
no reallocs, nothing to configure. It also moves from arrays of
pointers (8 bytes on x86_64) to unsigned short offsets into the
data array, (2 bytes on all platforms).
The 3-level thing is all in one struct
- array indexed by the header enum, pointing to first "fragment" index
(ie, header type to fragment lookup, or 0 for none)
- array of fragments indexes, enough for 2 x the number of known headers
(fragment array... note that fragments can point to a "next"
fragment if the same header is spread across multiple entries)
- linear char array where the known header payload gets written
(fragments point into null-terminated strings stored in here,
only the known header content is stored)
http headers can legally be split over multiple headers of the same
name which should be concatenated. This scheme does not linearly
conatenate them but uses a linked list in the fragment structs to
link them. There are apis to get the total length and copy out a
linear, concatenated version to a buffer.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-02-10 18:02:31 +08:00
|
|
|
dump_handshake_info(wsi);
|
2011-02-13 08:54:05 +00:00
|
|
|
/* you could return non-zero here and kill the connection */
|
|
|
|
break;
|
2010-11-12 10:44:16 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2010-10-29 14:15:22 +01:00
|
|
|
}
|
2010-10-31 12:42:52 +00:00
|
|
|
|
2010-10-29 14:15:22 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
/* lws-mirror_protocol */
|
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
#define MAX_MESSAGE_QUEUE 32
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
static int
|
2012-04-09 15:09:01 +08:00
|
|
|
callback_lws_mirror(struct libwebsocket_context *context,
|
2011-02-14 09:14:25 +00:00
|
|
|
struct libwebsocket *wsi,
|
2010-11-12 13:10:40 +00:00
|
|
|
enum libwebsocket_callback_reasons reason,
|
2010-11-13 10:03:47 +00:00
|
|
|
void *user, void *in, size_t len)
|
2010-11-12 13:10:40 +00:00
|
|
|
{
|
|
|
|
int n;
|
2013-02-01 08:42:15 +08:00
|
|
|
struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
switch (reason) {
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
case LWS_CALLBACK_ESTABLISHED:
|
2013-03-16 11:24:23 +08:00
|
|
|
lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
|
2011-01-27 06:26:52 +00:00
|
|
|
pss->ringbuffer_tail = ringbuffer_head;
|
|
|
|
pss->wsi = wsi;
|
|
|
|
break;
|
|
|
|
|
2013-02-11 12:05:54 +08:00
|
|
|
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;
|
|
|
|
|
2011-03-07 20:47:39 +00:00
|
|
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
2011-03-07 07:08:07 +00:00
|
|
|
if (close_testing)
|
|
|
|
break;
|
2013-01-20 17:08:31 +08:00
|
|
|
while (pss->ringbuffer_tail != ringbuffer_head) {
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
n = libwebsocket_write(wsi, (unsigned char *)
|
|
|
|
ringbuffer[pss->ringbuffer_tail].payload +
|
|
|
|
LWS_SEND_BUFFER_PRE_PADDING,
|
|
|
|
ringbuffer[pss->ringbuffer_tail].len,
|
|
|
|
LWS_WRITE_TEXT);
|
2013-12-10 21:34:30 +08:00
|
|
|
if (n < 0) {
|
2013-02-17 09:14:08 +08:00
|
|
|
lwsl_err("ERROR %d writing to mirror socket\n", n);
|
|
|
|
return -1;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2013-03-16 11:24:23 +08:00
|
|
|
if (n < ringbuffer[pss->ringbuffer_tail].len)
|
|
|
|
lwsl_err("mirror partial write %d vs %d\n",
|
|
|
|
n, ringbuffer[pss->ringbuffer_tail].len);
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
|
|
|
|
pss->ringbuffer_tail = 0;
|
|
|
|
else
|
|
|
|
pss->ringbuffer_tail++;
|
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
if (((ringbuffer_head - pss->ringbuffer_tail) &
|
2013-03-16 12:32:27 +08:00
|
|
|
(MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
|
|
|
|
libwebsocket_rx_flow_allow_all_protocol(
|
|
|
|
libwebsockets_get_protocol(wsi));
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
// lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2014-08-18 22:49:39 +08:00
|
|
|
if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
|
2013-01-20 17:08:31 +08:00
|
|
|
libwebsocket_callback_on_writable(context, wsi);
|
2013-03-16 11:24:23 +08:00
|
|
|
break;
|
2013-01-20 17:08:31 +08:00
|
|
|
}
|
2013-03-16 11:24:23 +08:00
|
|
|
/*
|
|
|
|
* for tests with chrome on same machine as client and
|
|
|
|
* server, this is needed to stop chrome choking
|
|
|
|
*/
|
2014-03-29 07:52:01 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
Sleep(1);
|
|
|
|
#else
|
2013-03-16 11:24:23 +08:00
|
|
|
usleep(1);
|
2014-03-29 07:52:01 +01:00
|
|
|
#endif
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-11-12 13:10:40 +00:00
|
|
|
case LWS_CALLBACK_RECEIVE:
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
if (((ringbuffer_head - pss->ringbuffer_tail) &
|
|
|
|
(MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_err("dropping!\n");
|
2013-01-17 16:50:35 +08:00
|
|
|
goto choke;
|
|
|
|
}
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
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++;
|
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
if (((ringbuffer_head - pss->ringbuffer_tail) &
|
2013-01-20 17:08:31 +08:00
|
|
|
(MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
|
2013-01-17 16:50:35 +08:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
choke:
|
2013-03-16 12:32:27 +08:00
|
|
|
lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
|
|
|
|
libwebsocket_rx_flow_control(wsi, 0);
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-19 11:32:18 +08:00
|
|
|
// lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
|
2013-01-17 16:50:35 +08:00
|
|
|
done:
|
2011-01-27 06:26:52 +00:00
|
|
|
libwebsocket_callback_on_writable_all_protocol(
|
|
|
|
libwebsockets_get_protocol(wsi));
|
2010-11-12 13:10:40 +00:00
|
|
|
break;
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2011-02-13 08:54:05 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
replace per header mallocs with single malloc 3 level struct
This big patch replaces the malloc / realloc per header
approach used until now with a single three-level struct
that gets malloc'd during the header union phase and freed
in one go when we transition to a different union phase.
It's more expensive in that we malloc a bit more than 4Kbytes,
but it's a lot cheaper in terms of malloc, frees, heap fragmentation,
no reallocs, nothing to configure. It also moves from arrays of
pointers (8 bytes on x86_64) to unsigned short offsets into the
data array, (2 bytes on all platforms).
The 3-level thing is all in one struct
- array indexed by the header enum, pointing to first "fragment" index
(ie, header type to fragment lookup, or 0 for none)
- array of fragments indexes, enough for 2 x the number of known headers
(fragment array... note that fragments can point to a "next"
fragment if the same header is spread across multiple entries)
- linear char array where the known header payload gets written
(fragments point into null-terminated strings stored in here,
only the known header content is stored)
http headers can legally be split over multiple headers of the same
name which should be concatenated. This scheme does not linearly
conatenate them but uses a linked list in the fragment structs to
link them. There are apis to get the total length and copy out a
linear, concatenated version to a buffer.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-02-10 18:02:31 +08:00
|
|
|
dump_handshake_info(wsi);
|
2011-02-13 08:54:05 +00:00
|
|
|
/* you could return non-zero here and kill the connection */
|
|
|
|
break;
|
2010-11-12 13:10:40 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-12 10:44:16 +00:00
|
|
|
/* list of supported protocols and callbacks */
|
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
static struct libwebsocket_protocols protocols[] = {
|
2010-12-19 20:50:01 +00:00
|
|
|
/* first protocol must always be HTTP handler */
|
2011-03-02 22:03:47 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
"http-only", /* name */
|
|
|
|
callback_http, /* callback */
|
2013-02-14 23:06:37 +08:00
|
|
|
sizeof (struct per_session_data__http), /* per_session_data_size */
|
2013-02-06 21:10:16 +09:00
|
|
|
0, /* max frame size / rx buffer */
|
2010-11-12 10:44:16 +00:00
|
|
|
},
|
2011-03-02 22:03:47 +00:00
|
|
|
{
|
|
|
|
"dumb-increment-protocol",
|
|
|
|
callback_dumb_increment,
|
|
|
|
sizeof(struct per_session_data__dumb_increment),
|
2013-02-06 21:10:16 +09:00
|
|
|
10,
|
2010-11-12 10:44:16 +00:00
|
|
|
},
|
2011-03-02 22:03:47 +00:00
|
|
|
{
|
|
|
|
"lws-mirror-protocol",
|
|
|
|
callback_lws_mirror,
|
2013-02-06 21:10:16 +09:00
|
|
|
sizeof(struct per_session_data__lws_mirror),
|
2013-02-09 14:07:32 +08:00
|
|
|
128,
|
2010-11-12 13:10:40 +00:00
|
|
|
},
|
2013-02-06 21:10:16 +09:00
|
|
|
{ NULL, NULL, 0, 0 } /* terminator */
|
2010-11-12 10:44:16 +00:00
|
|
|
};
|
|
|
|
|
2013-01-30 08:02:26 +08:00
|
|
|
void sighandler(int sig)
|
|
|
|
{
|
|
|
|
force_exit = 1;
|
2014-03-06 11:57:50 +01:00
|
|
|
libwebsocket_cancel_service(context);
|
2013-01-30 08:02:26 +08:00
|
|
|
}
|
|
|
|
|
2010-10-31 07:40:33 +00:00
|
|
|
static struct option options[] = {
|
2011-01-27 06:26:52 +00:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2013-01-10 22:28:59 +08:00
|
|
|
{ "debug", required_argument, NULL, 'd' },
|
2011-01-27 06:26:52 +00:00
|
|
|
{ "port", required_argument, NULL, 'p' },
|
|
|
|
{ "ssl", no_argument, NULL, 's' },
|
2013-12-14 11:41:29 +08:00
|
|
|
{ "allow-non-ssl", no_argument, NULL, 'a' },
|
2012-04-09 15:09:01 +08:00
|
|
|
{ "interface", required_argument, NULL, 'i' },
|
2011-03-07 07:08:07 +00:00
|
|
|
{ "closetest", no_argument, NULL, 'c' },
|
2014-04-11 13:14:37 +08:00
|
|
|
{ "libev", no_argument, NULL, 'e' },
|
|
|
|
#ifndef LWS_NO_DAEMONIZE
|
2013-01-19 11:11:42 +08:00
|
|
|
{ "daemonize", no_argument, NULL, 'D' },
|
|
|
|
#endif
|
2013-03-18 17:08:25 +00:00
|
|
|
{ "resource_path", required_argument, NULL, 'r' },
|
2010-10-31 07:40:33 +00:00
|
|
|
{ NULL, 0, 0, 0 }
|
|
|
|
};
|
2010-10-29 14:15:22 +01:00
|
|
|
|
2010-10-31 07:40:33 +00:00
|
|
|
int main(int argc, char **argv)
|
2010-10-29 14:15:22 +01:00
|
|
|
{
|
2013-03-18 17:08:25 +00:00
|
|
|
char cert_path[1024];
|
|
|
|
char key_path[1024];
|
2010-10-31 07:40:33 +00:00
|
|
|
int n = 0;
|
2010-12-19 20:50:01 +00:00
|
|
|
int use_ssl = 0;
|
2011-01-30 20:57:25 +00:00
|
|
|
int opts = 0;
|
2011-02-19 08:32:53 +00:00
|
|
|
char interface_name[128] = "";
|
2013-02-11 17:52:23 +01:00
|
|
|
const char *iface = NULL;
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
2013-01-19 11:11:42 +08:00
|
|
|
int syslog_options = LOG_PID | LOG_PERROR;
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2014-07-05 11:52:58 +08:00
|
|
|
unsigned int ms, oldms = 0;
|
2013-02-09 14:01:09 +08:00
|
|
|
struct lws_context_creation_info info;
|
2013-01-29 17:57:39 +08:00
|
|
|
|
2013-01-19 11:32:18 +08:00
|
|
|
int debug_level = 7;
|
2013-01-21 12:58:04 +08:00
|
|
|
#ifndef LWS_NO_DAEMONIZE
|
2013-01-19 11:11:42 +08:00
|
|
|
int daemonize = 0;
|
|
|
|
#endif
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2013-02-09 14:01:09 +08:00
|
|
|
memset(&info, 0, sizeof info);
|
|
|
|
info.port = 7681;
|
|
|
|
|
2010-10-31 07:40:33 +00:00
|
|
|
while (n >= 0) {
|
2014-04-11 13:14:37 +08:00
|
|
|
n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
|
2010-10-31 07:40:33 +00:00
|
|
|
if (n < 0)
|
|
|
|
continue;
|
|
|
|
switch (n) {
|
2014-04-11 13:14:37 +08:00
|
|
|
case 'e':
|
|
|
|
opts |= LWS_SERVER_OPTION_LIBEV;
|
|
|
|
break;
|
2013-01-21 12:58:04 +08:00
|
|
|
#ifndef LWS_NO_DAEMONIZE
|
2013-01-19 11:11:42 +08:00
|
|
|
case 'D':
|
|
|
|
daemonize = 1;
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
2013-01-19 11:11:42 +08:00
|
|
|
syslog_options &= ~LOG_PERROR;
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-19 11:11:42 +08:00
|
|
|
break;
|
|
|
|
#endif
|
2013-01-10 22:28:59 +08:00
|
|
|
case 'd':
|
2013-01-19 11:32:18 +08:00
|
|
|
debug_level = atoi(optarg);
|
2013-01-10 22:28:59 +08:00
|
|
|
break;
|
2010-11-08 17:03:03 +00:00
|
|
|
case 's':
|
|
|
|
use_ssl = 1;
|
|
|
|
break;
|
2013-12-14 11:41:29 +08:00
|
|
|
case 'a':
|
|
|
|
opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
|
|
|
|
break;
|
2010-10-31 07:40:33 +00:00
|
|
|
case 'p':
|
2013-02-09 14:01:09 +08:00
|
|
|
info.port = atoi(optarg);
|
2010-10-31 07:40:33 +00:00
|
|
|
break;
|
2011-02-19 08:32:53 +00:00
|
|
|
case 'i':
|
|
|
|
strncpy(interface_name, optarg, sizeof interface_name);
|
|
|
|
interface_name[(sizeof interface_name) - 1] = '\0';
|
2013-02-11 17:52:23 +01:00
|
|
|
iface = interface_name;
|
2011-02-19 08:32:53 +00:00
|
|
|
break;
|
2011-03-07 07:08:07 +00:00
|
|
|
case 'c':
|
|
|
|
close_testing = 1;
|
|
|
|
fprintf(stderr, " Close testing mode -- closes on "
|
|
|
|
"client after 50 dumb increments"
|
|
|
|
"and suppresses lws_mirror spam\n");
|
|
|
|
break;
|
2013-03-18 17:08:25 +00:00
|
|
|
case 'r':
|
|
|
|
resource_path = optarg;
|
|
|
|
printf("Setting resource path to \"%s\"\n", resource_path);
|
|
|
|
break;
|
2010-10-31 07:40:33 +00:00
|
|
|
case 'h':
|
2010-10-31 12:42:52 +00:00
|
|
|
fprintf(stderr, "Usage: test-server "
|
2013-01-10 22:28:59 +08:00
|
|
|
"[--port=<p>] [--ssl] "
|
2013-03-18 17:08:25 +00:00
|
|
|
"[-d <log bitfield>] "
|
|
|
|
"[--resource_path <path>]\n");
|
2010-10-31 07:40:33 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2010-11-08 17:03:03 +00:00
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
|
2013-01-19 11:11:42 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
2013-01-19 13:56:10 +08:00
|
|
|
#endif
|
2013-01-30 08:02:26 +08:00
|
|
|
|
|
|
|
signal(SIGINT, sighandler);
|
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
2013-01-19 11:11:42 +08:00
|
|
|
/* we will only try to log things according to our debug_level */
|
|
|
|
setlogmask(LOG_UPTO (LOG_DEBUG));
|
|
|
|
openlog("lwsts", syslog_options, LOG_DAEMON);
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-19 11:32:18 +08:00
|
|
|
|
|
|
|
/* tell the library what debug level to emit and to send it to syslog */
|
|
|
|
lws_set_log_level(debug_level, lwsl_emit_syslog);
|
|
|
|
|
2013-01-19 11:11:42 +08:00
|
|
|
lwsl_notice("libwebsockets test server - "
|
2015-03-28 11:35:40 +08:00
|
|
|
"(C) Copyright 2010-2015 Andy Green <andy@warmcat.com> - "
|
2013-01-19 11:32:18 +08:00
|
|
|
"licensed under LGPL2.1\n");
|
2014-10-14 15:24:31 +02:00
|
|
|
|
|
|
|
printf("Using resource path \"%s\"\n", resource_path);
|
2013-01-20 20:51:14 +08:00
|
|
|
#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
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2013-02-11 17:52:23 +01:00
|
|
|
info.iface = iface;
|
2013-02-09 14:01:09 +08:00
|
|
|
info.protocols = protocols;
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2013-02-13 09:29:26 +08:00
|
|
|
info.extensions = libwebsocket_get_internal_extensions();
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2013-02-09 14:01:09 +08:00
|
|
|
if (!use_ssl) {
|
|
|
|
info.ssl_cert_filepath = NULL;
|
|
|
|
info.ssl_private_key_filepath = NULL;
|
|
|
|
} else {
|
2013-05-02 08:15:53 +08:00
|
|
|
if (strlen(resource_path) > sizeof(cert_path) - 32) {
|
|
|
|
lwsl_err("resource path too long\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
|
|
|
|
resource_path);
|
2013-03-18 17:08:25 +00:00
|
|
|
|
|
|
|
info.ssl_cert_filepath = cert_path;
|
|
|
|
info.ssl_private_key_filepath = key_path;
|
2013-02-09 14:01:09 +08:00
|
|
|
}
|
|
|
|
info.gid = -1;
|
|
|
|
info.uid = -1;
|
|
|
|
info.options = opts;
|
|
|
|
|
|
|
|
context = libwebsocket_create_context(&info);
|
2011-01-22 12:51:57 +00:00
|
|
|
if (context == NULL) {
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_err("libwebsocket init failed\n");
|
2010-10-29 14:15:22 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-20 12:58:38 +08:00
|
|
|
n = 0;
|
2013-01-30 08:02:26 +08:00
|
|
|
while (n >= 0 && !force_exit) {
|
2011-01-27 06:26:52 +00:00
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
2010-12-18 15:13:50 +00:00
|
|
|
|
|
|
|
/*
|
2013-01-29 17:57:39 +08:00
|
|
|
* 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)
|
2010-12-18 15:13:50 +00:00
|
|
|
*/
|
|
|
|
|
2014-07-05 11:52:58 +08:00
|
|
|
ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
|
|
|
if ((ms - oldms) > 50) {
|
2013-01-29 17:57:39 +08:00
|
|
|
libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
|
2014-07-05 11:52:58 +08:00
|
|
|
oldms = ms;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this represents an existing server's single poll action
|
|
|
|
* which also includes libwebsocket sockets
|
|
|
|
*/
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
n = poll(pollfds, count_pollfds, 50);
|
|
|
|
if (n < 0)
|
|
|
|
continue;
|
|
|
|
|
2013-01-15 13:40:23 +08:00
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
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
|
2013-02-02 23:02:56 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-07-20 12:58:38 +08:00
|
|
|
n = libwebsocket_service(context, 50);
|
2013-01-15 12:39:48 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
|
|
|
|
2013-01-15 12:39:48 +08:00
|
|
|
#ifdef EXTERNAL_POLL
|
|
|
|
done:
|
2011-01-20 10:23:50 +00:00
|
|
|
#endif
|
|
|
|
|
2011-01-23 16:50:33 +00:00
|
|
|
libwebsocket_context_destroy(context);
|
|
|
|
|
2013-01-19 11:32:18 +08:00
|
|
|
lwsl_notice("libwebsockets-test-server exited cleanly\n");
|
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
2013-01-19 11:32:18 +08:00
|
|
|
closelog();
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-19 11:32:18 +08:00
|
|
|
|
2010-10-29 14:15:22 +01:00
|
|
|
return 0;
|
|
|
|
}
|