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
|
|
|
*
|
2016-01-26 20:56:56 +08:00
|
|
|
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
2010-11-08 17:12:19 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation:
|
|
|
|
* version 2.1 of the License.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301 USA
|
2010-10-31 17:51:39 +00:00
|
|
|
*/
|
|
|
|
|
2010-11-08 20:20:42 +00:00
|
|
|
#include "private-libwebsockets.h"
|
2016-02-29 18:48:55 +08:00
|
|
|
|
|
|
|
#ifdef LWS_HAVE_SYS_TYPES_H
|
2016-02-21 21:25:48 +08:00
|
|
|
#include <sys/types.h>
|
2016-02-29 18:48:55 +08:00
|
|
|
#endif
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
#else
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#endif
|
2010-10-28 22:36:01 +01:00
|
|
|
|
2016-12-15 09:58:20 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
#include <Iphlpapi.h>
|
|
|
|
#else
|
|
|
|
#include <net/if.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2014-04-03 07:29:50 +08:00
|
|
|
int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
2017-01-17 07:01:02 +08:00
|
|
|
static void (*lwsl_emit)(int level, const char *line)
|
|
|
|
#ifndef LWS_PLAT_OPTEE
|
|
|
|
= lwsl_emit_stderr
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2013-02-11 17:13:32 +08:00
|
|
|
static const char * const log_level_names[] = {
|
2013-01-10 19:50:35 +08:00
|
|
|
"ERR",
|
|
|
|
"WARN",
|
2013-01-19 12:18:07 +08:00
|
|
|
"NOTICE",
|
2013-01-10 19:50:35 +08:00
|
|
|
"INFO",
|
|
|
|
"DEBUG",
|
|
|
|
"PARSER",
|
|
|
|
"HEADER",
|
|
|
|
"EXTENSION",
|
|
|
|
"CLIENT",
|
2013-01-29 12:36:17 +08:00
|
|
|
"LATENCY",
|
2017-02-06 10:16:45 +08:00
|
|
|
"USER",
|
|
|
|
"?",
|
|
|
|
"?"
|
2013-01-10 19:50:35 +08:00
|
|
|
};
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2015-06-25 17:51:07 +02:00
|
|
|
void
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_free_wsi(struct lws *wsi)
|
2015-06-25 17:51:07 +02:00
|
|
|
{
|
|
|
|
if (!wsi)
|
|
|
|
return;
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2015-06-25 17:51:07 +02:00
|
|
|
/* Protocol user data may be allocated either internally by lws
|
2015-12-04 16:54:12 +08:00
|
|
|
* or by specified the user.
|
|
|
|
* We should only free what we allocated. */
|
|
|
|
if (wsi->protocol && wsi->protocol->per_session_data_size &&
|
|
|
|
wsi->user_space && !wsi->user_space_externally_allocated)
|
2015-06-25 17:51:07 +02:00
|
|
|
lws_free(wsi->user_space);
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
lws_free_set_NULL(wsi->rxflow_buffer);
|
|
|
|
lws_free_set_NULL(wsi->trunc_alloc);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
|
2016-02-25 21:50:49 +08:00
|
|
|
/* we may not have an ah, but may be on the waiting list... */
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_info("ah det due to close\n");
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_detach(wsi, 0);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2016-02-14 09:27:41 +08:00
|
|
|
wsi->context->count_wsi_allocated--;
|
|
|
|
lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
|
|
|
|
wsi->context->count_wsi_allocated);
|
|
|
|
|
2015-06-25 17:51:07 +02:00
|
|
|
lws_free(wsi);
|
|
|
|
}
|
|
|
|
|
2017-02-21 23:38:40 +08:00
|
|
|
void
|
2016-01-19 04:32:14 +08:00
|
|
|
lws_remove_from_timeout_list(struct lws *wsi)
|
|
|
|
{
|
2016-01-26 20:56:56 +08:00
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
if (!wsi->timeout_list_prev) /* ie, not part of the list */
|
2016-01-19 04:32:14 +08:00
|
|
|
return;
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
lws_pt_lock(pt);
|
2016-02-25 21:39:01 +08:00
|
|
|
/* if we have a next guy, set his prev to our prev */
|
2016-01-19 23:11:39 +08:00
|
|
|
if (wsi->timeout_list)
|
|
|
|
wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
|
2016-02-25 21:39:01 +08:00
|
|
|
/* set our prev guy to our next guy instead of us */
|
2016-01-19 04:32:14 +08:00
|
|
|
*wsi->timeout_list_prev = wsi->timeout_list;
|
2016-01-19 23:11:39 +08:00
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
/* we're out of the list, we should not point anywhere any more */
|
2016-01-19 04:32:14 +08:00
|
|
|
wsi->timeout_list_prev = NULL;
|
|
|
|
wsi->timeout_list = NULL;
|
2016-01-26 20:56:56 +08:00
|
|
|
lws_pt_unlock(pt);
|
2016-01-19 04:32:14 +08:00
|
|
|
}
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
LWS_VISIBLE void
|
|
|
|
lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
|
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
|
|
time_t now;
|
|
|
|
|
|
|
|
lws_pt_lock(pt);
|
|
|
|
|
|
|
|
time(&now);
|
|
|
|
|
2016-02-25 21:39:01 +08:00
|
|
|
if (reason && !wsi->timeout_list_prev) {
|
|
|
|
/* our next guy is current first guy */
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->timeout_list = pt->timeout_list;
|
2016-02-25 21:39:01 +08:00
|
|
|
/* if there is a next guy, set his prev ptr to our next ptr */
|
2016-01-26 20:56:56 +08:00
|
|
|
if (wsi->timeout_list)
|
|
|
|
wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
|
2016-02-25 21:39:01 +08:00
|
|
|
/* our prev ptr is first ptr */
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->timeout_list_prev = &pt->timeout_list;
|
2016-02-25 21:39:01 +08:00
|
|
|
/* set the first guy to be us */
|
2016-01-26 20:56:56 +08:00
|
|
|
*wsi->timeout_list_prev = wsi;
|
|
|
|
}
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs);
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->pending_timeout_limit = now + secs;
|
|
|
|
wsi->pending_timeout = reason;
|
|
|
|
|
|
|
|
lws_pt_unlock(pt);
|
|
|
|
|
|
|
|
if (!reason)
|
|
|
|
lws_remove_from_timeout_list(wsi);
|
|
|
|
}
|
2016-01-19 04:32:14 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
static void
|
|
|
|
lws_remove_child_from_any_parent(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws **pwsi;
|
|
|
|
|
|
|
|
if (wsi->parent) {
|
|
|
|
/* detach ourselves from parent's child list */
|
|
|
|
pwsi = &wsi->parent->child_list;
|
|
|
|
while (*pwsi) {
|
|
|
|
if (*pwsi == wsi) {
|
|
|
|
//lwsl_notice("%s: detach %p from parent %p\n",
|
|
|
|
// __func__, wsi, wsi->parent);
|
|
|
|
*pwsi = wsi->sibling_list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pwsi = &(*pwsi)->sibling_list;
|
|
|
|
}
|
|
|
|
if (*pwsi)
|
|
|
|
lwsl_err("%s: failed to detach from parent\n",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
int
|
|
|
|
lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p)
|
|
|
|
{
|
|
|
|
// if (wsi->protocol == p)
|
|
|
|
// return 0;
|
|
|
|
|
|
|
|
if (wsi->protocol)
|
|
|
|
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
|
|
|
wsi->user_space, NULL, 0);
|
|
|
|
if (!wsi->user_space_externally_allocated)
|
|
|
|
lws_free_set_NULL(wsi->user_space);
|
|
|
|
|
|
|
|
wsi->protocol = p;
|
|
|
|
if (!p)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (lws_ensure_user_space(wsi))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL,
|
|
|
|
wsi->user_space, NULL, 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-19 22:13:26 +00:00
|
|
|
void
|
2015-12-15 21:15:58 +08:00
|
|
|
lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
2010-11-03 11:13:06 +00:00
|
|
|
{
|
2016-01-19 21:32:08 +08:00
|
|
|
struct lws_context_per_thread *pt;
|
2017-03-03 07:36:08 +08:00
|
|
|
struct lws *wsi1, *wsi2;
|
2016-03-02 09:17:22 +08:00
|
|
|
struct lws_context *context;
|
2011-03-07 07:08:18 +00:00
|
|
|
struct lws_tokens eff_buf;
|
2016-03-02 09:17:22 +08:00
|
|
|
int n, m, ret;
|
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;
|
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_access_log(wsi);
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
if (wsi->premature_rx)
|
|
|
|
lws_free(wsi->premature_rx);
|
|
|
|
|
|
|
|
if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) {
|
|
|
|
lwsl_notice("delaying close\n");
|
|
|
|
wsi->close_is_pending_send_completion = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2016-10-04 18:05:10 +08:00
|
|
|
if (wsi->u.hdr.ah)
|
|
|
|
/* we're closing, losing some rx is OK */
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
|
|
|
|
2016-01-19 21:32:08 +08:00
|
|
|
context = wsi->context;
|
|
|
|
pt = &context->pt[(int)wsi->tsi];
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2016-03-02 09:17:22 +08:00
|
|
|
/* if we have children, close them first */
|
|
|
|
if (wsi->child_list) {
|
|
|
|
wsi2 = wsi->child_list;
|
|
|
|
while (wsi2) {
|
2016-03-20 11:59:53 +08:00
|
|
|
//lwsl_notice("%s: closing %p: close child %p\n",
|
|
|
|
// __func__, wsi, wsi2);
|
2016-03-02 09:17:22 +08:00
|
|
|
wsi1 = wsi2->sibling_list;
|
2016-03-20 11:59:53 +08:00
|
|
|
//lwsl_notice("%s: closing %p: next sibling %p\n",
|
|
|
|
// __func__, wsi2, wsi1);
|
|
|
|
wsi2->parent = NULL;
|
|
|
|
/* stop it doing shutdown processing */
|
|
|
|
wsi2->socket_is_permanently_unusable = 1;
|
2016-03-02 09:17:22 +08:00
|
|
|
lws_close_free_wsi(wsi2, reason);
|
|
|
|
wsi2 = wsi1;
|
|
|
|
}
|
2016-03-20 11:59:53 +08:00
|
|
|
wsi->child_list = NULL;
|
2016-03-02 09:17:22 +08:00
|
|
|
}
|
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (wsi->mode == LWSCM_RAW_FILEDESC) {
|
|
|
|
lws_remove_child_from_any_parent(wsi);
|
|
|
|
remove_wsi_socket_from_fds(wsi);
|
|
|
|
wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_RAW_CLOSE_FILE,
|
|
|
|
wsi->user_space, NULL, 0);
|
2017-03-03 12:38:10 +08:00
|
|
|
goto async_close;
|
2017-03-03 07:36:08 +08:00
|
|
|
}
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
if (wsi->mode == LWSCM_CGI) {
|
|
|
|
/* we are not a network connection, but a handler for CGI io */
|
2016-03-02 09:17:22 +08:00
|
|
|
if (wsi->parent && wsi->parent->cgi)
|
2016-04-13 11:49:07 +08:00
|
|
|
/* end the binding between us and master */
|
|
|
|
wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
|
2016-02-21 21:25:48 +08:00
|
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_debug("------ %s: detected cgi fdhandler wsi %p\n", __func__, wsi);
|
2016-02-21 21:25:48 +08:00
|
|
|
goto just_kill_connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wsi->cgi) {
|
2016-04-20 06:10:56 +08:00
|
|
|
struct lws_cgi **pcgi = &pt->cgi_list;
|
|
|
|
/* remove us from the cgi list */
|
2016-08-22 07:07:10 +08:00
|
|
|
lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->cgi);
|
2016-04-20 06:10:56 +08:00
|
|
|
while (*pcgi) {
|
|
|
|
if (*pcgi == wsi->cgi) {
|
|
|
|
/* drop us from the pt cgi list */
|
|
|
|
*pcgi = (*pcgi)->cgi_list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
}
|
2016-03-02 09:17:22 +08:00
|
|
|
/* we have a cgi going, we must kill it */
|
2016-02-21 21:25:48 +08:00
|
|
|
wsi->cgi->being_closed = 1;
|
|
|
|
lws_cgi_kill(wsi);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
if (wsi->mode == LWSCM_RAW) {
|
2017-02-27 12:55:56 +08:00
|
|
|
wsi->protocol->callback(wsi,
|
2017-02-12 20:32:49 +08:00
|
|
|
LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0);
|
|
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
|
|
goto just_kill_connection;
|
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
|
2017-02-25 12:42:45 +08:00
|
|
|
wsi->u.http.fop_fd != NULL) {
|
2017-03-03 12:38:10 +08:00
|
|
|
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
2016-06-08 10:07:02 +08:00
|
|
|
wsi->vhost->protocols->callback(wsi,
|
2016-03-28 10:10:43 +08:00
|
|
|
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
|
2016-09-06 15:36:51 +08:00
|
|
|
wsi->told_user_closed = 1;
|
2015-12-04 16:54:12 +08:00
|
|
|
}
|
2015-04-12 08:17:26 +08:00
|
|
|
if (wsi->socket_is_permanently_unusable ||
|
2016-01-26 20:56:56 +08:00
|
|
|
reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
|
|
|
|
wsi->state == LWSS_SHUTDOWN)
|
2014-10-16 08:23:46 +08:00
|
|
|
goto just_kill_connection;
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->state_pre_close = wsi->state;
|
|
|
|
|
|
|
|
switch (wsi->state_pre_close) {
|
2015-12-17 17:03:59 +08:00
|
|
|
case LWSS_DEAD_SOCKET:
|
2011-02-10 09:07:05 +00:00
|
|
|
return;
|
|
|
|
|
2013-02-15 22:48:58 +08:00
|
|
|
/* we tried the polite way... */
|
2015-12-17 17:03:59 +08:00
|
|
|
case LWSS_AWAITING_CLOSE_ACK:
|
2013-02-15 22:48:58 +08:00
|
|
|
goto just_kill_connection;
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE:
|
|
|
|
if (wsi->trunc_len) {
|
2015-12-16 18:19:08 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
2014-04-10 14:25:24 +08:00
|
|
|
return;
|
|
|
|
}
|
2015-12-17 17:03:59 +08:00
|
|
|
lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
|
2014-04-10 14:25:24 +08:00
|
|
|
goto just_kill_connection;
|
|
|
|
default:
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->trunc_len) {
|
|
|
|
lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
|
|
|
|
wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
|
2014-04-10 14:25:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
|
|
|
|
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
|
2013-09-20 20:26:12 +08:00
|
|
|
goto just_kill_connection;
|
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
if (wsi->mode == LWSCM_HTTP_SERVING) {
|
|
|
|
if (wsi->user_space)
|
|
|
|
wsi->vhost->protocols->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
2015-12-17 07:54:44 +08:00
|
|
|
wsi->user_space, NULL, 0);
|
2016-06-08 10:07:02 +08:00
|
|
|
wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
|
|
|
wsi->user_space, NULL, 0);
|
2016-09-06 15:36:51 +08:00
|
|
|
wsi->told_user_closed = 1;
|
2016-06-08 10:07:02 +08:00
|
|
|
}
|
2016-09-06 15:36:51 +08:00
|
|
|
if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) {
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
2016-02-29 13:18:30 +08:00
|
|
|
wsi->user_space, NULL, 0);
|
2016-09-06 15:36:51 +08:00
|
|
|
wsi->told_user_closed = 1;
|
|
|
|
}
|
2015-01-28 04:15:13 +08:00
|
|
|
|
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?
|
|
|
|
*/
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (lws_ext_cb_active(wsi,
|
|
|
|
LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
|
2014-04-02 21:02:54 +08:00
|
|
|
lwsl_ext("extension vetoed close\n");
|
|
|
|
return;
|
2011-05-25 21:41:57 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2014-04-02 21:02:54 +08:00
|
|
|
do {
|
2011-03-07 07:08:18 +00:00
|
|
|
ret = 0;
|
|
|
|
eff_buf.token = NULL;
|
|
|
|
eff_buf.token_len = 0;
|
|
|
|
|
|
|
|
/* show every extension the new incoming data */
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
m = lws_ext_cb_active(wsi,
|
|
|
|
LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
|
2014-04-02 21:02:54 +08:00
|
|
|
if (m < 0) {
|
|
|
|
lwsl_ext("Extension reports fatal error\n");
|
|
|
|
goto just_kill_connection;
|
2011-03-07 07:08:18 +00:00
|
|
|
}
|
2014-04-02 21:02:54 +08:00
|
|
|
if (m)
|
|
|
|
/*
|
|
|
|
* at least one extension told us he has more
|
|
|
|
* to spill, so we will go around again after
|
|
|
|
*/
|
|
|
|
ret = 1;
|
2011-03-07 07:08:18 +00:00
|
|
|
|
|
|
|
/* assuming they left us something to send, send it */
|
|
|
|
|
|
|
|
if (eff_buf.token_len)
|
|
|
|
if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
|
2015-12-04 16:54:12 +08:00
|
|
|
eff_buf.token_len) !=
|
|
|
|
eff_buf.token_len) {
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_debug("close: ext spill failed\n");
|
2011-03-07 07:08:18 +00:00
|
|
|
goto just_kill_connection;
|
2013-01-17 14:46:43 +08:00
|
|
|
}
|
2014-04-02 21:02:54 +08:00
|
|
|
} while (ret);
|
2011-03-07 07:08:18 +00:00
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/*
|
2015-12-04 08:43:54 +08:00
|
|
|
* signal we are closing, lws_write will
|
2011-03-07 07:08:12 +00:00
|
|
|
* 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
|
2015-12-17 17:03:59 +08:00
|
|
|
* LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
|
2011-03-07 07:08:12 +00:00
|
|
|
*
|
|
|
|
* 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
|
2015-12-17 17:03:59 +08:00
|
|
|
* LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
|
2011-03-07 07:08:12 +00:00
|
|
|
*/
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
if (wsi->state_pre_close == LWSS_ESTABLISHED &&
|
2015-12-26 17:20:34 +08:00
|
|
|
(wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
|
|
|
|
(reason != LWS_CLOSE_STATUS_NOSTATUS &&
|
|
|
|
(reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("sending close indication...\n");
|
2015-12-26 17:20:34 +08:00
|
|
|
|
|
|
|
/* if no prepared close reason, use 1000 and no aux data */
|
|
|
|
if (!wsi->u.ws.close_in_ping_buffer_len) {
|
|
|
|
wsi->u.ws.close_in_ping_buffer_len = 2;
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->u.ws.ping_payload_buf[LWS_PRE] =
|
2017-02-14 23:14:09 +08:00
|
|
|
(reason >> 8) & 0xff;
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
|
2015-12-26 17:20:34 +08:00
|
|
|
reason & 0xff;
|
|
|
|
}
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined (LWS_WITH_ESP8266)
|
|
|
|
wsi->close_is_pending_send_completion = 1;
|
|
|
|
#endif
|
2016-01-26 20:56:56 +08:00
|
|
|
n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
|
2015-12-26 17:20:34 +08:00
|
|
|
wsi->u.ws.close_in_ping_buffer_len,
|
|
|
|
LWS_WRITE_CLOSE);
|
2013-02-23 10:50:10 +08:00
|
|
|
if (n >= 0) {
|
2011-03-07 07:08:12 +00:00
|
|
|
/*
|
|
|
|
* we have sent a nice protocol level indication we
|
|
|
|
* now wish to close, we should not send anything more
|
|
|
|
*/
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_AWAITING_CLOSE_ACK;
|
2011-03-07 07:08:12 +00:00
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
/*
|
|
|
|
* ...and we should wait for a reply for a bit
|
|
|
|
* out of politeness
|
|
|
|
*/
|
2015-12-04 16:54:12 +08:00
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("sent close indication, awaiting ack\n");
|
2011-03-07 07:08:12 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-11 17:13:32 +08:00
|
|
|
lwsl_info("close: sending close packet failed, hanging up\n");
|
2013-01-17 14:46:43 +08:00
|
|
|
|
2011-03-07 07:08:12 +00:00
|
|
|
/* else, the send failed and we should just hang up */
|
|
|
|
}
|
|
|
|
|
2011-03-07 07:08:18 +00:00
|
|
|
just_kill_connection:
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
lws_remove_child_from_any_parent(wsi);
|
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
#if 0
|
|
|
|
/* manage the vhost same protocol list entry */
|
|
|
|
|
|
|
|
if (wsi->same_vh_protocol_prev) { // we are on the vh list
|
|
|
|
|
|
|
|
// make guy who pointed to us, point to what our next was pointing to
|
|
|
|
*wsi->same_vh_protocol_prev = wsi->same_vh_protocol_next;
|
|
|
|
|
|
|
|
// if we had a next guy...
|
|
|
|
if (wsi->same_vh_protocol_next)
|
|
|
|
// have him point back to our prev
|
|
|
|
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
|
|
|
wsi->same_vh_protocol_prev;
|
|
|
|
}
|
|
|
|
#endif
|
2011-05-24 22:07:45 +01:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
#if LWS_POSIX
|
|
|
|
/*
|
|
|
|
* Testing with ab shows that we have to stage the socket close when
|
|
|
|
* the system is under stress... shutdown any further TX, change the
|
|
|
|
* state to one that won't emit anything more, and wait with a timeout
|
|
|
|
* for the POLLIN to show a zero-size rx before coming back and doing
|
|
|
|
* the actual close.
|
|
|
|
*/
|
2017-02-12 20:32:49 +08:00
|
|
|
if (wsi->mode != LWSCM_RAW && wsi->state != LWSS_SHUTDOWN &&
|
2016-05-03 07:26:10 +08:00
|
|
|
wsi->state != LWSS_CLIENT_UNCONNECTED &&
|
2016-02-14 09:27:41 +08:00
|
|
|
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
|
|
|
|
!wsi->socket_is_permanently_unusable) {
|
2017-02-20 05:44:56 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
if (lws_is_ssl(wsi) && wsi->ssl)
|
2017-04-19 20:53:51 +08:00
|
|
|
{
|
|
|
|
lwsl_info("%s: shutting down SSL connection: %p (ssl %p, sock %d, state %d)\n", __func__, wsi, wsi->ssl, (int)(long)wsi->desc.sockfd, wsi->state);
|
2017-02-20 05:44:56 +08:00
|
|
|
n = SSL_shutdown(wsi->ssl);
|
2017-04-19 20:53:51 +08:00
|
|
|
if (n == 1) /* If finished the SSL shutdown, then do socket shutdown, else need to retry SSL shutdown */
|
|
|
|
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
|
|
|
else if (n == 0)
|
|
|
|
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
|
|
|
else /* n < 0 */
|
|
|
|
{
|
|
|
|
int shutdown_error = SSL_get_error(wsi->ssl, n);
|
|
|
|
lwsl_debug("SSL_shutdown returned %d, SSL_get_error: %d\n", n, shutdown_error);
|
|
|
|
if (shutdown_error == SSL_ERROR_WANT_READ) {
|
|
|
|
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
|
|
|
n = 0;
|
|
|
|
} else if (shutdown_error == SSL_ERROR_WANT_WRITE) {
|
|
|
|
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLOUT);
|
|
|
|
n = 0;
|
|
|
|
} else { // actual error occurred, just close the connection
|
|
|
|
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-20 05:44:56 +08:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_info("%s: shutting down connection: %p (sock %d, state %d)\n", __func__, wsi, (int)(long)wsi->desc.sockfd, wsi->state);
|
|
|
|
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
2017-02-20 05:44:56 +08:00
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
if (n)
|
2016-05-03 07:26:10 +08:00
|
|
|
lwsl_debug("closing: shutdown (state %d) ret %d\n", wsi->state, LWS_ERRNO);
|
2016-02-24 21:27:46 +08:00
|
|
|
|
2016-02-29 18:48:55 +08:00
|
|
|
// This causes problems with disconnection when the events are half closing connection
|
|
|
|
// FD_READ | FD_CLOSE (33)
|
|
|
|
#ifndef _WIN32_WCE
|
2016-02-29 10:34:29 +08:00
|
|
|
/* libuv: no event available to guarantee completion */
|
|
|
|
if (!LWS_LIBUV_ENABLED(context)) {
|
|
|
|
|
|
|
|
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
|
|
|
wsi->state = LWSS_SHUTDOWN;
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
|
|
|
|
context->timeout_secs);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-29 18:48:55 +08:00
|
|
|
#endif
|
2016-01-26 20:56:56 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-20 11:59:53 +08:00
|
|
|
lwsl_info("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
|
2017-02-27 12:55:56 +08:00
|
|
|
wsi, wsi->desc.sockfd);
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-03-20 11:55:25 +08:00
|
|
|
#ifdef LWS_WITH_HTTP_PROXY
|
2016-03-20 11:59:53 +08:00
|
|
|
if (wsi->rw) {
|
|
|
|
lws_rewrite_destroy(wsi->rw);
|
|
|
|
wsi->rw = NULL;
|
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
#endif
|
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
|
|
|
*/
|
2015-12-15 21:15:58 +08:00
|
|
|
lws_ssl_remove_wsi_from_buffered_list(wsi);
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-01-19 04:32:14 +08:00
|
|
|
lws_remove_from_timeout_list(wsi);
|
2015-01-29 08:36:18 +08:00
|
|
|
|
2015-12-04 16:54:12 +08:00
|
|
|
/* checking return redundant since we anyway close */
|
2017-02-27 12:55:56 +08:00
|
|
|
if (wsi->desc.sockfd != LWS_SOCK_INVALID)
|
2016-08-28 09:21:56 +08:00
|
|
|
remove_wsi_socket_from_fds(wsi);
|
2011-02-14 08:03:48 +00:00
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
2017-02-27 12:55:56 +08:00
|
|
|
espconn_disconnect(wsi->desc.sockfd);
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_DEAD_SOCKET;
|
2014-12-05 00:09:20 +01:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
lws_free_set_NULL(wsi->rxflow_buffer);
|
2014-12-05 00:09:20 +01:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
if (wsi->state_pre_close == LWSS_ESTABLISHED ||
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode == LWSCM_WS_SERVING ||
|
|
|
|
wsi->mode == LWSCM_WS_CLIENT) {
|
2013-02-12 12:52:39 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (wsi->u.ws.rx_draining_ext) {
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws **w = &pt->rx_draining_ext_list;
|
2016-01-11 11:34:01 +08:00
|
|
|
|
|
|
|
wsi->u.ws.rx_draining_ext = 0;
|
|
|
|
/* remove us from context draining ext list */
|
|
|
|
while (*w) {
|
|
|
|
if (*w == wsi) {
|
|
|
|
*w = wsi->u.ws.rx_draining_ext_list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w = &((*w)->u.ws.rx_draining_ext_list);
|
|
|
|
}
|
|
|
|
wsi->u.ws.rx_draining_ext_list = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wsi->u.ws.tx_draining_ext) {
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws **w = &pt->tx_draining_ext_list;
|
2016-01-11 11:34:01 +08:00
|
|
|
|
|
|
|
wsi->u.ws.tx_draining_ext = 0;
|
|
|
|
/* remove us from context draining ext list */
|
|
|
|
while (*w) {
|
|
|
|
if (*w == wsi) {
|
|
|
|
*w = wsi->u.ws.tx_draining_ext_list;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w = &((*w)->u.ws.tx_draining_ext_list);
|
|
|
|
}
|
|
|
|
wsi->u.ws.tx_draining_ext_list = NULL;
|
|
|
|
}
|
|
|
|
lws_free_set_NULL(wsi->u.ws.rx_ubuf);
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->trunc_alloc)
|
add explicit error for partial send
This patch adds code to handle the situation that a prepared user buffer could not all be sent on the
socket at once. There are two kinds of situation to handle
1) User code handles it: The connection only has extensions active that do not rewrite the buffer.
In this case, the patch caused libwebsocket_write() to simply return the amount of user buffer that
was consumed (this is specifically the amount of user buffer used in sending what was accepted,
nothing else). So user code can just advance its buffer that much and resume sending when the socket
is writable again. This continues the frame rather than starting a new one or new fragment.
2) The connections has extensions active which actually send something quite different than what the
user buffer contains, for example a compression extension. In this case, libwebsockets will dynamically
malloc a buffer to contain a copy of the remaining unsent data, request notifiction when writeable again,
and automatically spill and free this buffer with the highest priority before passing on the writable
notification to anything else. For this situation, the call to write will return that it used the
whole user buffer, even though part is still rebuffered.
This patch should enable libwebsockets to detect the two cases and take the appropriate action.
There are also two choices for user code to deal with partial sends.
1) Leave the no_buffer_all_partial_tx member in the protocol struct at zero. The library will dyamically
buffer anything you send that did not get completely written to the socket, and automatically spill it next
time the socket is writable. You can use this method if your sent frames are relatvely small and unlikely to get
truncated anyway.
2) Set the no_buffer_all_partial_tx member in the protocol struct. User code now needs to take care of the
return value from libwebsocket_write() and deal with resending the remainder if not all of the requested amount
got sent. You should use this method if you are sending large messages and want to maximize throughput and efficiency.
Since the new member no_buffer_all_partial_tx will be zero by default, this patch will auto-rebuffer any
partial sends by default. That's good for most cases but if you attempt to send large blocks, make sure you
follow option 2) above.
Signed-off-by: Andy Green <andy.green@linaro.org>
2013-10-17 08:09:19 +08:00
|
|
|
/* not going to be completed... nuke it */
|
2015-12-17 17:03:59 +08:00
|
|
|
lws_free_set_NULL(wsi->trunc_alloc);
|
2015-12-04 16:54:12 +08:00
|
|
|
|
2015-12-25 13:14:09 +08:00
|
|
|
wsi->u.ws.ping_payload_len = 0;
|
|
|
|
wsi->u.ws.ping_pending_flag = 0;
|
2013-02-06 21:10:16 +09:00
|
|
|
}
|
|
|
|
|
2011-02-14 08:03:48 +00:00
|
|
|
/* tell the user it's all over for this guy */
|
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
if (wsi->mode != LWSCM_RAW && wsi->protocol && wsi->protocol->callback &&
|
2016-01-26 20:56:56 +08:00
|
|
|
((wsi->state_pre_close == LWSS_ESTABLISHED) ||
|
|
|
|
(wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
|
|
|
|
(wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
|
2016-02-24 12:54:37 +02:00
|
|
|
(wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
|
|
|
|
(wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
|
|
|
|
(wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
|
2016-06-26 06:29:20 +08:00
|
|
|
|
|
|
|
if (wsi->user_space) {
|
2017-02-28 18:29:57 +08:00
|
|
|
lwsl_debug("%s: doing LWS_CALLBACK_HTTP_DROP_PROTOCOL for %p prot %s\n", __func__, wsi, wsi->protocol->name);
|
2016-06-26 06:29:20 +08:00
|
|
|
wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
|
|
|
|
wsi->user_space, NULL, 0);
|
|
|
|
}
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("calling back CLOSED\n");
|
2015-12-17 07:54:44 +08:00
|
|
|
wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
|
2015-12-04 16:54:12 +08:00
|
|
|
wsi->user_space, NULL, 0);
|
2015-12-17 17:03:59 +08:00
|
|
|
} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
|
2013-06-29 10:16:18 +08:00
|
|
|
lwsl_debug("calling back CLOSED_HTTP\n");
|
2016-06-08 10:07:02 +08:00
|
|
|
wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
2015-12-17 07:54:44 +08:00
|
|
|
wsi->user_space, NULL, 0 );
|
2016-07-01 08:54:39 +08:00
|
|
|
} else if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
|
|
|
|
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) &&
|
|
|
|
!wsi->already_did_cce) {
|
2016-06-13 17:23:44 +08:00
|
|
|
wsi->vhost->protocols[0].callback(wsi,
|
2015-12-04 16:54:12 +08:00
|
|
|
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
|
|
|
wsi->user_space, NULL, 0);
|
2011-11-07 19:53:23 +08:00
|
|
|
} else
|
2015-04-16 19:55:42 +08:00
|
|
|
lwsl_debug("not calling back closed mode=%d state=%d\n",
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->mode, wsi->state_pre_close);
|
2010-11-03 11:13:06 +00:00
|
|
|
|
2011-03-06 10:29:38 +00:00
|
|
|
/* deallocate any active extension contexts */
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
|
2014-04-02 21:02:54 +08:00
|
|
|
lwsl_warn("extension destruction failed\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
|
|
|
|
*/
|
2015-12-17 17:03:59 +08:00
|
|
|
if (lws_ext_cb_all_exts(context, wsi,
|
2016-01-11 11:34:01 +08:00
|
|
|
LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
|
2014-04-02 21:02:54 +08:00
|
|
|
lwsl_warn("ext destroy wsi failed\n");
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
async_close:
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
|
|
|
2016-02-14 09:27:41 +08:00
|
|
|
#ifdef LWS_USE_LIBUV
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
if (LWS_LIBUV_ENABLED(context)) {
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
/* libuv has to do his own close handle processing asynchronously */
|
|
|
|
lws_libuv_closehandle(wsi);
|
2016-02-14 09:27:41 +08:00
|
|
|
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
return;
|
|
|
|
}
|
2016-02-14 09:27:41 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
lws_close_free_wsi_final(wsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lws_close_free_wsi_final(struct lws *wsi)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->desc.sockfd)) {
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2017-02-27 12:55:56 +08:00
|
|
|
//lwsl_err("*** closing sockfd %d\n", wsi->desc.sockfd);
|
|
|
|
n = compatible_close(wsi->desc.sockfd);
|
2014-04-12 10:07:02 +08:00
|
|
|
if (n)
|
|
|
|
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
|
2015-11-02 20:34:12 +08:00
|
|
|
|
2015-11-08 12:10:26 +08:00
|
|
|
#else
|
2017-02-27 12:55:56 +08:00
|
|
|
compatible_close(wsi->desc.sockfd);
|
2016-07-23 14:18:25 +08:00
|
|
|
(void)n;
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2017-02-27 12:55:56 +08:00
|
|
|
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
2010-11-08 17:03:03 +00:00
|
|
|
}
|
2014-02-15 19:25:50 +08:00
|
|
|
|
|
|
|
/* outermost destroy notification for wsi (user_space still intact) */
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
|
2015-12-04 16:54:12 +08:00
|
|
|
wsi->user_space, NULL, 0);
|
2014-02-15 19:25:50 +08:00
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
if (wsi->cgi) {
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
for (n = 0; n < 6; n++) {
|
|
|
|
if (wsi->cgi->pipe_fds[n / 2][n & 1] == 0)
|
|
|
|
lwsl_err("ZERO FD IN CGI CLOSE");
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
if (wsi->cgi->pipe_fds[n / 2][n & 1] >= 0)
|
2016-04-13 11:42:53 +08:00
|
|
|
close(wsi->cgi->pipe_fds[n / 2][n & 1]);
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
}
|
2016-04-20 06:10:56 +08:00
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_free(wsi->cgi);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-06-25 17:51:07 +02:00
|
|
|
lws_free_wsi(wsi);
|
2010-11-03 11:13:06 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 09:04:15 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len)
|
|
|
|
{
|
|
|
|
int n = 0, sl = strlen(name);
|
|
|
|
|
|
|
|
while (lws_hdr_copy_fragment(wsi, buf, len,
|
|
|
|
WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) {
|
|
|
|
|
|
|
|
if (!strncmp(buf, name, sl))
|
|
|
|
return buf + sl;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-18 17:26:40 +08:00
|
|
|
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
2016-01-16 12:09:38 +08:00
|
|
|
LWS_VISIBLE int
|
2016-06-03 21:19:40 +08:00
|
|
|
interface_to_sa(struct lws_vhost *vh, const char *ifname, struct sockaddr_in *addr, size_t addrlen)
|
2016-01-16 12:09:38 +08:00
|
|
|
{
|
|
|
|
int ipv6 = 0;
|
|
|
|
#ifdef LWS_USE_IPV6
|
2016-06-03 21:19:40 +08:00
|
|
|
ipv6 = LWS_IPV6_ENABLED(vh);
|
2016-01-16 12:09:38 +08:00
|
|
|
#endif
|
2016-06-03 21:19:40 +08:00
|
|
|
(void)vh;
|
2016-01-16 12:09:38 +08:00
|
|
|
|
|
|
|
return lws_interface_to_sa(ipv6, ifname, addr, addrlen);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2016-07-23 14:18:25 +08:00
|
|
|
#if LWS_POSIX
|
2016-06-03 21:19:40 +08:00
|
|
|
static int
|
|
|
|
lws_get_addresses(struct lws_vhost *vh, void *ads, char *name,
|
2015-12-04 11:30:53 +08:00
|
|
|
int name_len, char *rip, int rip_len)
|
2015-01-28 21:03:49 +08:00
|
|
|
{
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2015-01-28 21:03:49 +08:00
|
|
|
struct addrinfo ai, *res;
|
2015-10-15 13:02:03 +03:00
|
|
|
struct sockaddr_in addr4;
|
2015-01-28 21:03:49 +08:00
|
|
|
|
2015-12-06 11:07:41 +08:00
|
|
|
if (rip)
|
|
|
|
rip[0] = '\0';
|
2015-01-28 21:03:49 +08:00
|
|
|
name[0] = '\0';
|
2015-10-15 13:02:03 +03:00
|
|
|
addr4.sin_family = AF_UNSPEC;
|
2015-01-28 21:03:49 +08:00
|
|
|
|
|
|
|
#ifdef LWS_USE_IPV6
|
2016-06-03 21:19:40 +08:00
|
|
|
if (LWS_IPV6_ENABLED(vh)) {
|
2015-01-28 21:03:49 +08:00
|
|
|
if (!lws_plat_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ads)->sin6_addr, rip, rip_len)) {
|
2017-02-14 17:55:13 +08:00
|
|
|
lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO));
|
2015-01-28 21:03:49 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Strip off the IPv4 to IPv6 header if one exists
|
|
|
|
if (strncmp(rip, "::ffff:", 7) == 0)
|
|
|
|
memmove(rip, rip + 7, strlen(rip) - 6);
|
|
|
|
|
|
|
|
getnameinfo((struct sockaddr *)ads,
|
|
|
|
sizeof(struct sockaddr_in6), name,
|
|
|
|
name_len, NULL, 0, 0);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2015-01-28 21:03:49 +08:00
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2015-10-15 13:02:03 +03:00
|
|
|
struct addrinfo *result;
|
|
|
|
|
2015-01-28 21:03:49 +08:00
|
|
|
memset(&ai, 0, sizeof ai);
|
|
|
|
ai.ai_family = PF_UNSPEC;
|
|
|
|
ai.ai_socktype = SOCK_STREAM;
|
|
|
|
ai.ai_flags = AI_CANONNAME;
|
2017-02-18 17:26:40 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2015-01-28 21:03:49 +08:00
|
|
|
if (getnameinfo((struct sockaddr *)ads,
|
|
|
|
sizeof(struct sockaddr_in),
|
|
|
|
name, name_len, NULL, 0, 0))
|
|
|
|
return -1;
|
2017-02-18 17:26:40 +08:00
|
|
|
#endif
|
2015-01-28 21:03:49 +08:00
|
|
|
if (!rip)
|
|
|
|
return 0;
|
|
|
|
|
2015-10-15 13:02:03 +03:00
|
|
|
if (getaddrinfo(name, NULL, &ai, &result))
|
2015-01-28 21:03:49 +08:00
|
|
|
return -1;
|
|
|
|
|
2015-10-15 13:02:03 +03:00
|
|
|
res = result;
|
|
|
|
while (addr4.sin_family == AF_UNSPEC && res) {
|
2015-01-28 21:03:49 +08:00
|
|
|
switch (res->ai_family) {
|
|
|
|
case AF_INET:
|
2015-10-15 13:02:03 +03:00
|
|
|
addr4.sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
|
|
|
addr4.sin_family = AF_INET;
|
2015-01-28 21:03:49 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = res->ai_next;
|
|
|
|
}
|
2015-10-15 13:02:03 +03:00
|
|
|
freeaddrinfo(result);
|
2015-01-28 21:03:49 +08:00
|
|
|
}
|
|
|
|
|
2015-10-15 13:02:03 +03:00
|
|
|
if (addr4.sin_family == AF_UNSPEC)
|
2015-01-28 21:03:49 +08:00
|
|
|
return -1;
|
|
|
|
|
2016-01-20 16:56:06 +08:00
|
|
|
if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL)
|
|
|
|
return -1;
|
2015-01-28 21:03:49 +08:00
|
|
|
|
|
|
|
return 0;
|
2015-11-02 20:34:12 +08:00
|
|
|
#else
|
2016-06-03 21:19:40 +08:00
|
|
|
(void)vh;
|
2015-11-02 20:34:12 +08:00
|
|
|
(void)ads;
|
|
|
|
(void)name;
|
|
|
|
(void)name_len;
|
|
|
|
(void)rip;
|
|
|
|
(void)rip_len;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
#endif
|
2015-01-28 21:03:49 +08:00
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2015-01-28 21:03:49 +08:00
|
|
|
|
2017-01-17 07:01:02 +08:00
|
|
|
|
2016-05-25 08:04:52 +08:00
|
|
|
LWS_VISIBLE const char *
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
|
|
|
|
{
|
|
|
|
#if LWS_POSIX
|
|
|
|
socklen_t len, olen;
|
|
|
|
#ifdef LWS_USE_IPV6
|
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
#endif
|
|
|
|
struct sockaddr_in sin4;
|
|
|
|
int af = AF_INET;
|
|
|
|
void *p, *q;
|
|
|
|
|
|
|
|
#ifdef LWS_USE_IPV6
|
2016-06-03 21:19:40 +08:00
|
|
|
if (LWS_IPV6_ENABLED(wsi->vhost)) {
|
2016-04-15 12:00:23 +08:00
|
|
|
len = sizeof(sin6);
|
|
|
|
p = &sin6;
|
|
|
|
af = AF_INET6;
|
|
|
|
q = &sin6.sin6_addr;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
len = sizeof(sin4);
|
|
|
|
p = &sin4;
|
|
|
|
q = &sin4.sin_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
olen = len;
|
2017-02-27 12:55:56 +08:00
|
|
|
if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) {
|
2016-04-15 12:00:23 +08:00
|
|
|
lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-17 19:26:04 -03:00
|
|
|
return lws_plat_inet_ntop(af, q, name, namelen);
|
2016-07-23 14:18:25 +08:00
|
|
|
#else
|
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
return lws_plat_get_peer_simple(wsi, name, namelen);
|
2016-04-15 12:00:23 +08:00
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2016-04-15 12:00:23 +08:00
|
|
|
}
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void
|
2015-12-16 18:19:08 +08:00
|
|
|
lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
|
|
|
|
int name_len, char *rip, int rip_len)
|
2011-02-13 08:37:12 +00:00
|
|
|
{
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2013-04-25 09:16:30 +08:00
|
|
|
socklen_t len;
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
#endif
|
|
|
|
struct sockaddr_in sin4;
|
2015-12-16 18:19:08 +08:00
|
|
|
struct lws_context *context = wsi->context;
|
2014-03-24 16:09:25 +08:00
|
|
|
int ret = -1;
|
2015-01-28 21:03:49 +08:00
|
|
|
void *p;
|
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);
|
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2016-06-03 21:19:40 +08:00
|
|
|
if (LWS_IPV6_ENABLED(wsi->vhost)) {
|
2014-03-24 16:09:25 +08:00
|
|
|
len = sizeof(sin6);
|
2015-01-28 21:03:49 +08:00
|
|
|
p = &sin6;
|
2014-03-24 16:09:25 +08:00
|
|
|
} else
|
2011-03-10 18:14:01 +00:00
|
|
|
#endif
|
2014-03-24 16:09:25 +08:00
|
|
|
{
|
|
|
|
len = sizeof(sin4);
|
2015-01-28 21:03:49 +08:00
|
|
|
p = &sin4;
|
|
|
|
}
|
2011-02-13 08:37:12 +00:00
|
|
|
|
2015-01-28 21:03:49 +08:00
|
|
|
if (getpeername(fd, p, &len) < 0) {
|
|
|
|
lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
|
|
|
|
goto bail;
|
2011-02-13 08:37:12 +00:00
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-06-03 21:19:40 +08:00
|
|
|
ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len);
|
2013-01-30 08:12:20 +08:00
|
|
|
|
|
|
|
bail:
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1);
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
2015-11-02 20:34:12 +08:00
|
|
|
(void)wsi;
|
|
|
|
(void)fd;
|
|
|
|
(void)name;
|
|
|
|
(void)name_len;
|
|
|
|
(void)rip;
|
|
|
|
(void)rip_len;
|
2017-01-17 07:01:02 +08:00
|
|
|
|
2011-02-13 08:37:12 +00:00
|
|
|
}
|
2011-02-12 11:57:45 +00:00
|
|
|
|
2012-10-19 11:21:56 +02:00
|
|
|
LWS_EXTERN void *
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_context_user(struct lws_context *context)
|
2012-10-19 11:21:56 +02:00
|
|
|
{
|
2013-02-11 17:13:32 +08:00
|
|
|
return context->user_space;
|
2012-10-19 11:21:56 +02:00
|
|
|
}
|
|
|
|
|
2016-04-06 16:15:40 +08:00
|
|
|
LWS_VISIBLE struct lws_vhost *
|
|
|
|
lws_vhost_get(struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->vhost;
|
|
|
|
}
|
|
|
|
|
2016-05-17 13:47:44 +08:00
|
|
|
LWS_VISIBLE struct lws_vhost *
|
|
|
|
lws_get_vhost(struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->vhost;
|
|
|
|
}
|
|
|
|
|
2016-04-06 16:15:40 +08:00
|
|
|
LWS_VISIBLE const struct lws_protocols *
|
|
|
|
lws_protocol_get(struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->protocol;
|
|
|
|
}
|
|
|
|
|
2017-02-15 09:12:39 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
|
|
|
|
lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < vh->count_protocols; n++)
|
|
|
|
if (!strcmp(name, vh->protocols[n].name))
|
|
|
|
return &vh->protocols[n];
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-15 16:36:38 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-11 10:45:35 +08:00
|
|
|
lws_callback_all_protocol(struct lws_context *context,
|
|
|
|
const struct lws_protocols *protocol, int reason)
|
2014-02-15 16:36:38 +08:00
|
|
|
{
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[0];
|
2016-01-26 20:56:56 +08:00
|
|
|
unsigned int n, m = context->count_threads;
|
2016-01-29 21:18:54 +08:00
|
|
|
struct lws *wsi;
|
2016-01-19 03:34:24 +08:00
|
|
|
|
|
|
|
while (m--) {
|
|
|
|
for (n = 0; n < pt->fds_count; n++) {
|
|
|
|
wsi = wsi_from_fd(context, pt->fds[n].fd);
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->protocol == protocol)
|
2016-01-29 21:18:54 +08:00
|
|
|
protocol->callback(wsi, reason, wsi->user_space,
|
|
|
|
NULL, 0);
|
2016-01-19 03:34:24 +08:00
|
|
|
}
|
|
|
|
pt++;
|
2014-02-15 16:36:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-06 16:15:40 +08:00
|
|
|
LWS_VISIBLE int
|
|
|
|
lws_callback_all_protocol_vhost(struct lws_vhost *vh,
|
|
|
|
const struct lws_protocols *protocol, int reason)
|
|
|
|
{
|
|
|
|
struct lws_context *context = vh->context;
|
|
|
|
struct lws_context_per_thread *pt = &context->pt[0];
|
|
|
|
unsigned int n, m = context->count_threads;
|
|
|
|
struct lws *wsi;
|
|
|
|
|
|
|
|
while (m--) {
|
|
|
|
for (n = 0; n < pt->fds_count; n++) {
|
|
|
|
wsi = wsi_from_fd(context, pt->fds[n].fd);
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->vhost == vh && wsi->protocol == protocol)
|
|
|
|
protocol->callback(wsi, reason, wsi->user_space,
|
|
|
|
NULL, 0);
|
|
|
|
}
|
|
|
|
pt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:58 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < wsi->vhost->count_protocols; n++)
|
|
|
|
if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-18 17:26:40 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
2017-03-08 11:11:41 +08:00
|
|
|
lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops)
|
2017-02-18 17:26:40 +08:00
|
|
|
{
|
2017-03-08 11:11:41 +08:00
|
|
|
context->fops = fops;
|
2017-03-03 12:38:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN lws_filepos_t
|
|
|
|
lws_vfs_tell(lws_fop_fd_t fop_fd)
|
|
|
|
{
|
|
|
|
return fop_fd->pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN lws_filepos_t
|
|
|
|
lws_vfs_get_length(lws_fop_fd_t fop_fd)
|
|
|
|
{
|
|
|
|
return fop_fd->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN uint32_t
|
|
|
|
lws_vfs_get_mod_time(lws_fop_fd_t fop_fd)
|
|
|
|
{
|
|
|
|
return fop_fd->mod_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE lws_fileofs_t
|
|
|
|
lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
|
|
|
|
{
|
|
|
|
lws_fileofs_t ofs;
|
2017-03-08 11:11:41 +08:00
|
|
|
lwsl_debug("%s: seeking to %ld, len %ld\n", __func__, (long)offset, (long)fop_fd->len);
|
2017-03-03 12:38:10 +08:00
|
|
|
ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos);
|
2017-03-08 11:11:41 +08:00
|
|
|
lwsl_debug("%s: result %ld, fop_fd pos %ld\n", __func__, (long)ofs, (long)fop_fd->pos);
|
2017-03-03 12:38:10 +08:00
|
|
|
return ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LWS_VISIBLE lws_fileofs_t
|
|
|
|
lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
|
|
|
|
{
|
|
|
|
return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + fop_fd->pos + offset);
|
2017-03-01 14:28:56 +08:00
|
|
|
}
|
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
|
|
|
|
const struct lws_plat_file_ops *
|
|
|
|
lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
|
|
|
|
const char **vpath)
|
2017-03-01 14:28:56 +08:00
|
|
|
{
|
2017-03-03 12:38:10 +08:00
|
|
|
const struct lws_plat_file_ops *pf;
|
|
|
|
const char *p = vfs_path;
|
|
|
|
int n;
|
2017-03-01 14:28:56 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
*vpath = NULL;
|
|
|
|
|
|
|
|
/* no non-platform fops, just use that */
|
|
|
|
|
|
|
|
if (!fops->next)
|
|
|
|
return fops;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* scan the vfs path looking for indications we are to be
|
|
|
|
* handled by a specific fops
|
|
|
|
*/
|
2017-03-01 14:28:56 +08:00
|
|
|
|
2017-03-08 11:11:41 +08:00
|
|
|
while (p && *p) {
|
2017-03-03 12:38:10 +08:00
|
|
|
if (*p != '/') {
|
|
|
|
p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* the first one is always platform fops, so skip */
|
|
|
|
pf = fops->next;
|
|
|
|
while (pf) {
|
|
|
|
n = 0;
|
|
|
|
while (n < ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
|
|
|
|
if (p >= vfs_path + pf->fi[n].len)
|
|
|
|
if (!strncmp(p - (pf->fi[n].len - 1),
|
|
|
|
pf->fi[n].sig,
|
|
|
|
pf->fi[n].len - 1)) {
|
|
|
|
*vpath = p + 1;
|
|
|
|
return pf;
|
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
2017-03-01 14:28:56 +08:00
|
|
|
}
|
2017-03-03 12:38:10 +08:00
|
|
|
pf = pf->next;
|
|
|
|
}
|
|
|
|
p++;
|
2017-03-01 14:28:56 +08:00
|
|
|
}
|
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
return fops;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT
|
|
|
|
lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
|
|
|
|
lws_fop_flags_t *flags)
|
|
|
|
{
|
2017-03-08 11:11:41 +08:00
|
|
|
const char *vpath = "";
|
2017-03-03 12:38:10 +08:00
|
|
|
const struct lws_plat_file_ops *selected = lws_vfs_select_fops(
|
|
|
|
fops, vfs_path, &vpath);
|
2017-03-01 14:28:56 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags);
|
2017-02-18 17:26:40 +08:00
|
|
|
}
|
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
|
2016-06-17 09:41:22 +08:00
|
|
|
/**
|
|
|
|
* lws_now_secs() - seconds since 1970-1-1
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
LWS_VISIBLE LWS_EXTERN unsigned long
|
|
|
|
lws_now_secs(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
return tv.tv_sec;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_get_socket_fd(struct lws *wsi)
|
2011-01-27 20:06:03 +00:00
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
return wsi->desc.sockfd;
|
2011-01-27 20:06:03 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
|
|
|
|
2013-01-29 12:36:17 +08:00
|
|
|
#ifdef LWS_LATENCY
|
|
|
|
void
|
2015-12-04 11:30:53 +08:00
|
|
|
lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
|
|
|
|
int ret, int completed)
|
2013-01-29 12:36:17 +08:00
|
|
|
{
|
2014-02-26 21:37:31 +01:00
|
|
|
unsigned long long u;
|
2013-01-29 12:36:17 +08:00
|
|
|
char buf[256];
|
|
|
|
|
2014-02-26 21:37:31 +01:00
|
|
|
u = time_in_microseconds();
|
2013-01-29 12:36:17 +08:00
|
|
|
|
2014-03-31 11:01:32 +08:00
|
|
|
if (!action) {
|
2013-01-29 12:36:17 +08:00
|
|
|
wsi->latency_start = u;
|
|
|
|
if (!wsi->action_start)
|
|
|
|
wsi->action_start = u;
|
2014-03-31 11:01:32 +08:00
|
|
|
return;
|
2013-01-29 12:36:17 +08:00
|
|
|
}
|
2014-03-31 11:01:32 +08:00
|
|
|
if (completed) {
|
|
|
|
if (wsi->action_start == wsi->latency_start)
|
|
|
|
sprintf(buf,
|
2014-07-05 10:50:47 +08:00
|
|
|
"Completion first try lat %lluus: %p: ret %d: %s\n",
|
2014-03-31 11:01:32 +08:00
|
|
|
u - wsi->latency_start,
|
|
|
|
(void *)wsi, ret, action);
|
|
|
|
else
|
|
|
|
sprintf(buf,
|
2014-07-05 10:50:47 +08:00
|
|
|
"Completion %lluus: lat %lluus: %p: ret %d: %s\n",
|
2014-03-31 11:01:32 +08:00
|
|
|
u - wsi->action_start,
|
|
|
|
u - wsi->latency_start,
|
|
|
|
(void *)wsi, ret, action);
|
|
|
|
wsi->action_start = 0;
|
|
|
|
} else
|
2014-07-05 10:50:47 +08:00
|
|
|
sprintf(buf, "lat %lluus: %p: ret %d: %s\n",
|
2014-03-31 11:01:32 +08:00
|
|
|
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);
|
2013-01-29 12:36:17 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_rx_flow_control(struct lws *wsi, int enable)
|
2013-01-17 16:50:35 +08:00
|
|
|
{
|
2014-10-08 12:00:53 +08:00
|
|
|
if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
|
2013-03-16 11:24:23 +08:00
|
|
|
return 0;
|
|
|
|
|
2015-12-04 16:54:12 +08:00
|
|
|
lwsl_info("%s: (0x%p, %d)\n", __func__, wsi, enable);
|
2014-10-08 12:00:53 +08:00
|
|
|
wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
|
2013-01-17 16:50:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE void
|
2015-12-11 10:45:35 +08:00
|
|
|
lws_rx_flow_allow_all_protocol(const struct lws_context *context,
|
|
|
|
const struct lws_protocols *protocol)
|
2013-03-16 12:32:27 +08:00
|
|
|
{
|
2016-01-19 03:34:24 +08:00
|
|
|
const struct lws_context_per_thread *pt = &context->pt[0];
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *wsi;
|
2016-01-26 20:56:56 +08:00
|
|
|
unsigned int n, m = context->count_threads;
|
2016-01-19 03:34:24 +08:00
|
|
|
|
|
|
|
while (m--) {
|
|
|
|
for (n = 0; n < pt->fds_count; n++) {
|
|
|
|
wsi = wsi_from_fd(context, pt->fds[n].fd);
|
|
|
|
if (!wsi)
|
|
|
|
continue;
|
|
|
|
if (wsi->protocol == protocol)
|
|
|
|
lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
|
|
|
|
}
|
|
|
|
pt++;
|
2013-03-16 12:32:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE extern const char *
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_canonical_hostname(struct lws_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
|
|
|
}
|
|
|
|
|
2015-12-21 18:06:38 +01:00
|
|
|
int user_callback_handle_rxflow(lws_callback_function callback_function,
|
2015-12-17 07:54:44 +08:00
|
|
|
struct lws *wsi,
|
2015-12-04 11:30:53 +08:00
|
|
|
enum lws_callback_reasons reason, void *user,
|
|
|
|
void *in, size_t len)
|
2013-01-17 16:50:35 +08:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
2015-12-17 07:54:44 +08:00
|
|
|
n = callback_function(wsi, reason, user, in, len);
|
2013-02-10 21:21:24 +08:00
|
|
|
if (!n)
|
2015-12-04 08:43:54 +08:00
|
|
|
n = _lws_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
|
|
|
}
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
#undef strchr
|
|
|
|
#define strchr ets_strchr
|
|
|
|
#endif
|
|
|
|
|
2013-10-24 22:12:03 +08:00
|
|
|
LWS_VISIBLE int
|
2016-03-28 10:10:43 +08:00
|
|
|
lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
|
2013-10-24 22:12:03 +08:00
|
|
|
{
|
2016-07-23 14:18:25 +08:00
|
|
|
#if !defined(LWS_WITH_ESP8266)
|
2013-10-24 22:12:03 +08:00
|
|
|
char *p;
|
2015-11-08 10:15:01 +08:00
|
|
|
char authstring[96];
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2013-10-24 22:12:03 +08:00
|
|
|
if (!proxy)
|
|
|
|
return -1;
|
|
|
|
|
2017-01-26 07:27:11 +08:00
|
|
|
/* we have to deal with a possible redundant leading http:// */
|
|
|
|
if (!strncmp(proxy, "http://", 7))
|
|
|
|
proxy += 7;
|
|
|
|
|
2015-11-08 10:15:01 +08:00
|
|
|
p = strchr(proxy, '@');
|
|
|
|
if (p) { /* auth is around */
|
|
|
|
|
2015-11-08 12:10:26 +08:00
|
|
|
if ((unsigned int)(p - proxy) > sizeof(authstring) - 1)
|
2015-11-08 10:15:01 +08:00
|
|
|
goto auth_too_long;
|
|
|
|
|
2015-11-18 19:32:01 +08:00
|
|
|
strncpy(authstring, proxy, p - proxy);
|
2015-11-08 10:15:01 +08:00
|
|
|
// null termination not needed on input
|
|
|
|
if (lws_b64_encode_string(authstring, (p - proxy),
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->proxy_basic_auth_token,
|
|
|
|
sizeof vhost->proxy_basic_auth_token) < 0)
|
2015-11-08 10:15:01 +08:00
|
|
|
goto auth_too_long;
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_info(" Proxy auth in use\n");
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2015-11-08 10:15:01 +08:00
|
|
|
proxy = p + 1;
|
|
|
|
} else
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->proxy_basic_auth_token[0] = '\0';
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
strncpy(vhost->http_proxy_address, proxy,
|
|
|
|
sizeof(vhost->http_proxy_address) - 1);
|
|
|
|
vhost->http_proxy_address[
|
|
|
|
sizeof(vhost->http_proxy_address) - 1] = '\0';
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
p = strchr(vhost->http_proxy_address, ':');
|
|
|
|
if (!p && !vhost->http_proxy_port) {
|
2013-10-24 22:12:03 +08:00
|
|
|
lwsl_err("http_proxy needs to be ads:port\n");
|
|
|
|
|
|
|
|
return -1;
|
2015-11-08 10:15:01 +08:00
|
|
|
} else {
|
2015-12-06 11:04:05 +08:00
|
|
|
if (p) {
|
|
|
|
*p = '\0';
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->http_proxy_port = atoi(p + 1);
|
2015-12-06 11:04:05 +08:00
|
|
|
}
|
2013-10-24 22:12:03 +08:00
|
|
|
}
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address,
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->http_proxy_port);
|
2013-10-24 22:12:03 +08:00
|
|
|
|
|
|
|
return 0;
|
2015-11-08 10:15:01 +08:00
|
|
|
|
|
|
|
auth_too_long:
|
|
|
|
lwsl_err("proxy auth too long\n");
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2015-11-08 10:15:01 +08:00
|
|
|
return -1;
|
2013-10-24 22:12:03 +08:00
|
|
|
}
|
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
LWS_VISIBLE const struct lws_protocols *
|
|
|
|
lws_get_protocol(struct lws *wsi)
|
2010-12-18 15:13:50 +00:00
|
|
|
{
|
|
|
|
return wsi->protocol;
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_is_final_fragment(struct lws *wsi)
|
2011-03-07 21:16:31 +00:00
|
|
|
{
|
2017-02-28 18:29:57 +08:00
|
|
|
lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
|
2017-02-05 22:07:34 +08:00
|
|
|
wsi->u.ws.final, (long)wsi->u.ws.rx_packet_length,
|
|
|
|
(long)wsi->u.ws.rx_draining_ext);
|
2016-01-11 11:34:01 +08:00
|
|
|
return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext;
|
2011-03-07 21:16:31 +00:00
|
|
|
}
|
2011-11-07 17:19:25 +08:00
|
|
|
|
2013-03-30 09:52:21 +08:00
|
|
|
LWS_VISIBLE unsigned char
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_get_reserved_bits(struct lws *wsi)
|
2013-01-09 18:06:55 +08:00
|
|
|
{
|
2013-01-21 11:04:23 +08:00
|
|
|
return wsi->u.ws.rsv;
|
2013-01-09 18:06:55 +08:00
|
|
|
}
|
|
|
|
|
2013-02-18 16:30:10 +08:00
|
|
|
int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_ensure_user_space(struct lws *wsi)
|
2011-11-07 17:19:25 +08:00
|
|
|
{
|
2014-10-22 15:37:28 +08:00
|
|
|
lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol);
|
2013-02-15 22:31:55 +08:00
|
|
|
if (!wsi->protocol)
|
2013-02-18 16:30:10 +08:00
|
|
|
return 1;
|
2013-02-15 22:31:55 +08:00
|
|
|
|
2011-11-07 17:19:25 +08:00
|
|
|
/* allocate the per-connection user memory (if any) */
|
|
|
|
|
|
|
|
if (wsi->protocol->per_session_data_size && !wsi->user_space) {
|
2014-12-04 23:59:35 +01:00
|
|
|
wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size);
|
2011-11-07 17:19:25 +08:00
|
|
|
if (wsi->user_space == NULL) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_err("Out of memory for conn user space\n");
|
2013-02-18 16:30:10 +08:00
|
|
|
return 1;
|
2011-11-07 17:19:25 +08:00
|
|
|
}
|
2014-10-22 15:37:28 +08:00
|
|
|
} else
|
2017-02-05 22:07:34 +08:00
|
|
|
lwsl_info("%s: %p protocol pss %lu, user_space=%p\n",
|
|
|
|
__func__, wsi, (long)wsi->protocol->per_session_data_size,
|
2015-12-04 16:54:12 +08:00
|
|
|
wsi->user_space);
|
2013-02-18 16:30:10 +08:00
|
|
|
return 0;
|
2011-11-07 17:19:25 +08:00
|
|
|
}
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2016-02-25 15:01:55 +08:00
|
|
|
LWS_VISIBLE int
|
|
|
|
lwsl_timestamp(int level, char *p, int len)
|
2013-01-12 09:17:42 +08:00
|
|
|
{
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2016-02-21 07:42:49 +08:00
|
|
|
time_t o_now = time(NULL);
|
2014-02-26 21:37:31 +01:00
|
|
|
unsigned long long now;
|
2016-02-21 21:36:48 +08:00
|
|
|
struct tm *ptm = NULL;
|
|
|
|
#ifndef WIN32
|
|
|
|
struct tm tm;
|
|
|
|
#endif
|
|
|
|
int n;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2016-02-29 18:48:55 +08:00
|
|
|
#ifndef _WIN32_WCE
|
2016-02-21 21:36:48 +08:00
|
|
|
#ifdef WIN32
|
|
|
|
ptm = localtime(&o_now);
|
|
|
|
#else
|
|
|
|
if (localtime_r(&o_now, &tm))
|
|
|
|
ptm = &tm;
|
2016-02-29 18:48:55 +08:00
|
|
|
#endif
|
2016-02-21 21:36:48 +08:00
|
|
|
#endif
|
2016-02-25 15:01:55 +08:00
|
|
|
p[0] = '\0';
|
2015-12-04 16:54:12 +08:00
|
|
|
for (n = 0; n < LLL_COUNT; n++) {
|
|
|
|
if (level != (1 << n))
|
|
|
|
continue;
|
|
|
|
now = time_in_microseconds() / 100;
|
2016-02-21 21:36:48 +08:00
|
|
|
if (ptm)
|
2016-09-15 02:22:57 +08:00
|
|
|
n = lws_snprintf(p, len,
|
2016-02-25 15:01:55 +08:00
|
|
|
"[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ",
|
2016-02-21 21:36:48 +08:00
|
|
|
ptm->tm_year + 1900,
|
2016-06-01 08:32:18 +08:00
|
|
|
ptm->tm_mon + 1,
|
2016-02-21 21:36:48 +08:00
|
|
|
ptm->tm_mday,
|
|
|
|
ptm->tm_hour,
|
|
|
|
ptm->tm_min,
|
|
|
|
ptm->tm_sec,
|
2016-02-21 07:42:49 +08:00
|
|
|
(int)(now % 10000), log_level_names[n]);
|
|
|
|
else
|
2016-09-15 02:22:57 +08:00
|
|
|
n = lws_snprintf(p, len, "[%llu:%04d] %s: ",
|
2016-02-21 07:42:49 +08:00
|
|
|
(unsigned long long) now / 10000,
|
|
|
|
(int)(now % 10000), log_level_names[n]);
|
2016-02-25 15:01:55 +08:00
|
|
|
return n;
|
2015-12-04 16:54:12 +08:00
|
|
|
}
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2016-02-25 15:01:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2016-02-25 15:01:55 +08:00
|
|
|
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
|
|
|
|
{
|
2016-07-23 14:18:25 +08:00
|
|
|
#if !defined(LWS_WITH_ESP8266)
|
2016-02-25 15:01:55 +08:00
|
|
|
char buf[50];
|
|
|
|
|
|
|
|
lwsl_timestamp(level, buf, sizeof(buf));
|
2013-01-19 11:17:56 +08:00
|
|
|
fprintf(stderr, "%s%s", buf, line);
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2013-01-19 11:17:56 +08:00
|
|
|
}
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2013-01-19 11:12:16 +08:00
|
|
|
|
2014-12-10 18:50:28 -06:00
|
|
|
LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl)
|
2013-01-19 11:17:56 +08:00
|
|
|
{
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
char buf[128];
|
|
|
|
#else
|
2013-01-19 11:17:56 +08:00
|
|
|
char buf[256];
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
|
|
|
int n;
|
2013-01-19 11:17:56 +08:00
|
|
|
|
|
|
|
if (!(log_level & filter))
|
|
|
|
return;
|
2013-01-10 19:50:35 +08:00
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
n = vsnprintf(buf, sizeof(buf) - 1, format, vl);
|
|
|
|
(void)n;
|
|
|
|
#if defined(LWS_WITH_ESP8266)
|
2013-02-11 17:13:32 +08:00
|
|
|
buf[sizeof(buf) - 1] = '\0';
|
2016-07-23 14:18:25 +08:00
|
|
|
#else
|
|
|
|
/* vnsprintf returns what it would have written, even if truncated */
|
|
|
|
if (n > sizeof(buf) - 1)
|
|
|
|
n = sizeof(buf) - 1;
|
|
|
|
if (n > 0)
|
|
|
|
buf[n] = '\0';
|
|
|
|
#endif
|
2013-01-12 09:17:42 +08:00
|
|
|
|
2013-01-19 11:17:56 +08:00
|
|
|
lwsl_emit(filter, buf);
|
2013-01-10 19:50:35 +08:00
|
|
|
}
|
|
|
|
|
2014-12-10 18:50:28 -06:00
|
|
|
LWS_VISIBLE void _lws_log(int filter, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
_lws_logv(filter, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2015-12-04 11:30:53 +08:00
|
|
|
LWS_VISIBLE void lws_set_log_level(int level,
|
2015-12-04 16:54:12 +08:00
|
|
|
void (*func)(int level, const char *line))
|
2013-01-10 19:50:35 +08:00
|
|
|
{
|
|
|
|
log_level = level;
|
2015-12-04 16:54:12 +08:00
|
|
|
if (func)
|
|
|
|
lwsl_emit = func;
|
2014-04-27 13:28:22 +02:00
|
|
|
}
|
2014-08-16 09:54:27 +08:00
|
|
|
|
2017-02-09 09:11:17 +08:00
|
|
|
LWS_VISIBLE int lwsl_visible(int level)
|
|
|
|
{
|
|
|
|
return log_level & level;
|
|
|
|
}
|
|
|
|
|
2014-08-16 09:54:27 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_is_ssl(struct lws *wsi)
|
2014-08-16 09:54:27 +08:00
|
|
|
{
|
2014-08-19 08:41:26 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2014-08-16 09:54:27 +08:00
|
|
|
return wsi->use_ssl;
|
2014-08-19 08:41:26 +08:00
|
|
|
#else
|
2015-11-02 13:10:33 +08:00
|
|
|
(void)wsi;
|
2014-08-19 08:41:26 +08:00
|
|
|
return 0;
|
|
|
|
#endif
|
2014-08-16 09:54:27 +08:00
|
|
|
}
|
2014-08-18 22:49:39 +08:00
|
|
|
|
2017-01-23 19:34:46 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
LWS_VISIBLE SSL*
|
|
|
|
lws_get_ssl(struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->ssl;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-18 22:49:39 +08:00
|
|
|
LWS_VISIBLE int
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_partial_buffered(struct lws *wsi)
|
2014-08-18 22:49:39 +08:00
|
|
|
{
|
2015-12-17 17:03:59 +08:00
|
|
|
return !!wsi->trunc_len;
|
2014-08-18 22:49:39 +08:00
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2015-12-15 21:15:58 +08:00
|
|
|
void lws_set_protocol_write_pending(struct lws *wsi,
|
2014-10-08 12:00:53 +08:00
|
|
|
enum lws_pending_protocol_send pend)
|
|
|
|
{
|
2014-10-29 09:39:08 +08:00
|
|
|
lwsl_info("setting pps %d\n", pend);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
if (wsi->pps)
|
|
|
|
lwsl_err("pps overwrite\n");
|
|
|
|
wsi->pps = pend;
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_rx_flow_control(wsi, 0);
|
2015-12-16 18:19:08 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
2014-10-18 18:52:39 +08:00
|
|
|
}
|
2014-10-29 09:39:08 +08:00
|
|
|
|
|
|
|
LWS_VISIBLE size_t
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_get_peer_write_allowance(struct lws *wsi)
|
2014-10-29 09:39:08 +08:00
|
|
|
{
|
|
|
|
#ifdef LWS_USE_HTTP2
|
|
|
|
/* only if we are using HTTP2 on this connection */
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->mode != LWSCM_HTTP2_SERVING)
|
2014-10-29 09:39:08 +08:00
|
|
|
return -1;
|
|
|
|
/* user is only interested in how much he can send, or that he can't */
|
|
|
|
if (wsi->u.http2.tx_credit <= 0)
|
|
|
|
return 0;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-29 09:39:08 +08:00
|
|
|
return wsi->u.http2.tx_credit;
|
|
|
|
#else
|
2015-11-02 13:10:33 +08:00
|
|
|
(void)wsi;
|
2014-10-29 09:39:08 +08:00
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
2014-11-08 11:18:47 +08:00
|
|
|
|
|
|
|
LWS_VISIBLE void
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_union_transition(struct lws *wsi, enum connection_mode mode)
|
2014-11-08 11:18:47 +08:00
|
|
|
{
|
2015-12-25 12:44:12 +08:00
|
|
|
lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode);
|
2014-11-08 11:18:47 +08:00
|
|
|
memset(&wsi->u, 0, sizeof(wsi->u));
|
|
|
|
wsi->mode = mode;
|
|
|
|
}
|
2015-12-04 10:39:23 +08:00
|
|
|
|
lws_plat_fd implement platform default handlers
This is a rewrite of the patch from Soapyman here
https://github.com/warmcat/libwebsockets/pull/363
The main changes compared to Soapyman's original patch are
- There's no new stuff in the info struct user code does any overrides
it may want to do explicitly after lws_context_create returns
- User overrides for file ops can call through (subclass) to the original
platform implementation using lws_get_fops_plat()
- A typedef is provided for plat-specific fd type
- Public helpers are provided to allow user code to be platform-independent
about file access, using the lws platform file operations underneath:
static inline lws_filefd_type
lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
unsigned long *filelen, int flags)
static inline int
lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)
static inline unsigned long
lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
long offset_from_cur_pos)
static inline int
lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
static inline int
lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
There's example documentation and implementation in the test server.
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:58:58 +08:00
|
|
|
LWS_VISIBLE struct lws_plat_file_ops *
|
|
|
|
lws_get_fops(struct lws_context *context)
|
|
|
|
{
|
2017-03-03 12:38:10 +08:00
|
|
|
return (struct lws_plat_file_ops *)context->fops;
|
lws_plat_fd implement platform default handlers
This is a rewrite of the patch from Soapyman here
https://github.com/warmcat/libwebsockets/pull/363
The main changes compared to Soapyman's original patch are
- There's no new stuff in the info struct user code does any overrides
it may want to do explicitly after lws_context_create returns
- User overrides for file ops can call through (subclass) to the original
platform implementation using lws_get_fops_plat()
- A typedef is provided for plat-specific fd type
- Public helpers are provided to allow user code to be platform-independent
about file access, using the lws platform file operations underneath:
static inline lws_filefd_type
lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
unsigned long *filelen, int flags)
static inline int
lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)
static inline unsigned long
lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
long offset_from_cur_pos)
static inline int
lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
static inline int
lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
There's example documentation and implementation in the test server.
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:58:58 +08:00
|
|
|
}
|
2015-12-04 10:39:23 +08:00
|
|
|
|
2015-12-11 09:36:14 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN struct lws_context *
|
2015-12-17 18:25:25 +08:00
|
|
|
lws_get_context(const struct lws *wsi)
|
2015-12-11 09:36:14 +08:00
|
|
|
{
|
|
|
|
return wsi->context;
|
|
|
|
}
|
|
|
|
|
2016-01-19 03:34:24 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_get_count_threads(struct lws_context *context)
|
|
|
|
{
|
|
|
|
return context->count_threads;
|
|
|
|
}
|
|
|
|
|
2015-12-14 07:16:32 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void *
|
|
|
|
lws_wsi_user(struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->user_space;
|
|
|
|
}
|
2015-12-26 17:20:34 +08:00
|
|
|
|
2017-02-28 21:17:25 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
|
|
lws_set_wsi_user(struct lws *wsi, void *data)
|
|
|
|
{
|
|
|
|
if (wsi->user_space_externally_allocated)
|
|
|
|
wsi->user_space = data;
|
|
|
|
else
|
|
|
|
lwsl_err("%s: Cannot set internally-allocated user_space\n",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
|
2016-03-02 09:17:22 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN struct lws *
|
|
|
|
lws_get_parent(const struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN struct lws *
|
|
|
|
lws_get_child(const struct lws *wsi)
|
|
|
|
{
|
|
|
|
return wsi->child_list;
|
|
|
|
}
|
|
|
|
|
2015-12-26 17:20:34 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
|
|
lws_close_reason(struct lws *wsi, enum lws_close_status status,
|
|
|
|
unsigned char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned char *p, *start;
|
2016-01-19 03:34:24 +08:00
|
|
|
int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE;
|
2015-12-26 17:20:34 +08:00
|
|
|
|
|
|
|
assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
|
2015-12-26 17:20:34 +08:00
|
|
|
|
|
|
|
*p++ = (((int)status) >> 8) & 0xff;
|
|
|
|
*p++ = ((int)status) & 0xff;
|
|
|
|
|
|
|
|
if (buf)
|
|
|
|
while (len-- && p < start + budget)
|
|
|
|
*p++ = *buf++;
|
|
|
|
|
|
|
|
wsi->u.ws.close_in_ping_buffer_len = p - start;
|
|
|
|
}
|
2015-12-28 14:24:49 +08:00
|
|
|
|
|
|
|
LWS_EXTERN int
|
|
|
|
_lws_rx_flow_control(struct lws *wsi)
|
|
|
|
{
|
2017-03-05 15:32:47 +08:00
|
|
|
struct lws *wsic = wsi->child_list;
|
|
|
|
|
|
|
|
/* if he has children, do those if they were changed */
|
|
|
|
while (wsic) {
|
|
|
|
if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)
|
|
|
|
_lws_rx_flow_control(wsic);
|
|
|
|
|
|
|
|
wsic = wsic->sibling_list;
|
|
|
|
}
|
|
|
|
|
2015-12-28 14:24:49 +08:00
|
|
|
/* there is no pending change */
|
2016-01-11 11:34:01 +08:00
|
|
|
if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) {
|
2016-01-19 03:34:24 +08:00
|
|
|
lwsl_debug("%s: no pending change\n", __func__);
|
2015-12-28 14:24:49 +08:00
|
|
|
return 0;
|
2016-01-11 11:34:01 +08:00
|
|
|
}
|
2015-12-28 14:24:49 +08:00
|
|
|
|
|
|
|
/* stuff is still buffered, not ready to really accept new input */
|
|
|
|
if (wsi->rxflow_buffer) {
|
|
|
|
/* get ourselves called back to deal with stashed buffer */
|
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pending is cleared, we can change rxflow state */
|
|
|
|
|
|
|
|
wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
|
|
|
|
|
|
|
|
lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
|
|
|
|
wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);
|
|
|
|
|
|
|
|
/* adjust the pollfd for this wsi */
|
|
|
|
|
|
|
|
if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
|
|
|
|
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
|
|
|
lwsl_info("%s: fail\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-30 11:43:36 +08:00
|
|
|
|
|
|
|
LWS_EXTERN int
|
|
|
|
lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
|
|
|
|
{
|
|
|
|
static const unsigned char e0f4[] = {
|
|
|
|
0xa0 | ((2 - 1) << 2) | 1, /* e0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e1 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e3 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e4 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e5 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e6 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e7 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e8 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* e9 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ea */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* eb */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ec */
|
|
|
|
0x80 | ((2 - 1) << 2) | 1, /* ed */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ee */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* ef */
|
|
|
|
0x90 | ((3 - 1) << 2) | 2, /* f0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f1 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 2, /* f3 */
|
|
|
|
0x80 | ((1 - 1) << 2) | 2, /* f4 */
|
|
|
|
|
|
|
|
0, /* s0 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 0, /* s2 */
|
|
|
|
0x80 | ((4 - 1) << 2) | 1, /* s3 */
|
|
|
|
};
|
|
|
|
unsigned char s = *state;
|
|
|
|
|
|
|
|
while (len--) {
|
|
|
|
unsigned char c = *buf++;
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
if (c >= 0x80) {
|
|
|
|
if (c < 0xc2 || c > 0xf4)
|
|
|
|
return 1;
|
|
|
|
if (c < 0xe0)
|
|
|
|
s = 0x80 | ((4 - 1) << 2);
|
|
|
|
else
|
|
|
|
s = e0f4[c - 0xe0];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c < (s & 0xf0) ||
|
|
|
|
c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
|
|
|
|
return 1;
|
|
|
|
s = e0f4[21 + (s & 3)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*state = s;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-11 11:34:01 +08:00
|
|
|
|
2016-01-14 13:39:02 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
2016-01-19 03:34:24 +08:00
|
|
|
lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
|
|
|
|
const char **path)
|
2016-01-14 13:39:02 +08:00
|
|
|
{
|
|
|
|
const char *end;
|
|
|
|
static const char *slash = "/";
|
|
|
|
|
|
|
|
/* cut up the location into address, port and path */
|
|
|
|
*prot = p;
|
|
|
|
while (*p && (*p != ':' || p[1] != '/' || p[2] != '/'))
|
|
|
|
p++;
|
|
|
|
if (!*p) {
|
|
|
|
end = p;
|
|
|
|
p = (char *)*prot;
|
|
|
|
*prot = end;
|
|
|
|
} else {
|
|
|
|
*p = '\0';
|
|
|
|
p += 3;
|
|
|
|
}
|
|
|
|
*ads = p;
|
|
|
|
if (!strcmp(*prot, "http") || !strcmp(*prot, "ws"))
|
|
|
|
*port = 80;
|
|
|
|
else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
|
|
|
|
*port = 443;
|
2016-01-19 04:32:14 +08:00
|
|
|
|
2017-02-21 07:27:07 +08:00
|
|
|
if (*p == '[')
|
|
|
|
{
|
|
|
|
++(*ads);
|
|
|
|
while (*p && *p != ']')
|
|
|
|
p++;
|
|
|
|
if (*p)
|
|
|
|
*p++ = '\0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (*p && *p != ':' && *p != '/')
|
|
|
|
p++;
|
|
|
|
}
|
2016-01-14 13:39:02 +08:00
|
|
|
if (*p == ':') {
|
|
|
|
*p++ = '\0';
|
|
|
|
*port = atoi(p);
|
|
|
|
while (*p && *p != '/')
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*path = slash;
|
|
|
|
if (*p) {
|
|
|
|
*p++ = '\0';
|
|
|
|
if (*p)
|
|
|
|
*path = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
#ifdef LWS_NO_EXTENSIONS
|
|
|
|
|
|
|
|
/* we need to provide dummy callbacks for internal exts
|
|
|
|
* so user code runs when faced with a lib compiled with
|
|
|
|
* extensions disabled.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_extension_callback_pm_deflate(struct lws_context *context,
|
|
|
|
const struct lws_extension *ext,
|
|
|
|
struct lws *wsi,
|
|
|
|
enum lws_extension_callback_reasons reason,
|
|
|
|
void *user, void *in, size_t len)
|
|
|
|
{
|
2016-01-20 17:35:18 +08:00
|
|
|
(void)context;
|
|
|
|
(void)ext;
|
|
|
|
(void)wsi;
|
|
|
|
(void)reason;
|
|
|
|
(void)user;
|
|
|
|
(void)in;
|
|
|
|
(void)len;
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-12 08:18:58 +08:00
|
|
|
LWS_EXTERN int
|
2016-11-16 08:59:47 +08:00
|
|
|
lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
2016-03-12 08:18:58 +08:00
|
|
|
const char *iface)
|
|
|
|
{
|
|
|
|
#if LWS_POSIX
|
2016-03-30 22:47:02 -07:00
|
|
|
#ifdef LWS_USE_UNIX_SOCK
|
|
|
|
struct sockaddr_un serv_unix;
|
|
|
|
#endif
|
2016-03-12 08:18:58 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
|
|
|
struct sockaddr_in6 serv_addr6;
|
|
|
|
#endif
|
|
|
|
struct sockaddr_in serv_addr4;
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2017-06-01 06:57:59 +08:00
|
|
|
socklen_t len = sizeof(struct sockaddr_storage);
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2016-03-12 08:18:58 +08:00
|
|
|
int n;
|
2017-06-01 06:57:59 +08:00
|
|
|
struct sockaddr_storage sin;
|
2016-03-12 08:18:58 +08:00
|
|
|
struct sockaddr *v;
|
|
|
|
|
2016-03-30 22:47:02 -07:00
|
|
|
#ifdef LWS_USE_UNIX_SOCK
|
2016-04-14 12:11:51 +08:00
|
|
|
if (LWS_UNIX_SOCK_ENABLED(vhost)) {
|
2016-03-30 22:47:02 -07:00
|
|
|
v = (struct sockaddr *)&serv_unix;
|
|
|
|
n = sizeof(struct sockaddr_un);
|
|
|
|
bzero((char *) &serv_unix, sizeof(serv_unix));
|
|
|
|
serv_unix.sun_family = AF_UNIX;
|
|
|
|
if (sizeof(serv_unix.sun_path) <= strlen(iface)) {
|
|
|
|
lwsl_err("\"%s\" too long for UNIX domain socket\n",
|
|
|
|
iface);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(serv_unix.sun_path, iface);
|
2016-04-10 13:19:16 +09:00
|
|
|
if (serv_unix.sun_path[0] == '@')
|
|
|
|
serv_unix.sun_path[0] = '\0';
|
|
|
|
|
2016-03-30 22:47:02 -07:00
|
|
|
} else
|
|
|
|
#endif
|
2017-02-18 17:26:40 +08:00
|
|
|
#if defined(LWS_USE_IPV6) && !defined(LWS_WITH_ESP32)
|
2016-06-03 21:19:40 +08:00
|
|
|
if (LWS_IPV6_ENABLED(vhost)) {
|
2016-03-12 08:18:58 +08:00
|
|
|
v = (struct sockaddr *)&serv_addr6;
|
|
|
|
n = sizeof(struct sockaddr_in6);
|
|
|
|
bzero((char *) &serv_addr6, sizeof(serv_addr6));
|
2016-06-04 08:37:50 +08:00
|
|
|
if (iface &&
|
|
|
|
interface_to_sa(vhost, iface,
|
|
|
|
(struct sockaddr_in *)v, n) < 0) {
|
|
|
|
lwsl_err("Unable to find interface %s\n", iface);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-12-15 09:58:20 +08:00
|
|
|
|
2017-06-01 06:57:59 +08:00
|
|
|
#ifndef WIN32
|
2016-12-15 09:58:20 +08:00
|
|
|
if (iface) {
|
|
|
|
struct ifaddrs *addrs, *addr;
|
|
|
|
char ip[NI_MAXHOST];
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
getifaddrs(&addrs);
|
|
|
|
for (addr = addrs; addr; addr = addr->ifa_next) {
|
|
|
|
if (!addr->ifa_addr ||
|
|
|
|
addr->ifa_addr->sa_family != AF_INET6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
getnameinfo(addr->ifa_addr,
|
|
|
|
sizeof(struct sockaddr_in6),
|
|
|
|
ip, sizeof(ip),
|
|
|
|
NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (ip[i])
|
|
|
|
if (ip[i++] == '%') {
|
|
|
|
ip[i - 1] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(ip, iface)) {
|
|
|
|
serv_addr6.sin6_scope_id =
|
|
|
|
if_nametoindex(addr->ifa_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
freeifaddrs(addrs);
|
|
|
|
}
|
2017-06-01 06:57:59 +08:00
|
|
|
#endif
|
2016-12-15 09:58:20 +08:00
|
|
|
|
2016-03-12 08:18:58 +08:00
|
|
|
serv_addr6.sin6_family = AF_INET6;
|
|
|
|
serv_addr6.sin6_port = htons(port);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
v = (struct sockaddr *)&serv_addr4;
|
|
|
|
n = sizeof(serv_addr4);
|
|
|
|
bzero((char *) &serv_addr4, sizeof(serv_addr4));
|
|
|
|
serv_addr4.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
serv_addr4.sin_family = AF_INET;
|
2017-02-18 17:26:40 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2016-03-12 08:18:58 +08:00
|
|
|
|
|
|
|
if (iface &&
|
2016-06-03 21:19:40 +08:00
|
|
|
interface_to_sa(vhost, iface,
|
2016-03-12 08:18:58 +08:00
|
|
|
(struct sockaddr_in *)v, n) < 0) {
|
|
|
|
lwsl_err("Unable to find interface %s\n", iface);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-02-18 17:26:40 +08:00
|
|
|
#endif
|
2016-03-12 08:18:58 +08:00
|
|
|
serv_addr4.sin_port = htons(port);
|
|
|
|
} /* ipv4 */
|
|
|
|
|
|
|
|
n = bind(sockfd, v, n);
|
2016-03-30 22:47:02 -07:00
|
|
|
#ifdef LWS_USE_UNIX_SOCK
|
2016-04-14 12:11:51 +08:00
|
|
|
if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) {
|
2016-03-30 22:47:02 -07:00
|
|
|
lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n",
|
|
|
|
sockfd, iface, n, LWS_ERRNO);
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
#endif
|
2016-03-12 08:18:58 +08:00
|
|
|
if (n < 0) {
|
2016-03-28 10:10:43 +08:00
|
|
|
lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n",
|
|
|
|
sockfd, port, n, LWS_ERRNO);
|
2016-03-12 08:18:58 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-01-17 07:01:02 +08:00
|
|
|
#ifndef LWS_PLAT_OPTEE
|
2016-03-12 08:18:58 +08:00
|
|
|
if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1)
|
|
|
|
lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
|
|
|
|
else
|
2017-01-17 07:01:02 +08:00
|
|
|
#endif
|
2017-06-01 06:57:59 +08:00
|
|
|
#if defined(LWS_USE_IPV6)
|
|
|
|
port = (sin.ss_family == AF_INET6) ?
|
|
|
|
ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) :
|
|
|
|
ntohs(((struct sockaddr_in *) &sin)->sin_port);
|
|
|
|
#else
|
|
|
|
port = ntohs(((struct sockaddr_in *) &sin)->sin_port);
|
|
|
|
#endif
|
2016-03-12 08:18:58 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return port;
|
|
|
|
}
|
|
|
|
|
2016-07-15 13:41:38 +08:00
|
|
|
LWS_EXTERN void
|
|
|
|
lws_restart_ws_ping_pong_timer(struct lws *wsi)
|
|
|
|
{
|
|
|
|
if (!wsi->context->ws_ping_pong_interval)
|
|
|
|
return;
|
|
|
|
if (wsi->state != LWSS_ESTABLISHED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() +
|
|
|
|
wsi->context->ws_ping_pong_interval;
|
|
|
|
}
|
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
static const char *hex = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_sql_purify(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
|
|
|
while (*p && len-- > 2) {
|
|
|
|
if (*p == '\'') {
|
2016-05-19 15:28:31 +08:00
|
|
|
*q++ = '\'';
|
2016-06-08 10:07:02 +08:00
|
|
|
*q++ = '\'';
|
|
|
|
len --;
|
|
|
|
p++;
|
|
|
|
} else
|
|
|
|
*q++ = *p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2016-06-17 10:05:23 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_json_purify(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (!p) {
|
|
|
|
escaped[0] = '\0';
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2016-06-17 10:05:23 +08:00
|
|
|
while (*p && len-- > 6) {
|
|
|
|
if (*p == '\"' || *p == '\\' || *p < 0x20) {
|
|
|
|
*q++ = '\\';
|
|
|
|
*q++ = 'u';
|
|
|
|
*q++ = '0';
|
|
|
|
*q++ = '0';
|
|
|
|
*q++ = hex[((*p) >> 4) & 15];
|
|
|
|
*q++ = hex[(*p) & 15];
|
|
|
|
len -= 5;
|
|
|
|
p++;
|
|
|
|
} else
|
|
|
|
*q++ = *p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_urlencode(char *escaped, const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *p = string;
|
|
|
|
char *q = escaped;
|
|
|
|
|
|
|
|
while (*p && len-- > 3) {
|
|
|
|
if (*p == ' ') {
|
|
|
|
*q++ = '+';
|
|
|
|
p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((*p >= '0' && *p <= '9') ||
|
|
|
|
(*p >= 'A' && *p <= 'Z') ||
|
|
|
|
(*p >= 'a' && *p <= 'z')) {
|
|
|
|
*q++ = *p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*q++ = '%';
|
|
|
|
*q++ = hex[(*p >> 4) & 0xf];
|
|
|
|
*q++ = hex[*p & 0xf];
|
|
|
|
|
|
|
|
len -= 2;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_urldecode(char *string, const char *escaped, int len)
|
|
|
|
{
|
|
|
|
int state = 0, n;
|
|
|
|
char sum = 0;
|
|
|
|
|
|
|
|
while (*escaped && len) {
|
|
|
|
switch (state) {
|
|
|
|
case 0:
|
|
|
|
if (*escaped == '%') {
|
|
|
|
state++;
|
|
|
|
escaped++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*escaped == '+') {
|
|
|
|
escaped++;
|
|
|
|
*string++ = ' ';
|
|
|
|
len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*string++ = *escaped++;
|
|
|
|
len--;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
n = char_to_hex(*escaped);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
escaped++;
|
|
|
|
sum = n << 4;
|
|
|
|
state++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
n = char_to_hex(*escaped);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
escaped++;
|
|
|
|
*string++ = sum | n;
|
|
|
|
len--;
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
*string = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_finalize_startup(struct lws_context *context)
|
|
|
|
{
|
|
|
|
struct lws_context_creation_info info;
|
|
|
|
|
|
|
|
info.uid = context->uid;
|
|
|
|
info.gid = context->gid;
|
|
|
|
|
|
|
|
if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
|
|
|
|
lws_plat_drop_app_privileges(&info);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
int
|
|
|
|
lws_snprintf(char *str, size_t size, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
n = vsnprintf(str, size, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (n >= size)
|
|
|
|
return size;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_is_cgi(struct lws *wsi) {
|
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
return !!wsi->cgi;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
static int
|
|
|
|
urlencode(const char *in, int inlen, char *out, int outlen)
|
|
|
|
{
|
|
|
|
char *start = out, *end = out + outlen;
|
|
|
|
|
|
|
|
while (inlen-- && out < end - 4) {
|
|
|
|
if ((*in >= 'A' && *in <= 'Z') ||
|
|
|
|
(*in >= 'a' && *in <= 'z') ||
|
|
|
|
(*in >= '0' && *in <= '9') ||
|
|
|
|
*in == '-' ||
|
|
|
|
*in == '_' ||
|
|
|
|
*in == '.' ||
|
|
|
|
*in == '~') {
|
|
|
|
*out++ = *in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*in == ' ') {
|
|
|
|
*out++ = '+';
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*out++ = '%';
|
|
|
|
*out++ = hex[(*in) >> 4];
|
|
|
|
*out++ = hex[(*in++) & 15];
|
|
|
|
}
|
|
|
|
*out = '\0';
|
|
|
|
|
|
|
|
if (out >= end - 4)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return out - start;
|
|
|
|
}
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
static struct lws *
|
|
|
|
lws_create_basic_wsi(struct lws_context *context, int tsi)
|
|
|
|
{
|
|
|
|
struct lws *new_wsi;
|
|
|
|
|
|
|
|
if ((unsigned int)context->pt[tsi].fds_count ==
|
|
|
|
context->fd_limit_per_thread - 1) {
|
|
|
|
lwsl_err("no space for new conn\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_wsi = lws_zalloc(sizeof(struct lws));
|
|
|
|
if (new_wsi == NULL) {
|
|
|
|
lwsl_err("Out of memory for new connection\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_wsi->tsi = tsi;
|
|
|
|
new_wsi->context = context;
|
|
|
|
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
|
|
|
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
|
|
|
|
2016-10-02 02:21:03 +03:00
|
|
|
/* initialize the instance struct */
|
2016-02-21 21:25:48 +08:00
|
|
|
|
|
|
|
new_wsi->state = LWSS_CGI;
|
|
|
|
new_wsi->mode = LWSCM_CGI;
|
|
|
|
new_wsi->hdr_parsing_completed = 0;
|
|
|
|
new_wsi->position_in_fds_table = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* these can only be set once the protocol is known
|
|
|
|
* we set an unestablished connection's protocol pointer
|
2016-03-28 10:10:43 +08:00
|
|
|
* to the start of the defauly vhost supported list, so it can look
|
2016-02-21 21:25:48 +08:00
|
|
|
* for matching ones during the handshake
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->protocol = context->vhost_list->protocols;
|
2016-02-21 21:25:48 +08:00
|
|
|
new_wsi->user_space = NULL;
|
|
|
|
new_wsi->ietf_spec_revision = 0;
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
|
2016-02-21 21:25:48 +08:00
|
|
|
context->count_wsi_allocated++;
|
|
|
|
|
|
|
|
return new_wsi;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
2016-04-13 11:49:07 +08:00
|
|
|
lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len,
|
2016-05-02 06:01:59 +08:00
|
|
|
int timeout_secs, const struct lws_protocol_vhost_options *mp_cgienv)
|
2016-02-21 21:25:48 +08:00
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
2016-03-20 11:55:25 +08:00
|
|
|
char *env_array[30], cgi_path[400], e[1024], *p = e,
|
2016-04-13 11:49:07 +08:00
|
|
|
*end = p + sizeof(e) - 1, tok[256], *t;
|
2016-02-21 21:25:48 +08:00
|
|
|
struct lws_cgi *cgi;
|
2016-08-20 06:34:46 +08:00
|
|
|
int n, m, i, uritok = -1;
|
2016-02-21 21:25:48 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* give the master wsi a cgi struct
|
|
|
|
*/
|
|
|
|
|
|
|
|
wsi->cgi = lws_zalloc(sizeof(*wsi->cgi));
|
|
|
|
if (!wsi->cgi) {
|
|
|
|
lwsl_err("%s: OOM\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cgi = wsi->cgi;
|
|
|
|
cgi->wsi = wsi; /* set cgi's owning wsi */
|
|
|
|
|
|
|
|
/* create pipes for [stdin|stdout] and [stderr] */
|
|
|
|
|
|
|
|
for (n = 0; n < 3; n++)
|
|
|
|
if (pipe(cgi->pipe_fds[n]) == -1)
|
|
|
|
goto bail1;
|
|
|
|
|
|
|
|
/* create cgi wsis for each stdin/out/err fd */
|
|
|
|
|
|
|
|
for (n = 0; n < 3; n++) {
|
|
|
|
cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi);
|
|
|
|
if (!cgi->stdwsi[n])
|
|
|
|
goto bail2;
|
|
|
|
cgi->stdwsi[n]->cgi_channel = n;
|
2016-04-13 11:49:07 +08:00
|
|
|
cgi->stdwsi[n]->vhost = wsi->vhost;
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
// lwsl_err("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, wsi, n,
|
|
|
|
// cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]);
|
2016-04-13 11:42:53 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
/* read side is 0, stdin we want the write side, others read */
|
2017-02-27 12:55:56 +08:00
|
|
|
cgi->stdwsi[n]->desc.sockfd = cgi->pipe_fds[n][!!(n == 0)];
|
2016-04-23 07:59:38 +08:00
|
|
|
if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) {
|
|
|
|
lwsl_err("%s: setting NONBLOCK failed\n", __func__);
|
|
|
|
goto bail2;
|
|
|
|
}
|
2016-02-21 21:25:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (n = 0; n < 3; n++) {
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->desc);
|
2016-02-21 21:25:48 +08:00
|
|
|
if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
|
|
|
|
goto bail3;
|
2016-03-02 09:17:22 +08:00
|
|
|
cgi->stdwsi[n]->parent = wsi;
|
|
|
|
cgi->stdwsi[n]->sibling_list = wsi->child_list;
|
|
|
|
wsi->child_list = cgi->stdwsi[n];
|
2016-02-21 21:25:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT);
|
|
|
|
lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN);
|
|
|
|
lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN);
|
|
|
|
|
|
|
|
lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
|
2017-02-27 12:55:56 +08:00
|
|
|
cgi->stdwsi[LWS_STDIN]->desc.sockfd,
|
|
|
|
cgi->stdwsi[LWS_STDOUT]->desc.sockfd,
|
|
|
|
cgi->stdwsi[LWS_STDERR]->desc.sockfd);
|
2016-02-21 21:25:48 +08:00
|
|
|
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
|
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
/* the cgi stdout is always sending us http1.x header data first */
|
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
2016-03-02 09:17:22 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
/* add us to the pt list of active cgis */
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->cgi);
|
2016-02-21 21:25:48 +08:00
|
|
|
cgi->cgi_list = pt->cgi_list;
|
|
|
|
pt->cgi_list = cgi;
|
|
|
|
|
|
|
|
/* prepare his CGI env */
|
|
|
|
|
|
|
|
n = 0;
|
2016-03-20 11:55:25 +08:00
|
|
|
|
|
|
|
if (lws_is_ssl(wsi))
|
|
|
|
env_array[n++] = "HTTPS=ON";
|
2016-02-21 21:25:48 +08:00
|
|
|
if (wsi->u.hdr.ah) {
|
2016-08-20 06:34:46 +08:00
|
|
|
static const unsigned char meths[] = {
|
|
|
|
WSI_TOKEN_GET_URI,
|
|
|
|
WSI_TOKEN_POST_URI,
|
|
|
|
WSI_TOKEN_OPTIONS_URI,
|
|
|
|
WSI_TOKEN_PUT_URI,
|
|
|
|
WSI_TOKEN_PATCH_URI,
|
|
|
|
WSI_TOKEN_DELETE_URI,
|
|
|
|
};
|
|
|
|
static const char * const meth_names[] = {
|
|
|
|
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
|
|
|
|
};
|
|
|
|
|
|
|
|
for (m = 0; m < ARRAY_SIZE(meths); m++)
|
|
|
|
if (lws_hdr_total_length(wsi, meths[m]) >=
|
|
|
|
script_uri_path_len) {
|
|
|
|
uritok = meths[m];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uritok < 0)
|
|
|
|
goto bail3;
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, uritok));
|
2016-02-21 21:25:48 +08:00
|
|
|
cgi_path[sizeof(cgi_path) - 1] = '\0';
|
|
|
|
env_array[n++] = cgi_path;
|
2016-08-20 06:34:46 +08:00
|
|
|
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s",
|
2016-08-20 06:34:46 +08:00
|
|
|
meth_names[m]);
|
|
|
|
p++;
|
2016-03-20 11:55:25 +08:00
|
|
|
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "QUERY_STRING=");
|
2016-03-20 11:55:25 +08:00
|
|
|
/* dump the individual URI Arg parameters */
|
|
|
|
m = 0;
|
|
|
|
while (1) {
|
|
|
|
i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
|
|
|
|
WSI_TOKEN_HTTP_URI_ARGS, m);
|
|
|
|
if (i < 0)
|
|
|
|
break;
|
2016-04-13 11:49:07 +08:00
|
|
|
t = tok;
|
|
|
|
while (*t && *t != '=' && p < end - 4)
|
|
|
|
*p++ = *t++;
|
|
|
|
if (*t == '=')
|
|
|
|
*p++ = *t++;
|
2016-06-08 10:07:02 +08:00
|
|
|
i = urlencode(t, i- (t - tok), p, end - p);
|
2016-04-13 11:49:07 +08:00
|
|
|
if (i > 0) {
|
|
|
|
p += i;
|
|
|
|
*p++ = '&';
|
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
m++;
|
|
|
|
}
|
|
|
|
if (m)
|
|
|
|
p--;
|
|
|
|
*p++ = '\0';
|
|
|
|
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "PATH_INFO=%s",
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, uritok) +
|
2016-03-20 11:55:25 +08:00
|
|
|
script_uri_path_len);
|
|
|
|
p++;
|
2016-02-21 21:25:48 +08:00
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
|
2016-03-20 11:55:25 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
|
2016-03-20 11:55:25 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
|
|
|
p++;
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "HTTP_COOKIE=%s",
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE));
|
|
|
|
p++;
|
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "USER_AGENT=%s",
|
2016-03-20 11:55:25 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
|
|
|
|
p++;
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
if (uritok == WSI_TOKEN_POST_URI) {
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
|
2016-04-13 11:42:53 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
|
2016-04-13 11:49:07 +08:00
|
|
|
|
|
|
|
while (mp_cgienv) {
|
|
|
|
env_array[n++] = p;
|
2016-09-15 02:22:57 +08:00
|
|
|
p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
|
2016-04-13 11:49:07 +08:00
|
|
|
mp_cgienv->value);
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug(" Applying mount-specific cgi env '%s'\n",
|
|
|
|
env_array[n - 1]);
|
2016-04-13 11:49:07 +08:00
|
|
|
p++;
|
|
|
|
mp_cgienv = mp_cgienv->next;
|
|
|
|
}
|
2016-03-20 11:55:25 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
|
2016-03-20 11:55:25 +08:00
|
|
|
env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";
|
2016-02-21 21:25:48 +08:00
|
|
|
env_array[n] = NULL;
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
#if 0
|
2016-03-20 11:55:25 +08:00
|
|
|
for (m = 0; m < n; m++)
|
|
|
|
lwsl_err(" %s\n", env_array[m]);
|
|
|
|
#endif
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
/*
|
|
|
|
* Actually having made the env, as a cgi we don't need the ah
|
|
|
|
* any more
|
|
|
|
*/
|
|
|
|
if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen)
|
|
|
|
lws_header_table_detach(wsi, 0);
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
/* we are ready with the redirection pipes... run the thing */
|
2016-03-20 11:55:25 +08:00
|
|
|
#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
|
2016-02-21 21:25:48 +08:00
|
|
|
cgi->pid = fork();
|
2016-03-20 11:55:25 +08:00
|
|
|
#else
|
|
|
|
cgi->pid = vfork();
|
2016-02-21 21:25:48 +08:00
|
|
|
#endif
|
|
|
|
if (cgi->pid < 0) {
|
|
|
|
lwsl_err("fork failed, errno %d", errno);
|
|
|
|
goto bail3;
|
|
|
|
}
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
#if defined(__linux__)
|
|
|
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
|
|
#endif
|
2016-05-10 10:16:52 +08:00
|
|
|
setpgrp(); /* stops on-daemonized main processess getting SIGINT from TTY */
|
2016-04-20 06:10:56 +08:00
|
|
|
|
|
|
|
if (cgi->pid) {
|
2016-02-21 21:25:48 +08:00
|
|
|
/* we are the parent process */
|
2016-04-20 06:10:56 +08:00
|
|
|
wsi->context->count_cgi_spawned++;
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: cgi %p spawned PID %d\n", __func__, cgi, cgi->pid);
|
2016-02-21 21:25:48 +08:00
|
|
|
return 0;
|
2016-04-20 06:10:56 +08:00
|
|
|
}
|
2016-02-21 21:25:48 +08:00
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
/* somewhere we can at least read things and enter it */
|
2016-04-13 11:42:53 +08:00
|
|
|
if (chdir("/tmp"))
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_notice("%s: Failed to chdir\n", __func__);
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
/* We are the forked process, redirect and kill inherited things.
|
|
|
|
*
|
|
|
|
* Because of vfork(), we cannot do anything that changes pages in
|
|
|
|
* the parent environment. Stuff that changes kernel state for the
|
|
|
|
* process is OK. Stuff that happens after the execvpe() is OK.
|
|
|
|
*/
|
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
for (n = 0; n < 3; n++)
|
2016-02-21 21:25:48 +08:00
|
|
|
if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
|
|
|
|
lwsl_err("%s: stdin dup2 failed\n", __func__);
|
|
|
|
goto bail3;
|
|
|
|
}
|
|
|
|
|
2016-03-20 11:55:25 +08:00
|
|
|
#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
|
|
|
|
for (m = 0; m < n; m++) {
|
|
|
|
p = strchr(env_array[m], '=');
|
|
|
|
*p++ = '\0';
|
|
|
|
setenv(env_array[m], p, 1);
|
|
|
|
}
|
2016-04-13 11:49:07 +08:00
|
|
|
execvp(exec_array[0], (char * const *)&exec_array[0]);
|
2016-03-20 11:55:25 +08:00
|
|
|
#else
|
2016-04-13 11:49:07 +08:00
|
|
|
execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]);
|
2016-03-20 11:55:25 +08:00
|
|
|
#endif
|
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
exit(1);
|
|
|
|
|
|
|
|
bail3:
|
|
|
|
/* drop us from the pt cgi list */
|
|
|
|
pt->cgi_list = cgi->cgi_list;
|
|
|
|
|
|
|
|
while (--n >= 0)
|
|
|
|
remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]);
|
|
|
|
bail2:
|
|
|
|
for (n = 0; n < 3; n++)
|
|
|
|
if (wsi->cgi->stdwsi[n])
|
|
|
|
lws_free_wsi(cgi->stdwsi[n]);
|
|
|
|
|
|
|
|
bail1:
|
|
|
|
for (n = 0; n < 3; n++) {
|
|
|
|
if (cgi->pipe_fds[n][0])
|
|
|
|
close(cgi->pipe_fds[n][0]);
|
|
|
|
if (cgi->pipe_fds[n][1])
|
|
|
|
close(cgi->pipe_fds[n][1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_free_set_NULL(wsi->cgi);
|
|
|
|
|
|
|
|
lwsl_err("%s: failed\n", __func__);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2016-06-20 17:05:31 +08:00
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_cgi_write_split_stdout_headers(struct lws *wsi)
|
|
|
|
{
|
2016-04-13 11:42:53 +08:00
|
|
|
int n, m, match = 0, lp = 0;
|
|
|
|
static const char * const content_length = "content-length: ";
|
2016-03-09 07:41:59 +08:00
|
|
|
char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
|
2016-04-13 11:42:53 +08:00
|
|
|
*end = &buf[sizeof(buf) - 1 - LWS_PRE], c, l[12];
|
|
|
|
|
|
|
|
if (!wsi->cgi)
|
|
|
|
return -1;
|
2016-03-09 07:41:59 +08:00
|
|
|
|
|
|
|
while (wsi->hdr_state != LHCS_PAYLOAD) {
|
|
|
|
/* we have to separate header / finalize and
|
|
|
|
* payload chunks, since they need to be
|
|
|
|
* handled separately
|
|
|
|
*/
|
|
|
|
n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1);
|
|
|
|
if (n < 0) {
|
2016-04-13 11:42:53 +08:00
|
|
|
if (errno != EAGAIN) {
|
|
|
|
lwsl_debug("%s: read says %d\n", __func__, n);
|
2016-03-09 07:41:59 +08:00
|
|
|
return -1;
|
2016-04-13 11:42:53 +08:00
|
|
|
}
|
2016-03-09 07:41:59 +08:00
|
|
|
else
|
|
|
|
n = 0;
|
|
|
|
}
|
|
|
|
if (n) {
|
2016-04-13 11:42:53 +08:00
|
|
|
lwsl_debug("-- 0x%02X %c\n", (unsigned char)c, c);
|
2016-03-09 07:41:59 +08:00
|
|
|
switch (wsi->hdr_state) {
|
|
|
|
case LCHS_HEADER:
|
2016-04-13 11:42:53 +08:00
|
|
|
if (!content_length[match] &&
|
|
|
|
(c >= '0' && c <= '9') &&
|
|
|
|
lp < sizeof(l) - 1) {
|
|
|
|
l[lp++] = c;
|
|
|
|
l[lp] = '\0';
|
|
|
|
wsi->cgi->content_length = atol(l);
|
|
|
|
}
|
|
|
|
if (tolower(c) == content_length[match])
|
|
|
|
match++;
|
|
|
|
else
|
|
|
|
match = 0;
|
|
|
|
|
|
|
|
/* some cgi only send us \x0a for EOL */
|
|
|
|
if (c == '\x0a') {
|
|
|
|
wsi->hdr_state = LCHS_SINGLE_0A;
|
|
|
|
*p++ = '\x0d';
|
|
|
|
}
|
2016-03-09 07:41:59 +08:00
|
|
|
*p++ = c;
|
|
|
|
if (c == '\x0d') {
|
|
|
|
wsi->hdr_state = LCHS_LF1;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
break;
|
|
|
|
case LCHS_LF1:
|
|
|
|
*p++ = c;
|
|
|
|
if (c == '\x0a') {
|
|
|
|
wsi->hdr_state = LCHS_CR2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* we got \r[^\n]... it's unreasonable */
|
2016-04-13 11:42:53 +08:00
|
|
|
lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, (unsigned char)c);
|
2016-03-09 07:41:59 +08:00
|
|
|
return -1;
|
2016-04-13 11:42:53 +08:00
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
case LCHS_CR2:
|
|
|
|
if (c == '\x0d') {
|
|
|
|
/* drop the \x0d */
|
|
|
|
wsi->hdr_state = LCHS_LF2;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
|
|
|
match = 0;
|
2016-03-09 07:41:59 +08:00
|
|
|
*p++ = c;
|
|
|
|
break;
|
|
|
|
case LCHS_LF2:
|
2016-04-13 11:42:53 +08:00
|
|
|
case LCHS_SINGLE_0A:
|
|
|
|
m = wsi->hdr_state;
|
2016-03-09 07:41:59 +08:00
|
|
|
if (c == '\x0a') {
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("Content-Length: %ld\n", wsi->cgi->content_length);
|
2016-03-09 07:41:59 +08:00
|
|
|
wsi->hdr_state = LHCS_PAYLOAD;
|
|
|
|
/* drop the \0xa ... finalize will add it if needed */
|
2016-04-23 07:53:46 +08:00
|
|
|
if (lws_finalize_http_header(wsi,
|
2016-03-09 07:41:59 +08:00
|
|
|
(unsigned char **)&p,
|
2016-04-23 07:53:46 +08:00
|
|
|
(unsigned char *)end))
|
|
|
|
return -1;
|
2016-03-09 07:41:59 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
if (m == LCHS_LF2)
|
|
|
|
/* we got \r\n\r[^\n]... it's unreasonable */
|
|
|
|
return -1;
|
|
|
|
/* we got \x0anext header, it's reasonable */
|
|
|
|
*p++ = c;
|
|
|
|
wsi->hdr_state = LCHS_HEADER;
|
|
|
|
break;
|
2016-03-09 07:41:59 +08:00
|
|
|
case LHCS_PAYLOAD:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ran out of input, ended the headers, or filled up the headers buf */
|
|
|
|
if (!n || wsi->hdr_state == LHCS_PAYLOAD || (p + 4) == end) {
|
2016-03-20 11:59:53 +08:00
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
m = lws_write(wsi, (unsigned char *)start,
|
|
|
|
p - start, LWS_WRITE_HTTP_HEADERS);
|
2016-04-13 11:42:53 +08:00
|
|
|
if (m < 0) {
|
|
|
|
lwsl_debug("%s: write says %d\n", __func__, m);
|
2016-03-09 07:41:59 +08:00
|
|
|
return -1;
|
2016-04-13 11:42:53 +08:00
|
|
|
}
|
2016-03-09 07:41:59 +08:00
|
|
|
/* writeability becomes uncertain now we wrote
|
|
|
|
* something, we must return to the event loop
|
|
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
|
2016-03-09 07:41:59 +08:00
|
|
|
n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]),
|
|
|
|
start, sizeof(buf) - LWS_PRE);
|
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
if (n < 0 && errno != EAGAIN) {
|
|
|
|
lwsl_debug("%s: stdout read says %d\n", __func__, n);
|
2016-03-09 07:41:59 +08:00
|
|
|
return -1;
|
2016-04-13 11:42:53 +08:00
|
|
|
}
|
2016-03-09 07:41:59 +08:00
|
|
|
if (n > 0) {
|
2016-04-13 11:42:53 +08:00
|
|
|
m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP);
|
2016-03-09 07:41:59 +08:00
|
|
|
//lwsl_notice("write %d\n", m);
|
2016-04-13 11:42:53 +08:00
|
|
|
if (m < 0) {
|
|
|
|
lwsl_debug("%s: stdout write says %d\n", __func__, m);
|
2016-03-09 07:41:59 +08:00
|
|
|
return -1;
|
2016-04-13 11:42:53 +08:00
|
|
|
}
|
|
|
|
wsi->cgi->content_length_seen += m;
|
2016-03-09 07:41:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-02-21 21:25:48 +08:00
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_cgi_kill(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_cgi_args args;
|
2016-04-20 06:10:56 +08:00
|
|
|
int status, n;
|
2016-02-21 21:25:48 +08:00
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
lwsl_debug("%s: %p\n", __func__, wsi);
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
if (!wsi->cgi)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (wsi->cgi->pid > 0) {
|
2016-04-20 06:10:56 +08:00
|
|
|
n = waitpid(wsi->cgi->pid, &status, WNOHANG);
|
|
|
|
if (n > 0) {
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: PID %d reaped\n", __func__,
|
2016-04-20 06:10:56 +08:00
|
|
|
wsi->cgi->pid);
|
|
|
|
goto handled;
|
|
|
|
}
|
|
|
|
/* kill the process group */
|
|
|
|
n = kill(-wsi->cgi->pid, SIGTERM);
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n", __func__,
|
2016-04-20 06:10:56 +08:00
|
|
|
wsi->cgi->pid, n, errno);
|
2016-02-21 21:25:48 +08:00
|
|
|
if (n < 0) {
|
2016-04-20 06:10:56 +08:00
|
|
|
/*
|
|
|
|
* hum seen errno=3 when process is listed in ps,
|
|
|
|
* it seems we don't always retain process grouping
|
|
|
|
*
|
|
|
|
* Direct these fallback attempt to the exact child
|
|
|
|
*/
|
|
|
|
n = kill(wsi->cgi->pid, SIGTERM);
|
|
|
|
if (n < 0) {
|
|
|
|
n = kill(wsi->cgi->pid, SIGPIPE);
|
|
|
|
if (n < 0) {
|
|
|
|
n = kill(wsi->cgi->pid, SIGKILL);
|
|
|
|
if (n < 0)
|
|
|
|
lwsl_err("%s: SIGKILL PID %d failed errno %d (maybe zombie)\n",
|
|
|
|
__func__, wsi->cgi->pid, errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* He could be unkillable because he's a zombie */
|
|
|
|
n = 1;
|
|
|
|
while (n > 0) {
|
|
|
|
n = waitpid(-wsi->cgi->pid, &status, WNOHANG);
|
|
|
|
if (n > 0)
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: reaped PID %d\n", __func__, n);
|
2016-04-20 06:10:56 +08:00
|
|
|
if (n <= 0) {
|
|
|
|
n = waitpid(wsi->cgi->pid, &status, WNOHANG);
|
|
|
|
if (n > 0)
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: reaped PID %d\n", __func__, n);
|
2016-04-20 06:10:56 +08:00
|
|
|
}
|
2016-02-21 21:25:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
handled:
|
2016-02-21 21:25:48 +08:00
|
|
|
args.stdwsi = &wsi->cgi->stdwsi[0];
|
|
|
|
|
|
|
|
if (wsi->cgi->pid != -1 && user_callback_handle_rxflow(
|
|
|
|
wsi->protocol->callback,
|
|
|
|
wsi, LWS_CALLBACK_CGI_TERMINATED,
|
|
|
|
wsi->user_space,
|
|
|
|
(void *)&args, 0)) {
|
|
|
|
wsi->cgi->pid = -1;
|
2016-04-20 06:10:56 +08:00
|
|
|
if (!wsi->cgi->being_closed)
|
|
|
|
lws_close_free_wsi(wsi, 0);
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-02-21 21:25:48 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_EXTERN int
|
|
|
|
lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
|
|
|
|
{
|
2016-04-20 06:10:56 +08:00
|
|
|
struct lws_cgi **pcgi, *cgi = NULL;
|
|
|
|
int status, n = 1;
|
|
|
|
|
|
|
|
while (n > 0) {
|
|
|
|
/* find finished guys but don't reap yet */
|
2017-01-03 08:18:37 +08:00
|
|
|
n = waitpid(-1, &status, WNOHANG);
|
2016-04-20 06:10:56 +08:00
|
|
|
if (n <= 0)
|
|
|
|
continue;
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: observed PID %d terminated\n", __func__, n);
|
2016-04-20 06:10:56 +08:00
|
|
|
|
|
|
|
pcgi = &pt->cgi_list;
|
|
|
|
|
|
|
|
/* check all the subprocesses on the cgi list */
|
|
|
|
while (*pcgi) {
|
|
|
|
/* get the next one first as list may change */
|
|
|
|
cgi = *pcgi;
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
|
|
|
|
if (cgi->pid <= 0)
|
|
|
|
continue;
|
2016-02-21 21:25:48 +08:00
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
/* wait for stdout to be drained */
|
|
|
|
if (cgi->content_length > cgi->content_length_seen)
|
|
|
|
continue;
|
|
|
|
|
2016-04-23 07:14:03 +08:00
|
|
|
if (cgi->content_length) {
|
|
|
|
lwsl_debug("%s: wsi %p: expected content length seen: %ld\n",
|
2016-04-20 06:10:56 +08:00
|
|
|
__func__, cgi->wsi, cgi->content_length_seen);
|
2016-04-23 07:14:03 +08:00
|
|
|
}
|
2016-04-20 06:10:56 +08:00
|
|
|
|
|
|
|
/* reap it */
|
|
|
|
waitpid(n, &status, WNOHANG);
|
|
|
|
/*
|
|
|
|
* he's already terminated so no need for kill()
|
|
|
|
* but we should do the terminated cgi callback
|
|
|
|
* and close him if he's not already closing
|
|
|
|
*/
|
|
|
|
if (n == cgi->pid) {
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: found PID %d on cgi list\n",
|
2016-04-20 06:10:56 +08:00
|
|
|
__func__, n);
|
2017-01-03 08:18:37 +08:00
|
|
|
|
|
|
|
if (!cgi->content_length) {
|
|
|
|
/*
|
|
|
|
* well, if he sends chunked... give him 5s after the
|
|
|
|
* cgi terminated to send buffered
|
|
|
|
*/
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-20 06:10:56 +08:00
|
|
|
/* defeat kill() */
|
|
|
|
cgi->pid = 0;
|
|
|
|
lws_cgi_kill(cgi->wsi);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cgi = NULL;
|
|
|
|
}
|
|
|
|
/* if not found on the cgi list, as he's one of ours, reap */
|
|
|
|
if (!cgi) {
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: reading PID %d although no cgi match\n",
|
2016-04-20 06:10:56 +08:00
|
|
|
__func__, n);
|
|
|
|
waitpid(n, &status, WNOHANG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disable this to confirm timeout cgi cleanup flow */
|
|
|
|
#if 1
|
|
|
|
pcgi = &pt->cgi_list;
|
|
|
|
|
|
|
|
/* check all the subprocesses on the cgi list */
|
2016-02-21 21:25:48 +08:00
|
|
|
while (*pcgi) {
|
2016-04-20 06:10:56 +08:00
|
|
|
/* get the next one first as list may change */
|
2016-02-21 21:25:48 +08:00
|
|
|
cgi = *pcgi;
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
if (cgi->pid <= 0)
|
|
|
|
continue;
|
|
|
|
|
2017-01-03 08:18:37 +08:00
|
|
|
/* we deferred killing him after reaping his PID */
|
|
|
|
if (cgi->chunked_grace) {
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
if (cgi->chunked_grace < 5)
|
|
|
|
continue;
|
|
|
|
goto finish_him;
|
|
|
|
}
|
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
/* wait for stdout to be drained */
|
|
|
|
if (cgi->content_length > cgi->content_length_seen)
|
|
|
|
continue;
|
|
|
|
|
2017-01-03 08:18:37 +08:00
|
|
|
if (cgi->content_length)
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: wsi %p: expected content length seen: %ld\n",
|
2016-04-20 06:10:56 +08:00
|
|
|
__func__, cgi->wsi, cgi->content_length_seen);
|
|
|
|
|
|
|
|
/* reap it */
|
2016-04-13 11:42:53 +08:00
|
|
|
if (waitpid(cgi->pid, &status, WNOHANG) > 0) {
|
2016-04-20 06:10:56 +08:00
|
|
|
|
2017-01-03 08:18:37 +08:00
|
|
|
if (!cgi->content_length) {
|
|
|
|
/*
|
|
|
|
* well, if he sends chunked... give him 5s after the
|
|
|
|
* cgi terminated to send buffered
|
|
|
|
*/
|
|
|
|
cgi->chunked_grace++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
finish_him:
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: found PID %d on cgi list\n",
|
2016-04-20 06:10:56 +08:00
|
|
|
__func__, cgi->pid);
|
|
|
|
/* defeat kill() */
|
2016-02-21 21:25:48 +08:00
|
|
|
cgi->pid = 0;
|
|
|
|
lws_cgi_kill(cgi->wsi);
|
2016-04-20 06:10:56 +08:00
|
|
|
|
|
|
|
break;
|
2016-02-21 21:25:48 +08:00
|
|
|
}
|
|
|
|
}
|
2016-04-20 06:10:56 +08:00
|
|
|
#endif
|
2016-02-21 21:25:48 +08:00
|
|
|
|
2016-05-04 12:23:27 +08:00
|
|
|
/* general anti zombie defence */
|
2017-01-03 08:18:37 +08:00
|
|
|
// n = waitpid(-1, &status, WNOHANG);
|
2016-07-23 14:18:25 +08:00
|
|
|
//if (n > 0)
|
|
|
|
// lwsl_notice("%s: anti-zombie wait says %d\n", __func__, n);
|
2016-05-04 12:23:27 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-08 09:45:49 +08:00
|
|
|
|
|
|
|
#ifdef LWS_NO_EXTENSIONS
|
|
|
|
LWS_EXTERN int
|
|
|
|
lws_set_extension_option(struct lws *wsi, const char *ext_name,
|
|
|
|
const char *opt_name, const char *opt_val)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-14 15:07:44 +08:00
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
|
|
|
int
|
|
|
|
lws_access_log(struct lws *wsi)
|
|
|
|
{
|
|
|
|
char *p = wsi->access_log.user_agent, ass[512];
|
|
|
|
int l;
|
|
|
|
|
|
|
|
if (!wsi->access_log_pending)
|
|
|
|
return 0;
|
|
|
|
|
2016-05-13 10:57:23 +08:00
|
|
|
if (!wsi->access_log.header_log)
|
|
|
|
return 0;
|
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
if (!p)
|
|
|
|
p = "";
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
l = lws_snprintf(ass, sizeof(ass) - 1, "%s %d %lu %s\n",
|
2016-04-15 12:00:23 +08:00
|
|
|
wsi->access_log.header_log,
|
|
|
|
wsi->access_log.response, wsi->access_log.sent, p);
|
|
|
|
|
2016-05-15 08:29:37 +08:00
|
|
|
if (wsi->vhost->log_fd != (int)LWS_INVALID_FILE) {
|
2016-04-15 12:00:23 +08:00
|
|
|
if (write(wsi->vhost->log_fd, ass, l) != l)
|
|
|
|
lwsl_err("Failed to write log\n");
|
|
|
|
} else
|
|
|
|
lwsl_err("%s", ass);
|
|
|
|
|
2016-05-13 10:57:23 +08:00
|
|
|
if (wsi->access_log.header_log) {
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_free(wsi->access_log.header_log);
|
2016-05-13 10:57:23 +08:00
|
|
|
wsi->access_log.header_log = NULL;
|
|
|
|
}
|
|
|
|
if (wsi->access_log.user_agent) {
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_free(wsi->access_log.user_agent);
|
2016-05-13 10:57:23 +08:00
|
|
|
wsi->access_log.user_agent = NULL;
|
|
|
|
}
|
2016-04-15 12:00:23 +08:00
|
|
|
wsi->access_log_pending = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-14 15:07:44 +08:00
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
void
|
|
|
|
lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs)
|
|
|
|
{
|
|
|
|
const struct lws_vhost *vh = ctx->vhost_list;
|
|
|
|
|
|
|
|
while (vh) {
|
|
|
|
|
|
|
|
cs->rx += vh->conn_stats.rx;
|
|
|
|
cs->tx += vh->conn_stats.tx;
|
|
|
|
cs->conn += vh->conn_stats.conn;
|
|
|
|
cs->trans += vh->conn_stats.trans;
|
|
|
|
cs->ws_upg += vh->conn_stats.ws_upg;
|
|
|
|
cs->http2_upg += vh->conn_stats.http2_upg;
|
|
|
|
cs->rejected += vh->conn_stats.rejected;
|
|
|
|
|
|
|
|
vh = vh->vhost_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 14:01:29 +08:00
|
|
|
#ifdef LWS_WITH_SERVER_STATUS
|
|
|
|
|
2016-04-14 15:07:44 +08:00
|
|
|
LWS_EXTERN int
|
|
|
|
lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
|
|
|
|
{
|
|
|
|
static const char * const prots[] = {
|
|
|
|
"http://",
|
|
|
|
"https://",
|
|
|
|
"file://",
|
|
|
|
"cgi://",
|
|
|
|
">http://",
|
|
|
|
">https://",
|
2016-05-09 09:37:01 +08:00
|
|
|
"callback://"
|
2016-04-14 15:07:44 +08:00
|
|
|
};
|
|
|
|
char *orig = buf, *end = buf + len - 1, first = 1;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
if (len < 100)
|
|
|
|
return 0;
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-04-14 15:07:44 +08:00
|
|
|
"{\n \"name\":\"%s\",\n"
|
|
|
|
" \"port\":\"%d\",\n"
|
2016-04-15 14:01:29 +08:00
|
|
|
" \"use_ssl\":\"%d\",\n"
|
2016-04-14 15:07:44 +08:00
|
|
|
" \"sts\":\"%d\",\n"
|
2016-05-04 15:59:27 +08:00
|
|
|
" \"rx\":\"%llu\",\n"
|
|
|
|
" \"tx\":\"%llu\",\n"
|
2016-04-15 14:01:29 +08:00
|
|
|
" \"conn\":\"%lu\",\n"
|
|
|
|
" \"trans\":\"%lu\",\n"
|
|
|
|
" \"ws_upg\":\"%lu\",\n"
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
" \"rejected\":\"%lu\",\n"
|
2016-04-15 14:01:29 +08:00
|
|
|
" \"http2_upg\":\"%lu\""
|
|
|
|
,
|
2016-04-14 15:07:44 +08:00
|
|
|
vh->name, vh->listen_port,
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
vh->use_ssl,
|
|
|
|
#else
|
|
|
|
0,
|
|
|
|
#endif
|
|
|
|
!!(vh->options & LWS_SERVER_OPTION_STS),
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
vh->conn_stats.rx, vh->conn_stats.tx,
|
|
|
|
vh->conn_stats.conn, vh->conn_stats.trans,
|
|
|
|
vh->conn_stats.ws_upg,
|
|
|
|
vh->conn_stats.rejected,
|
|
|
|
vh->conn_stats.http2_upg
|
2016-04-14 15:07:44 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
if (vh->mount_list) {
|
2016-05-02 04:59:54 +08:00
|
|
|
const struct lws_http_mount *m = vh->mount_list;
|
2016-04-14 15:07:44 +08:00
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":[");
|
2016-04-14 15:07:44 +08:00
|
|
|
while (m) {
|
|
|
|
if (!first)
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",");
|
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-04-14 15:07:44 +08:00
|
|
|
"\n {\n \"mountpoint\":\"%s\",\n"
|
2016-04-22 08:53:49 +08:00
|
|
|
" \"origin\":\"%s%s\",\n"
|
|
|
|
" \"cache_max_age\":\"%d\",\n"
|
|
|
|
" \"cache_reuse\":\"%d\",\n"
|
|
|
|
" \"cache_revalidate\":\"%d\",\n"
|
|
|
|
" \"cache_intermediaries\":\"%d\"\n"
|
2016-04-14 15:07:44 +08:00
|
|
|
,
|
|
|
|
m->mountpoint,
|
|
|
|
prots[m->origin_protocol],
|
2016-04-22 08:53:49 +08:00
|
|
|
m->origin,
|
|
|
|
m->cache_max_age,
|
|
|
|
m->cache_reusable,
|
|
|
|
m->cache_revalidate,
|
|
|
|
m->cache_intermediaries);
|
2016-04-14 15:07:44 +08:00
|
|
|
if (m->def)
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-04-14 15:07:44 +08:00
|
|
|
",\n \"default\":\"%s\"",
|
|
|
|
m->def);
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\n }");
|
2016-04-14 15:07:44 +08:00
|
|
|
first = 0;
|
|
|
|
m = m->mount_next;
|
|
|
|
}
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\n ]");
|
2016-04-14 15:07:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vh->protocols) {
|
|
|
|
n = 0;
|
|
|
|
first = 1;
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":[");
|
2016-04-14 15:07:44 +08:00
|
|
|
while (n < vh->count_protocols) {
|
|
|
|
if (!first)
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",");
|
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-05-15 08:29:37 +08:00
|
|
|
"\n {\n \"%s\":{\n"
|
2016-04-14 15:07:44 +08:00
|
|
|
" \"status\":\"ok\"\n }\n }"
|
|
|
|
,
|
|
|
|
vh->protocols[n].name);
|
|
|
|
first = 0;
|
|
|
|
n++;
|
|
|
|
}
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\n ]");
|
2016-04-14 15:07:44 +08:00
|
|
|
}
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\n}");
|
2016-04-14 15:07:44 +08:00
|
|
|
|
|
|
|
return buf - orig;
|
|
|
|
}
|
2016-04-15 14:01:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
LWS_EXTERN LWS_VISIBLE int
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
lws_json_dump_context(const struct lws_context *context, char *buf, int len,
|
|
|
|
int hide_vhosts)
|
2016-04-15 14:01:29 +08:00
|
|
|
{
|
|
|
|
char *orig = buf, *end = buf + len - 1, first = 1;
|
2016-04-20 06:10:56 +08:00
|
|
|
const struct lws_vhost *vh = context->vhost_list;
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
const struct lws_context_per_thread *pt;
|
|
|
|
time_t t = time(NULL);
|
2017-02-06 10:04:04 +08:00
|
|
|
int n, listening = 0, cgi_count = 0;
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
struct lws_conn_stats cs;
|
|
|
|
double d = 0;
|
2016-04-20 06:10:56 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
struct lws_cgi * const *pcgi;
|
|
|
|
#endif
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
|
|
|
|
#ifdef LWS_USE_LIBUV
|
|
|
|
uv_uptime(&d);
|
|
|
|
#endif
|
2016-04-15 14:01:29 +08:00
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "{ "
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
"\"version\":\"%s\",\n"
|
|
|
|
"\"uptime\":\"%ld\",\n",
|
|
|
|
lws_get_library_version(),
|
|
|
|
(long)d);
|
|
|
|
|
2016-04-26 07:45:45 +08:00
|
|
|
#ifdef LWS_HAVE_GETLOADAVG
|
|
|
|
{
|
|
|
|
double d[3];
|
|
|
|
int m;
|
|
|
|
|
|
|
|
m = getloadavg(d, 3);
|
|
|
|
for (n = 0; n < m; n++) {
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-04-26 07:45:45 +08:00
|
|
|
"\"l%d\":\"%.2f\",\n",
|
|
|
|
n + 1, d[n]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-15 14:01:29 +08:00
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\"contexts\":[\n");
|
|
|
|
|
|
|
|
buf += lws_snprintf(buf, end - buf, "{ "
|
|
|
|
"\"context_uptime\":\"%ld\",\n"
|
|
|
|
"\"cgi_spawned\":\"%d\",\n"
|
|
|
|
"\"pt_fd_max\":\"%d\",\n"
|
|
|
|
"\"ah_pool_max\":\"%d\",\n"
|
|
|
|
"\"deprecated\":\"%d\",\n"
|
|
|
|
"\"wsi_alive\":\"%d\",\n",
|
|
|
|
(unsigned long)(t - context->time_up),
|
|
|
|
context->count_cgi_spawned,
|
|
|
|
context->fd_limit_per_thread,
|
|
|
|
context->max_http_header_pool,
|
|
|
|
context->deprecated,
|
|
|
|
context->count_wsi_allocated);
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "\"pt\":[\n ");
|
2016-04-20 06:10:56 +08:00
|
|
|
for (n = 0; n < context->count_threads; n++) {
|
|
|
|
pt = &context->pt[n];
|
|
|
|
if (n)
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",");
|
|
|
|
buf += lws_snprintf(buf, end - buf,
|
2016-04-20 06:10:56 +08:00
|
|
|
"\n {\n"
|
|
|
|
" \"fds_count\":\"%d\",\n"
|
|
|
|
" \"ah_pool_inuse\":\"%d\",\n"
|
|
|
|
" \"ah_wait_list\":\"%d\"\n"
|
|
|
|
" }",
|
|
|
|
pt->fds_count,
|
|
|
|
pt->ah_count_in_use,
|
|
|
|
pt->ah_wait_list_length);
|
|
|
|
}
|
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "]");
|
2016-04-20 06:10:56 +08:00
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n ");
|
|
|
|
|
|
|
|
first = 1;
|
|
|
|
vh = context->vhost_list;
|
|
|
|
listening = 0;
|
|
|
|
cs = context->conn_stats;
|
|
|
|
lws_sum_stats(context, &cs);
|
2016-04-15 14:01:29 +08:00
|
|
|
while (vh) {
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
|
|
|
|
if (!hide_vhosts) {
|
|
|
|
if (!first)
|
|
|
|
if(buf != end)
|
|
|
|
*buf++ = ',';
|
|
|
|
buf += lws_json_dump_vhost(vh, buf, end - buf);
|
|
|
|
first = 0;
|
|
|
|
}
|
2016-04-20 06:10:56 +08:00
|
|
|
if (vh->lserv_wsi)
|
|
|
|
listening++;
|
2016-04-15 14:01:29 +08:00
|
|
|
vh = vh->vhost_next;
|
|
|
|
}
|
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf,
|
|
|
|
"],\n\"listen_wsi\":\"%d\",\n"
|
|
|
|
" \"rx\":\"%llu\",\n"
|
|
|
|
" \"tx\":\"%llu\",\n"
|
|
|
|
" \"conn\":\"%lu\",\n"
|
|
|
|
" \"trans\":\"%lu\",\n"
|
|
|
|
" \"ws_upg\":\"%lu\",\n"
|
|
|
|
" \"rejected\":\"%lu\",\n"
|
|
|
|
" \"http2_upg\":\"%lu\"",
|
|
|
|
listening,
|
|
|
|
cs.rx, cs.tx, cs.conn, cs.trans,
|
|
|
|
cs.ws_upg, cs.rejected, cs.http2_upg);
|
2016-04-20 06:10:56 +08:00
|
|
|
|
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
for (n = 0; n < context->count_threads; n++) {
|
|
|
|
pt = &context->pt[n];
|
|
|
|
pcgi = &pt->cgi_list;
|
|
|
|
|
|
|
|
while (*pcgi) {
|
|
|
|
pcgi = &(*pcgi)->cgi_list;
|
|
|
|
|
|
|
|
cgi_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-15 02:22:57 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ",
|
2016-04-20 06:10:56 +08:00
|
|
|
cgi_count);
|
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
buf += lws_snprintf(buf, end - buf, "}");
|
|
|
|
|
|
|
|
|
|
|
|
buf += lws_snprintf(buf, end - buf, "]}\n ");
|
2016-04-15 14:01:29 +08:00
|
|
|
|
|
|
|
return buf - orig;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|