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
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef WIN32
|
2013-01-09 15:29:00 +08:00
|
|
|
#include <tchar.h>
|
|
|
|
#include <io.h>
|
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
|
|
|
|
|
|
|
#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-01-10 19:50:35 +08:00
|
|
|
static const char *log_level_names[] = {
|
|
|
|
"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-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
|
|
|
|
*/
|
|
|
|
|
|
|
|
const char *
|
|
|
|
lws_get_library_version(void)
|
|
|
|
{
|
|
|
|
return library_version;
|
|
|
|
}
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
int
|
2013-01-17 12:26:48 +08:00
|
|
|
insert_wsi_socket_into_fds(struct libwebsocket_context *context, struct libwebsocket *wsi)
|
2011-02-12 11:57:43 +00:00
|
|
|
{
|
2013-01-17 12:26:48 +08:00
|
|
|
if (context->fds_count >= context->max_fds) {
|
|
|
|
lwsl_err("Reached limit of fds tracking (%d)\n", context->max_fds);
|
|
|
|
return 1;
|
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (wsi->sock > context->max_fds) {
|
|
|
|
lwsl_err("Socket fd %d is beyond what we can index (%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);
|
|
|
|
assert(wsi->sock);
|
|
|
|
|
|
|
|
lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", wsi, wsi->sock, context->fds_count);
|
|
|
|
|
|
|
|
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,
|
|
|
|
(void *)(long)wsi->sock, NULL, POLLIN);
|
2011-02-12 11:57:43 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
static int
|
|
|
|
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;
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (!--context->fds_count)
|
|
|
|
goto do_ext;
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (wsi->sock > context->max_fds) {
|
|
|
|
lwsl_err("Socket fd %d is beyond what we can index (%d)\n", wsi->sock, context->max_fds);
|
|
|
|
return 1;
|
|
|
|
}
|
2011-02-12 11:57:43 +00:00
|
|
|
|
2013-01-17 12:26:48 +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);
|
|
|
|
|
|
|
|
m = wsi->position_in_fds_table; /* replace the contents for this */
|
|
|
|
|
|
|
|
/* have the last guy take up the vacant slot */
|
|
|
|
context->fds[m] = context->fds[context->fds_count]; /* vacant fds slot filled with end one */
|
|
|
|
/* end guy's fds_lookup entry remains unchanged (still same fd pointing to same wsi) */
|
|
|
|
/* end guy's "position in fds table" changed */
|
|
|
|
context->lws_lookup[context->fds[context->fds_count].fd]->position_in_fds_table = m;
|
|
|
|
/* deletion guy's lws_lookup entry needs nuking */
|
|
|
|
context->lws_lookup[wsi->sock] = NULL; /* no WSI for the socket of the wsi being removed*/
|
|
|
|
wsi->position_in_fds_table = -1; /* removed wsi has no position any more */
|
|
|
|
|
|
|
|
do_ext:
|
|
|
|
/* remove also from external POLL support via protocol 0 */
|
|
|
|
if (wsi->sock)
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_DEL_POLL_FD, (void *)(long)wsi->sock, NULL, 0);
|
|
|
|
|
|
|
|
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-01-21 11:04:23 +08:00
|
|
|
wsi->u.ws.close_reason = reason;
|
2011-03-07 07:08:12 +00:00
|
|
|
|
2013-01-22 07:20:08 +08:00
|
|
|
if (wsi->mode == LWS_CONNMODE_HTTP_SERVING && wsi->u.http.fd) {
|
|
|
|
close(wsi->u.http.fd);
|
|
|
|
wsi->u.http.fd = 0;
|
|
|
|
}
|
|
|
|
|
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-01-17 14:46:43 +08:00
|
|
|
eff_buf.token_len)) {
|
|
|
|
lwsl_debug("close: sending final extension spill had problems\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 */
|
|
|
|
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);
|
|
|
|
if (!n) {
|
|
|
|
/*
|
|
|
|
* 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-01-17 14:46:43 +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-01-17 14:46:43 +08:00
|
|
|
lwsl_info("close: sending the close packet failed, hanging up\n");
|
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/* else, the send failed and we should just hang up */
|
|
|
|
}
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-03-07 07:08:18 +00:00
|
|
|
just_kill_connection:
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("libwebsocket_close_and_free_session: 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 14:12:32 +08:00
|
|
|
if ((old_state == WSI_STATE_ESTABLISHED || wsi->mode == LWS_CONNMODE_WS_SERVING || wsi->mode == LWS_CONNMODE_WS_CLIENT) && wsi->u.ws.rx_user_buffer) {
|
2013-02-06 21:10:16 +09:00
|
|
|
free(wsi->u.ws.rx_user_buffer);
|
|
|
|
wsi->u.ws.rx_user_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
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);
|
2011-11-07 19:53:23 +08:00
|
|
|
} else
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("not calling back closed, old_state=%d\n", old_state);
|
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-21 11:04:23 +08:00
|
|
|
if (wsi->u.ws.rxflow_buffer)
|
|
|
|
free(wsi->u.ws.rxflow_buffer);
|
2013-01-17 16:50:35 +08: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)
|
|
|
|
lwsl_debug("closing: shutdown returned %d\n", errno);
|
|
|
|
|
|
|
|
n = compatible_close(wsi->sock);
|
|
|
|
if (n)
|
|
|
|
lwsl_debug("closing: close returned %d\n", errno);
|
|
|
|
}
|
2010-11-08 17:03:03 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
}
|
|
|
|
#endif
|
2012-04-20 12:19:01 +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 09:04:21 +00:00
|
|
|
/**
|
|
|
|
* libwebsockets_hangup_on_client() - Server calls to terminate client
|
2012-04-09 15:09:01 +08:00
|
|
|
* connection
|
2011-03-02 22:03:47 +00:00
|
|
|
* @context: libwebsockets context
|
2011-02-13 09:04:21 +00:00
|
|
|
* @fd: Connection socket descriptor
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsockets_hangup_on_client(struct libwebsocket_context *context, int fd)
|
2011-02-13 09:04:21 +00:00
|
|
|
{
|
2013-01-17 12:26:48 +08:00
|
|
|
struct libwebsocket *wsi = context->lws_lookup[fd];
|
2011-02-13 09:04:21 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (wsi) {
|
2013-01-30 12:28:34 +08:00
|
|
|
lwsl_info("closing connection at libwebsockets_hangup_on_client:\n");
|
2013-01-17 12:26:48 +08:00
|
|
|
libwebsocket_close_and_free_session(context,
|
|
|
|
wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
|
|
|
} else
|
|
|
|
close(fd);
|
2011-02-13 09:04:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
unsigned int len;
|
|
|
|
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
|
|
|
|
struct sockaddr_un *un;
|
|
|
|
#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);
|
|
|
|
|
2011-02-13 08:37:12 +00:00
|
|
|
len = sizeof sin;
|
|
|
|
if (getpeername(fd, (struct sockaddr *) &sin, &len) < 0) {
|
|
|
|
perror("getpeername");
|
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
|
|
|
|
2011-02-13 08:37:12 +00:00
|
|
|
host = gethostbyaddr((char *) &sin.sin_addr, sizeof sin.sin_addr,
|
|
|
|
AF_INET);
|
|
|
|
if (host == NULL) {
|
|
|
|
perror("gethostbyaddr");
|
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
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
int libwebsockets_get_random(struct libwebsocket_context *context,
|
|
|
|
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
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
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);
|
|
|
|
#ifdef WIN32
|
|
|
|
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-02-10 09:39:47 +08:00
|
|
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* didn't find a way to set these per-socket, need to
|
|
|
|
* tune kernel systemwide values
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
|
|
optval = context->ka_probes;
|
|
|
|
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
|
|
|
|
(const void *)&optval, optlen) < 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
optval = context->ka_interval;
|
|
|
|
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... */
|
|
|
|
#ifdef WIN32
|
|
|
|
ioctlsocket(fd, FIONBIO, &optl);
|
|
|
|
#else
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-06 10:29:39 +00:00
|
|
|
int lws_send_pipe_choked(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
struct pollfd fds;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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) {
|
|
|
|
if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
|
|
|
|
eff_buf.token_len))
|
|
|
|
return -1;
|
|
|
|
} 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 */
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
if (pollfd) {
|
|
|
|
pollfd->events &= ~POLLOUT;
|
2011-03-06 13:14:42 +00:00
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
/* external POLL support via protocol 0 */
|
|
|
|
context->protocols[0].callback(context, wsi,
|
|
|
|
LWS_CALLBACK_CLEAR_MODE_POLL_FD,
|
|
|
|
(void *)(long)wsi->sock, NULL, POLLOUT);
|
|
|
|
}
|
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-01-17 16:50:35 +08:00
|
|
|
wsi, (enum libwebsocket_callback_reasons) n, wsi->user_space, NULL, 0);
|
2011-03-06 13:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
void
|
|
|
|
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)
|
|
|
|
return;
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
* services it according to the state of the associated struct libwebsocket.
|
|
|
|
*
|
|
|
|
* The one call deals with all "service" that might happen on a socket
|
|
|
|
* including listen accepts, http files as well as websocket protocol.
|
2011-02-12 11:57:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
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;
|
2011-02-14 17:59:43 +00:00
|
|
|
struct timeval tv;
|
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-01-16 11:47:40 +08:00
|
|
|
#ifndef LWS_NO_CLIENT
|
2013-01-16 11:53:05 +08:00
|
|
|
extern int lws_client_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd);
|
2013-01-18 11:43:21 +08:00
|
|
|
#endif
|
|
|
|
#ifndef LWS_NO_SERVER
|
|
|
|
extern int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd);
|
2013-01-16 11:47:40 +08:00
|
|
|
#endif
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
if (context->last_timeout_check_s != tv.tv_sec) {
|
|
|
|
context->last_timeout_check_s = tv.tv_sec;
|
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 */
|
|
|
|
if (context->started_with_parent && kill(context->started_with_parent, 0) < 0)
|
|
|
|
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 */
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
for (n = 0; n < context->fds_count; n++) {
|
2013-01-17 12:26:48 +08:00
|
|
|
struct libwebsocket *new_wsi = context->lws_lookup[context->fds[n].fd];
|
|
|
|
if (!new_wsi)
|
|
|
|
continue;
|
|
|
|
libwebsocket_service_timeout_check(context,
|
|
|
|
new_wsi, tv.tv_sec);
|
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 */
|
|
|
|
|
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-10 09:06:38 +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 ||
|
|
|
|
context->listen_service_count == context->listen_service_modulo) {
|
|
|
|
context->listen_service_count = 0;
|
|
|
|
m = 1;
|
|
|
|
if (context->listen_service_extraseen > 5)
|
|
|
|
m = 2;
|
|
|
|
while (m--) {
|
|
|
|
/* even with extpoll, we prepared this internal fds for listen */
|
2013-02-10 09:06:38 +08:00
|
|
|
n = poll(&context->fds[listen_socket_fds_index], 1, 0);
|
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
|
|
|
if (n > 0) { /* there's a connection waiting for us */
|
2013-02-10 09:06:38 +08:00
|
|
|
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)
|
|
|
|
context->listen_service_extraseen--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* okay, what we came here to do... */
|
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
wsi = context->lws_lookup[pollfd->fd];
|
2013-01-15 13:40:23 +08:00
|
|
|
if (wsi == NULL) {
|
2013-01-17 12:26:48 +08:00
|
|
|
if (pollfd->fd > 11)
|
|
|
|
lwsl_err("unexpected NULL wsi fd=%d fds_count=%d\n", pollfd->fd, context->fds_count);
|
2012-10-07 20:40:35 +08:00
|
|
|
return 0;
|
2013-01-15 13:40:23 +08:00
|
|
|
}
|
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:
|
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-01-18 11:43:21 +08:00
|
|
|
return lws_server_socket_service(context, wsi, pollfd);
|
|
|
|
#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:
|
|
|
|
|
|
|
|
/* handle session socket closed */
|
|
|
|
|
|
|
|
if (pollfd->revents & (POLLERR | POLLHUP)) {
|
|
|
|
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("Session Socket %p (fd=%d) dead\n",
|
2011-02-12 11:57:43 +00:00
|
|
|
(void *)wsi, pollfd->fd);
|
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_close_and_free_session(context, wsi,
|
2011-02-26 11:04:01 +00:00
|
|
|
LWS_CLOSE_STATUS_NOSTATUS);
|
2013-01-16 13:40:43 +08:00
|
|
|
return 0;
|
2011-02-12 11:57:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* the guy requested a callback when it was OK to write */
|
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
if ((pollfd->revents & POLLOUT) &&
|
|
|
|
wsi->state == WSI_STATE_ESTABLISHED)
|
|
|
|
if (lws_handle_POLLOUT_event(context, wsi,
|
|
|
|
pollfd) < 0) {
|
replace per header mallocs with single malloc 3 level struct
This big patch replaces the malloc / realloc per header
approach used until now with a single three-level struct
that gets malloc'd during the header union phase and freed
in one go when we transition to a different union phase.
It's more expensive in that we malloc a bit more than 4Kbytes,
but it's a lot cheaper in terms of malloc, frees, heap fragmentation,
no reallocs, nothing to configure. It also moves from arrays of
pointers (8 bytes on x86_64) to unsigned short offsets into the
data array, (2 bytes on all platforms).
The 3-level thing is all in one struct
- array indexed by the header enum, pointing to first "fragment" index
(ie, header type to fragment lookup, or 0 for none)
- array of fragments indexes, enough for 2 x the number of known headers
(fragment array... note that fragments can point to a "next"
fragment if the same header is spread across multiple entries)
- linear char array where the known header payload gets written
(fragments point into null-terminated strings stored in here,
only the known header content is stored)
http headers can legally be split over multiple headers of the same
name which should be concatenated. This scheme does not linearly
conatenate them but uses a linked list in the fragment structs to
link them. There are apis to get the total length and copy out a
linear, concatenated version to a buffer.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-02-10 18:02:31 +08:00
|
|
|
lwsl_info("libwebsocket_service_fd: closing");
|
2011-03-07 07:08:12 +00:00
|
|
|
libwebsocket_close_and_free_session(
|
|
|
|
context, wsi, LWS_CLOSE_STATUS_NORMAL);
|
2013-01-16 13:40:43 +08:00
|
|
|
return 0;
|
2011-03-06 13:14:42 +00:00
|
|
|
}
|
2011-02-12 13:14:11 +00:00
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
|
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,
|
|
|
|
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-10 15:10:10 +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,
|
|
|
|
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-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("Socket read returned %d\n",
|
2011-03-06 13:14:15 +00:00
|
|
|
eff_buf.token_len);
|
2012-10-19 11:21:57 +02:00
|
|
|
if (errno != EINTR && errno != EAGAIN)
|
2012-04-09 15:09:01 +08:00
|
|
|
libwebsocket_close_and_free_session(context,
|
|
|
|
wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
2013-01-16 13:40:43 +08:00
|
|
|
return 0;
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
2011-03-06 13:14:15 +00:00
|
|
|
if (!eff_buf.token_len) {
|
2013-01-30 12:28:34 +08:00
|
|
|
lwsl_info("closing connection due to zero length read\n");
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_close_and_free_session(context, wsi,
|
2012-04-09 15:09:01 +08:00
|
|
|
LWS_CLOSE_STATUS_NOSTATUS);
|
2012-10-07 20:40:35 +08:00
|
|
|
return 0;
|
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-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");
|
|
|
|
libwebsocket_close_and_free_session(
|
|
|
|
context, wsi,
|
|
|
|
LWS_CLOSE_STATUS_NOSTATUS);
|
2013-01-16 13:40:43 +08:00
|
|
|
return 0;
|
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);
|
2011-03-06 13:14:15 +00:00
|
|
|
if (n < 0)
|
|
|
|
/* we closed wsi */
|
2013-01-16 13:40:43 +08:00
|
|
|
return 0;
|
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-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-01-16 11:53:05 +08:00
|
|
|
return lws_client_socket_service(context, wsi, pollfd);
|
2013-01-16 11:47:40 +08:00
|
|
|
#endif
|
2010-12-18 15:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
void
|
2011-03-02 22:03:47 +00:00
|
|
|
libwebsocket_context_destroy(struct libwebsocket_context *context)
|
2011-01-23 16:50:33 +00:00
|
|
|
{
|
2013-01-20 17:08:31 +08:00
|
|
|
#ifndef LWS_NO_EXTENSIONS
|
2011-02-12 11:57:43 +00:00
|
|
|
int n;
|
|
|
|
int m;
|
2011-05-23 10:00:03 +01:00
|
|
|
struct libwebsocket_extension *ext;
|
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++) {
|
|
|
|
struct libwebsocket *wsi = context->lws_lookup[context->fds[n].fd];
|
|
|
|
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
|
|
|
|
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-01-10 12:35:18 +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++;
|
|
|
|
}
|
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++;
|
|
|
|
}
|
|
|
|
|
2013-01-20 17:08:31 +08:00
|
|
|
#endif
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#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);
|
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);
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
WSACleanup();
|
|
|
|
#endif
|
2011-01-23 16:50:33 +00:00
|
|
|
}
|
|
|
|
|
2013-01-22 12:40:35 +08:00
|
|
|
/**
|
|
|
|
* libwebsocket_context_user() - get the user data associated with the whole context
|
|
|
|
* @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)
|
|
|
|
{
|
|
|
|
return context->user_space;
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
|
2011-01-19 13:11:55 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2011-02-12 11:57:43 +00:00
|
|
|
/* wait for something to need service */
|
2011-01-23 16:50:33 +00:00
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
n = poll(context->fds, context->fds_count, timeout_ms);
|
2011-02-12 13:14:11 +00:00
|
|
|
if (n == 0) /* poll timeout */
|
|
|
|
return 0;
|
2011-01-19 13:11:55 +00:00
|
|
|
|
2013-01-17 12:26:48 +08:00
|
|
|
if (n < 0)
|
2012-07-20 12:58:38 +08:00
|
|
|
return -1;
|
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
|
|
|
|
2011-03-02 22:03:47 +00:00
|
|
|
for (n = 0; n < context->fds_count; n++)
|
|
|
|
if (context->fds[n].revents)
|
2012-07-20 12:58:38 +08:00
|
|
|
if (libwebsocket_service_fd(context,
|
|
|
|
&context->fds[n]) < 0)
|
|
|
|
return -1;
|
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
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("libwebsocket_callback_on_writable: "
|
2012-04-09 15:09:01 +08:00
|
|
|
"failed to find socket %d\n", wsi->sock);
|
2013-01-17 12:26:48 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
context->fds[wsi->position_in_fds_table].events |= POLLOUT;
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2011-02-12 13:14:11 +00:00
|
|
|
/* external POLL support via protocol 0 */
|
2011-03-02 22:03:47 +00:00
|
|
|
context->protocols[0].callback(context, wsi,
|
2011-02-12 13:14:11 +00:00
|
|
|
LWS_CALLBACK_SET_MODE_POLL_FD,
|
|
|
|
(void *)(long)wsi->sock, NULL, POLLOUT);
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
libwebsocket_set_timeout(struct libwebsocket *wsi,
|
|
|
|
enum pending_timeout reason, int secs)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
wsi->pending_timeout_limit = tv.tv_sec + secs;
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
libwebsocket_get_socket_fd(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
return wsi->sock;
|
|
|
|
}
|
|
|
|
|
2013-01-29 12:36:17 +08:00
|
|
|
#ifdef LWS_LATENCY
|
|
|
|
void
|
|
|
|
lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi, const char *action, int ret, int completed)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
unsigned long u;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
u = (tv.tv_sec * 1000000) + tv.tv_usec;
|
|
|
|
|
|
|
|
if (action) {
|
|
|
|
if (completed) {
|
|
|
|
if (wsi->action_start == wsi->latency_start)
|
|
|
|
sprintf(buf, "Completion first try lat %luus: %p: ret %d: %s\n", u - wsi->latency_start, (void *)wsi, ret, action);
|
|
|
|
else
|
|
|
|
sprintf(buf, "Completion %luus: lat %luus: %p: ret %d: %s\n", u - wsi->action_start, u - wsi->latency_start, (void *)wsi, ret, action);
|
|
|
|
wsi->action_start = 0;
|
|
|
|
} else
|
|
|
|
sprintf(buf, "lat %luus: %p: ret %d: %s\n", u - wsi->latency_start, (void *)wsi, ret, action);
|
|
|
|
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
|
|
|
|
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
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
|
|
|
int n;
|
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
if (!(wsi->u.ws.rxflow_change_to & 2))
|
2013-01-17 16:50:35 +08:00
|
|
|
return 0;
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
wsi->u.ws.rxflow_change_to &= ~2;
|
2013-01-17 16:50:35 +08:00
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
lwsl_info("rxflow: wsi %p change_to %d\n", wsi, wsi->u.ws.rxflow_change_to);
|
2013-01-17 16:50:35 +08:00
|
|
|
|
|
|
|
/* if we're letting it come again, did we interrupt anything? */
|
2013-01-21 11:04:23 +08:00
|
|
|
if ((wsi->u.ws.rxflow_change_to & 1) && wsi->u.ws.rxflow_buffer) {
|
2013-01-17 16:50:35 +08:00
|
|
|
n = libwebsocket_interpret_incoming_packet(wsi, NULL, 0);
|
|
|
|
if (n < 0) {
|
2013-02-10 21:21:24 +08:00
|
|
|
lwsl_info("returning that we want to close connection at libwebsocket_rx_flow_control:\n");
|
2013-01-17 16:50:35 +08:00
|
|
|
return -1;
|
2011-01-27 06:26:52 +00:00
|
|
|
}
|
2013-01-17 16:50:35 +08:00
|
|
|
if (n)
|
|
|
|
/* oh he stuck again, do nothing */
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
if (wsi->u.ws.rxflow_change_to & 1)
|
2013-01-17 16:50:35 +08:00
|
|
|
context->fds[wsi->position_in_fds_table].events |= POLLIN;
|
|
|
|
else
|
|
|
|
context->fds[wsi->position_in_fds_table].events &= ~POLLIN;
|
|
|
|
|
2013-01-21 11:04:23 +08:00
|
|
|
if (wsi->u.ws.rxflow_change_to & 1)
|
2011-02-12 13:14:11 +00:00
|
|
|
/* external POLL support via protocol 0 */
|
2011-03-02 22:03:47 +00:00
|
|
|
context->protocols[0].callback(context, wsi,
|
2011-02-12 13:14:11 +00:00
|
|
|
LWS_CALLBACK_SET_MODE_POLL_FD,
|
|
|
|
(void *)(long)wsi->sock, NULL, POLLIN);
|
|
|
|
else
|
|
|
|
/* external POLL support via protocol 0 */
|
2011-03-02 22:03:47 +00:00
|
|
|
context->protocols[0].callback(context, wsi,
|
2011-02-12 13:14:11 +00:00
|
|
|
LWS_CALLBACK_CLEAR_MODE_POLL_FD,
|
|
|
|
(void *)(long)wsi->sock, NULL, POLLIN);
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
|
|
|
|
{
|
2013-01-21 11:04:23 +08:00
|
|
|
wsi->u.ws.rxflow_change_to = 2 | !!enable;
|
2013-01-17 16:50:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
struct libwebsocket_context * context,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2011-01-19 13:11:55 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
int n;
|
2011-03-02 22:03:47 +00:00
|
|
|
struct libwebsocket_context *context = NULL;
|
2011-01-27 22:01:43 +00:00
|
|
|
char *p;
|
2013-01-31 09:57:05 +08:00
|
|
|
#ifndef LWS_NO_SERVER
|
|
|
|
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
|
|
|
extern int get_daemonize_pid();
|
|
|
|
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_NAME_LENGTH: %u\n", LWS_MAX_HEADER_NAME_LENGTH);
|
|
|
|
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-01-12 23:42:17 +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);
|
|
|
|
lwsl_info(" CIPHERS_LIST_STRING: '%s'\n", CIPHERS_LIST_STRING);
|
|
|
|
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
|
|
|
|
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;
|
2011-03-02 22:03:47 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-01-10 12:35:18 +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-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;
|
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,
|
|
|
|
sizeof(struct libwebsocket_context) + ((sizeof(struct pollfd) + sizeof(struct libwebsocket *)) * context->max_fds));
|
2013-01-17 12:26:48 +08:00
|
|
|
|
|
|
|
context->fds = (struct pollfd *)malloc(sizeof(struct pollfd) * context->max_fds);
|
|
|
|
if (context->fds == NULL) {
|
|
|
|
lwsl_err("Unable to allocate fds array for %d connections\n", context->max_fds);
|
|
|
|
free(context);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-01-19 20:01:01 +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) {
|
|
|
|
lwsl_err("Unable to allocate lws_lookup array for %d connections\n", context->max_fds);
|
|
|
|
free(context->fds);
|
|
|
|
free(context);
|
|
|
|
return NULL;
|
|
|
|
}
|
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
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
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;
|
|
|
|
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)) {
|
2013-01-18 11:43:21 +08:00
|
|
|
struct sockaddr sa;
|
2013-02-10 15:10:10 +08:00
|
|
|
context->service_buffer[0] = '\0';
|
2012-05-03 12:32:38 +08:00
|
|
|
|
2012-10-22 12:29:57 +01:00
|
|
|
/* find canonical hostname */
|
|
|
|
|
2013-02-10 15:10:10 +08:00
|
|
|
context->service_buffer[(sizeof context->service_buffer) - 1] = '\0';
|
2012-10-22 12:29:57 +01:00
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_family = AF_INET;
|
|
|
|
sa.sa_data[(sizeof sa.sa_data) - 1] = '\0';
|
2013-02-10 15:10:10 +08:00
|
|
|
gethostname((char *)context->service_buffer, (sizeof context->service_buffer) - 1);
|
2012-10-22 12:29:57 +01:00
|
|
|
|
|
|
|
n = 0;
|
2011-01-28 10:00:18 +00:00
|
|
|
|
2013-02-10 15:10:10 +08:00
|
|
|
if (strlen((char *)context->service_buffer) < sizeof(sa.sa_data) - 1) {
|
|
|
|
strcpy(sa.sa_data, (char *)context->service_buffer);
|
2013-01-31 10:05:43 +08:00
|
|
|
lwsl_debug("my host name is %s\n", sa.sa_data);
|
2013-02-10 15:10:10 +08:00
|
|
|
n = getnameinfo(&sa, sizeof(sa), (char *)context->service_buffer,
|
|
|
|
(sizeof context->service_buffer) - 1, NULL, 0, NI_NAMEREQD);
|
2012-10-22 12:29:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!n) {
|
2013-02-10 15:10:10 +08:00
|
|
|
strncpy(context->canonical_hostname, (char *)context->service_buffer,
|
2012-10-22 12:29:57 +01:00
|
|
|
sizeof context->canonical_hostname - 1);
|
|
|
|
context->canonical_hostname[
|
|
|
|
sizeof context->canonical_hostname - 1] = '\0';
|
|
|
|
} else
|
2013-02-10 15:10:10 +08:00
|
|
|
strncpy(context->canonical_hostname, (char *)context->service_buffer,
|
2012-10-22 12:29:57 +01:00
|
|
|
sizeof context->canonical_hostname - 1);
|
|
|
|
|
2013-01-19 13:08:17 +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,
|
2012-04-09 15:09:01 +08:00
|
|
|
sizeof context->http_proxy_address - 1);
|
2011-03-02 22:03:47 +00:00
|
|
|
context->http_proxy_address[
|
|
|
|
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-01-19 13:08:17 +08:00
|
|
|
lwsl_notice(" Compiled without SSL support, "
|
2011-01-27 06:26:52 +00:00
|
|
|
"serving unencrypted\n");
|
2010-11-08 17:03:03 +00:00
|
|
|
#endif
|
2013-01-20 20:21:54 +08:00
|
|
|
|
replace per header mallocs with single malloc 3 level struct
This big patch replaces the malloc / realloc per header
approach used until now with a single three-level struct
that gets malloc'd during the header union phase and freed
in one go when we transition to a different union phase.
It's more expensive in that we malloc a bit more than 4Kbytes,
but it's a lot cheaper in terms of malloc, frees, heap fragmentation,
no reallocs, nothing to configure. It also moves from arrays of
pointers (8 bytes on x86_64) to unsigned short offsets into the
data array, (2 bytes on all platforms).
The 3-level thing is all in one struct
- array indexed by the header enum, pointing to first "fragment" index
(ie, header type to fragment lookup, or 0 for none)
- array of fragments indexes, enough for 2 x the number of known headers
(fragment array... note that fragments can point to a "next"
fragment if the same header is spread across multiple entries)
- linear char array where the known header payload gets written
(fragments point into null-terminated strings stored in here,
only the known header content is stored)
http headers can legally be split over multiple headers of the same
name which should be concatenated. This scheme does not linearly
conatenate them but uses a linked list in the fragment structs to
link them. There are apis to get the total length and copy out a
linear, concatenated version to a buffer.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-02-10 18:02:31 +08:00
|
|
|
lwsl_notice(" per-connection allocation: %u + %u headers (only during handshake) + frame buffer set by protocol\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 */
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("problem creating ssl method: %s\n",
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("problem creating ssl context: %s\n",
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (char *)context->service_buffer));
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-27 06:26:52 +00: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-01-10 10:15:19 +08:00
|
|
|
SSL_CTX_set_cipher_list(context->ssl_ctx, CIPHERS_LIST_STRING);
|
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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("problem creating ssl method: %s\n",
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("problem creating ssl context: %s\n",
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (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-01-10 10:11:57 +08:00
|
|
|
SSL_CTX_set_options(context->ssl_client_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_client_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
2013-01-10 10:15:19 +08:00
|
|
|
SSL_CTX_set_cipher_list(context->ssl_client_ctx, CIPHERS_LIST_STRING);
|
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-01-09 16:25:54 +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);
|
|
|
|
} 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
|
|
|
|
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-09 14:01:09 +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
|
|
|
}
|
|
|
|
|
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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("problem getting cert '%s': %s\n",
|
2013-02-09 14:01:09 +08:00
|
|
|
info->ssl_cert_filepath,
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (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-01-10 19:50:35 +08:00
|
|
|
lwsl_err("ssl problem getting key '%s': %s\n",
|
2013-02-09 14:01:09 +08:00
|
|
|
info->ssl_private_key_filepath,
|
2013-02-10 15:10:10 +08:00
|
|
|
ERR_error_string(ERR_get_error(), (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
|
|
|
|
2011-01-18 15:39:02 +00:00
|
|
|
/* selftest */
|
|
|
|
|
|
|
|
if (lws_b64_selftest())
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2011-01-18 15:39:02 +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
|
|
|
extern int interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen);
|
|
|
|
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-06 15:26:58 +09:00
|
|
|
#ifndef WIN32
|
|
|
|
/* allow us to restart even if old sockets in TIME_WAIT
|
|
|
|
* (REUSEADDR on Unix means, "don't hang on to this address after the
|
|
|
|
* listener is closed." On Windows, though, it means "don't keep other
|
|
|
|
* processes from binding to this address while we're using it) */
|
2012-04-09 15:09:01 +08:00
|
|
|
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
(const void *)&opt, sizeof(opt));
|
2013-02-06 15:26:58 +09:00
|
|
|
#endif
|
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-02-06 15:26:58 +09:00
|
|
|
#ifdef WIN32
|
|
|
|
opt = 0;
|
|
|
|
ioctlsocket(sockfd, FIONBIO, (unsigned long *)&opt );
|
|
|
|
#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-09 14:01:09 +08:00
|
|
|
if (info->interface == NULL)
|
2011-02-19 08:32:53 +00:00
|
|
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
else
|
2013-02-09 14:01:09 +08:00
|
|
|
interface_to_sa(info->interface, &serv_addr,
|
2011-02-19 08:32:53 +00:00
|
|
|
sizeof(serv_addr));
|
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-01-12 13:21:08 +08:00
|
|
|
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-01-10 12:35:18 +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");
|
|
|
|
close(sockfd);
|
2013-02-07 16:31:19 +02:00
|
|
|
goto bail;
|
2013-01-12 13:21:08 +08:00
|
|
|
}
|
2013-01-10 12:35:18 +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
|
|
|
|
*/
|
2011-03-02 22:03:47 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
#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;
|
2012-05-20 08:17:09 +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
|
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
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
2010-12-18 15:13:50 +00:00
|
|
|
const struct libwebsocket_protocols *
|
|
|
|
libwebsockets_get_protocol(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
return wsi->protocol;
|
|
|
|
}
|
|
|
|
|
2011-03-07 21:16:31 +00:00
|
|
|
int
|
|
|
|
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-01-09 18:06:55 +08:00
|
|
|
unsigned char
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2011-11-07 17:19:25 +08:00
|
|
|
void *
|
|
|
|
libwebsocket_ensure_user_space(struct libwebsocket *wsi)
|
|
|
|
{
|
|
|
|
/* 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");
|
2011-11-07 17:19:25 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
return wsi->user_space;
|
|
|
|
}
|
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];
|
2013-01-12 09:25:07 +08:00
|
|
|
struct timeval tv;
|
2013-01-19 11:17:56 +08:00
|
|
|
int n;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2013-01-12 09:25:07 +08:00
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
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)) {
|
2013-01-19 11:32:18 +08:00
|
|
|
sprintf(buf, "[%ld:%04d] %s: ", tv.tv_sec,
|
2013-01-17 15:02:02 +08:00
|
|
|
(int)(tv.tv_usec / 100), log_level_names[n]);
|
2013-01-10 19:50:35 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-01-19 11:17:56 +08:00
|
|
|
|
|
|
|
fprintf(stderr, "%s%s", buf, line);
|
|
|
|
}
|
|
|
|
|
2013-02-06 15:26:58 +09:00
|
|
|
#ifdef WIN32
|
|
|
|
void lwsl_emit_syslog(int level, const char *line)
|
|
|
|
{
|
|
|
|
lwsl_emit_stderr(level, line);
|
|
|
|
}
|
|
|
|
#else
|
2013-01-19 11:12:16 +08:00
|
|
|
void lwsl_emit_syslog(int level, const char *line)
|
|
|
|
{
|
|
|
|
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-01-19 11:17:56 +08:00
|
|
|
void _lws_log(int filter, const char *format, ...)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (!(log_level & filter))
|
|
|
|
return;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
|
|
|
va_start(ap, format);
|
2013-01-19 11:17:56 +08:00
|
|
|
vsnprintf(buf, (sizeof buf), format, ap);
|
2013-01-12 09:17:42 +08:00
|
|
|
buf[(sizeof buf) - 1] = '\0';
|
|
|
|
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-01-12 09:17:42 +08:00
|
|
|
* log level defaults to "err" and "warn" contexts enabled only and
|
|
|
|
* emission on stderr.
|
2013-01-10 19:50:35 +08:00
|
|
|
*/
|
|
|
|
|
2013-01-19 11:32:18 +08:00
|
|
|
void lws_set_log_level(int level, void (*log_emit_function)(int level, 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
|
|
|
}
|