2011-03-07 17:54:06 +00:00
|
|
|
/*
|
2010-11-08 17:12:19 +00:00
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
2010-12-19 22:13:26 +00:00
|
|
|
*
|
2010-11-08 17:12:19 +00:00
|
|
|
* Copyright (C) 2010 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
|
2010-10-31 17:51:39 +00:00
|
|
|
*/
|
|
|
|
|
2010-11-08 20:20:42 +00:00
|
|
|
#include "private-libwebsockets.h"
|
2010-10-28 22:36:01 +01:00
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2013-01-09 15:29:00 +08:00
|
|
|
#include <tchar.h>
|
2013-02-11 17:52:23 +01:00
|
|
|
#include <mstcpip.h>
|
2014-02-28 01:31:39 +01:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
#define vsnprintf _vsnprintf
|
|
|
|
#endif
|
2011-03-02 22:03:47 +00:00
|
|
|
#else
|
2013-01-12 20:39:47 +08:00
|
|
|
#ifdef LWS_BUILTIN_GETIFADDRS
|
|
|
|
#include <getifaddrs.h>
|
|
|
|
#else
|
2011-03-02 22:03:47 +00:00
|
|
|
#include <ifaddrs.h>
|
2013-01-12 20:39:47 +08:00
|
|
|
#endif
|
2013-02-06 15:26:58 +09:00
|
|
|
#include <syslog.h>
|
2011-03-09 15:13:52 +00:00
|
|
|
#include <sys/un.h>
|
2012-05-03 12:32:38 +08:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netdb.h>
|
2011-03-02 22:03:47 +00:00
|
|
|
#endif
|
2011-03-05 16:12:04 +00:00
|
|
|
|
2014-02-28 00:57:19 +01:00
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
2013-12-25 16:34:37 +08:00
|
|
|
#include <sys/types.h>
|
2014-02-28 00:57:19 +01:00
|
|
|
#endif
|
2013-12-25 16:34:37 +08:00
|
|
|
|
2011-03-05 16:12:04 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
int openssl_websocket_private_data_index;
|
|
|
|
#endif
|
|
|
|
|
2012-04-12 13:26:49 +08:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
#include "../win32port/win32helpers/websock-w32.c"
|
|
|
|
#else
|
|
|
|
#ifdef __MINGW64__
|
|
|
|
#include "../win32port/win32helpers/websock-w32.c"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2013-02-01 10:50:15 +08:00
|
|
|
#ifndef LWS_BUILD_HASH
|
|
|
|
#define LWS_BUILD_HASH "unknown-build-hash"
|
|
|
|
#endif
|
2013-01-20 17:08:31 +08:00
|
|
|
|
2013-01-19 12:18:07 +08:00
|
|
|
static int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
2013-01-19 11:32:18 +08:00
|
|
|
static void lwsl_emit_stderr(int level, const char *line);
|
|
|
|
static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr;
|
|
|
|
|
2013-02-01 10:50:15 +08:00
|
|
|
static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
static const char * const log_level_names[] = {
|
2013-01-10 19:50:35 +08:00
|
|
|
"ERR",
|
|
|
|
"WARN",
|
2013-01-19 12:18:07 +08:00
|
|
|
"NOTICE",
|
2013-01-10 19:50:35 +08:00
|
|
|
"INFO",
|
|
|
|
"DEBUG",
|
|
|
|
"PARSER",
|
|
|
|
"HEADER",
|
|
|
|
"EXTENSION",
|
|
|
|
"CLIENT",
|
2013-01-29 12:36:17 +08:00
|
|
|
"LATENCY",
|
2013-01-10 19:50:35 +08:00
|
|
|
};
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
#ifndef LWS_NO_CLIENT
|
|
|
|
extern int lws_client_socket_service(
|
|
|
|
struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi, struct pollfd *pollfd);
|
|
|
|
#endif
|
|
|
|
#ifndef LWS_NO_SERVER
|
|
|
|
extern int lws_server_socket_service(
|
|
|
|
struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi, struct pollfd *pollfd);
|
|
|
|
#endif
|
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
|
|
|
|
#ifdef LWS_HAS_PPOLL
|
|
|
|
/*
|
|
|
|
* set to the Thread ID that's doing the service loop just before entry to ppoll
|
|
|
|
* indicates service thread likely idling in ppoll()
|
|
|
|
* volatile because other threads may check it as part of processing for pollfd
|
|
|
|
* event change.
|
|
|
|
*/
|
|
|
|
static volatile int lws_idling_ppoll_tid;
|
|
|
|
#endif
|
|
|
|
|
2013-02-01 10:50:15 +08:00
|
|
|
/**
|
|
|
|
* lws_get_library_version: get version and git hash library built from
|
|
|
|
*
|
|
|
|
* returns a const char * to a string like "1.1 178d78c"
|
|
|
|
* representing the library version followed by the git head hash it
|
|
|
|
* was built from
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE const char *
|
2013-02-01 10:50:15 +08:00
|
|
|
lws_get_library_version(void)
|
|
|
|
{
|
|
|
|
return library_version;
|
|
|
|
}
|
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
static unsigned long long time_in_microseconds()
|
|
|
|
{
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
|
|
|
|
FILETIME filetime;
|
|
|
|
ULARGE_INTEGER datetime;
|
|
|
|
|
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
GetCurrentFT(&filetime);
|
|
|
|
#else
|
|
|
|
GetSystemTimeAsFileTime(&filetime);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
|
|
|
|
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
|
|
|
|
* prevent alignment faults on 64-bit Windows).
|
|
|
|
*/
|
|
|
|
memcpy(&datetime, &filetime, sizeof(datetime));
|
|
|
|
|
|
|
|
/* Windows file times are in 100s of nanoseconds. */
|
|
|
|
return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10;
|
|
|
|
#else
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return (tv.tv_sec * 1000000) + tv.tv_usec;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-02-28 01:31:39 +01:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
static inline time_t time(time_t *t)
|
|
|
|
{
|
|
|
|
time_t ret = time_in_microseconds() / 1000000;
|
|
|
|
*t = ret;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
int
|
2013-02-11 17:13:32 +08:00
|
|
|
insert_wsi_socket_into_fds(struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi)
|
2011-02-12 11:57:43 +00:00
|
|
|
{
|
2014-02-18 13:38:14 +01:00
|
|
|
struct libwebsocket_pollargs pa = { wsi->sock, POLLIN, 0 };
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (context->fds_count >= context->max_fds) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err("Too many fds (%d)\n", context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2014-01-11 13:12:34 +08:00
|
|
|
if (wsi->sock >= context->max_fds) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err("Socket fd %d is too high (%d)\n",
|
|
|
|
wsi->sock, context->max_fds);
|
2011-02-12 11:57:43 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
assert(wsi);
|
2013-09-18 12:59:33 +08:00
|
|
|
assert(wsi->sock >= 0);
|
2013-01-17 12:26:48 +08:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n",
|
|
|
|
wsi, wsi->sock, context->fds_count);
|
2013-01-17 12:26:48 +08:00
|
|
|
|
2013-12-18 09:48:26 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_LOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
context->lws_lookup[wsi->sock] = wsi;
|
|
|
|
wsi->position_in_fds_table = context->fds_count;
|
|
|
|
context->fds[context->fds_count].fd = wsi->sock;
|
|
|
|
context->fds[context->fds_count].events = POLLIN;
|
|
|
|
context->fds[context->fds_count++].revents = 0;
|
|
|
|
|
|
|
|
/* external POLL support via protocol 0 */
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_ADD_POLL_FD,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-12-18 09:48:26 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_UNLOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *)&pa, 0);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
static int
|
2013-02-11 17:13:32 +08:00
|
|
|
remove_wsi_socket_from_fds(struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi)
|
2011-02-12 11:57:43 +00:00
|
|
|
{
|
2013-01-17 12:26:48 +08:00
|
|
|
int m;
|
2014-02-15 20:19:26 +08:00
|
|
|
struct libwebsocket_pollargs pa = { wsi->sock, 0, 0};
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-12-18 09:48:26 +08:00
|
|
|
if (!--context->fds_count) {
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_LOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-01-17 12:26:48 +08:00
|
|
|
goto do_ext;
|
2013-12-18 09:48:26 +08:00
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (wsi->sock > context->max_fds) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err("Socket fd %d too high (%d)\n",
|
|
|
|
wsi->sock, context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_info("remove_wsi_socket_from_fds: wsi=%p, sock=%d, fds pos=%d\n",
|
|
|
|
wsi, wsi->sock, wsi->position_in_fds_table);
|
2013-01-17 12:26:48 +08:00
|
|
|
|
2013-12-18 09:48:26 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_LOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *)&pa, 0);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
m = wsi->position_in_fds_table; /* replace the contents for this */
|
|
|
|
|
|
|
|
/* have the last guy take up the vacant slot */
|
2013-02-11 17:13:32 +08:00
|
|
|
context->fds[m] = context->fds[context->fds_count];
|
|
|
|
/*
|
|
|
|
* end guy's fds_lookup entry remains unchanged
|
|
|
|
* (still same fd pointing to same wsi)
|
|
|
|
*/
|
2013-01-17 12:26:48 +08:00
|
|
|
/* end guy's "position in fds table" changed */
|
2013-02-11 17:13:32 +08:00
|
|
|
context->lws_lookup[context->fds[context->fds_count].fd]->
|
|
|
|
position_in_fds_table = m;
|
2013-01-17 12:26:48 +08:00
|
|
|
/* deletion guy's lws_lookup entry needs nuking */
|
2013-02-11 17:13:32 +08:00
|
|
|
context->lws_lookup[wsi->sock] = NULL;
|
|
|
|
/* removed wsi has no position any more */
|
|
|
|
wsi->position_in_fds_table = -1;
|
2013-01-17 12:26:48 +08:00
|
|
|
|
|
|
|
do_ext:
|
|
|
|
/* remove also from external POLL support via protocol 0 */
|
2014-02-15 20:19:26 +08:00
|
|
|
if (wsi->sock) {
|
2013-01-17 12:26:48 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
2013-02-15 22:36:30 +08:00
|
|
|
LWS_CALLBACK_DEL_POLL_FD, wsi->user_space,
|
2014-02-15 20:19:26 +08:00
|
|
|
(void *) &pa, 0);
|
|
|
|
}
|
2013-12-18 09:48:26 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
2014-02-15 20:19:26 +08:00
|
|
|
LWS_CALLBACK_UNLOCK_POLL,
|
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-01-17 12:26:48 +08:00
|
|
|
return 0;
|
2011-02-12 11:57:43 +00:00
|
|
|
}
|
|
|
|
|
2011-02-19 08:32:53 +00:00
|
|
|
|
2010-12-19 22:13:26 +00:00
|
|
|
void
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_close_and_free_session(struct libwebsocket_context *context,
|
2011-02-26 11:04:01 +00:00
|
|
|
struct libwebsocket *wsi, enum lws_close_status reason)
|
2010-11-03 11:13:06 +00:00
|
|
|
{
|
2010-12-18 15:13:50 +00:00
|
|
|
int n;
|
2011-02-14 09:14:25 +00:00
|
|
|
int old_state;
|
2011-02-10 09:07:05 +00:00
|
|
|
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 +
|
|
|
|
LWS_SEND_BUFFER_POST_PADDING];
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-07 07:08:18 +00:00
|
|
|
int ret;
|
|
|
|
int m;
|
|
|
|
struct lws_tokens eff_buf;
|
2011-05-23 10:00:03 +01:00
|
|
|
struct libwebsocket_extension *ext;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2011-02-14 08:03:48 +00:00
|
|
|
if (!wsi)
|
2010-12-18 15:13:50 +00:00
|
|
|
return;
|
|
|
|
|
2011-02-14 09:14:25 +00:00
|
|
|
old_state = wsi->state;
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2011-02-14 09:14:25 +00:00
|
|
|
if (old_state == WSI_STATE_DEAD_SOCKET)
|
2011-02-10 09:07:05 +00:00
|
|
|
return;
|
|
|
|
|
2013-02-15 22:48:58 +08:00
|
|
|
/* we tried the polite way... */
|
|
|
|
if (old_state == WSI_STATE_AWAITING_CLOSE_ACK)
|
|
|
|
goto just_kill_connection;
|
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
wsi->u.ws.close_reason = reason;
|
2011-03-07 07:08:12 +00:00
|
|
|
|
2013-09-20 20:26:12 +08:00
|
|
|
if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT ||
|
|
|
|
wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE) {
|
|
|
|
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL, NULL, 0);
|
|
|
|
|
|
|
|
free(wsi->u.hdr.ah);
|
|
|
|
goto just_kill_connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-11-19 13:38:16 +01:00
|
|
|
if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
|
|
|
|
if (wsi->u.http.post_buffer) {
|
|
|
|
free(wsi->u.http.post_buffer);
|
|
|
|
wsi->u.http.post_buffer = NULL;
|
|
|
|
}
|
2014-02-27 03:21:50 +01:00
|
|
|
if (wsi->u.http.fd != LWS_INVALID_FILE) {
|
|
|
|
lwsl_debug("closing http file\n");
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
CloseHandle(wsi->u.http.fd);
|
|
|
|
#else
|
2013-11-19 13:38:16 +01:00
|
|
|
close(wsi->u.http.fd);
|
2014-02-27 03:21:50 +01:00
|
|
|
#endif
|
|
|
|
wsi->u.http.fd = LWS_INVALID_FILE;
|
2013-11-19 13:38:16 +01:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
|
|
|
|
}
|
2013-01-22 07:20:08 +08:00
|
|
|
}
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-25 21:41:57 +01:00
|
|
|
/*
|
|
|
|
* are his extensions okay with him closing? Eg he might be a mux
|
|
|
|
* parent and just his ch1 aspect is closing?
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
if (!wsi->active_extensions[n]->callback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m = wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
|
|
|
LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE,
|
|
|
|
wsi->active_extensions_user[n], NULL, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if somebody vetoed actually closing him at this time....
|
|
|
|
* up to the extension to track the attempted close, let's
|
|
|
|
* just bail
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (m) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_ext("extension vetoed close\n");
|
2011-05-25 21:41:57 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-07 07:08:18 +00:00
|
|
|
/*
|
|
|
|
* flush any tx pending from extensions, since we may send close packet
|
|
|
|
* if there are problems with send, just nuke the connection
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
while (ret == 1) {
|
|
|
|
|
|
|
|
/* default to nobody has more to spill */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
eff_buf.token = NULL;
|
|
|
|
eff_buf.token_len = 0;
|
|
|
|
|
|
|
|
/* show every extension the new incoming data */
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
m = wsi->active_extensions[n]->callback(
|
2011-03-22 09:04:01 +00:00
|
|
|
wsi->protocol->owning_server,
|
|
|
|
wsi->active_extensions[n], wsi,
|
2011-03-07 07:08:18 +00:00
|
|
|
LWS_EXT_CALLBACK_FLUSH_PENDING_TX,
|
|
|
|
wsi->active_extensions_user[n], &eff_buf, 0);
|
|
|
|
if (m < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_ext("Extension reports fatal error\n");
|
2011-03-07 07:08:18 +00:00
|
|
|
goto just_kill_connection;
|
|
|
|
}
|
|
|
|
if (m)
|
|
|
|
/*
|
|
|
|
* at least one extension told us he has more
|
|
|
|
* to spill, so we will go around again after
|
|
|
|
*/
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* assuming they left us something to send, send it */
|
|
|
|
|
|
|
|
if (eff_buf.token_len)
|
|
|
|
if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
|
2013-02-23 10:50:10 +08:00
|
|
|
eff_buf.token_len) != eff_buf.token_len) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("close: ext spill failed\n");
|
2011-03-07 07:08:18 +00:00
|
|
|
goto just_kill_connection;
|
2013-01-17 14:46:43 +08:00
|
|
|
}
|
2011-03-07 07:08:18 +00:00
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-03-07 07:08:18 +00:00
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/*
|
|
|
|
* signal we are closing, libsocket_write will
|
|
|
|
* add any necessary version-specific stuff. If the write fails,
|
|
|
|
* no worries we are closing anyway. If we didn't initiate this
|
|
|
|
* close, then our state has been changed to
|
|
|
|
* WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this.
|
|
|
|
*
|
|
|
|
* Likewise if it's a second call to close this connection after we
|
|
|
|
* sent the close indication to the peer already, we are in state
|
|
|
|
* WSI_STATE_AWAITING_CLOSE_ACK and will skip doing this a second time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (old_state == WSI_STATE_ESTABLISHED &&
|
|
|
|
reason != LWS_CLOSE_STATUS_NOSTATUS) {
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("sending close indication...\n");
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2013-02-11 14:32:48 +08:00
|
|
|
/* make valgrind happy */
|
2013-02-11 17:13:32 +08:00
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
n = libwebsocket_write(wsi,
|
|
|
|
&buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
|
2011-03-07 07:08:12 +00:00
|
|
|
0, LWS_WRITE_CLOSE);
|
2013-02-23 10:50:10 +08:00
|
|
|
if (n >= 0) {
|
2011-03-07 07:08:12 +00:00
|
|
|
/*
|
|
|
|
* we have sent a nice protocol level indication we
|
|
|
|
* now wish to close, we should not send anything more
|
|
|
|
*/
|
|
|
|
|
|
|
|
wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
/*
|
|
|
|
* ...and we should wait for a reply for a bit
|
|
|
|
* out of politeness
|
|
|
|
*/
|
2011-03-07 07:08:12 +00:00
|
|
|
|
|
|
|
libwebsocket_set_timeout(wsi,
|
2013-01-17 14:46:43 +08:00
|
|
|
PENDING_TIMEOUT_CLOSE_ACK, 1);
|
2011-03-07 07:08:12 +00:00
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("sent close indication, awaiting ack\n");
|
2011-03-07 07:08:12 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_info("close: sending close packet failed, hanging up\n");
|
2013-01-17 14:46:43 +08:00
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/* else, the send failed and we should just hang up */
|
|
|
|
}
|
|
|
|
|
2011-03-07 07:08:18 +00:00
|
|
|
just_kill_connection:
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("close: just_kill_connection\n");
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/*
|
|
|
|
* we won't be servicing or receiving anything further from this guy
|
2013-01-17 12:26:48 +08:00
|
|
|
* delete socket from the internal poll list if still present
|
2011-03-07 07:08:12 +00:00
|
|
|
*/
|
2011-02-14 08:03:48 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
remove_wsi_socket_from_fds(context, wsi);
|
2011-02-14 08:03:48 +00:00
|
|
|
|
2010-11-03 11:13:06 +00:00
|
|
|
wsi->state = WSI_STATE_DEAD_SOCKET;
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
if ((old_state == WSI_STATE_ESTABLISHED ||
|
|
|
|
wsi->mode == LWS_CONNMODE_WS_SERVING ||
|
2013-02-12 12:52:39 +08:00
|
|
|
wsi->mode == LWS_CONNMODE_WS_CLIENT)) {
|
|
|
|
|
|
|
|
if (wsi->u.ws.rx_user_buffer) {
|
|
|
|
free(wsi->u.ws.rx_user_buffer);
|
|
|
|
wsi->u.ws.rx_user_buffer = NULL;
|
|
|
|
}
|
|
|
|
if (wsi->u.ws.rxflow_buffer) {
|
|
|
|
free(wsi->u.ws.rxflow_buffer);
|
|
|
|
wsi->u.ws.rxflow_buffer = NULL;
|
|
|
|
}
|
2013-12-09 14:16:17 +08:00
|
|
|
if (wsi->truncated_send_malloc) {
|
add explicit error for partial send
This patch adds code to handle the situation that a prepared user buffer could not all be sent on the
socket at once. There are two kinds of situation to handle
1) User code handles it: The connection only has extensions active that do not rewrite the buffer.
In this case, the patch caused libwebsocket_write() to simply return the amount of user buffer that
was consumed (this is specifically the amount of user buffer used in sending what was accepted,
nothing else). So user code can just advance its buffer that much and resume sending when the socket
is writable again. This continues the frame rather than starting a new one or new fragment.
2) The connections has extensions active which actually send something quite different than what the
user buffer contains, for example a compression extension. In this case, libwebsockets will dynamically
malloc a buffer to contain a copy of the remaining unsent data, request notifiction when writeable again,
and automatically spill and free this buffer with the highest priority before passing on the writable
notification to anything else. For this situation, the call to write will return that it used the
whole user buffer, even though part is still rebuffered.
This patch should enable libwebsockets to detect the two cases and take the appropriate action.
There are also two choices for user code to deal with partial sends.
1) Leave the no_buffer_all_partial_tx member in the protocol struct at zero. The library will dyamically
buffer anything you send that did not get completely written to the socket, and automatically spill it next
time the socket is writable. You can use this method if your sent frames are relatvely small and unlikely to get
truncated anyway.
2) Set the no_buffer_all_partial_tx member in the protocol struct. User code now needs to take care of the
return value from libwebsocket_write() and deal with resending the remainder if not all of the requested amount
got sent. You should use this method if you are sending large messages and want to maximize throughput and efficiency.
Since the new member no_buffer_all_partial_tx will be zero by default, this patch will auto-rebuffer any
partial sends by default. That's good for most cases but if you attempt to send large blocks, make sure you
follow option 2) above.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-10-17 08:09:19 +08:00
|
|
|
/* not going to be completed... nuke it */
|
2013-12-09 14:16:17 +08:00
|
|
|
free(wsi->truncated_send_malloc);
|
|
|
|
wsi->truncated_send_malloc = NULL;
|
add explicit error for partial send
This patch adds code to handle the situation that a prepared user buffer could not all be sent on the
socket at once. There are two kinds of situation to handle
1) User code handles it: The connection only has extensions active that do not rewrite the buffer.
In this case, the patch caused libwebsocket_write() to simply return the amount of user buffer that
was consumed (this is specifically the amount of user buffer used in sending what was accepted,
nothing else). So user code can just advance its buffer that much and resume sending when the socket
is writable again. This continues the frame rather than starting a new one or new fragment.
2) The connections has extensions active which actually send something quite different than what the
user buffer contains, for example a compression extension. In this case, libwebsockets will dynamically
malloc a buffer to contain a copy of the remaining unsent data, request notifiction when writeable again,
and automatically spill and free this buffer with the highest priority before passing on the writable
notification to anything else. For this situation, the call to write will return that it used the
whole user buffer, even though part is still rebuffered.
This patch should enable libwebsockets to detect the two cases and take the appropriate action.
There are also two choices for user code to deal with partial sends.
1) Leave the no_buffer_all_partial_tx member in the protocol struct at zero. The library will dyamically
buffer anything you send that did not get completely written to the socket, and automatically spill it next
time the socket is writable. You can use this method if your sent frames are relatvely small and unlikely to get
truncated anyway.
2) Set the no_buffer_all_partial_tx member in the protocol struct. User code now needs to take care of the
return value from libwebsocket_write() and deal with resending the remainder if not all of the requested amount
got sent. You should use this method if you are sending large messages and want to maximize throughput and efficiency.
Since the new member no_buffer_all_partial_tx will be zero by default, this patch will auto-rebuffer any
partial sends by default. That's good for most cases but if you attempt to send large blocks, make sure you
follow option 2) above.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-10-17 08:09:19 +08:00
|
|
|
}
|
2013-02-06 21:10:16 +09:00
|
|
|
}
|
|
|
|
|
2011-02-14 08:03:48 +00:00
|
|
|
/* tell the user it's all over for this guy */
|
|
|
|
|
2011-02-28 07:45:29 +00:00
|
|
|
if (wsi->protocol && wsi->protocol->callback &&
|
2012-04-09 15:09:01 +08:00
|
|
|
((old_state == WSI_STATE_ESTABLISHED) ||
|
|
|
|
(old_state == WSI_STATE_RETURNED_CLOSE_ALREADY) ||
|
|
|
|
(old_state == WSI_STATE_AWAITING_CLOSE_ACK))) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("calling back CLOSED\n");
|
2011-03-02 22:03:47 +00:00
|
|
|
wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED,
|
2010-11-13 10:03:47 +00:00
|
|
|
wsi->user_space, NULL, 0);
|
2013-06-29 10:16:18 +08:00
|
|
|
} else if ( wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED ) {
|
|
|
|
lwsl_debug("calling back CLOSED_HTTP\n");
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 );
|
2011-11-07 19:53:23 +08:00
|
|
|
} else
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("not calling back closed\n");
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-06 10:29:38 +00:00
|
|
|
/* deallocate any active extension contexts */
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
if (!wsi->active_extensions[n]->callback)
|
|
|
|
continue;
|
|
|
|
|
2011-03-22 09:04:01 +00:00
|
|
|
wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
|
|
|
LWS_EXT_CALLBACK_DESTROY,
|
|
|
|
wsi->active_extensions_user[n], NULL, 0);
|
2011-03-06 10:29:38 +00:00
|
|
|
|
|
|
|
free(wsi->active_extensions_user[n]);
|
|
|
|
}
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
/*
|
|
|
|
* inform all extensions in case they tracked this guy out of band
|
|
|
|
* even though not active on him specifically
|
|
|
|
*/
|
|
|
|
|
|
|
|
ext = context->extensions;
|
|
|
|
while (ext && ext->callback) {
|
|
|
|
ext->callback(context, ext, wsi,
|
|
|
|
LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
|
|
|
|
NULL, NULL, 0);
|
|
|
|
ext++;
|
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
/* lwsl_info("closing fd=%d\n", wsi->sock); */
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2010-11-08 17:03:03 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2011-01-27 06:26:52 +00:00
|
|
|
if (wsi->ssl) {
|
2010-11-08 17:03:03 +00:00
|
|
|
n = SSL_get_fd(wsi->ssl);
|
|
|
|
SSL_shutdown(wsi->ssl);
|
2013-01-14 15:35:02 +08:00
|
|
|
compatible_close(n);
|
2010-11-08 17:03:03 +00:00
|
|
|
SSL_free(wsi->ssl);
|
|
|
|
} else {
|
|
|
|
#endif
|
2013-01-17 14:46:43 +08:00
|
|
|
if (wsi->sock) {
|
|
|
|
n = shutdown(wsi->sock, SHUT_RDWR);
|
|
|
|
if (n)
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("closing: shutdown returned %d\n",
|
|
|
|
errno);
|
2013-01-17 14:46:43 +08:00
|
|
|
|
|
|
|
n = compatible_close(wsi->sock);
|
|
|
|
if (n)
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("closing: close returned %d\n",
|
|
|
|
errno);
|
2013-01-17 14:46:43 +08:00
|
|
|
}
|
2010-11-08 17:03:03 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
}
|
|
|
|
#endif
|
2014-02-15 19:25:50 +08:00
|
|
|
|
|
|
|
/* outermost destroy notification for wsi (user_space still intact) */
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0);
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
if (wsi->protocol && wsi->protocol->per_session_data_size &&
|
|
|
|
wsi->user_space) /* user code may own */
|
2010-11-12 10:44:16 +00:00
|
|
|
free(wsi->user_space);
|
|
|
|
|
2010-11-03 11:13:06 +00:00
|
|
|
free(wsi);
|
|
|
|
}
|
|
|
|
|
2011-02-13 08:37:12 +00:00
|
|
|
/**
|
|
|
|
* libwebsockets_get_peer_addresses() - Get client address information
|
2013-01-30 08:12:20 +08:00
|
|
|
* @context: Libwebsockets context
|
|
|
|
* @wsi: Local struct libwebsocket associated with
|
2011-02-13 08:37:12 +00:00
|
|
|
* @fd: Connection socket descriptor
|
|
|
|
* @name: Buffer to take client address name
|
|
|
|
* @name_len: Length of client address name buffer
|
|
|
|
* @rip: Buffer to take client address IP qotted quad
|
|
|
|
* @rip_len: Length of client address IP buffer
|
|
|
|
*
|
|
|
|
* This function fills in @name and @rip with the name and IP of
|
2012-04-09 15:09:01 +08:00
|
|
|
* the client connected with socket descriptor @fd. Names may be
|
|
|
|
* truncated if there is not enough room. If either cannot be
|
|
|
|
* determined, they will be returned as valid zero-length strings.
|
2011-02-13 08:37:12 +00:00
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void
|
2013-01-30 08:12:20 +08:00
|
|
|
libwebsockets_get_peer_addresses(struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi, int fd, char *name, int name_len,
|
2011-02-13 08:37:12 +00:00
|
|
|
char *rip, int rip_len)
|
|
|
|
{
|
2013-04-25 09:16:30 +08:00
|
|
|
socklen_t len;
|
2011-02-13 08:37:12 +00:00
|
|
|
struct sockaddr_in sin;
|
|
|
|
struct hostent *host;
|
|
|
|
struct hostent *host1;
|
|
|
|
char ip[128];
|
2011-03-09 15:02:20 +00:00
|
|
|
unsigned char *p;
|
2011-02-13 08:37:12 +00:00
|
|
|
int n;
|
2013-01-30 08:12:20 +08:00
|
|
|
int ret = -1;
|
2013-01-09 15:29:00 +08:00
|
|
|
#ifdef AF_LOCAL
|
2013-02-11 17:13:32 +08:00
|
|
|
struct sockaddr_un *un;
|
2013-01-09 15:29:00 +08:00
|
|
|
#endif
|
2011-02-13 08:37:12 +00:00
|
|
|
|
|
|
|
rip[0] = '\0';
|
|
|
|
name[0] = '\0';
|
|
|
|
|
2013-01-30 08:12:20 +08:00
|
|
|
lws_latency_pre(context, wsi);
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
len = sizeof(sin);
|
2011-02-13 08:37:12 +00:00
|
|
|
if (getpeername(fd, (struct sockaddr *) &sin, &len) < 0) {
|
2014-02-26 19:52:11 +01:00
|
|
|
lwsl_warn("getpeername: %s\n", strerror(errno));
|
2013-01-30 08:12:20 +08:00
|
|
|
goto bail;
|
2011-02-13 08:37:12 +00:00
|
|
|
}
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
host = gethostbyaddr((char *) &sin.sin_addr, sizeof(sin.sin_addr),
|
2011-02-13 08:37:12 +00:00
|
|
|
AF_INET);
|
|
|
|
if (host == NULL) {
|
2014-02-26 19:52:11 +01:00
|
|
|
lwsl_warn("gethostbyaddr: %s\n", strerror(errno));
|
2013-01-30 08:12:20 +08:00
|
|
|
goto bail;
|
2011-02-13 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(name, host->h_name, name_len);
|
|
|
|
name[name_len - 1] = '\0';
|
|
|
|
|
|
|
|
host1 = gethostbyname(host->h_name);
|
|
|
|
if (host1 == NULL)
|
2013-01-30 08:12:20 +08:00
|
|
|
goto bail;
|
2011-03-09 15:02:20 +00:00
|
|
|
p = (unsigned char *)host1;
|
2011-02-13 08:37:12 +00:00
|
|
|
n = 0;
|
|
|
|
while (p != NULL) {
|
2011-03-09 15:02:20 +00:00
|
|
|
p = (unsigned char *)host1->h_addr_list[n++];
|
2011-02-13 08:37:12 +00:00
|
|
|
if (p == NULL)
|
|
|
|
continue;
|
2011-03-10 18:14:01 +00:00
|
|
|
if ((host1->h_addrtype != AF_INET)
|
|
|
|
#ifdef AF_LOCAL
|
|
|
|
&& (host1->h_addrtype != AF_LOCAL)
|
|
|
|
#endif
|
|
|
|
)
|
2011-02-13 08:37:12 +00:00
|
|
|
continue;
|
|
|
|
|
2011-03-09 15:13:52 +00:00
|
|
|
if (host1->h_addrtype == AF_INET)
|
|
|
|
sprintf(ip, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
|
2011-03-10 18:14:01 +00:00
|
|
|
#ifdef AF_LOCAL
|
2011-03-09 15:13:52 +00:00
|
|
|
else {
|
|
|
|
un = (struct sockaddr_un *)p;
|
2012-04-09 15:09:01 +08:00
|
|
|
strncpy(ip, un->sun_path, sizeof(ip) - 1);
|
2011-03-09 15:13:52 +00:00
|
|
|
ip[sizeof(ip) - 1] = '\0';
|
|
|
|
}
|
2011-03-10 18:14:01 +00:00
|
|
|
#endif
|
2011-02-13 08:37:12 +00:00
|
|
|
p = NULL;
|
|
|
|
strncpy(rip, ip, rip_len);
|
|
|
|
rip[rip_len - 1] = '\0';
|
|
|
|
}
|
2013-01-30 08:12:20 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
bail:
|
|
|
|
lws_latency(context, wsi, "libwebsockets_get_peer_addresses", ret, 1);
|
2011-02-13 08:37:12 +00:00
|
|
|
}
|
2011-02-12 11:57:45 +00:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
|
2011-03-02 22:03:47 +00:00
|
|
|
void *buf, int len)
|
|
|
|
{
|
|
|
|
int n;
|
2013-01-10 12:35:18 +08:00
|
|
|
char *p = (char *)buf;
|
2011-03-02 22:03:47 +00:00
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
for (n = 0; n < len; n++)
|
|
|
|
p[n] = (unsigned char)rand();
|
|
|
|
#else
|
|
|
|
n = read(context->fd_random, p, len);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2013-02-09 12:25:31 +08:00
|
|
|
int lws_set_socket_options(struct libwebsocket_context *context, int fd)
|
|
|
|
{
|
|
|
|
int optval = 1;
|
|
|
|
socklen_t optlen = sizeof(optval);
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2013-02-09 12:25:31 +08:00
|
|
|
unsigned long optl = 0;
|
|
|
|
#endif
|
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
|
|
|
struct protoent *tcp_proto;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (context->ka_time) {
|
|
|
|
/* enable keepalive on this socket */
|
|
|
|
optval = 1;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
|
|
|
(const void *)&optval, optlen) < 0)
|
|
|
|
return 1;
|
|
|
|
|
2013-04-25 09:16:30 +08:00
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
|
2013-02-10 09:39:47 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* didn't find a way to set these per-socket, need to
|
|
|
|
* tune kernel systemwide values
|
|
|
|
*/
|
2013-02-11 17:52:23 +01:00
|
|
|
#elif WIN32
|
|
|
|
{
|
|
|
|
DWORD dwBytesRet;
|
|
|
|
struct tcp_keepalive alive;
|
|
|
|
alive.onoff = TRUE;
|
|
|
|
alive.keepalivetime = context->ka_time;
|
|
|
|
alive.keepaliveinterval = context->ka_interval;
|
|
|
|
|
|
|
|
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
|
|
|
|
NULL, 0, &dwBytesRet, NULL, NULL))
|
|
|
|
return 1;
|
|
|
|
}
|
2013-02-10 09:39:47 +08:00
|
|
|
#else
|
2013-02-09 12:25:31 +08:00
|
|
|
/* set the keepalive conditions we want on it too */
|
|
|
|
optval = context->ka_time;
|
|
|
|
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
|
|
|
|
(const void *)&optval, optlen) < 0)
|
|
|
|
return 1;
|
|
|
|
|
2013-02-22 09:16:20 +08:00
|
|
|
optval = context->ka_interval;
|
2013-02-09 12:25:31 +08:00
|
|
|
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
|
|
|
|
(const void *)&optval, optlen) < 0)
|
|
|
|
return 1;
|
|
|
|
|
2013-02-22 09:16:20 +08:00
|
|
|
optval = context->ka_probes;
|
2013-02-09 12:25:31 +08:00
|
|
|
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
|
|
|
|
(const void *)&optval, optlen) < 0)
|
|
|
|
return 1;
|
2013-02-10 09:39:47 +08:00
|
|
|
#endif
|
2013-02-09 12:25:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable Nagle */
|
|
|
|
optval = 1;
|
|
|
|
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
|
|
|
|
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
|
|
|
|
#else
|
|
|
|
tcp_proto = getprotobyname("TCP");
|
|
|
|
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* We are nonblocking... */
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2013-02-09 12:25:31 +08:00
|
|
|
ioctlsocket(fd, FIONBIO, &optl);
|
|
|
|
#else
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
|
2011-03-06 10:29:39 +00:00
|
|
|
{
|
|
|
|
struct pollfd fds;
|
|
|
|
|
2013-12-09 14:16:17 +08:00
|
|
|
/* treat the fact we got a truncated send pending as if we're choked */
|
|
|
|
if (wsi->truncated_send_malloc)
|
|
|
|
return 1;
|
|
|
|
|
2011-03-06 10:29:39 +00:00
|
|
|
fds.fd = wsi->sock;
|
|
|
|
fds.events = POLLOUT;
|
|
|
|
fds.revents = 0;
|
|
|
|
|
|
|
|
if (poll(&fds, 1, 0) != 1)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if ((fds.revents & POLLOUT) == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* okay to send another packet without blocking */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
int
|
2011-03-06 13:14:42 +00:00
|
|
|
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi, struct pollfd *pollfd)
|
|
|
|
{
|
|
|
|
int n;
|
2013-01-29 17:57:39 +08:00
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
|
|
|
struct lws_tokens eff_buf;
|
2011-03-06 13:14:42 +00:00
|
|
|
int ret;
|
|
|
|
int m;
|
2011-05-23 10:00:03 +01:00
|
|
|
int handled = 0;
|
|
|
|
|
add explicit error for partial send
This patch adds code to handle the situation that a prepared user buffer could not all be sent on the
socket at once. There are two kinds of situation to handle
1) User code handles it: The connection only has extensions active that do not rewrite the buffer.
In this case, the patch caused libwebsocket_write() to simply return the amount of user buffer that
was consumed (this is specifically the amount of user buffer used in sending what was accepted,
nothing else). So user code can just advance its buffer that much and resume sending when the socket
is writable again. This continues the frame rather than starting a new one or new fragment.
2) The connections has extensions active which actually send something quite different than what the
user buffer contains, for example a compression extension. In this case, libwebsockets will dynamically
malloc a buffer to contain a copy of the remaining unsent data, request notifiction when writeable again,
and automatically spill and free this buffer with the highest priority before passing on the writable
notification to anything else. For this situation, the call to write will return that it used the
whole user buffer, even though part is still rebuffered.
This patch should enable libwebsockets to detect the two cases and take the appropriate action.
There are also two choices for user code to deal with partial sends.
1) Leave the no_buffer_all_partial_tx member in the protocol struct at zero. The library will dyamically
buffer anything you send that did not get completely written to the socket, and automatically spill it next
time the socket is writable. You can use this method if your sent frames are relatvely small and unlikely to get
truncated anyway.
2) Set the no_buffer_all_partial_tx member in the protocol struct. User code now needs to take care of the
return value from libwebsocket_write() and deal with resending the remainder if not all of the requested amount
got sent. You should use this method if you are sending large messages and want to maximize throughput and efficiency.
Since the new member no_buffer_all_partial_tx will be zero by default, this patch will auto-rebuffer any
partial sends by default. That's good for most cases but if you attempt to send large blocks, make sure you
follow option 2) above.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-10-17 08:09:19 +08:00
|
|
|
/* pending truncated sends have uber priority */
|
|
|
|
|
2013-12-09 14:16:17 +08:00
|
|
|
if (wsi->truncated_send_malloc) {
|
|
|
|
lws_issue_raw(wsi, wsi->truncated_send_malloc +
|
|
|
|
wsi->truncated_send_offset,
|
|
|
|
wsi->truncated_send_len);
|
add explicit error for partial send
This patch adds code to handle the situation that a prepared user buffer could not all be sent on the
socket at once. There are two kinds of situation to handle
1) User code handles it: The connection only has extensions active that do not rewrite the buffer.
In this case, the patch caused libwebsocket_write() to simply return the amount of user buffer that
was consumed (this is specifically the amount of user buffer used in sending what was accepted,
nothing else). So user code can just advance its buffer that much and resume sending when the socket
is writable again. This continues the frame rather than starting a new one or new fragment.
2) The connections has extensions active which actually send something quite different than what the
user buffer contains, for example a compression extension. In this case, libwebsockets will dynamically
malloc a buffer to contain a copy of the remaining unsent data, request notifiction when writeable again,
and automatically spill and free this buffer with the highest priority before passing on the writable
notification to anything else. For this situation, the call to write will return that it used the
whole user buffer, even though part is still rebuffered.
This patch should enable libwebsockets to detect the two cases and take the appropriate action.
There are also two choices for user code to deal with partial sends.
1) Leave the no_buffer_all_partial_tx member in the protocol struct at zero. The library will dyamically
buffer anything you send that did not get completely written to the socket, and automatically spill it next
time the socket is writable. You can use this method if your sent frames are relatvely small and unlikely to get
truncated anyway.
2) Set the no_buffer_all_partial_tx member in the protocol struct. User code now needs to take care of the
return value from libwebsocket_write() and deal with resending the remainder if not all of the requested amount
got sent. You should use this method if you are sending large messages and want to maximize throughput and efficiency.
Since the new member no_buffer_all_partial_tx will be zero by default, this patch will auto-rebuffer any
partial sends by default. That's good for most cases but if you attempt to send large blocks, make sure you
follow option 2) above.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-10-17 08:09:19 +08:00
|
|
|
/* leave POLLOUT active either way */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
if (!wsi->active_extensions[n]->callback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m = wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
|
|
|
LWS_EXT_CALLBACK_IS_WRITEABLE,
|
|
|
|
wsi->active_extensions_user[n], NULL, 0);
|
|
|
|
if (m > handled)
|
|
|
|
handled = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handled == 1)
|
|
|
|
goto notify_action;
|
2011-03-06 13:14:42 +00:00
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
if (!wsi->extension_data_pending || handled == 2)
|
2011-03-06 13:14:42 +00:00
|
|
|
goto user_service;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check in on the active extensions, see if they
|
|
|
|
* had pending stuff to spill... they need to get the
|
|
|
|
* first look-in otherwise sequence will be disordered
|
|
|
|
*
|
|
|
|
* NULL, zero-length eff_buf means just spill pending
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
while (ret == 1) {
|
|
|
|
|
|
|
|
/* default to nobody has more to spill */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
eff_buf.token = NULL;
|
|
|
|
eff_buf.token_len = 0;
|
|
|
|
|
|
|
|
/* give every extension a chance to spill */
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
m = wsi->active_extensions[n]->callback(
|
2011-03-22 09:04:01 +00:00
|
|
|
wsi->protocol->owning_server,
|
|
|
|
wsi->active_extensions[n], wsi,
|
2011-03-06 13:14:42 +00:00
|
|
|
LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
|
|
|
|
wsi->active_extensions_user[n], &eff_buf, 0);
|
|
|
|
if (m < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("ext reports fatal error\n");
|
2011-03-06 13:14:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (m)
|
|
|
|
/*
|
|
|
|
* at least one extension told us he has more
|
|
|
|
* to spill, so we will go around again after
|
|
|
|
*/
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* assuming they gave us something to send, send it */
|
|
|
|
|
|
|
|
if (eff_buf.token_len) {
|
2013-02-23 10:50:10 +08:00
|
|
|
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
|
|
|
|
eff_buf.token_len);
|
|
|
|
if (n < 0)
|
2011-03-06 13:14:42 +00:00
|
|
|
return -1;
|
2013-02-23 10:50:10 +08:00
|
|
|
/*
|
|
|
|
* Keep amount spilled small to minimize chance of this
|
|
|
|
*/
|
|
|
|
if (n != eff_buf.token_len) {
|
|
|
|
lwsl_err("Unable to spill ext %d vs %s\n",
|
|
|
|
eff_buf.token_len, n);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-03-06 13:14:42 +00:00
|
|
|
} else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* no extension has more to spill */
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There's more to spill from an extension, but we just sent
|
|
|
|
* something... did that leave the pipe choked?
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!lws_send_pipe_choked(wsi))
|
|
|
|
/* no we could add more */
|
|
|
|
continue;
|
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_info("choked in POLLOUT service\n");
|
2011-03-06 13:14:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Yes, he's choked. Leave the POLLOUT masked on so we will
|
|
|
|
* come back here when he is unchoked. Don't call the user
|
|
|
|
* callback to enforce ordering of spilling, he'll get called
|
|
|
|
* when we come back here and there's nothing more to spill.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
wsi->extension_data_pending = 0;
|
|
|
|
|
|
|
|
user_service:
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-03-06 13:14:42 +00:00
|
|
|
/* one shot */
|
|
|
|
|
2013-12-21 11:18:34 +08:00
|
|
|
if (pollfd)
|
|
|
|
lws_change_pollfd(wsi, POLLOUT, 0);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-23 10:00:03 +01:00
|
|
|
notify_action:
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-03-06 13:14:42 +00:00
|
|
|
|
2011-03-07 20:47:39 +00:00
|
|
|
if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
|
|
|
|
n = LWS_CALLBACK_CLIENT_WRITEABLE;
|
|
|
|
else
|
|
|
|
n = LWS_CALLBACK_SERVER_WRITEABLE;
|
|
|
|
|
2013-01-22 07:20:08 +08:00
|
|
|
return user_callback_handle_rxflow(wsi->protocol->callback, context,
|
2013-02-11 17:13:32 +08:00
|
|
|
wsi, (enum libwebsocket_callback_reasons) n,
|
|
|
|
wsi->user_space, NULL, 0);
|
2011-03-06 13:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-20 19:11:31 +08:00
|
|
|
int
|
2011-05-23 10:00:03 +01:00
|
|
|
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
|
|
|
|
struct libwebsocket *wsi, unsigned int sec)
|
|
|
|
{
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-23 10:00:03 +01:00
|
|
|
int n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if extensions want in on it (eg, we are a mux parent)
|
|
|
|
* give them a chance to service child timeouts
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++)
|
|
|
|
wsi->active_extensions[n]->callback(
|
|
|
|
context, wsi->active_extensions[n],
|
|
|
|
wsi, LWS_EXT_CALLBACK_1HZ,
|
|
|
|
wsi->active_extensions_user[n], NULL, sec);
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-05-23 10:00:03 +01:00
|
|
|
if (!wsi->pending_timeout)
|
2013-02-20 19:11:31 +08:00
|
|
|
return 0;
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
/*
|
|
|
|
* if we went beyond the allowed time, kill the
|
|
|
|
* connection
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (sec > wsi->pending_timeout_limit) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_info("TIMEDOUT WAITING\n");
|
2011-05-23 10:00:03 +01:00
|
|
|
libwebsocket_close_and_free_session(context,
|
|
|
|
wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
2013-02-20 19:11:31 +08:00
|
|
|
return 1;
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
2013-02-20 19:11:31 +08:00
|
|
|
|
|
|
|
return 0;
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
|
|
|
|
2011-02-12 11:57:45 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_service_fd() - Service polled socket with something waiting
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: Websocket context
|
2011-02-12 11:57:45 +00:00
|
|
|
* @pollfd: The pollfd entry describing the socket fd and which events
|
2012-04-09 15:09:01 +08:00
|
|
|
* happened.
|
2011-02-12 11:57:45 +00:00
|
|
|
*
|
2013-01-22 12:32:11 +08:00
|
|
|
* This function takes a pollfd that has POLLIN or POLLOUT activity and
|
2013-02-11 17:13:32 +08:00
|
|
|
* services it according to the state of the associated
|
|
|
|
* struct libwebsocket.
|
2013-01-22 12:32:11 +08:00
|
|
|
*
|
|
|
|
* The one call deals with all "service" that might happen on a socket
|
|
|
|
* including listen accepts, http files as well as websocket protocol.
|
2013-03-11 20:37:03 +08:00
|
|
|
*
|
|
|
|
* If a pollfd says it has something, you can just pass it to
|
|
|
|
* libwebsocket_serice_fd() whether it is a socket handled by lws or not.
|
|
|
|
* If it sees it is a lws socket, the traffic will be handled and
|
|
|
|
* pollfd->revents will be zeroed now.
|
|
|
|
*
|
|
|
|
* If the socket is foreign to lws, it leaves revents alone. So you can
|
|
|
|
* see if you should service yourself by checking the pollfd revents
|
|
|
|
* after letting lws try to service it.
|
2011-02-12 11:57:45 +00:00
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_service_fd(struct libwebsocket_context *context,
|
2011-02-12 11:57:43 +00:00
|
|
|
struct pollfd *pollfd)
|
2010-12-18 15:13:50 +00:00
|
|
|
{
|
2013-01-18 11:43:21 +08:00
|
|
|
struct libwebsocket *wsi;
|
2010-12-18 15:13:50 +00:00
|
|
|
int n;
|
2011-02-12 11:57:43 +00:00
|
|
|
int m;
|
2013-02-10 10:46:45 +08:00
|
|
|
int listen_socket_fds_index = 0;
|
2014-02-26 21:37:31 +01:00
|
|
|
time_t now;
|
2013-02-20 19:11:31 +08:00
|
|
|
int timed_out = 0;
|
|
|
|
int our_fd = 0;
|
2013-03-16 11:24:23 +08:00
|
|
|
char draining_flow = 0;
|
2013-01-29 17:57:39 +08:00
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-06 13:15:31 +00:00
|
|
|
int more = 1;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-03-06 13:14:15 +00:00
|
|
|
struct lws_tokens eff_buf;
|
2013-02-10 10:46:45 +08:00
|
|
|
|
|
|
|
if (context->listen_service_fd)
|
|
|
|
listen_socket_fds_index = context->lws_lookup[
|
|
|
|
context->listen_service_fd]->position_in_fds_table;
|
|
|
|
|
2011-02-14 17:59:43 +00:00
|
|
|
/*
|
|
|
|
* you can call us with pollfd = NULL to just allow the once-per-second
|
|
|
|
* global timeout checks; if less than a second since the last check
|
|
|
|
* it returns immediately then.
|
|
|
|
*/
|
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
time(&now);
|
2011-02-14 17:59:43 +00:00
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
if (context->last_timeout_check_s != now) {
|
|
|
|
context->last_timeout_check_s = now;
|
2011-02-14 17:59:43 +00:00
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
2013-01-19 13:56:10 +08:00
|
|
|
/* if our parent went down, don't linger around */
|
2013-02-11 17:13:32 +08:00
|
|
|
if (context->started_with_parent &&
|
|
|
|
kill(context->started_with_parent, 0) < 0)
|
2013-01-19 13:56:10 +08:00
|
|
|
kill(getpid(), SIGTERM);
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-19 13:56:10 +08:00
|
|
|
|
2011-02-14 17:59:43 +00:00
|
|
|
/* global timeout check once per second */
|
|
|
|
|
2013-02-20 19:11:31 +08:00
|
|
|
if (pollfd)
|
|
|
|
our_fd = pollfd->fd;
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
2013-02-20 19:11:31 +08:00
|
|
|
m = context->fds[n].fd;
|
|
|
|
wsi = context->lws_lookup[m];
|
|
|
|
if (!wsi)
|
2013-01-17 12:26:48 +08:00
|
|
|
continue;
|
2013-02-20 19:11:31 +08:00
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
if (libwebsocket_service_timeout_check(context, wsi, now))
|
2013-02-20 19:11:31 +08:00
|
|
|
/* he did time out... */
|
2013-03-09 12:34:30 +08:00
|
|
|
if (m == our_fd) {
|
2013-02-20 19:11:31 +08:00
|
|
|
/* it was the guy we came to service! */
|
|
|
|
timed_out = 1;
|
2013-03-09 12:34:30 +08:00
|
|
|
/* mark as handled */
|
|
|
|
pollfd->revents = 0;
|
|
|
|
}
|
2011-02-14 17:59:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-20 19:11:31 +08:00
|
|
|
/* the socket we came to service timed out, nothing to do */
|
|
|
|
if (timed_out)
|
|
|
|
return 0;
|
|
|
|
|
2011-02-14 17:59:43 +00:00
|
|
|
/* just here for timeout management? */
|
|
|
|
if (pollfd == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* no, here to service a socket descriptor */
|
2013-03-09 12:34:30 +08:00
|
|
|
wsi = context->lws_lookup[pollfd->fd];
|
|
|
|
if (wsi == NULL)
|
|
|
|
/* not lws connection ... leave revents alone and return */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* so that caller can tell we handled, past here we need to
|
|
|
|
* zero down pollfd->revents after handling
|
|
|
|
*/
|
|
|
|
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
/*
|
|
|
|
* deal with listen service piggybacking
|
|
|
|
* every listen_service_modulo services of other fds, we
|
|
|
|
* sneak one in to service the listen socket if there's anything waiting
|
|
|
|
*
|
|
|
|
* To handle connection storms, as found in ab, if we previously saw a
|
|
|
|
* pending connection here, it causes us to check again next time.
|
|
|
|
*/
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
if (context->listen_service_fd && pollfd !=
|
|
|
|
&context->fds[listen_socket_fds_index]) {
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
context->listen_service_count++;
|
|
|
|
if (context->listen_service_extraseen ||
|
2013-02-11 17:13:32 +08:00
|
|
|
context->listen_service_count ==
|
|
|
|
context->listen_service_modulo) {
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
context->listen_service_count = 0;
|
|
|
|
m = 1;
|
|
|
|
if (context->listen_service_extraseen > 5)
|
|
|
|
m = 2;
|
|
|
|
while (m--) {
|
2013-02-11 17:13:32 +08:00
|
|
|
/*
|
|
|
|
* even with extpoll, we prepared this
|
|
|
|
* internal fds for listen
|
|
|
|
*/
|
|
|
|
n = poll(&context->fds[listen_socket_fds_index],
|
|
|
|
1, 0);
|
|
|
|
if (n > 0) { /* there's a conn waiting for us */
|
|
|
|
libwebsocket_service_fd(context,
|
|
|
|
&context->
|
|
|
|
fds[listen_socket_fds_index]);
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
context->listen_service_extraseen++;
|
|
|
|
} else {
|
|
|
|
if (context->listen_service_extraseen)
|
2013-02-11 17:13:32 +08:00
|
|
|
context->
|
|
|
|
listen_service_extraseen--;
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-11-04 09:47:06 +08:00
|
|
|
/* handle session socket closed */
|
|
|
|
|
|
|
|
if ((!(pollfd->revents & POLLIN)) &&
|
|
|
|
(pollfd->revents & (POLLERR | POLLHUP))) {
|
|
|
|
|
|
|
|
lwsl_debug("Session Socket %p (fd=%d) dead\n",
|
|
|
|
(void *)wsi, pollfd->fd);
|
|
|
|
|
|
|
|
goto close_and_handled;
|
|
|
|
}
|
|
|
|
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
/* okay, what we came here to do... */
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
switch (wsi->mode) {
|
2013-01-15 13:40:23 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
2013-01-15 13:40:23 +08:00
|
|
|
case LWS_CONNMODE_HTTP_SERVING:
|
2013-05-19 14:04:10 +08:00
|
|
|
case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
|
2011-02-12 11:57:43 +00:00
|
|
|
case LWS_CONNMODE_SERVER_LISTENER:
|
2013-01-28 12:19:10 +08:00
|
|
|
case LWS_CONNMODE_SSL_ACK_PENDING:
|
2013-03-09 12:34:30 +08:00
|
|
|
n = lws_server_socket_service(context, wsi, pollfd);
|
|
|
|
goto handled;
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2011-02-14 20:25:43 +00:00
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
case LWS_CONNMODE_WS_SERVING:
|
|
|
|
case LWS_CONNMODE_WS_CLIENT:
|
|
|
|
|
|
|
|
/* the guy requested a callback when it was OK to write */
|
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
if ((pollfd->revents & POLLOUT) &&
|
2013-03-16 11:24:23 +08:00
|
|
|
wsi->state == WSI_STATE_ESTABLISHED &&
|
|
|
|
lws_handle_POLLOUT_event(context, wsi, pollfd) < 0) {
|
2013-12-21 11:18:34 +08:00
|
|
|
lwsl_info("libwebsocket_service_fd: closing\n");
|
|
|
|
goto close_and_handled;
|
2013-11-04 09:47:06 +08:00
|
|
|
}
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
if (wsi->u.ws.rxflow_buffer &&
|
|
|
|
(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
|
|
|
lwsl_info("draining rxflow\n");
|
|
|
|
/* well, drain it */
|
|
|
|
eff_buf.token = (char *)wsi->u.ws.rxflow_buffer +
|
|
|
|
wsi->u.ws.rxflow_pos;
|
|
|
|
eff_buf.token_len = wsi->u.ws.rxflow_len -
|
|
|
|
wsi->u.ws.rxflow_pos;
|
|
|
|
draining_flow = 1;
|
|
|
|
goto drain;
|
|
|
|
}
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
/* any incoming data ready? */
|
|
|
|
|
|
|
|
if (!(pollfd->revents & POLLIN))
|
|
|
|
break;
|
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2013-01-10 10:35:32 +08:00
|
|
|
read_pending:
|
2013-01-30 12:28:34 +08:00
|
|
|
if (wsi->ssl) {
|
2013-02-08 13:01:02 +08:00
|
|
|
eff_buf.token_len = SSL_read(wsi->ssl,
|
|
|
|
context->service_buffer,
|
2013-02-11 17:13:32 +08:00
|
|
|
sizeof(context->service_buffer));
|
2013-01-30 12:28:34 +08:00
|
|
|
if (!eff_buf.token_len) {
|
|
|
|
n = SSL_get_error(wsi->ssl, eff_buf.token_len);
|
2013-02-08 13:01:02 +08:00
|
|
|
lwsl_err("SSL_read returned 0 with reason %s\n",
|
2013-02-11 17:13:32 +08:00
|
|
|
ERR_error_string(n,
|
|
|
|
(char *)context->service_buffer));
|
2013-01-30 12:28:34 +08:00
|
|
|
}
|
|
|
|
} else
|
2010-12-18 15:13:50 +00:00
|
|
|
#endif
|
2013-02-08 13:01:02 +08:00
|
|
|
eff_buf.token_len = recv(pollfd->fd,
|
2013-02-11 17:13:32 +08:00
|
|
|
context->service_buffer,
|
|
|
|
sizeof(context->service_buffer), 0);
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2011-03-06 13:14:15 +00:00
|
|
|
if (eff_buf.token_len < 0) {
|
2013-03-23 09:44:47 +08:00
|
|
|
lwsl_debug("service_fd read ret = %d, errno = %d\n",
|
|
|
|
eff_buf.token_len, errno);
|
2012-10-19 11:21:57 +02:00
|
|
|
if (errno != EINTR && errno != EAGAIN)
|
2013-03-09 12:34:30 +08:00
|
|
|
goto close_and_handled;
|
|
|
|
n = 0;
|
|
|
|
goto handled;
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
2011-03-06 13:14:15 +00:00
|
|
|
if (!eff_buf.token_len) {
|
2013-03-23 09:44:47 +08:00
|
|
|
lwsl_info("service_fd: closing due to 0 length read\n");
|
2013-03-09 12:34:30 +08:00
|
|
|
goto close_and_handled;
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
|
|
|
|
2011-03-06 13:14:15 +00:00
|
|
|
/*
|
|
|
|
* give any active extensions a chance to munge the buffer
|
|
|
|
* before parse. We pass in a pointer to an lws_tokens struct
|
|
|
|
* prepared with the default buffer and content length that's in
|
|
|
|
* there. Rather than rewrite the default buffer, extensions
|
|
|
|
* that expect to grow the buffer can adapt .token to
|
|
|
|
* point to their own per-connection buffer in the extension
|
|
|
|
* user allocation. By default with no extensions or no
|
|
|
|
* extension callback handling, just the normal input buffer is
|
|
|
|
* used then so it is efficient.
|
|
|
|
*/
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2013-02-08 13:01:02 +08:00
|
|
|
eff_buf.token = (char *)context->service_buffer;
|
2013-03-16 11:24:23 +08:00
|
|
|
drain:
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-06 13:14:15 +00:00
|
|
|
more = 1;
|
|
|
|
while (more) {
|
|
|
|
|
|
|
|
more = 0;
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
2011-03-22 09:04:01 +00:00
|
|
|
m = wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
2011-03-06 13:14:15 +00:00
|
|
|
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE,
|
2011-03-22 09:04:01 +00:00
|
|
|
wsi->active_extensions_user[n],
|
|
|
|
&eff_buf, 0);
|
2011-03-06 13:14:15 +00:00
|
|
|
if (m < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_ext(
|
2012-04-09 15:09:01 +08:00
|
|
|
"Extension reports fatal error\n");
|
2013-03-09 12:34:30 +08:00
|
|
|
goto close_and_handled;
|
2011-03-06 13:14:15 +00:00
|
|
|
}
|
|
|
|
if (m)
|
|
|
|
more = 1;
|
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-03-06 13:14:15 +00:00
|
|
|
/* service incoming data */
|
2011-02-12 13:14:11 +00:00
|
|
|
|
2011-03-06 13:14:15 +00:00
|
|
|
if (eff_buf.token_len) {
|
|
|
|
n = libwebsocket_read(context, wsi,
|
2012-04-09 15:09:01 +08:00
|
|
|
(unsigned char *)eff_buf.token,
|
|
|
|
eff_buf.token_len);
|
2013-03-09 12:34:30 +08:00
|
|
|
if (n < 0) {
|
2011-03-06 13:14:15 +00:00
|
|
|
/* we closed wsi */
|
2013-03-09 12:34:30 +08:00
|
|
|
n = 0;
|
|
|
|
goto handled;
|
|
|
|
}
|
2011-03-06 13:14:15 +00:00
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-06 13:14:15 +00:00
|
|
|
eff_buf.token = NULL;
|
|
|
|
eff_buf.token_len = 0;
|
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2013-03-16 11:24:23 +08:00
|
|
|
if (draining_flow && wsi->u.ws.rxflow_buffer &&
|
|
|
|
wsi->u.ws.rxflow_pos == wsi->u.ws.rxflow_len) {
|
|
|
|
lwsl_info("flow buffer: drained\n");
|
|
|
|
free(wsi->u.ws.rxflow_buffer);
|
|
|
|
wsi->u.ws.rxflow_buffer = NULL;
|
|
|
|
/* having drained the rxflow buffer, can rearm POLLIN */
|
|
|
|
_libwebsocket_rx_flow_control(wsi);
|
|
|
|
}
|
2013-01-10 10:35:32 +08:00
|
|
|
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
if (wsi->ssl && SSL_pending(wsi->ssl))
|
|
|
|
goto read_pending;
|
|
|
|
#endif
|
2011-03-06 13:14:15 +00:00
|
|
|
break;
|
2013-01-16 11:53:05 +08:00
|
|
|
|
|
|
|
default:
|
2013-01-16 11:47:40 +08:00
|
|
|
#ifdef LWS_NO_CLIENT
|
|
|
|
break;
|
|
|
|
#else
|
2013-03-09 12:34:30 +08:00
|
|
|
n = lws_client_socket_service(context, wsi, pollfd);
|
|
|
|
goto handled;
|
2013-01-16 11:47:40 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 12:34:30 +08:00
|
|
|
n = 0;
|
|
|
|
goto handled;
|
|
|
|
|
|
|
|
close_and_handled:
|
2013-03-16 11:24:23 +08:00
|
|
|
libwebsocket_close_and_free_session(context, wsi,
|
|
|
|
LWS_CLOSE_STATUS_NOSTATUS);
|
|
|
|
n = 1;
|
2013-03-09 12:34:30 +08:00
|
|
|
|
|
|
|
handled:
|
|
|
|
pollfd->revents = 0;
|
|
|
|
return n;
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2011-01-23 16:50:33 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_context_destroy() - Destroy the websocket context
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: Websocket context
|
2011-01-23 16:50:33 +00:00
|
|
|
*
|
|
|
|
* This function closes any active connections and then frees the
|
|
|
|
* context. After calling this, any further use of the context is
|
|
|
|
* undefined.
|
|
|
|
*/
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_context_destroy(struct libwebsocket_context *context)
|
2011-01-23 16:50:33 +00:00
|
|
|
{
|
2011-02-12 11:57:43 +00:00
|
|
|
int n;
|
2014-02-15 14:39:40 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-02-12 11:57:43 +00:00
|
|
|
int m;
|
2011-05-23 10:00:03 +01:00
|
|
|
struct libwebsocket_extension *ext;
|
2014-02-15 14:39:40 +08:00
|
|
|
#endif /* ndef LWS_NO_EXTENSIONS */
|
2013-02-11 12:05:54 +08:00
|
|
|
struct libwebsocket_protocols *protocol = context->protocols;
|
2011-01-23 16:50:33 +00:00
|
|
|
|
2013-01-29 12:36:17 +08:00
|
|
|
#ifdef LWS_LATENCY
|
|
|
|
if (context->worst_latency_info[0])
|
|
|
|
lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
|
|
|
|
#endif
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
2013-02-11 17:13:32 +08:00
|
|
|
struct libwebsocket *wsi =
|
|
|
|
context->lws_lookup[context->fds[n].fd];
|
2013-01-17 12:26:48 +08:00
|
|
|
libwebsocket_close_and_free_session(context,
|
2013-02-11 11:43:05 +08:00
|
|
|
wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
|
|
|
|
n--;
|
2013-01-17 12:26:48 +08:00
|
|
|
}
|
2011-01-23 16:50:33 +00:00
|
|
|
|
2014-02-15 14:39:40 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-23 10:00:03 +01:00
|
|
|
/*
|
|
|
|
* give all extensions a chance to clean up any per-context
|
|
|
|
* allocations they might have made
|
|
|
|
*/
|
|
|
|
|
|
|
|
ext = context->extensions;
|
|
|
|
m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT;
|
|
|
|
if (context->listen_port)
|
|
|
|
m = LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT;
|
2012-06-04 08:40:28 +08:00
|
|
|
while (ext && ext->callback) {
|
2013-02-11 17:13:32 +08:00
|
|
|
ext->callback(context, ext, NULL,
|
|
|
|
(enum libwebsocket_extension_callback_reasons)m,
|
|
|
|
NULL, NULL, 0);
|
2011-05-23 10:00:03 +01:00
|
|
|
ext++;
|
|
|
|
}
|
2014-02-15 14:39:40 +08:00
|
|
|
#endif /* ndef LWS_NO_EXTENSIONS */
|
2013-02-11 12:05:54 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* inform all the protocols that they are done and will have no more
|
|
|
|
* callbacks
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (protocol->callback) {
|
|
|
|
protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
|
|
|
|
NULL, NULL, 0);
|
|
|
|
protocol++;
|
|
|
|
}
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
#else
|
|
|
|
close(context->fd_random);
|
|
|
|
#endif
|
2011-02-10 09:32:24 +00:00
|
|
|
|
2011-01-23 16:50:33 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2011-03-02 22:03:47 +00:00
|
|
|
if (context->ssl_ctx)
|
|
|
|
SSL_CTX_free(context->ssl_ctx);
|
|
|
|
if (context->ssl_client_ctx)
|
|
|
|
SSL_CTX_free(context->ssl_client_ctx);
|
2013-02-11 14:50:45 +08:00
|
|
|
|
|
|
|
ERR_remove_state(0);
|
|
|
|
ERR_free_strings();
|
|
|
|
EVP_cleanup();
|
|
|
|
CRYPTO_cleanup_all_ex_data();
|
2011-01-23 16:50:33 +00:00
|
|
|
#endif
|
|
|
|
|
2013-02-11 11:04:01 +08:00
|
|
|
if (context->fds)
|
|
|
|
free(context->fds);
|
|
|
|
if (context->lws_lookup)
|
|
|
|
free(context->lws_lookup);
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
free(context);
|
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
WSACleanup();
|
|
|
|
#endif
|
2011-01-23 16:50:33 +00:00
|
|
|
}
|
|
|
|
|
2013-01-22 12:40:35 +08:00
|
|
|
/**
|
2013-02-11 17:13:32 +08:00
|
|
|
* libwebsocket_context_user() - get the user data associated with the context
|
2013-01-22 12:40:35 +08:00
|
|
|
* @context: Websocket context
|
|
|
|
*
|
|
|
|
* This returns the optional user allocation that can be attached to
|
|
|
|
* the context the sockets live in at context_create time. It's a way
|
|
|
|
* to let all sockets serviced in the same context share data without
|
|
|
|
* using globals statics in the user code.
|
|
|
|
*/
|
2012-10-19 11:21:56 +02:00
|
|
|
LWS_EXTERN void *
|
|
|
|
libwebsocket_context_user(struct libwebsocket_context *context)
|
|
|
|
{
|
2013-02-11 17:13:32 +08:00
|
|
|
return context->user_space;
|
2012-10-19 11:21:56 +02:00
|
|
|
}
|
|
|
|
|
2011-01-23 16:50:33 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_service() - Service any pending websocket activity
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: Websocket context
|
2011-01-23 16:50:33 +00:00
|
|
|
* @timeout_ms: Timeout for poll; 0 means return immediately if nothing needed
|
|
|
|
* service otherwise block and service immediately, returning
|
|
|
|
* after the timeout if nothing needed service.
|
|
|
|
*
|
|
|
|
* This function deals with any pending websocket traffic, for three
|
|
|
|
* kinds of event. It handles these events on both server and client
|
|
|
|
* types of connection the same.
|
|
|
|
*
|
|
|
|
* 1) Accept new connections to our context's server
|
|
|
|
*
|
2013-01-29 17:57:39 +08:00
|
|
|
* 2) Call the receive callback for incoming frame data received by
|
2011-01-23 16:50:33 +00:00
|
|
|
* server or client connections.
|
|
|
|
*
|
|
|
|
* You need to call this service function periodically to all the above
|
|
|
|
* functions to happen; if your application is single-threaded you can
|
|
|
|
* just call it in your main event loop.
|
|
|
|
*
|
|
|
|
* Alternatively you can fork a new process that asynchronously handles
|
|
|
|
* calling this service in a loop. In that case you are happy if this
|
|
|
|
* call blocks your thread until it needs to take care of something and
|
|
|
|
* would call it with a large nonzero timeout. Your loop then takes no
|
|
|
|
* CPU while there is nothing happening.
|
|
|
|
*
|
|
|
|
* If you are calling it in a single-threaded app, you don't want it to
|
|
|
|
* wait around blocking other things in your loop from happening, so you
|
|
|
|
* would call it with a timeout_ms of 0, so it returns immediately if
|
|
|
|
* nothing is pending, or as soon as it services whatever was pending.
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_service(struct libwebsocket_context *context, int timeout_ms)
|
2011-01-19 13:11:55 +00:00
|
|
|
{
|
|
|
|
int n;
|
2013-03-16 11:24:23 +08:00
|
|
|
int m;
|
2013-12-25 16:34:37 +08:00
|
|
|
#ifdef LWS_HAS_PPOLL
|
|
|
|
struct timespec timeout_ts;
|
|
|
|
sigset_t sigmask;
|
|
|
|
#endif
|
2011-01-19 13:11:55 +00:00
|
|
|
|
|
|
|
/* stay dead once we are dead */
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
if (context == NULL)
|
2011-01-19 13:11:55 +00:00
|
|
|
return 1;
|
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
#ifdef LWS_HAS_PPOLL
|
|
|
|
lws_idling_ppoll_tid = context->protocols[0].callback(context, NULL,
|
|
|
|
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
|
|
|
|
|
|
|
timeout_ts.tv_sec = timeout_ms / 1000;
|
|
|
|
timeout_ts.tv_nsec = timeout_ms % 1000;
|
2014-01-11 12:37:07 +08:00
|
|
|
|
|
|
|
sigprocmask(SIG_BLOCK, NULL, &sigmask);
|
|
|
|
sigdelset(&sigmask, SIGUSR2);
|
2013-12-25 16:34:37 +08:00
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
/* wait for something to need service */
|
2011-01-23 16:50:33 +00:00
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
n = ppoll(context->fds, context->fds_count, &timeout_ts, &sigmask);
|
|
|
|
lws_idling_ppoll_tid = 0;
|
|
|
|
#else
|
2011-03-02 22:03:47 +00:00
|
|
|
n = poll(context->fds, context->fds_count, timeout_ms);
|
2013-12-25 16:34:37 +08:00
|
|
|
#endif
|
2013-09-20 20:26:12 +08:00
|
|
|
if (n == 0) /* poll timeout */ {
|
|
|
|
libwebsocket_service_fd(context, NULL);
|
2011-02-12 13:14:11 +00:00
|
|
|
return 0;
|
2013-09-20 20:26:12 +08:00
|
|
|
}
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2014-01-11 12:37:07 +08:00
|
|
|
if (n < 0) {
|
|
|
|
if (errno != EINTR)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
/* any socket with events to service? */
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
|
|
|
if (!context->fds[n].revents)
|
|
|
|
continue;
|
|
|
|
m = libwebsocket_service_fd(context, &context->fds[n]);
|
|
|
|
if (m < 0)
|
|
|
|
return -1;
|
|
|
|
/* if something closed, retry this slot */
|
|
|
|
if (m)
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
|
2011-01-19 13:11:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-23 10:00:03 +01:00
|
|
|
int
|
|
|
|
lws_any_extension_handled(struct libwebsocket_context *context,
|
2012-04-09 15:09:01 +08:00
|
|
|
struct libwebsocket *wsi,
|
|
|
|
enum libwebsocket_extension_callback_reasons r,
|
2011-05-23 10:00:03 +01:00
|
|
|
void *v, size_t len)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
/* maybe an extension will take care of it for us */
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions && !handled; n++) {
|
|
|
|
if (!wsi->active_extensions[n]->callback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
handled |= wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
|
|
|
r, wsi->active_extensions_user[n], v, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
lws_get_extension_user_matching_ext(struct libwebsocket *wsi,
|
2012-04-09 15:09:01 +08:00
|
|
|
struct libwebsocket_extension *ext)
|
2011-05-23 10:00:03 +01:00
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
2011-05-25 21:41:57 +01:00
|
|
|
if (wsi == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
while (n < wsi->count_active_extensions) {
|
|
|
|
if (wsi->active_extensions[n] != ext) {
|
|
|
|
n++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return wsi->active_extensions_user[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-12-21 11:18:34 +08:00
|
|
|
void
|
|
|
|
lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or)
|
|
|
|
{
|
|
|
|
struct libwebsocket_context *context = wsi->protocol->owning_server;
|
2013-12-25 16:34:37 +08:00
|
|
|
int events;
|
|
|
|
#ifdef LWS_HAS_PPOLL
|
|
|
|
int tid;
|
|
|
|
int sampled_ppoll_tid;
|
|
|
|
#endif
|
2014-02-15 20:19:26 +08:00
|
|
|
struct libwebsocket_pollargs pa;
|
|
|
|
|
|
|
|
pa.fd = wsi->sock;
|
2013-12-21 11:18:34 +08:00
|
|
|
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_LOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-12-21 11:18:34 +08:00
|
|
|
|
2014-02-15 20:19:26 +08:00
|
|
|
pa.prev_events = events = context->fds[wsi->position_in_fds_table].events;
|
2013-12-25 16:34:37 +08:00
|
|
|
|
2014-02-15 20:19:26 +08:00
|
|
|
pa.events = context->fds[wsi->position_in_fds_table].events = (events & ~_and) | _or;
|
2013-12-21 11:18:34 +08:00
|
|
|
|
2014-02-15 20:19:26 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
|
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-12-21 11:18:34 +08:00
|
|
|
|
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
#ifdef LWS_HAS_PPOLL
|
|
|
|
/*
|
|
|
|
* if we changed something in this pollfd...
|
|
|
|
* ... and we're running in a different thread context
|
|
|
|
* than the service thread...
|
|
|
|
* ... and the service thread is waiting in ppoll()...
|
|
|
|
* then fire a SIGUSR2 at the service thread to force it to
|
|
|
|
* restart the ppoll() with our changed events
|
|
|
|
*/
|
|
|
|
if (events != context->fds[wsi->position_in_fds_table].events) {
|
|
|
|
sampled_ppoll_tid = lws_idling_ppoll_tid;
|
|
|
|
if (sampled_ppoll_tid) {
|
|
|
|
tid = context->protocols[0].callback(context, NULL,
|
|
|
|
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
|
|
|
if (tid != sampled_ppoll_tid)
|
|
|
|
kill(sampled_ppoll_tid, SIGUSR2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-12-21 11:18:34 +08:00
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_UNLOCK_POLL,
|
2014-02-15 20:19:26 +08:00
|
|
|
wsi->user_space, (void *) &pa, 0);
|
2013-12-21 11:18:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_callback_on_writable() - Request a callback when this socket
|
|
|
|
* becomes able to be written to without
|
|
|
|
* blocking
|
2011-02-19 08:32:53 +00:00
|
|
|
*
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: libwebsockets context
|
2011-01-27 06:26:52 +00:00
|
|
|
* @wsi: Websocket connection instance to get callback for
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_callback_on_writable(struct libwebsocket_context *context,
|
2012-04-09 15:09:01 +08:00
|
|
|
struct libwebsocket *wsi)
|
2011-01-27 06:26:52 +00:00
|
|
|
{
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-01-27 06:26:52 +00:00
|
|
|
int n;
|
2011-05-23 10:00:03 +01:00
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
/* maybe an extension will take care of it for us */
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->count_active_extensions; n++) {
|
|
|
|
if (!wsi->active_extensions[n]->callback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
handled |= wsi->active_extensions[n]->callback(context,
|
|
|
|
wsi->active_extensions[n], wsi,
|
|
|
|
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE,
|
|
|
|
wsi->active_extensions_user[n], NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handled)
|
|
|
|
return 1;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2013-01-17 12:26:48 +08:00
|
|
|
if (wsi->position_in_fds_table < 0) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err("libwebsocket_callback_on_writable: failed to find socket %d\n",
|
|
|
|
wsi->sock);
|
2013-01-17 12:26:48 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-12-21 11:18:34 +08:00
|
|
|
lws_change_pollfd(wsi, 0, POLLOUT);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* libwebsocket_callback_on_writable_all_protocol() - Request a callback for
|
|
|
|
* all connections using the given protocol when it
|
|
|
|
* becomes possible to write to each socket without
|
|
|
|
* blocking in turn.
|
|
|
|
*
|
|
|
|
* @protocol: Protocol whose connections will get callbacks
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-01-27 06:26:52 +00:00
|
|
|
libwebsocket_callback_on_writable_all_protocol(
|
|
|
|
const struct libwebsocket_protocols *protocol)
|
|
|
|
{
|
2011-03-02 22:03:47 +00:00
|
|
|
struct libwebsocket_context *context = protocol->owning_server;
|
2011-01-27 06:26:52 +00:00
|
|
|
int n;
|
2011-02-12 11:57:43 +00:00
|
|
|
struct libwebsocket *wsi;
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
|
|
|
wsi = context->lws_lookup[context->fds[n].fd];
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->protocol == protocol)
|
|
|
|
libwebsocket_callback_on_writable(context, wsi);
|
2011-02-12 11:57:43 +00:00
|
|
|
}
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-15 16:36:38 +08:00
|
|
|
/**
|
|
|
|
* libwebsocket_callback_all_protocol() - Callback all connections using
|
|
|
|
* the given protocol with the given reason
|
|
|
|
*
|
|
|
|
* @protocol: Protocol whose connections will get callbacks
|
|
|
|
* @reason: Callback reason index
|
|
|
|
*/
|
|
|
|
|
|
|
|
LWS_VISIBLE int
|
|
|
|
libwebsocket_callback_all_protocol(
|
|
|
|
const struct libwebsocket_protocols *protocol, int reason)
|
|
|
|
{
|
|
|
|
struct libwebsocket_context *context = protocol->owning_server;
|
|
|
|
int n;
|
|
|
|
struct libwebsocket *wsi;
|
|
|
|
|
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
|
|
|
wsi = context->lws_lookup[context->fds[n].fd];
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->protocol == protocol)
|
|
|
|
protocol->callback(context, wsi,
|
|
|
|
reason, wsi->user_space, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-14 20:25:43 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_set_timeout() - marks the wsi as subject to a timeout
|
|
|
|
*
|
|
|
|
* You will not need this unless you are doing something special
|
|
|
|
*
|
|
|
|
* @wsi: Websocket connection instance
|
|
|
|
* @reason: timeout reason
|
|
|
|
* @secs: how many seconds
|
|
|
|
*/
|
|
|
|
|
2014-02-19 09:24:17 +11:00
|
|
|
LWS_VISIBLE void
|
2011-02-14 20:25:43 +00:00
|
|
|
libwebsocket_set_timeout(struct libwebsocket *wsi,
|
|
|
|
enum pending_timeout reason, int secs)
|
|
|
|
{
|
2014-02-26 21:37:31 +01:00
|
|
|
time_t now;
|
2011-02-14 20:25:43 +00:00
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
time(&now);
|
2011-02-14 20:25:43 +00:00
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
wsi->pending_timeout_limit = now + secs;
|
2011-02-14 20:25:43 +00:00
|
|
|
wsi->pending_timeout = reason;
|
|
|
|
}
|
|
|
|
|
2011-01-27 20:06:03 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* libwebsocket_get_socket_fd() - returns the socket file descriptor
|
|
|
|
*
|
|
|
|
* You will not need this unless you are doing something special
|
|
|
|
*
|
|
|
|
* @wsi: Websocket connection instance
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-01-27 20:06:03 +00:00
|
|
|
libwebsocket_get_socket_fd(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
return wsi->sock;
|
|
|
|
}
|
|
|
|
|
2013-01-29 12:36:17 +08:00
|
|
|
#ifdef LWS_LATENCY
|
|
|
|
void
|
2013-02-11 17:13:32 +08:00
|
|
|
lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
|
|
|
|
const char *action, int ret, int completed)
|
2013-01-29 12:36:17 +08:00
|
|
|
{
|
2014-02-26 21:37:31 +01:00
|
|
|
unsigned long long u;
|
2013-01-29 12:36:17 +08:00
|
|
|
char buf[256];
|
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
u = time_in_microseconds();
|
2013-01-29 12:36:17 +08:00
|
|
|
|
|
|
|
if (action) {
|
|
|
|
if (completed) {
|
|
|
|
if (wsi->action_start == wsi->latency_start)
|
2013-02-11 17:13:32 +08:00
|
|
|
sprintf(buf,
|
|
|
|
"Completion first try lat %luus: %p: ret %d: %s\n",
|
|
|
|
u - wsi->latency_start,
|
|
|
|
(void *)wsi, ret, action);
|
2013-01-29 12:36:17 +08:00
|
|
|
else
|
2013-02-11 17:13:32 +08:00
|
|
|
sprintf(buf,
|
|
|
|
"Completion %luus: lat %luus: %p: ret %d: %s\n",
|
|
|
|
u - wsi->action_start,
|
|
|
|
u - wsi->latency_start,
|
|
|
|
(void *)wsi, ret, action);
|
2013-01-29 12:36:17 +08:00
|
|
|
wsi->action_start = 0;
|
|
|
|
} else
|
2013-02-11 17:13:32 +08:00
|
|
|
sprintf(buf, "lat %luus: %p: ret %d: %s\n",
|
|
|
|
u - wsi->latency_start,
|
|
|
|
(void *)wsi, ret, action);
|
2013-01-29 12:36:17 +08:00
|
|
|
if (u - wsi->latency_start > context->worst_latency) {
|
|
|
|
context->worst_latency = u - wsi->latency_start;
|
|
|
|
strcpy(context->worst_latency_info, buf);
|
|
|
|
}
|
|
|
|
lwsl_latency("%s", buf);
|
|
|
|
} else {
|
|
|
|
wsi->latency_start = u;
|
|
|
|
if (!wsi->action_start)
|
|
|
|
wsi->action_start = u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifdef LWS_NO_SERVER
|
|
|
|
int
|
2013-10-24 21:47:06 +08:00
|
|
|
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
|
2013-01-18 11:43:21 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
2011-01-27 06:26:52 +00:00
|
|
|
int
|
2013-01-17 16:50:35 +08:00
|
|
|
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
|
2011-01-27 06:26:52 +00:00
|
|
|
{
|
2011-03-02 22:03:47 +00:00
|
|
|
struct libwebsocket_context *context = wsi->protocol->owning_server;
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
/* there is no pending change */
|
|
|
|
if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
|
2013-01-17 16:50:35 +08:00
|
|
|
return 0;
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
/* stuff is still buffered, not ready to really accept new input */
|
|
|
|
if (wsi->u.ws.rxflow_buffer) {
|
|
|
|
/* get ourselves called back to deal with stashed buffer */
|
|
|
|
libwebsocket_callback_on_writable(context, wsi);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
/* pending is cleared, we can change rxflow state */
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
wsi->u.ws.rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
|
|
|
|
|
|
|
|
lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
|
|
|
|
wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW);
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-03-16 11:24:23 +08:00
|
|
|
/* adjust the pollfd for this wsi */
|
|
|
|
|
|
|
|
if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)
|
2013-12-21 11:18:34 +08:00
|
|
|
lws_change_pollfd(wsi, 0, POLLIN);
|
2013-01-17 16:50:35 +08:00
|
|
|
else
|
2013-12-21 11:18:34 +08:00
|
|
|
lws_change_pollfd(wsi, POLLIN, 0);
|
2013-12-18 09:48:26 +08:00
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
/**
|
|
|
|
* libwebsocket_rx_flow_control() - Enable and disable socket servicing for
|
|
|
|
* receieved packets.
|
|
|
|
*
|
|
|
|
* If the output side of a server process becomes choked, this allows flow
|
|
|
|
* control for the input side.
|
|
|
|
*
|
|
|
|
* @wsi: Websocket connection instance to get callback for
|
|
|
|
* @enable: 0 = disable read servicing for this connection, 1 = enable
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2013-01-17 16:50:35 +08:00
|
|
|
libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
|
|
|
|
{
|
2013-03-16 11:24:23 +08:00
|
|
|
if (enable == (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lwsl_info("libwebsocket_rx_flow_control(0x%p, %d)\n", wsi, enable);
|
|
|
|
wsi->u.ws.rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
|
2013-01-17 16:50:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-16 12:32:27 +08:00
|
|
|
/**
|
|
|
|
* libwebsocket_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive
|
|
|
|
*
|
|
|
|
* When the user server code realizes it can accept more input, it can
|
|
|
|
* call this to have the RX flow restriction removed from all connections using
|
|
|
|
* the given protocol.
|
|
|
|
*
|
|
|
|
* @protocol: all connections using this protocol will be allowed to receive
|
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void
|
2013-03-16 12:32:27 +08:00
|
|
|
libwebsocket_rx_flow_allow_all_protocol(
|
|
|
|
const struct libwebsocket_protocols *protocol)
|
|
|
|
{
|
|
|
|
struct libwebsocket_context *context = protocol->owning_server;
|
|
|
|
int n;
|
|
|
|
struct libwebsocket *wsi;
|
|
|
|
|
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
|
|
|
wsi = context->lws_lookup[context->fds[n].fd];
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->protocol == protocol)
|
|
|
|
libwebsocket_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2011-01-28 10:00:18 +00:00
|
|
|
/**
|
|
|
|
* libwebsocket_canonical_hostname() - returns this host's hostname
|
|
|
|
*
|
|
|
|
* This is typically used by client code to fill in the host parameter
|
|
|
|
* when making a client connection. You can only call it after the context
|
|
|
|
* has been created.
|
|
|
|
*
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: Websocket context
|
2011-01-28 10:00:18 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE extern const char *
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_canonical_hostname(struct libwebsocket_context *context)
|
2011-01-28 10:00:18 +00:00
|
|
|
{
|
2011-03-02 22:03:47 +00:00
|
|
|
return (const char *)context->canonical_hostname;
|
2011-01-28 10:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
static void sigpipe_handler(int x)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-02-21 08:06:47 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
static int
|
|
|
|
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|
|
|
{
|
|
|
|
|
|
|
|
SSL *ssl;
|
|
|
|
int n;
|
2011-03-05 16:12:04 +00:00
|
|
|
struct libwebsocket_context *context;
|
2011-02-21 08:06:47 +00:00
|
|
|
|
|
|
|
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
|
|
|
SSL_get_ex_data_X509_STORE_CTX_idx());
|
|
|
|
|
|
|
|
/*
|
2011-03-05 16:12:04 +00:00
|
|
|
* !!! nasty openssl requires the index to come as a library-scope
|
|
|
|
* static
|
2011-02-21 08:06:47 +00:00
|
|
|
*/
|
2011-03-05 16:12:04 +00:00
|
|
|
context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
n = context->protocols[0].callback(NULL, NULL,
|
2011-02-21 08:06:47 +00:00
|
|
|
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
|
|
|
|
x509_ctx, ssl, preverify_ok);
|
|
|
|
|
|
|
|
/* convert return code from 0 = OK to 1 = OK */
|
|
|
|
|
|
|
|
if (!n)
|
|
|
|
n = 1;
|
|
|
|
else
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-01-17 16:50:35 +08:00
|
|
|
int user_callback_handle_rxflow(callback_function callback_function,
|
2013-02-11 17:13:32 +08:00
|
|
|
struct libwebsocket_context *context,
|
2013-01-17 16:50:35 +08:00
|
|
|
struct libwebsocket *wsi,
|
|
|
|
enum libwebsocket_callback_reasons reason, void *user,
|
|
|
|
void *in, size_t len)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = callback_function(context, wsi, reason, user, in, len);
|
2013-02-10 21:21:24 +08:00
|
|
|
if (!n)
|
|
|
|
n = _libwebsocket_rx_flow_control(wsi);
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2013-02-10 21:21:24 +08:00
|
|
|
return n;
|
2013-01-17 16:50:35 +08:00
|
|
|
}
|
|
|
|
|
2013-12-25 16:34:37 +08:00
|
|
|
/*
|
|
|
|
* This is just used to interrupt poll waiting
|
|
|
|
* we don't have to do anything with it.
|
|
|
|
*/
|
2014-02-15 14:39:40 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2013-12-25 16:34:37 +08:00
|
|
|
static void lws_sigusr2(int sig)
|
|
|
|
{
|
|
|
|
}
|
2014-02-15 14:39:40 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2010-10-31 12:42:52 +00:00
|
|
|
/**
|
2011-01-22 12:51:57 +00:00
|
|
|
* libwebsocket_create_context() - Create the websocket handler
|
2013-02-09 14:01:09 +08:00
|
|
|
* @info: pointer to struct with parameters
|
2010-11-12 10:44:18 +00:00
|
|
|
*
|
2013-02-09 14:01:09 +08:00
|
|
|
* This function creates the listening socket (if serving) and takes care
|
2010-12-19 22:13:26 +00:00
|
|
|
* of all initialization in one step.
|
|
|
|
*
|
2011-01-19 13:11:55 +00:00
|
|
|
* After initialization, it returns a struct libwebsocket_context * that
|
|
|
|
* represents this server. After calling, user code needs to take care
|
|
|
|
* of calling libwebsocket_service() with the context pointer to get the
|
|
|
|
* server's sockets serviced. This can be done in the same process context
|
|
|
|
* or a forked process, or another thread,
|
2010-11-12 10:44:18 +00:00
|
|
|
*
|
2010-12-19 22:13:26 +00:00
|
|
|
* The protocol callback functions are called for a handful of events
|
|
|
|
* including http requests coming in, websocket connections becoming
|
|
|
|
* established, and data arriving; it's also called periodically to allow
|
|
|
|
* async transmission.
|
|
|
|
*
|
|
|
|
* HTTP requests are sent always to the FIRST protocol in @protocol, since
|
|
|
|
* at that time websocket protocol has not been negotiated. Other
|
|
|
|
* protocols after the first one never see any HTTP callack activity.
|
|
|
|
*
|
|
|
|
* The server created is a simple http server by default; part of the
|
|
|
|
* websocket standard is upgrading this http connection to a websocket one.
|
|
|
|
*
|
|
|
|
* This allows the same server to provide files like scripts and favicon /
|
|
|
|
* images or whatever over http and dynamic data over websockets all in
|
|
|
|
* one place; they're all handled in the user callback.
|
2010-10-31 12:42:52 +00:00
|
|
|
*/
|
2010-10-30 12:15:07 +01:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE struct libwebsocket_context *
|
2013-02-09 14:01:09 +08:00
|
|
|
libwebsocket_create_context(struct lws_context_creation_info *info)
|
2010-10-28 22:36:01 +01:00
|
|
|
{
|
2011-03-02 22:03:47 +00:00
|
|
|
struct libwebsocket_context *context = NULL;
|
2011-01-27 22:01:43 +00:00
|
|
|
char *p;
|
2013-02-11 19:36:15 +08:00
|
|
|
int n;
|
2013-02-22 09:27:59 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
2013-01-31 09:57:05 +08:00
|
|
|
int opt = 1;
|
2011-02-12 11:57:43 +00:00
|
|
|
struct libwebsocket *wsi;
|
2013-01-31 09:57:05 +08:00
|
|
|
struct sockaddr_in serv_addr;
|
|
|
|
#endif
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
|
|
|
int m;
|
2013-02-09 14:01:09 +08:00
|
|
|
struct libwebsocket_extension *ext;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2010-11-08 17:03:03 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2010-11-15 22:08:00 +00:00
|
|
|
SSL_METHOD *method;
|
2011-01-27 06:26:52 +00:00
|
|
|
#endif
|
2010-11-08 17:03:03 +00:00
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifndef LWS_NO_DAEMONIZE
|
2013-02-06 15:27:39 +09:00
|
|
|
int pid_daemon = get_daemonize_pid();
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
|
|
|
|
2013-01-19 13:08:17 +08:00
|
|
|
lwsl_notice("Initial logging level %d\n", log_level);
|
2013-02-01 10:50:15 +08:00
|
|
|
lwsl_notice("Library version: %s\n", library_version);
|
2013-01-12 23:42:17 +08:00
|
|
|
lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN);
|
|
|
|
lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS);
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n",
|
|
|
|
LWS_MAX_EXTENSIONS_ACTIVE);
|
2013-01-20 17:08:31 +08:00
|
|
|
#else
|
|
|
|
lwsl_notice(" Configured without extension support\n");
|
|
|
|
#endif
|
2013-01-12 23:42:17 +08:00
|
|
|
lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED);
|
|
|
|
lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT);
|
2013-02-22 09:54:35 +08:00
|
|
|
if (info->ssl_cipher_list)
|
|
|
|
lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
|
2013-01-12 23:42:17 +08:00
|
|
|
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
|
|
|
|
lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER);
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
{
|
|
|
|
WORD wVersionRequested;
|
|
|
|
WSADATA wsaData;
|
|
|
|
int err;
|
2012-04-09 15:09:01 +08:00
|
|
|
HMODULE wsdll;
|
2011-03-02 22:03:47 +00:00
|
|
|
|
|
|
|
/* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */
|
|
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
|
|
|
|
|
|
err = WSAStartup(wVersionRequested, &wsaData);
|
|
|
|
if (err != 0) {
|
|
|
|
/* Tell the user that we could not find a usable */
|
|
|
|
/* Winsock DLL. */
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("WSAStartup failed with error: %d\n", err);
|
2011-03-02 22:03:47 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-10-04 19:55:18 +08:00
|
|
|
|
2012-04-09 15:09:01 +08:00
|
|
|
/* default to a poll() made out of select() */
|
|
|
|
poll = emulated_poll;
|
2011-10-04 19:55:18 +08:00
|
|
|
|
2014-02-28 01:31:39 +01:00
|
|
|
#ifndef _WIN32_WCE
|
2012-04-09 15:09:01 +08:00
|
|
|
/* if windows socket lib available, use his WSAPoll */
|
2013-01-09 15:29:00 +08:00
|
|
|
wsdll = GetModuleHandle(_T("Ws2_32.dll"));
|
2012-04-09 15:09:01 +08:00
|
|
|
if (wsdll)
|
|
|
|
poll = (PFNWSAPOLL)GetProcAddress(wsdll, "WSAPoll");
|
2013-02-06 15:26:58 +09:00
|
|
|
|
2013-02-06 15:27:39 +09:00
|
|
|
/* Finally fall back to emulated poll if all else fails */
|
2013-02-06 15:26:58 +09:00
|
|
|
if (!poll)
|
|
|
|
poll = emulated_poll;
|
2014-02-28 01:31:39 +01:00
|
|
|
#endif
|
2011-03-02 22:03:47 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
context = (struct libwebsocket_context *)
|
|
|
|
malloc(sizeof(struct libwebsocket_context));
|
2011-03-02 22:03:47 +00:00
|
|
|
if (!context) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("No memory for websocket context\n");
|
2011-01-19 13:11:55 +00:00
|
|
|
return NULL;
|
2010-11-08 17:03:03 +00:00
|
|
|
}
|
2013-02-07 16:31:19 +02:00
|
|
|
memset(context, 0, sizeof(*context));
|
2013-01-21 13:06:38 +08:00
|
|
|
#ifndef LWS_NO_DAEMONIZE
|
2013-01-19 13:56:10 +08:00
|
|
|
context->started_with_parent = pid_daemon;
|
|
|
|
lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
|
|
|
|
#endif
|
2013-02-11 17:13:32 +08:00
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
context->listen_service_extraseen = 0;
|
2013-02-09 14:01:09 +08:00
|
|
|
context->protocols = info->protocols;
|
|
|
|
context->listen_port = info->port;
|
2011-03-02 22:03:47 +00:00
|
|
|
context->http_proxy_port = 0;
|
|
|
|
context->http_proxy_address[0] = '\0';
|
2013-02-09 14:01:09 +08:00
|
|
|
context->options = info->options;
|
2014-02-18 10:06:57 +01:00
|
|
|
context->iface = info->iface;
|
2013-01-17 12:26:48 +08:00
|
|
|
/* to reduce this allocation, */
|
|
|
|
context->max_fds = getdtablesize();
|
2013-02-11 11:04:56 +08:00
|
|
|
lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n",
|
|
|
|
sizeof(struct libwebsocket_context),
|
|
|
|
sizeof(struct pollfd) + sizeof(struct libwebsocket *),
|
|
|
|
context->max_fds,
|
2013-02-11 17:13:32 +08:00
|
|
|
sizeof(struct libwebsocket_context) +
|
|
|
|
((sizeof(struct pollfd) + sizeof(struct libwebsocket *)) *
|
|
|
|
context->max_fds));
|
2013-01-17 12:26:48 +08:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
context->fds = (struct pollfd *)malloc(sizeof(struct pollfd) *
|
|
|
|
context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
if (context->fds == NULL) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err("Unable to allocate fds array for %d connections\n",
|
|
|
|
context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
free(context);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-02-11 17:13:32 +08:00
|
|
|
context->lws_lookup = (struct libwebsocket **)
|
|
|
|
malloc(sizeof(struct libwebsocket *) * context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
if (context->lws_lookup == NULL) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_err(
|
|
|
|
"Unable to allocate lws_lookup array for %d connections\n",
|
|
|
|
context->max_fds);
|
2013-01-17 12:26:48 +08:00
|
|
|
free(context->fds);
|
|
|
|
free(context);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-09 12:34:30 +08:00
|
|
|
memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) *
|
|
|
|
context->max_fds);
|
2013-01-20 20:21:54 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
context->fds_count = 0;
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2013-02-09 14:01:09 +08:00
|
|
|
context->extensions = info->extensions;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2012-06-04 10:52:19 +08:00
|
|
|
context->last_timeout_check_s = 0;
|
2013-02-09 14:01:09 +08:00
|
|
|
context->user_space = info->user;
|
2011-03-02 22:03:47 +00:00
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
context->fd_random = 0;
|
|
|
|
#else
|
|
|
|
context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
|
|
|
|
if (context->fd_random < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("Unable to open random device %s %d\n",
|
2011-03-02 22:03:47 +00:00
|
|
|
SYSTEM_RANDOM_FILEPATH, context->fd_random);
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-02-10 09:32:24 +00:00
|
|
|
}
|
2011-03-02 22:03:47 +00:00
|
|
|
#endif
|
2011-02-10 09:32:24 +00:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
context->use_ssl = 0;
|
2013-12-14 11:41:29 +08:00
|
|
|
context->allow_non_ssl_on_ssl_port = 0;
|
2011-03-02 22:03:47 +00:00
|
|
|
context->ssl_ctx = NULL;
|
|
|
|
context->ssl_client_ctx = NULL;
|
2011-03-05 16:12:04 +00:00
|
|
|
openssl_websocket_private_data_index = 0;
|
2011-03-02 22:03:47 +00:00
|
|
|
#endif
|
2011-01-28 10:00:18 +00:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
strcpy(context->canonical_hostname, "unknown");
|
2012-05-03 12:32:38 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
2013-02-09 14:01:09 +08:00
|
|
|
if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) {
|
2012-10-22 12:29:57 +01:00
|
|
|
/* find canonical hostname */
|
2013-02-14 17:11:22 +08:00
|
|
|
gethostname((char *)context->canonical_hostname,
|
2013-02-11 17:13:32 +08:00
|
|
|
sizeof(context->canonical_hostname) - 1);
|
2012-10-22 12:29:57 +01:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_notice(" canonical_hostname = %s\n",
|
|
|
|
context->canonical_hostname);
|
2012-10-22 12:29:57 +01:00
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2012-05-03 12:32:38 +08:00
|
|
|
|
2011-01-27 22:01:43 +00:00
|
|
|
/* split the proxy ads:port if given */
|
|
|
|
|
|
|
|
p = getenv("http_proxy");
|
|
|
|
if (p) {
|
2011-03-02 22:03:47 +00:00
|
|
|
strncpy(context->http_proxy_address, p,
|
2013-02-11 17:13:32 +08:00
|
|
|
sizeof(context->http_proxy_address) - 1);
|
2011-03-02 22:03:47 +00:00
|
|
|
context->http_proxy_address[
|
2013-02-11 17:13:32 +08:00
|
|
|
sizeof(context->http_proxy_address) - 1] = '\0';
|
2011-01-27 22:01:43 +00:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
p = strchr(context->http_proxy_address, ':');
|
2011-01-27 22:01:43 +00:00
|
|
|
if (p == NULL) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("http_proxy needs to be ads:port\n");
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-27 22:01:43 +00:00
|
|
|
}
|
|
|
|
*p = '\0';
|
2011-03-02 22:03:47 +00:00
|
|
|
context->http_proxy_port = atoi(p + 1);
|
2011-01-27 22:01:43 +00:00
|
|
|
|
2013-01-19 13:08:17 +08:00
|
|
|
lwsl_notice(" Proxy %s:%u\n",
|
2011-03-02 22:03:47 +00:00
|
|
|
context->http_proxy_address,
|
|
|
|
context->http_proxy_port);
|
2011-01-27 22:01:43 +00:00
|
|
|
}
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->port) {
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2013-02-09 14:01:09 +08:00
|
|
|
context->use_ssl = info->ssl_cert_filepath != NULL &&
|
|
|
|
info->ssl_private_key_filepath != NULL;
|
2013-02-06 15:43:00 +09:00
|
|
|
#ifdef USE_CYASSL
|
|
|
|
lwsl_notice(" Compiled with CYASSL support\n");
|
|
|
|
#else
|
|
|
|
lwsl_notice(" Compiled with OpenSSL support\n");
|
|
|
|
#endif
|
2011-03-02 22:03:47 +00:00
|
|
|
if (context->use_ssl)
|
2013-02-06 15:43:00 +09:00
|
|
|
lwsl_notice(" Using SSL mode\n");
|
2011-01-27 06:26:52 +00:00
|
|
|
else
|
2013-02-06 15:43:00 +09:00
|
|
|
lwsl_notice(" Using non-SSL mode\n");
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
#else
|
2013-02-10 22:22:01 +08:00
|
|
|
if (info->ssl_cert_filepath != NULL &&
|
|
|
|
info->ssl_private_key_filepath != NULL) {
|
2013-01-19 13:08:17 +08:00
|
|
|
lwsl_notice(" Not compiled for OpenSSl support!\n");
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_notice(" Compiled without SSL support\n");
|
2010-11-08 17:03:03 +00:00
|
|
|
#endif
|
2013-01-20 20:21:54 +08:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_notice(
|
|
|
|
" per-conn mem: %u + %u headers + protocol rx buf\n",
|
|
|
|
sizeof(struct libwebsocket),
|
|
|
|
sizeof(struct allocated_headers));
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
/* ignore SIGPIPE */
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
#else
|
2011-01-27 06:26:52 +00:00
|
|
|
signal(SIGPIPE, sigpipe_handler);
|
2011-03-02 22:03:47 +00:00
|
|
|
#endif
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2010-11-08 17:03:03 +00:00
|
|
|
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
/* basic openssl init */
|
2010-11-08 17:03:03 +00:00
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
SSL_library_init();
|
|
|
|
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
SSL_load_error_strings();
|
|
|
|
|
2011-03-05 16:12:04 +00:00
|
|
|
openssl_websocket_private_data_index =
|
2011-02-21 08:06:47 +00:00
|
|
|
SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL);
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
/*
|
|
|
|
* Firefox insists on SSLv23 not SSLv3
|
|
|
|
* Konq disables SSLv2 by default now, SSLv23 works
|
|
|
|
*/
|
|
|
|
|
|
|
|
method = (SSL_METHOD *)SSLv23_server_method();
|
|
|
|
if (!method) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("problem creating ssl method %lu: %s\n",
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
2013-02-11 17:13:32 +08:00
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2011-03-02 22:03:47 +00:00
|
|
|
context->ssl_ctx = SSL_CTX_new(method); /* create context */
|
|
|
|
if (!context->ssl_ctx) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("problem creating ssl context %lu: %s\n",
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 18:17:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
#else
|
2013-12-25 16:34:37 +08:00
|
|
|
signal(SIGUSR2, lws_sigusr2);
|
2014-01-11 12:37:07 +08:00
|
|
|
{
|
|
|
|
sigset_t mask;
|
|
|
|
sigemptyset (&mask);
|
|
|
|
sigaddset (&mask, SIGUSR2);
|
|
|
|
|
|
|
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
|
|
|
}
|
2014-01-22 18:17:03 +00:00
|
|
|
#endif
|
2013-12-25 16:34:37 +08:00
|
|
|
|
2013-01-10 10:18:59 +08:00
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
2013-01-10 10:11:57 +08:00
|
|
|
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
2013-01-10 10:18:59 +08:00
|
|
|
#endif
|
2013-01-10 10:14:12 +08:00
|
|
|
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
2013-02-22 09:54:35 +08:00
|
|
|
if (info->ssl_cipher_list)
|
|
|
|
SSL_CTX_set_cipher_list(context->ssl_ctx,
|
|
|
|
info->ssl_cipher_list);
|
2013-01-10 10:11:57 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifndef LWS_NO_CLIENT
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
/* client context */
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->port == CONTEXT_PORT_NO_LISTEN) {
|
2011-03-02 22:03:47 +00:00
|
|
|
method = (SSL_METHOD *)SSLv23_client_method();
|
|
|
|
if (!method) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("problem creating ssl method %lu: %s\n",
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
2013-02-11 17:13:32 +08:00
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-03-02 22:03:47 +00:00
|
|
|
}
|
|
|
|
/* create context */
|
|
|
|
context->ssl_client_ctx = SSL_CTX_new(method);
|
|
|
|
if (!context->ssl_client_ctx) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("problem creating ssl context %lu: %s\n",
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
2013-02-11 17:13:32 +08:00
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-03-02 22:03:47 +00:00
|
|
|
}
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-10 10:18:59 +08:00
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
2013-02-11 17:13:32 +08:00
|
|
|
SSL_CTX_set_options(context->ssl_client_ctx,
|
|
|
|
SSL_OP_NO_COMPRESSION);
|
2013-01-10 10:18:59 +08:00
|
|
|
#endif
|
2013-02-11 17:13:32 +08:00
|
|
|
SSL_CTX_set_options(context->ssl_client_ctx,
|
|
|
|
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
2013-02-22 09:54:35 +08:00
|
|
|
if (info->ssl_cipher_list)
|
|
|
|
SSL_CTX_set_cipher_list(context->ssl_client_ctx,
|
|
|
|
info->ssl_cipher_list);
|
2013-01-10 10:11:57 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
/* openssl init for cert verification (for client sockets) */
|
2013-02-09 14:01:09 +08:00
|
|
|
if (!info->ssl_ca_filepath) {
|
2013-01-09 16:25:54 +08:00
|
|
|
if (!SSL_CTX_load_verify_locations(
|
|
|
|
context->ssl_client_ctx, NULL,
|
|
|
|
LWS_OPENSSL_CLIENT_CERTS))
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err(
|
2013-02-11 17:13:32 +08:00
|
|
|
"Unable to load SSL Client certs from %s "
|
|
|
|
"(set by --with-client-cert-dir= "
|
|
|
|
"in configure) -- client ssl isn't "
|
|
|
|
"going to work", LWS_OPENSSL_CLIENT_CERTS);
|
2013-01-09 16:25:54 +08:00
|
|
|
} else
|
|
|
|
if (!SSL_CTX_load_verify_locations(
|
2013-02-09 14:01:09 +08:00
|
|
|
context->ssl_client_ctx, info->ssl_ca_filepath,
|
2013-01-09 16:25:54 +08:00
|
|
|
NULL))
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err(
|
2013-01-09 16:25:54 +08:00
|
|
|
"Unable to load SSL Client certs "
|
|
|
|
"file from %s -- client ssl isn't "
|
2013-02-09 14:01:09 +08:00
|
|
|
"going to work", info->ssl_ca_filepath);
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
/*
|
|
|
|
* callback allowing user code to load extra verification certs
|
|
|
|
* helping the client to verify server identity
|
|
|
|
*/
|
2011-02-19 09:09:11 +00:00
|
|
|
|
2013-11-12 17:21:01 -08:00
|
|
|
/* support for client-side certificate authentication */
|
|
|
|
if (info->ssl_cert_filepath) {
|
|
|
|
n = SSL_CTX_use_certificate_chain_file(
|
|
|
|
context->ssl_client_ctx,
|
|
|
|
info->ssl_cert_filepath);
|
|
|
|
if (n != 1) {
|
|
|
|
lwsl_err("problem getting cert '%s' %lu: %s\n",
|
|
|
|
info->ssl_cert_filepath,
|
|
|
|
ERR_get_error(),
|
|
|
|
ERR_error_string(ERR_get_error(),
|
|
|
|
(char *)context->service_buffer));
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (info->ssl_private_key_filepath) {
|
|
|
|
/* set the private key from KeyFile */
|
|
|
|
if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx,
|
|
|
|
info->ssl_private_key_filepath,
|
|
|
|
SSL_FILETYPE_PEM) != 1) {
|
|
|
|
lwsl_err("use_PrivateKey_file '%s' %lu: %s\n",
|
|
|
|
info->ssl_private_key_filepath,
|
|
|
|
ERR_get_error(),
|
|
|
|
ERR_error_string(ERR_get_error(),
|
|
|
|
(char *)context->service_buffer));
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify private key */
|
|
|
|
if (!SSL_CTX_check_private_key(context->ssl_client_ctx)) {
|
|
|
|
lwsl_err("Private SSL key doesn't match cert\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
context->protocols[0].callback(context, NULL,
|
|
|
|
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
|
|
|
|
context->ssl_client_ctx, NULL, 0);
|
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2011-02-20 11:10:47 +00:00
|
|
|
/* as a server, are we requiring clients to identify themselves? */
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
if (info->options &
|
|
|
|
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) {
|
2011-02-20 11:10:47 +00:00
|
|
|
|
|
|
|
/* absolutely require the client cert */
|
2012-04-09 15:09:01 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
SSL_CTX_set_verify(context->ssl_ctx,
|
2011-02-21 08:06:47 +00:00
|
|
|
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
|
|
OpenSSL_verify_callback);
|
2011-02-20 11:10:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* give user code a chance to load certs into the server
|
|
|
|
* allowing it to verify incoming client certs
|
|
|
|
*/
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
context->protocols[0].callback(context, NULL,
|
2011-02-20 11:10:47 +00:00
|
|
|
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
|
2011-03-02 22:03:47 +00:00
|
|
|
context->ssl_ctx, NULL, 0);
|
2011-02-20 11:10:47 +00:00
|
|
|
}
|
|
|
|
|
2013-12-14 11:41:29 +08:00
|
|
|
if(info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) {
|
|
|
|
/* Normally SSL listener rejects non-ssl, optionally allow */
|
|
|
|
context->allow_non_ssl_on_ssl_port = 1;
|
|
|
|
}
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
if (context->use_ssl) {
|
2011-01-27 06:26:52 +00:00
|
|
|
|
|
|
|
/* openssl init for server sockets */
|
2010-11-08 17:03:03 +00:00
|
|
|
|
|
|
|
/* set the local certificate from CertFile */
|
2013-01-10 10:11:21 +08:00
|
|
|
n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx,
|
2013-02-09 14:01:09 +08:00
|
|
|
info->ssl_cert_filepath);
|
2010-11-08 17:03:03 +00:00
|
|
|
if (n != 1) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("problem getting cert '%s' %lu: %s\n",
|
2013-02-09 14:01:09 +08:00
|
|
|
info->ssl_cert_filepath,
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
2013-02-11 17:13:32 +08:00
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2010-11-08 17:03:03 +00:00
|
|
|
}
|
|
|
|
/* set the private key from KeyFile */
|
2011-03-02 22:03:47 +00:00
|
|
|
if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx,
|
2013-02-09 14:01:09 +08:00
|
|
|
info->ssl_private_key_filepath,
|
|
|
|
SSL_FILETYPE_PEM) != 1) {
|
2013-11-22 13:14:26 +02:00
|
|
|
int error = ERR_get_error();
|
2013-02-22 09:28:15 +08:00
|
|
|
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
|
2013-02-11 17:13:32 +08:00
|
|
|
info->ssl_private_key_filepath,
|
2013-11-22 13:14:26 +02:00
|
|
|
error,
|
|
|
|
ERR_error_string(error,
|
2013-02-11 17:13:32 +08:00
|
|
|
(char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2010-11-08 17:03:03 +00:00
|
|
|
}
|
|
|
|
/* verify private key */
|
2011-03-02 22:03:47 +00:00
|
|
|
if (!SSL_CTX_check_private_key(context->ssl_ctx)) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("Private SSL key doesn't match cert\n");
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2010-11-08 17:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SSL is happy and has a cert it's content with */
|
|
|
|
}
|
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
2010-12-18 15:13:50 +00:00
|
|
|
/* set up our external listening socket we serve on */
|
2010-12-19 22:13:26 +00:00
|
|
|
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->port) {
|
2013-01-18 11:43:21 +08:00
|
|
|
int sockfd;
|
2010-12-19 22:13:26 +00:00
|
|
|
|
2011-01-22 12:51:57 +00:00
|
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sockfd < 0) {
|
2013-01-14 13:10:55 +08:00
|
|
|
lwsl_err("ERROR opening socket\n");
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-22 12:51:57 +00:00
|
|
|
}
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
/*
|
|
|
|
* allow us to restart even if old sockets in TIME_WAIT
|
|
|
|
*/
|
2012-04-09 15:09:01 +08:00
|
|
|
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
(const void *)&opt, sizeof(opt));
|
2011-03-08 08:56:57 +00:00
|
|
|
|
|
|
|
/* Disable Nagle */
|
|
|
|
opt = 1;
|
2012-04-09 15:09:01 +08:00
|
|
|
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
|
|
|
|
(const void *)&opt, sizeof(opt));
|
2011-03-08 08:56:57 +00:00
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2013-02-06 15:26:58 +09:00
|
|
|
opt = 0;
|
2013-02-11 17:13:32 +08:00
|
|
|
ioctlsocket(sockfd, FIONBIO, (unsigned long *)&opt);
|
2013-02-06 15:26:58 +09:00
|
|
|
#else
|
2013-01-28 12:19:10 +08:00
|
|
|
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-28 12:19:10 +08:00
|
|
|
|
2011-01-22 12:51:57 +00:00
|
|
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
|
|
|
serv_addr.sin_family = AF_INET;
|
2013-02-11 17:52:23 +01:00
|
|
|
if (info->iface == NULL)
|
2011-02-19 08:32:53 +00:00
|
|
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
else
|
2013-11-09 08:07:38 +08:00
|
|
|
if (interface_to_sa(info->iface, &serv_addr,
|
|
|
|
sizeof(serv_addr)) < 0) {
|
|
|
|
lwsl_err("Unable to find interface %s\n",
|
|
|
|
info->iface);
|
|
|
|
compatible_close(sockfd);
|
|
|
|
goto bail;
|
|
|
|
}
|
2013-02-09 14:01:09 +08:00
|
|
|
serv_addr.sin_port = htons(info->port);
|
2010-11-13 10:03:47 +00:00
|
|
|
|
2011-01-22 12:51:57 +00:00
|
|
|
n = bind(sockfd, (struct sockaddr *) &serv_addr,
|
|
|
|
sizeof(serv_addr));
|
|
|
|
if (n < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("ERROR on binding to port %d (%d %d)\n",
|
2013-02-09 14:01:09 +08:00
|
|
|
info->port, n, errno);
|
2013-10-22 06:49:30 +08:00
|
|
|
compatible_close(sockfd);
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-22 12:51:57 +00:00
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
wsi = (struct libwebsocket *)malloc(
|
|
|
|
sizeof(struct libwebsocket));
|
2013-01-12 13:21:08 +08:00
|
|
|
if (wsi == NULL) {
|
|
|
|
lwsl_err("Out of mem\n");
|
2013-10-22 06:49:30 +08:00
|
|
|
compatible_close(sockfd);
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2013-01-12 13:21:08 +08:00
|
|
|
}
|
2013-02-11 17:13:32 +08:00
|
|
|
memset(wsi, 0, sizeof(struct libwebsocket));
|
2011-02-12 11:57:43 +00:00
|
|
|
wsi->sock = sockfd;
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-05 16:12:15 +00:00
|
|
|
wsi->count_active_extensions = 0;
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-02-12 11:57:43 +00:00
|
|
|
wsi->mode = LWS_CONNMODE_SERVER_LISTENER;
|
2013-01-17 12:26:48 +08:00
|
|
|
|
|
|
|
insert_wsi_socket_into_fds(context, wsi);
|
2011-02-12 11:57:43 +00:00
|
|
|
|
listen socket more frequent service
From an idea by Edwin van den Oetelaar <oetelaar.automatisering@gmail.com>
When testing libwebsockets with ab, Edwin found an unexpected bump in
the distribution of latencies, some connections were held back almost
the whole test duration.
http://ml.libwebsockets.org/pipermail/libwebsockets/2013-January/000006.html
Studying the problem revealed that when there are mass pending connections
amongst many active connections, we do not service the listen socket often
enough to clear the backlog, some seem to get stale violating FIFO ordering.
This patch introduces listen socket service "piggybacking", where every n
normal socket service actions we also check the listen socket and deal with
pending connections there.
Normally, it checks the listen socket gratuitously every 10 normal socket
services. However, if it finds something waiting, it forces a check on the
next normal socket service too by keeping stats on how often something was
waiting. If the probability of something waiting each time becomes high,
it will allow up to two waiting connections to be serviced for each normal
socket service.
In that way it has low burden in the normal case, but rapidly adapts by
detecting mass connection loads as found in ab.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-01-16 07:59:47 +08:00
|
|
|
context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO;
|
|
|
|
context->listen_service_count = 0;
|
|
|
|
context->listen_service_fd = sockfd;
|
|
|
|
|
2013-01-15 20:52:29 +08:00
|
|
|
listen(sockfd, LWS_SOMAXCONN);
|
2013-02-09 14:01:09 +08:00
|
|
|
lwsl_notice(" Listening on port %d\n", info->port);
|
2010-12-19 22:13:26 +00:00
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2012-04-09 15:09:01 +08:00
|
|
|
/*
|
|
|
|
* drop any root privs for this process
|
|
|
|
* to listen on port < 1023 we would have needed root, but now we are
|
|
|
|
* listening, we don't want the power for anything else
|
|
|
|
*/
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2011-03-02 22:03:47 +00:00
|
|
|
#else
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->gid != -1)
|
|
|
|
if (setgid(info->gid))
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_warn("setgid: %s\n", strerror(errno));
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->uid != -1)
|
|
|
|
if (setuid(info->uid))
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_warn("setuid: %s\n", strerror(errno));
|
2011-03-02 22:03:47 +00:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2013-01-29 17:57:39 +08:00
|
|
|
/* initialize supported protocols */
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
for (context->count_protocols = 0;
|
2013-02-09 14:01:09 +08:00
|
|
|
info->protocols[context->count_protocols].callback;
|
2011-03-02 22:03:47 +00:00
|
|
|
context->count_protocols++) {
|
2011-05-24 10:14:41 +01:00
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_parser(" Protocol: %s\n",
|
2013-02-09 14:01:09 +08:00
|
|
|
info->protocols[context->count_protocols].name);
|
2011-05-24 10:14:41 +01:00
|
|
|
|
2013-02-09 14:01:09 +08:00
|
|
|
info->protocols[context->count_protocols].owning_server =
|
|
|
|
context;
|
|
|
|
info->protocols[context->count_protocols].protocol_index =
|
2011-03-02 22:03:47 +00:00
|
|
|
context->count_protocols;
|
2013-02-11 12:05:54 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* inform all the protocols that they are doing their one-time
|
|
|
|
* initialization if they want to
|
|
|
|
*/
|
|
|
|
info->protocols[context->count_protocols].callback(context,
|
|
|
|
NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
2013-01-21 09:09:52 +08:00
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-05-23 10:00:03 +01:00
|
|
|
/*
|
|
|
|
* give all extensions a chance to create any per-context
|
|
|
|
* allocations they need
|
|
|
|
*/
|
|
|
|
|
|
|
|
m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT;
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->port)
|
2011-05-23 10:00:03 +01:00
|
|
|
m = LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT;
|
2013-02-11 17:13:32 +08:00
|
|
|
|
2013-02-09 14:01:09 +08:00
|
|
|
if (info->extensions) {
|
|
|
|
ext = info->extensions;
|
|
|
|
while (ext->callback) {
|
|
|
|
lwsl_ext(" Extension: %s\n", ext->name);
|
|
|
|
ext->callback(context, ext, NULL,
|
2013-01-10 12:35:18 +08:00
|
|
|
(enum libwebsocket_extension_callback_reasons)m,
|
|
|
|
NULL, NULL, 0);
|
2013-02-09 14:01:09 +08:00
|
|
|
ext++;
|
|
|
|
}
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2013-12-25 16:34:37 +08:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
return context;
|
2013-02-07 16:31:19 +02:00
|
|
|
|
|
|
|
bail:
|
|
|
|
libwebsocket_context_destroy(context);
|
|
|
|
return NULL;
|
2011-01-19 13:11:55 +00:00
|
|
|
}
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2013-10-24 22:12:03 +08:00
|
|
|
/**
|
|
|
|
* libwebsocket_set_proxy() - Setups proxy to libwebsocket_context.
|
|
|
|
* @context: pointer to struct libwebsocket_context you want set proxy to
|
|
|
|
* @proxy: pointer to c string containing proxy in format address:port
|
|
|
|
*
|
|
|
|
* Returns 0 if proxy string was parsed and proxy was setup.
|
|
|
|
* Returns -1 if @proxy is NULL or has incorrect format.
|
|
|
|
*
|
|
|
|
* This is only required if your OS does not provide the http_proxy
|
|
|
|
* enviroment variable (eg, OSX)
|
|
|
|
*
|
|
|
|
* IMPORTANT! You should call this function right after creation of the
|
|
|
|
* libwebsocket_context and before call to connect. If you call this
|
|
|
|
* function after connect behavior is undefined.
|
|
|
|
* This function will override proxy settings made on libwebsocket_context
|
|
|
|
* creation with genenv() call.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LWS_VISIBLE int
|
|
|
|
libwebsocket_set_proxy(struct libwebsocket_context *context, const char *proxy)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (!proxy)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
strncpy(context->http_proxy_address, proxy,
|
|
|
|
sizeof(context->http_proxy_address) - 1);
|
|
|
|
context->http_proxy_address[
|
|
|
|
sizeof(context->http_proxy_address) - 1] = '\0';
|
|
|
|
|
|
|
|
p = strchr(context->http_proxy_address, ':');
|
|
|
|
if (!p) {
|
|
|
|
lwsl_err("http_proxy needs to be ads:port\n");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
context->http_proxy_port = atoi(p + 1);
|
|
|
|
|
|
|
|
lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
|
|
|
|
context->http_proxy_port);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
/**
|
|
|
|
* libwebsockets_get_protocol() - Returns a protocol pointer from a websocket
|
2010-12-19 22:13:26 +00:00
|
|
|
* connection.
|
2010-12-18 15:13:50 +00:00
|
|
|
* @wsi: pointer to struct websocket you want to know the protocol of
|
|
|
|
*
|
2010-12-19 22:13:26 +00:00
|
|
|
*
|
2013-01-29 17:57:39 +08:00
|
|
|
* Some apis can act on all live connections of a given protocol,
|
|
|
|
* this is how you can get a pointer to the active protocol if needed.
|
2010-12-18 15:13:50 +00:00
|
|
|
*/
|
2010-10-28 22:36:01 +01:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE const struct libwebsocket_protocols *
|
2010-12-18 15:13:50 +00:00
|
|
|
libwebsockets_get_protocol(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
return wsi->protocol;
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2011-03-07 21:16:31 +00:00
|
|
|
libwebsocket_is_final_fragment(struct libwebsocket *wsi)
|
|
|
|
{
|
2013-01-21 11:04:23 +08:00
|
|
|
return wsi->u.ws.final;
|
2011-03-07 21:16:31 +00:00
|
|
|
}
|
2011-11-07 17:19:25 +08:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE unsigned char
|
2013-01-09 18:06:55 +08:00
|
|
|
libwebsocket_get_reserved_bits(struct libwebsocket *wsi)
|
|
|
|
{
|
2013-01-21 11:04:23 +08:00
|
|
|
return wsi->u.ws.rsv;
|
2013-01-09 18:06:55 +08:00
|
|
|
}
|
|
|
|
|
2013-02-18 16:30:10 +08:00
|
|
|
int
|
2011-11-07 17:19:25 +08:00
|
|
|
libwebsocket_ensure_user_space(struct libwebsocket *wsi)
|
|
|
|
{
|
2013-02-15 22:31:55 +08:00
|
|
|
if (!wsi->protocol)
|
2013-02-18 16:30:10 +08:00
|
|
|
return 1;
|
2013-02-15 22:31:55 +08:00
|
|
|
|
2011-11-07 17:19:25 +08:00
|
|
|
/* allocate the per-connection user memory (if any) */
|
|
|
|
|
|
|
|
if (wsi->protocol->per_session_data_size && !wsi->user_space) {
|
|
|
|
wsi->user_space = malloc(
|
|
|
|
wsi->protocol->per_session_data_size);
|
|
|
|
if (wsi->user_space == NULL) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("Out of memory for conn user space\n");
|
2013-02-18 16:30:10 +08:00
|
|
|
return 1;
|
2011-11-07 17:19:25 +08:00
|
|
|
}
|
2012-04-09 15:09:01 +08:00
|
|
|
memset(wsi->user_space, 0,
|
|
|
|
wsi->protocol->per_session_data_size);
|
2011-11-07 17:19:25 +08:00
|
|
|
}
|
2013-02-18 16:30:10 +08:00
|
|
|
return 0;
|
2011-11-07 17:19:25 +08:00
|
|
|
}
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2013-01-19 11:17:56 +08:00
|
|
|
static void lwsl_emit_stderr(int level, const char *line)
|
2013-01-12 09:17:42 +08:00
|
|
|
{
|
2013-01-19 11:17:56 +08:00
|
|
|
char buf[300];
|
2014-02-26 21:37:31 +01:00
|
|
|
unsigned long long now;
|
2013-01-19 11:17:56 +08:00
|
|
|
int n;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2013-01-19 11:17:56 +08:00
|
|
|
buf[0] = '\0';
|
2013-01-10 19:50:35 +08:00
|
|
|
for (n = 0; n < LLL_COUNT; n++)
|
2013-01-19 11:17:56 +08:00
|
|
|
if (level == (1 << n)) {
|
2014-02-26 21:37:31 +01:00
|
|
|
now = time_in_microseconds() / 100;
|
|
|
|
sprintf(buf, "[%lu:%04d] %s: ", (unsigned long) now / 10000,
|
|
|
|
(int)(now % 10000), log_level_names[n]);
|
2013-01-10 19:50:35 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-02-11 17:13:32 +08:00
|
|
|
|
2013-01-19 11:17:56 +08:00
|
|
|
fprintf(stderr, "%s%s", buf, line);
|
|
|
|
}
|
|
|
|
|
2013-10-28 15:18:04 +01:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
2013-02-06 15:26:58 +09:00
|
|
|
{
|
|
|
|
lwsl_emit_stderr(level, line);
|
|
|
|
}
|
|
|
|
#else
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
2013-01-19 11:12:16 +08:00
|
|
|
{
|
|
|
|
int syslog_level = LOG_DEBUG;
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
case LLL_ERR:
|
|
|
|
syslog_level = LOG_ERR;
|
|
|
|
break;
|
|
|
|
case LLL_WARN:
|
|
|
|
syslog_level = LOG_WARNING;
|
|
|
|
break;
|
|
|
|
case LLL_NOTICE:
|
|
|
|
syslog_level = LOG_NOTICE;
|
|
|
|
break;
|
|
|
|
case LLL_INFO:
|
|
|
|
syslog_level = LOG_INFO;
|
|
|
|
break;
|
|
|
|
}
|
2013-01-19 20:01:01 +08:00
|
|
|
syslog(syslog_level, "%s", line);
|
2013-01-19 11:12:16 +08:00
|
|
|
}
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
2013-01-19 11:12:16 +08:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void _lws_log(int filter, const char *format, ...)
|
2013-01-19 11:17:56 +08:00
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (!(log_level & filter))
|
|
|
|
return;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
|
|
|
va_start(ap, format);
|
2013-02-11 17:13:32 +08:00
|
|
|
vsnprintf(buf, sizeof(buf), format, ap);
|
|
|
|
buf[sizeof(buf) - 1] = '\0';
|
2013-01-12 09:17:42 +08:00
|
|
|
va_end(ap);
|
|
|
|
|
2013-01-19 11:17:56 +08:00
|
|
|
lwsl_emit(filter, buf);
|
2013-01-10 19:50:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lws_set_log_level() - Set the logging bitfield
|
|
|
|
* @level: OR together the LLL_ debug contexts you want output from
|
2013-01-12 09:17:42 +08:00
|
|
|
* @log_emit_function: NULL to leave it as it is, or a user-supplied
|
|
|
|
* function to perform log string emission instead of
|
|
|
|
* the default stderr one.
|
2013-01-10 19:50:35 +08:00
|
|
|
*
|
2013-02-19 10:01:48 +08:00
|
|
|
* log level defaults to "err", "warn" and "notice" contexts enabled and
|
2013-01-12 09:17:42 +08:00
|
|
|
* emission on stderr.
|
2013-01-10 19:50:35 +08:00
|
|
|
*/
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void lws_set_log_level(int level, void (*log_emit_function)(int level,
|
2013-02-11 17:13:32 +08:00
|
|
|
const char *line))
|
2013-01-10 19:50:35 +08:00
|
|
|
{
|
|
|
|
log_level = level;
|
2013-01-12 09:17:42 +08:00
|
|
|
if (log_emit_function)
|
|
|
|
lwsl_emit = log_emit_function;
|
2013-01-10 19:50:35 +08:00
|
|
|
}
|
2014-02-21 18:43:42 +08:00
|
|
|
|
|
|
|
int
|
|
|
|
interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
/* TODO */
|
|
|
|
#else
|
|
|
|
struct ifaddrs *ifr;
|
|
|
|
struct ifaddrs *ifc;
|
|
|
|
struct sockaddr_in *sin;
|
|
|
|
|
|
|
|
getifaddrs(&ifr);
|
|
|
|
for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
|
|
|
|
if (ifc->ifa_addr == NULL)
|
|
|
|
continue;
|
|
|
|
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
|
|
|
|
if (strcmp(ifc->ifa_name, ifname))
|
|
|
|
continue;
|
|
|
|
sin = (struct sockaddr_in *)ifc->ifa_addr;
|
|
|
|
if (sin->sin_family != AF_INET)
|
|
|
|
continue;
|
|
|
|
memcpy(addr, sin, addrlen);
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeifaddrs(ifr);
|
|
|
|
#endif
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|