2013-01-18 11:43:21 +08:00
|
|
|
/*
|
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
|
|
*
|
2018-04-11 13:39:42 +08:00
|
|
|
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
2013-01-18 11:43:21 +08: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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "private-libwebsockets.h"
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
const char * const method_names[] = {
|
2017-10-13 10:33:02 +08:00
|
|
|
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_HTTP2
|
2017-10-13 10:33:02 +08:00
|
|
|
":path",
|
2017-09-06 09:30:32 +08:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
/*
|
|
|
|
* return 0: all done
|
|
|
|
* 1: nonfatal error
|
|
|
|
* <0: fatal error
|
2018-04-26 15:27:02 +08:00
|
|
|
*
|
|
|
|
* REQUIRES CONTEXT LOCK HELD
|
2018-03-29 13:32:33 +08:00
|
|
|
*/
|
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
int
|
2018-04-27 08:27:16 +08:00
|
|
|
_lws_context_init_server(const struct lws_context_creation_info *info,
|
|
|
|
struct lws_vhost *vhost)
|
2014-04-03 08:24:29 +08:00
|
|
|
{
|
2016-01-29 21:18:54 +08:00
|
|
|
int n, opt = 1, limit = 1;
|
2015-12-06 08:00:03 +08:00
|
|
|
lws_sockfd_type sockfd;
|
2016-03-28 10:10:43 +08:00
|
|
|
struct lws_vhost *vh;
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *wsi;
|
2018-03-29 13:32:33 +08:00
|
|
|
int m = 0, is;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2017-10-13 10:33:02 +08:00
|
|
|
(void)method_names;
|
2017-02-18 17:26:40 +08:00
|
|
|
(void)opt;
|
2018-03-29 13:32:33 +08:00
|
|
|
|
|
|
|
if (info) {
|
|
|
|
vhost->iface = info->iface;
|
|
|
|
vhost->listen_port = info->port;
|
|
|
|
}
|
|
|
|
|
2014-04-03 08:24:29 +08:00
|
|
|
/* set up our external listening socket we serve on */
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
|
|
|
|
vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
|
2014-04-03 08:24:29 +08:00
|
|
|
return 0;
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
vh = vhost->context->vhost_list;
|
|
|
|
while (vh) {
|
2018-03-29 13:32:33 +08:00
|
|
|
if (vh->listen_port == vhost->listen_port) {
|
|
|
|
if (((!vhost->iface && !vh->iface) ||
|
|
|
|
(vhost->iface && vh->iface &&
|
|
|
|
!strcmp(vhost->iface, vh->iface))) &&
|
|
|
|
vh->lserv_wsi
|
|
|
|
) {
|
2016-03-28 10:10:43 +08:00
|
|
|
lwsl_notice(" using listen skt from vhost %s\n",
|
|
|
|
vh->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vh = vh->vhost_next;
|
|
|
|
}
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
if (vhost->iface) {
|
|
|
|
/*
|
|
|
|
* let's check before we do anything else about the disposition
|
|
|
|
* of the interface he wants to bind to...
|
|
|
|
*/
|
|
|
|
is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port, vhost->iface);
|
|
|
|
lwsl_debug("initial if check says %d\n", is);
|
|
|
|
deal:
|
2018-04-26 15:27:02 +08:00
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
lws_start_foreach_llp(struct lws_vhost **, pv,
|
|
|
|
vhost->context->no_listener_vhost_list) {
|
|
|
|
if (is >= LWS_ITOSA_USABLE && *pv == vhost) {
|
|
|
|
/* on the list and shouldn't be: remove it */
|
|
|
|
lwsl_debug("deferred iface: removing vh %s\n", (*pv)->name);
|
|
|
|
*pv = vhost->no_listener_vhost_list;
|
|
|
|
vhost->no_listener_vhost_list = NULL;
|
|
|
|
goto done_list;
|
|
|
|
}
|
|
|
|
if (is < LWS_ITOSA_USABLE && *pv == vhost)
|
|
|
|
goto done_list;
|
|
|
|
} lws_end_foreach_llp(pv, no_listener_vhost_list);
|
|
|
|
|
|
|
|
/* not on the list... */
|
|
|
|
|
|
|
|
if (is < LWS_ITOSA_USABLE) {
|
|
|
|
|
|
|
|
/* ... but needs to be: so add it */
|
|
|
|
|
|
|
|
lwsl_debug("deferred iface: adding vh %s\n", vhost->name);
|
|
|
|
vhost->no_listener_vhost_list = vhost->context->no_listener_vhost_list;
|
|
|
|
vhost->context->no_listener_vhost_list = vhost;
|
|
|
|
}
|
|
|
|
|
|
|
|
done_list:
|
|
|
|
|
|
|
|
switch (is) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case LWS_ITOSA_NOT_EXIST:
|
|
|
|
/* can't add it */
|
|
|
|
if (info) /* first time */
|
|
|
|
lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n",
|
|
|
|
vhost->name, vhost->iface, vhost->listen_port);
|
|
|
|
return 1;
|
|
|
|
case LWS_ITOSA_NOT_USABLE:
|
|
|
|
/* can't add it */
|
|
|
|
if (info) /* first time */
|
|
|
|
lwsl_err("VH %s: iface %s port %d NOT USABLE\n",
|
|
|
|
vhost->name, vhost->iface, vhost->listen_port);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-23 13:08:46 +08:00
|
|
|
(void)n;
|
2016-01-26 20:56:56 +08:00
|
|
|
#if defined(__linux__)
|
2016-03-28 10:10:43 +08:00
|
|
|
limit = vhost->context->count_threads;
|
2016-01-26 20:56:56 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
for (m = 0; m < limit; m++) {
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_UNIX_SOCK
|
2018-03-08 12:04:13 +08:00
|
|
|
if (LWS_UNIX_SOCK_ENABLED(vhost))
|
|
|
|
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
else
|
2016-03-30 22:47:02 -07:00
|
|
|
#endif
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_IPV6
|
2018-03-08 12:04:13 +08:00
|
|
|
if (LWS_IPV6_ENABLED(vhost))
|
|
|
|
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
else
|
2014-04-03 08:24:29 +08:00
|
|
|
#endif
|
2018-03-08 12:04:13 +08:00
|
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
if (sockfd == LWS_SOCK_INVALID) {
|
|
|
|
lwsl_err("ERROR opening socket\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2018-04-11 13:39:42 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2017-06-23 13:08:46 +08:00
|
|
|
#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
|
2018-03-08 12:04:13 +08:00
|
|
|
/*
|
|
|
|
* only accept that we are the only listener on the port
|
|
|
|
* https://msdn.microsoft.com/zh-tw/library/
|
|
|
|
* windows/desktop/ms740621(v=vs.85).aspx
|
|
|
|
*
|
|
|
|
* for lws, to match Linux, we default to exclusive listen
|
|
|
|
*/
|
|
|
|
if (!lws_check_opt(vhost->options,
|
|
|
|
LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
|
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
|
|
|
(const void *)&opt, sizeof(opt)) < 0) {
|
|
|
|
lwsl_err("reuseaddr failed\n");
|
|
|
|
compatible_close(sockfd);
|
2018-03-29 13:32:33 +08:00
|
|
|
return -1;
|
2018-03-08 12:04:13 +08:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allow us to restart even if old sockets in TIME_WAIT
|
|
|
|
*/
|
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
2017-06-23 13:08:46 +08:00
|
|
|
(const void *)&opt, sizeof(opt)) < 0) {
|
|
|
|
lwsl_err("reuseaddr failed\n");
|
|
|
|
compatible_close(sockfd);
|
2018-03-29 13:32:33 +08:00
|
|
|
return -1;
|
2017-06-23 13:08:46 +08:00
|
|
|
}
|
2016-06-04 08:37:39 +08:00
|
|
|
|
2017-09-28 11:29:03 +08:00
|
|
|
#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
|
2018-03-08 12:04:13 +08:00
|
|
|
if (LWS_IPV6_ENABLED(vhost) &&
|
|
|
|
vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
|
2017-10-16 12:52:32 +08:00
|
|
|
int value = (vhost->options &
|
|
|
|
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
|
2016-11-23 23:02:13 +08:00
|
|
|
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
|
2017-10-16 12:52:32 +08:00
|
|
|
(const void*)&value, sizeof(value)) < 0) {
|
2016-06-04 08:37:39 +08:00
|
|
|
compatible_close(sockfd);
|
2018-03-29 13:32:33 +08:00
|
|
|
return -1;
|
2016-06-04 08:37:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-23 13:08:46 +08:00
|
|
|
#if defined(__linux__) && defined(SO_REUSEPORT)
|
2018-03-19 08:21:49 +08:00
|
|
|
/* keep coverity happy */
|
2017-06-23 13:08:46 +08:00
|
|
|
#if LWS_MAX_SMP > 1
|
2018-03-08 12:04:13 +08:00
|
|
|
n = 1;
|
2018-03-19 08:21:49 +08:00
|
|
|
#else
|
2018-04-20 21:14:07 +03:00
|
|
|
n = lws_check_opt(vhost->options,
|
|
|
|
LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
|
2017-06-23 13:08:46 +08:00
|
|
|
#endif
|
2018-03-08 12:04:13 +08:00
|
|
|
if (n && vhost->context->count_threads > 1)
|
2017-06-23 13:08:46 +08:00
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
|
|
|
|
(const void *)&opt, sizeof(opt)) < 0) {
|
|
|
|
compatible_close(sockfd);
|
2018-03-29 13:32:33 +08:00
|
|
|
return -1;
|
2017-06-23 13:08:46 +08:00
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
#endif
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2018-03-08 12:04:13 +08:00
|
|
|
lws_plat_set_socket_options(vhost, sockfd);
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface);
|
|
|
|
/*
|
|
|
|
* There is a race where the network device may come up and then
|
|
|
|
* go away and fail here. So correctly handle unexpected failure
|
|
|
|
* here despite we earlier confirmed it.
|
|
|
|
*/
|
|
|
|
if (is < 0) {
|
|
|
|
lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
|
|
|
|
compatible_close(sockfd);
|
|
|
|
goto deal;
|
|
|
|
}
|
|
|
|
vhost->listen_port = is;
|
|
|
|
|
|
|
|
lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
|
|
|
|
if (wsi == NULL) {
|
|
|
|
lwsl_err("Out of mem\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
wsi->context = vhost->context;
|
|
|
|
wsi->desc.sockfd = sockfd;
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
|
2018-03-08 12:04:13 +08:00
|
|
|
wsi->protocol = vhost->protocols;
|
|
|
|
wsi->tsi = m;
|
|
|
|
wsi->vhost = vhost;
|
|
|
|
wsi->listener = 1;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_LIBUV
|
2018-03-08 12:04:13 +08:00
|
|
|
if (LWS_LIBUV_ENABLED(vhost->context))
|
|
|
|
lws_uv_initvhost(vhost, wsi);
|
2016-11-30 07:05:13 +08:00
|
|
|
#endif
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
|
|
|
|
lwsl_notice("inserting wsi socket into fds failed\n");
|
2018-03-08 12:04:13 +08:00
|
|
|
goto bail;
|
2018-03-29 13:32:33 +08:00
|
|
|
}
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
vhost->context->count_wsi_allocated++;
|
|
|
|
vhost->lserv_wsi = wsi;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
|
|
|
|
if (n < 0) {
|
|
|
|
lwsl_err("listen failed with error %d\n", LWS_ERRNO);
|
|
|
|
vhost->lserv_wsi = NULL;
|
|
|
|
vhost->context->count_wsi_allocated--;
|
|
|
|
__remove_wsi_socket_from_fds(wsi);
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-04-22 08:53:49 +08:00
|
|
|
} /* for each thread able to independently listen */
|
2018-04-11 13:39:42 +08:00
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_UNIX_SOCK
|
2016-04-14 12:11:51 +08:00
|
|
|
if (LWS_UNIX_SOCK_ENABLED(vhost))
|
2018-03-29 13:32:33 +08:00
|
|
|
lwsl_info(" Listening on \"%s\"\n", vhost->iface);
|
2016-03-30 22:47:02 -07:00
|
|
|
else
|
|
|
|
#endif
|
2018-03-29 13:32:33 +08:00
|
|
|
lwsl_info(" Listening on port %d\n", vhost->listen_port);
|
2016-03-30 22:47:02 -07:00
|
|
|
}
|
2014-12-04 23:59:35 +01:00
|
|
|
|
2018-04-27 08:27:16 +08:00
|
|
|
// info->port = vhost->listen_port;
|
|
|
|
|
2014-04-03 08:24:29 +08:00
|
|
|
return 0;
|
2015-12-17 17:03:59 +08:00
|
|
|
|
|
|
|
bail:
|
|
|
|
compatible_close(sockfd);
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
return -1;
|
2014-04-03 08:24:29 +08:00
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
struct lws_vhost *
|
|
|
|
lws_select_vhost(struct lws_context *context, int port, const char *servername)
|
|
|
|
{
|
|
|
|
struct lws_vhost *vhost = context->vhost_list;
|
2016-07-11 09:44:17 +08:00
|
|
|
const char *p;
|
|
|
|
int n, m, colon;
|
|
|
|
|
2017-10-25 08:00:23 +08:00
|
|
|
n = (int)strlen(servername);
|
2016-07-11 09:44:17 +08:00
|
|
|
colon = n;
|
|
|
|
p = strchr(servername, ':');
|
|
|
|
if (p)
|
2017-10-25 08:00:23 +08:00
|
|
|
colon = lws_ptr_diff(p, servername);
|
2016-07-11 09:44:17 +08:00
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
/* Priotity 1: first try exact matches */
|
2016-03-28 10:10:43 +08:00
|
|
|
|
|
|
|
while (vhost) {
|
|
|
|
if (port == vhost->listen_port &&
|
2016-07-11 09:44:17 +08:00
|
|
|
!strncmp(vhost->name, servername, colon)) {
|
2016-03-28 10:10:43 +08:00
|
|
|
lwsl_info("SNI: Found: %s\n", servername);
|
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
2016-07-11 09:44:17 +08:00
|
|
|
/*
|
2017-03-07 16:06:05 +08:00
|
|
|
* Priority 2: if no exact matches, try matching *.vhost-name
|
2016-07-11 09:44:17 +08:00
|
|
|
* unintentional matches are possible but resolve to x.com for *.x.com
|
|
|
|
* which is reasonable. If exact match exists we already chose it and
|
|
|
|
* never reach here. SSL will still fail it if the cert doesn't allow
|
|
|
|
* *.x.com.
|
|
|
|
*/
|
|
|
|
vhost = context->vhost_list;
|
|
|
|
while (vhost) {
|
2017-10-25 08:00:23 +08:00
|
|
|
m = (int)strlen(vhost->name);
|
2016-07-11 09:44:17 +08:00
|
|
|
if (port == vhost->listen_port &&
|
|
|
|
m <= (colon - 2) &&
|
|
|
|
servername[colon - m - 1] == '.' &&
|
|
|
|
!strncmp(vhost->name, servername + colon - m, m)) {
|
|
|
|
lwsl_info("SNI: Found %s on wildcard: %s\n",
|
|
|
|
servername, vhost->name);
|
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
/* Priority 3: match the first vhost on our port */
|
|
|
|
|
|
|
|
vhost = context->vhost_list;
|
|
|
|
while (vhost) {
|
|
|
|
if (port == vhost->listen_port) {
|
|
|
|
lwsl_info("vhost match to %s based on port %d\n",
|
|
|
|
vhost->name, port);
|
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no match */
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-07-03 09:20:11 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_get_mimetype(const char *file, const struct lws_http_mount *m)
|
2016-03-28 10:10:43 +08:00
|
|
|
{
|
2017-10-25 08:00:23 +08:00
|
|
|
int n = (int)strlen(file);
|
2016-05-14 10:00:21 +08:00
|
|
|
const struct lws_protocol_vhost_options *pvo = NULL;
|
|
|
|
|
|
|
|
if (m)
|
|
|
|
pvo = m->extra_mimetypes;
|
2016-03-28 10:10:43 +08:00
|
|
|
|
|
|
|
if (n < 5)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".ico"))
|
|
|
|
return "image/x-icon";
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".gif"))
|
|
|
|
return "image/gif";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 3], ".js"))
|
|
|
|
return "text/javascript";
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".png"))
|
|
|
|
return "image/png";
|
|
|
|
|
2016-04-06 16:15:40 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".jpg"))
|
|
|
|
return "image/jpeg";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 3], ".gz"))
|
|
|
|
return "application/gzip";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".JPG"))
|
|
|
|
return "image/jpeg";
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!strcmp(&file[n - 5], ".html"))
|
|
|
|
return "text/html";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".css"))
|
|
|
|
return "text/css";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".txt"))
|
|
|
|
return "text/plain";
|
|
|
|
|
2016-09-23 00:04:40 +02:00
|
|
|
if (!strcmp(&file[n - 4], ".svg"))
|
|
|
|
return "image/svg+xml";
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".ttf"))
|
|
|
|
return "application/x-font-ttf";
|
|
|
|
|
2017-05-22 14:01:08 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".otf"))
|
|
|
|
return "application/font-woff";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 5], ".woff"))
|
|
|
|
return "application/font-woff";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".xml"))
|
|
|
|
return "application/xml";
|
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
while (pvo) {
|
2016-08-13 11:17:53 +02:00
|
|
|
if (pvo->name[0] == '*') /* ie, match anything */
|
|
|
|
return pvo->value;
|
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
if (!strcmp(&file[n - strlen(pvo->name)], pvo->name))
|
|
|
|
return pvo->value;
|
|
|
|
|
|
|
|
pvo = pvo->next;
|
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2017-03-03 12:38:10 +08:00
|
|
|
static lws_fop_flags_t
|
|
|
|
lws_vfs_prepare_flags(struct lws *wsi)
|
|
|
|
{
|
|
|
|
lws_fop_flags_t f = 0;
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
|
|
|
|
return f;
|
|
|
|
|
|
|
|
if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
|
|
|
|
"gzip")) {
|
|
|
|
lwsl_info("client indicates GZIP is acceptable\n");
|
|
|
|
f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
static int
|
|
|
|
lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
|
|
|
const struct lws_http_mount *m)
|
2016-03-28 10:10:43 +08:00
|
|
|
{
|
2016-05-19 15:28:31 +08:00
|
|
|
const struct lws_protocol_vhost_options *pvo = m->interpret;
|
|
|
|
struct lws_process_html_args args;
|
2017-03-08 11:11:41 +08:00
|
|
|
const char *mimetype;
|
2017-11-30 12:40:46 +08:00
|
|
|
#if !defined(_WIN32_WCE)
|
2017-03-03 12:38:10 +08:00
|
|
|
const struct lws_plat_file_ops *fops;
|
2017-03-08 11:11:41 +08:00
|
|
|
const char *vpath;
|
2017-03-03 12:38:10 +08:00
|
|
|
lws_fop_flags_t fflags = LWS_O_RDONLY;
|
2017-06-09 20:20:42 +08:00
|
|
|
#if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
|
|
|
|
struct _stat32i64 st;
|
|
|
|
#else
|
2016-04-13 11:49:07 +08:00
|
|
|
struct stat st;
|
2017-06-09 20:20:42 +08:00
|
|
|
#endif
|
2016-07-23 14:18:25 +08:00
|
|
|
int spin = 0;
|
2016-05-05 13:28:04 +02:00
|
|
|
#endif
|
2016-05-19 15:28:31 +08:00
|
|
|
char path[256], sym[512];
|
2016-04-22 08:53:49 +08:00
|
|
|
unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
|
|
|
|
unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
|
2018-04-11 13:39:42 +08:00
|
|
|
#if !defined(WIN32) && !defined(LWS_WITH_ESP32)
|
2016-04-26 22:52:16 +02:00
|
|
|
size_t len;
|
2016-04-26 22:38:42 +02:00
|
|
|
#endif
|
2016-07-23 14:18:25 +08:00
|
|
|
int n;
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2018-03-07 19:57:34 +08:00
|
|
|
wsi->handling_404 = 0;
|
2018-03-19 07:59:42 +08:00
|
|
|
if (!wsi->vhost)
|
|
|
|
return -1;
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
|
|
|
if (wsi->vhost->http.error_document_404 &&
|
|
|
|
!strcmp(uri, wsi->vhost->http.error_document_404))
|
2018-03-07 19:57:34 +08:00
|
|
|
wsi->handling_404 = 1;
|
2018-04-27 15:20:56 +08:00
|
|
|
#endif
|
2018-03-07 19:57:34 +08:00
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2017-11-30 12:40:46 +08:00
|
|
|
#if !defined(_WIN32_WCE)
|
2017-03-08 11:11:41 +08:00
|
|
|
|
|
|
|
fflags |= lws_vfs_prepare_flags(wsi);
|
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
do {
|
|
|
|
spin++;
|
2017-03-03 12:38:10 +08:00
|
|
|
fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if (wsi->http.fop_fd)
|
|
|
|
lws_vfs_file_close(&wsi->http.fop_fd);
|
2017-03-03 12:38:10 +08:00
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
|
2017-03-03 12:38:10 +08:00
|
|
|
path, vpath, &fflags);
|
2017-12-01 11:09:32 +08:00
|
|
|
if (!wsi->http.fop_fd) {
|
2018-04-13 06:43:11 +08:00
|
|
|
lwsl_info("%s: Unable to open '%s': errno %d\n",
|
|
|
|
__func__, path, errno);
|
2017-03-03 12:38:10 +08:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
/* if it can't be statted, don't try */
|
|
|
|
if (fflags & LWS_FOP_FLAG_VIRTUAL)
|
|
|
|
break;
|
2017-04-03 14:09:37 +08:00
|
|
|
#if defined(LWS_WITH_ESP32)
|
|
|
|
break;
|
|
|
|
#endif
|
2017-03-17 11:43:45 +08:00
|
|
|
#if !defined(WIN32)
|
2017-12-01 11:09:32 +08:00
|
|
|
if (fstat(wsi->http.fop_fd->fd, &st)) {
|
2016-04-25 10:04:49 +08:00
|
|
|
lwsl_info("unable to stat %s\n", path);
|
2016-04-13 11:49:07 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
2017-06-09 20:20:42 +08:00
|
|
|
#else
|
|
|
|
#if defined(LWS_HAVE__STAT32I64)
|
|
|
|
if (_stat32i64(path, &st)) {
|
|
|
|
lwsl_info("unable to stat %s\n", path);
|
|
|
|
goto bail;
|
|
|
|
}
|
2017-03-17 11:43:45 +08:00
|
|
|
#else
|
|
|
|
if (stat(path, &st)) {
|
|
|
|
lwsl_info("unable to stat %s\n", path);
|
|
|
|
goto bail;
|
|
|
|
}
|
2017-06-09 20:20:42 +08:00
|
|
|
#endif
|
2017-03-17 11:43:45 +08:00
|
|
|
#endif
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
|
2017-03-03 12:38:10 +08:00
|
|
|
fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
|
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
#if !defined(WIN32) && !defined(LWS_WITH_ESP32)
|
2016-04-13 11:49:07 +08:00
|
|
|
if ((S_IFMT & st.st_mode) == S_IFLNK) {
|
2016-04-23 07:49:57 +08:00
|
|
|
len = readlink(path, sym, sizeof(sym) - 1);
|
|
|
|
if (len) {
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_err("Failed to read link %s\n", path);
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-04-23 07:49:57 +08:00
|
|
|
sym[len] = '\0';
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_debug("symlink %s -> %s\n", path, sym);
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(path, sizeof(path) - 1, "%s", sym);
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
#endif
|
2016-04-13 11:49:07 +08:00
|
|
|
if ((S_IFMT & st.st_mode) == S_IFDIR) {
|
|
|
|
lwsl_debug("default filename append to dir\n");
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
|
2016-04-13 11:49:07 +08:00
|
|
|
origin, uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (spin == 5)
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_err("symlink loop %s \n", path);
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2017-07-07 08:32:04 +08:00
|
|
|
n = sprintf(sym, "%08llX%08lX",
|
2017-12-01 11:09:32 +08:00
|
|
|
(unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
|
|
|
|
(unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
/* disable ranges if IF_RANGE token invalid */
|
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
|
|
|
|
if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
|
|
|
|
/* differs - defeat Range: */
|
2018-04-27 15:20:56 +08:00
|
|
|
wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
|
|
|
|
/*
|
|
|
|
* he thinks he has some version of it already,
|
|
|
|
* check if the tag matches
|
|
|
|
*/
|
2017-03-03 12:38:10 +08:00
|
|
|
if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: ETAG match %s %s\n", __func__,
|
|
|
|
uri, origin);
|
2016-04-22 08:53:49 +08:00
|
|
|
|
|
|
|
/* we don't need to send the payload */
|
2017-03-08 07:51:47 +08:00
|
|
|
if (lws_add_http_header_status(wsi,
|
|
|
|
HTTP_STATUS_NOT_MODIFIED, &p, end))
|
2016-04-22 08:53:49 +08:00
|
|
|
return -1;
|
2016-08-27 17:07:06 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_ETAG,
|
|
|
|
(unsigned char *)sym, n, &p, end))
|
|
|
|
return -1;
|
2016-08-27 17:07:06 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = lws_write(wsi, start, p - start,
|
2017-10-13 10:33:02 +08:00
|
|
|
LWS_WRITE_HTTP_HEADERS |
|
|
|
|
LWS_WRITE_H2_STREAM_END);
|
2016-04-22 08:53:49 +08:00
|
|
|
if (n != (p - start)) {
|
2017-02-04 13:09:00 +01:00
|
|
|
lwsl_err("_write returned %d from %ld\n", n,
|
|
|
|
(long)(p - start));
|
2016-04-22 08:53:49 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
lws_vfs_file_close(&wsi->http.fop_fd);
|
2017-03-03 12:38:10 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
}
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
|
|
|
|
(unsigned char *)sym, n, &p, end))
|
|
|
|
return -1;
|
2016-05-05 13:28:04 +02:00
|
|
|
#endif
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2016-07-03 09:20:11 +08:00
|
|
|
mimetype = lws_get_mimetype(path, m);
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!mimetype) {
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_err("unknown mimetype for %s\n", path);
|
2016-08-13 11:17:53 +02:00
|
|
|
goto bail;
|
2016-03-28 10:10:43 +08:00
|
|
|
}
|
2016-08-13 11:17:53 +02:00
|
|
|
if (!mimetype[0])
|
|
|
|
lwsl_debug("sending no mimetype for %s\n", path);
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
wsi->sending_chunked = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check if this is in the list of file suffixes to be interpreted by
|
|
|
|
* a protocol
|
|
|
|
*/
|
|
|
|
while (pvo) {
|
2017-10-25 08:00:23 +08:00
|
|
|
n = (int)strlen(path);
|
2016-05-19 15:28:31 +08:00
|
|
|
if (n > (int)strlen(pvo->name) &&
|
|
|
|
!strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
|
2018-01-14 20:09:41 +08:00
|
|
|
wsi->interpreting = 1;
|
|
|
|
if (!wsi->http2_substream)
|
|
|
|
wsi->sending_chunked = 1;
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->protocol_interpret_idx =
|
|
|
|
(char)(lws_intptr_t)pvo->value;
|
2016-05-19 15:28:31 +08:00
|
|
|
lwsl_info("want %s interpreted by %s\n", path,
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->vhost->protocols[
|
|
|
|
(int)(lws_intptr_t)(pvo->value)].name);
|
|
|
|
wsi->protocol = &wsi->vhost->protocols[
|
|
|
|
(int)(lws_intptr_t)(pvo->value)];
|
2016-05-19 15:28:31 +08:00
|
|
|
if (lws_ensure_user_space(wsi))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pvo = pvo->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->protocol) {
|
|
|
|
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->vhost, m->protocol);
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-18 09:00:04 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-19 15:28:31 +08:00
|
|
|
args.p = (char *)p;
|
2017-10-25 08:00:23 +08:00
|
|
|
args.max_len = lws_ptr_diff(end, p);
|
2016-05-19 15:28:31 +08:00
|
|
|
if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
|
|
|
|
wsi->user_space, &args, 0))
|
|
|
|
return -1;
|
|
|
|
p = (unsigned char *)args.p;
|
|
|
|
}
|
|
|
|
|
2017-10-25 08:00:23 +08:00
|
|
|
n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
|
|
|
|
lws_ptr_diff(p, start));
|
2016-04-06 16:15:40 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
|
|
|
return -1; /* error or can't reuse connection: close the socket */
|
|
|
|
|
|
|
|
return 0;
|
2016-04-13 11:49:07 +08:00
|
|
|
bail:
|
|
|
|
|
|
|
|
return -1;
|
2016-03-28 10:10:43 +08:00
|
|
|
}
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
2016-06-26 06:29:20 +08:00
|
|
|
const struct lws_http_mount *
|
|
|
|
lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
|
|
|
|
{
|
|
|
|
const struct lws_http_mount *hm, *hit = NULL;
|
|
|
|
int best = 0;
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
hm = wsi->vhost->http.mount_list;
|
2016-06-26 06:29:20 +08:00
|
|
|
while (hm) {
|
|
|
|
if (uri_len >= hm->mountpoint_len &&
|
|
|
|
!strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
|
|
|
|
(uri_ptr[hm->mountpoint_len] == '\0' ||
|
|
|
|
uri_ptr[hm->mountpoint_len] == '/' ||
|
|
|
|
hm->mountpoint_len == 1)
|
|
|
|
) {
|
|
|
|
if (hm->origin_protocol == LWSMPRO_CALLBACK ||
|
|
|
|
((hm->origin_protocol == LWSMPRO_CGI ||
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
|
2017-10-13 10:33:02 +08:00
|
|
|
(wsi->http2_substream &&
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_hdr_total_length(wsi,
|
|
|
|
WSI_TOKEN_HTTP_COLON_PATH)) ||
|
2016-06-26 06:29:20 +08:00
|
|
|
hm->protocol) &&
|
|
|
|
hm->mountpoint_len > best)) {
|
|
|
|
best = hm->mountpoint_len;
|
|
|
|
hit = hm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hm = hm->mount_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hit;
|
|
|
|
}
|
2018-04-27 15:20:56 +08:00
|
|
|
#endif
|
2016-06-26 06:29:20 +08:00
|
|
|
|
2018-02-24 08:14:17 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2016-12-03 15:13:15 +08:00
|
|
|
static int
|
|
|
|
lws_find_string_in_file(const char *filename, const char *string, int stringlen)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
int fd, match = 0, pos = 0, n = 0, hit = 0;
|
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
lwsl_err("can't open auth file: %s\n", filename);
|
2018-04-19 08:41:16 +08:00
|
|
|
return 0;
|
2016-12-03 15:13:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (pos == n) {
|
|
|
|
n = read(fd, buf, sizeof(buf));
|
|
|
|
if (n <= 0) {
|
|
|
|
if (match == stringlen)
|
|
|
|
hit = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match == stringlen) {
|
|
|
|
if (buf[pos] == '\r' || buf[pos] == '\n') {
|
|
|
|
hit = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
match = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf[pos] == string[match])
|
|
|
|
match++;
|
|
|
|
else
|
|
|
|
match = 0;
|
|
|
|
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return hit;
|
|
|
|
}
|
2018-04-12 15:56:38 +08:00
|
|
|
#endif
|
2016-12-03 15:13:15 +08:00
|
|
|
|
|
|
|
static int
|
|
|
|
lws_unauthorised_basic_auth(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE,
|
|
|
|
*p = start, *end = p + 512;
|
|
|
|
char buf[64];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/* no auth... tell him it is required */
|
|
|
|
|
|
|
|
if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
|
|
|
|
(unsigned char *)buf, n, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |
|
|
|
|
LWS_WRITE_H2_STREAM_END);
|
2016-12-03 15:13:15 +08:00
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
int lws_clean_url(char *p)
|
|
|
|
{
|
2017-07-21 20:04:02 +08:00
|
|
|
if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
|
|
|
|
p += 4;
|
|
|
|
if (*p == 's')
|
|
|
|
p++;
|
|
|
|
if (*p == ':') {
|
|
|
|
p++;
|
|
|
|
if (*p == '/')
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
while (*p) {
|
|
|
|
if (p[0] == '/' && p[1] == '/') {
|
|
|
|
char *p1 = p;
|
|
|
|
while (*p1) {
|
|
|
|
*p1 = p1[1];
|
|
|
|
p1++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-06 09:30:32 +08:00
|
|
|
static const unsigned char methods[] = {
|
|
|
|
WSI_TOKEN_GET_URI,
|
|
|
|
WSI_TOKEN_POST_URI,
|
|
|
|
WSI_TOKEN_OPTIONS_URI,
|
|
|
|
WSI_TOKEN_PUT_URI,
|
|
|
|
WSI_TOKEN_PATCH_URI,
|
|
|
|
WSI_TOKEN_DELETE_URI,
|
|
|
|
WSI_TOKEN_CONNECT,
|
2017-10-13 10:33:02 +08:00
|
|
|
WSI_TOKEN_HEAD_URI,
|
2017-09-28 11:29:03 +08:00
|
|
|
#ifdef LWS_WITH_HTTP2
|
2017-09-06 09:30:32 +08:00
|
|
|
WSI_TOKEN_HTTP_COLON_PATH,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
|
|
|
|
{
|
|
|
|
int n, count = 0;
|
2015-01-10 19:01:52 -08:00
|
|
|
|
2017-10-20 17:45:02 +08:00
|
|
|
for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
|
2015-01-10 19:01:52 -08:00
|
|
|
if (lws_hdr_total_length(wsi, methods[n]))
|
|
|
|
count++;
|
|
|
|
if (!count) {
|
2014-10-08 12:00:53 +08:00
|
|
|
lwsl_warn("Missing URI in HTTP request\n");
|
2017-09-06 09:30:32 +08:00
|
|
|
return -1;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
2017-10-13 10:33:02 +08:00
|
|
|
if (count != 1 &&
|
|
|
|
!(wsi->http2_substream &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
|
2015-01-10 19:01:52 -08:00
|
|
|
lwsl_warn("multiple methods?\n");
|
2017-09-06 09:30:32 +08:00
|
|
|
return -1;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
2017-10-20 17:45:02 +08:00
|
|
|
for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
|
2015-01-10 19:01:52 -08:00
|
|
|
if (lws_hdr_total_length(wsi, methods[n])) {
|
2017-09-06 09:30:32 +08:00
|
|
|
*puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
|
|
|
|
*puri_len = lws_hdr_total_length(wsi, methods[n]);
|
|
|
|
return n;
|
2015-01-10 19:01:52 -08:00
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2017-09-06 09:30:32 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_http_action(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
|
|
enum http_connection_type connection_type;
|
|
|
|
enum http_version request_version;
|
|
|
|
char content_length_str[32];
|
|
|
|
struct lws_process_html_args args;
|
|
|
|
const struct lws_http_mount *hit = NULL;
|
|
|
|
unsigned int n;
|
|
|
|
char http_version_str[10];
|
|
|
|
char http_conn_str[20];
|
|
|
|
int http_version_len;
|
|
|
|
char *uri_ptr = NULL, *s;
|
|
|
|
int uri_len = 0, meth;
|
|
|
|
static const char * const oprot[] = {
|
|
|
|
"http://", "https://"
|
|
|
|
};
|
|
|
|
|
|
|
|
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
|
2017-10-20 17:45:02 +08:00
|
|
|
if (meth < 0 || meth >= (int)ARRAY_SIZE(method_names))
|
2017-09-06 09:30:32 +08:00
|
|
|
goto bail_nuke_ah;
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2016-04-02 07:36:17 +08:00
|
|
|
/* we insist on absolute paths */
|
|
|
|
|
2017-07-19 14:19:03 +08:00
|
|
|
if (!uri_ptr || uri_ptr[0] != '/') {
|
2016-04-02 07:36:17 +08:00
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
|
|
|
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
|
|
|
|
meth, uri_ptr);
|
2017-09-06 09:30:32 +08:00
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
if (wsi->role_ops && wsi->role_ops->check_upgrades)
|
|
|
|
switch (wsi->role_ops->check_upgrades(wsi)) {
|
|
|
|
case LWS_UPG_RET_DONE:
|
|
|
|
return 0;
|
|
|
|
case LWS_UPG_RET_CONTINUE:
|
|
|
|
break;
|
|
|
|
case LWS_UPG_RET_BAIL:
|
|
|
|
goto bail_nuke_ah;
|
2018-03-11 11:26:06 +08:00
|
|
|
}
|
|
|
|
|
2017-09-06 09:30:32 +08:00
|
|
|
if (lws_ensure_user_space(wsi))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* HTTP header had a content length? */
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.rx_content_length = 0;
|
2015-01-10 19:01:52 -08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.rx_content_length = 100 * 1024 * 1024;
|
2014-10-08 12:00:53 +08:00
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
|
|
|
lws_hdr_copy(wsi, content_length_str,
|
2015-12-16 18:19:08 +08:00
|
|
|
sizeof(content_length_str) - 1,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH);
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.rx_content_length = atoll(content_length_str);
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
2016-04-10 09:33:54 +08:00
|
|
|
if (wsi->http2_substream) {
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.request_version = HTTP_VERSION_2;
|
2016-04-10 09:33:54 +08:00
|
|
|
} else {
|
|
|
|
/* http_version? Default to 1.0, override with token: */
|
|
|
|
request_version = HTTP_VERSION_1_0;
|
|
|
|
|
|
|
|
/* Works for single digit HTTP versions. : */
|
|
|
|
http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
|
|
|
|
if (http_version_len > 7) {
|
|
|
|
lws_hdr_copy(wsi, http_version_str,
|
2017-10-16 12:52:32 +08:00
|
|
|
sizeof(http_version_str) - 1,
|
|
|
|
WSI_TOKEN_HTTP);
|
|
|
|
if (http_version_str[5] == '1' &&
|
|
|
|
http_version_str[7] == '1')
|
2016-04-10 09:33:54 +08:00
|
|
|
request_version = HTTP_VERSION_1_1;
|
|
|
|
}
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.request_version = request_version;
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2016-04-10 09:33:54 +08:00
|
|
|
/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
|
|
|
|
if (request_version == HTTP_VERSION_1_1)
|
2014-10-08 12:00:53 +08:00
|
|
|
connection_type = HTTP_CONNECTION_KEEP_ALIVE;
|
|
|
|
else
|
2016-04-10 09:33:54 +08:00
|
|
|
connection_type = HTTP_CONNECTION_CLOSE;
|
|
|
|
|
|
|
|
/* Override default if http "Connection:" header: */
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_hdr_copy(wsi, http_conn_str,
|
|
|
|
sizeof(http_conn_str) - 1,
|
2016-04-10 09:33:54 +08:00
|
|
|
WSI_TOKEN_CONNECTION);
|
|
|
|
http_conn_str[sizeof(http_conn_str) - 1] = '\0';
|
|
|
|
if (!strcasecmp(http_conn_str, "keep-alive"))
|
|
|
|
connection_type = HTTP_CONNECTION_KEEP_ALIVE;
|
|
|
|
else
|
|
|
|
if (!strcasecmp(http_conn_str, "close"))
|
|
|
|
connection_type = HTTP_CONNECTION_CLOSE;
|
|
|
|
}
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.connection_type = connection_type;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
2015-12-17 07:54:44 +08:00
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
|
2015-12-06 08:00:03 +08:00
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
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 (n) {
|
|
|
|
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
2014-10-08 12:00:53 +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 1;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
2016-02-06 08:44:34 +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
|
|
|
* if there is content supposed to be coming,
|
|
|
|
* put a timeout on it having arrived
|
2016-02-06 08:44:34 +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
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
2016-02-15 20:36:02 +08:00
|
|
|
wsi->context->timeout_secs);
|
2018-04-11 13:39:42 +08:00
|
|
|
#ifdef LWS_WITH_TLS
|
2016-03-17 15:26:49 +08:00
|
|
|
if (wsi->redirect_to_https) {
|
|
|
|
/*
|
2016-03-18 23:55:59 +08:00
|
|
|
* we accepted http:// only so we could redirect to
|
2016-03-17 15:26:49 +08:00
|
|
|
* https://, so issue the redirect. Create the redirection
|
|
|
|
* URI from the host: header and ignore the path part
|
|
|
|
*/
|
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
|
|
|
|
*end = p + 512;
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
|
|
|
|
goto bail_nuke_ah;
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2016-04-07 10:08:35 +08:00
|
|
|
n = sprintf((char *)end, "https://%s/",
|
2016-03-17 15:26:49 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2016-04-25 10:04:49 +08:00
|
|
|
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
|
|
|
|
end, n, &p, end);
|
2016-03-18 23:55:59 +08:00
|
|
|
if ((int)n < 0)
|
2016-03-17 15:26:49 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
#endif
|
2016-03-17 15:26:49 +08:00
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
2017-09-06 09:30:32 +08:00
|
|
|
lws_prepare_access_log_info(wsi, uri_ptr, meth);
|
2016-04-15 12:00:23 +08:00
|
|
|
#endif
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
/* can we serve it from the mount list? */
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
hit = lws_find_mount(wsi, uri_ptr, uri_len);
|
|
|
|
if (!hit) {
|
|
|
|
/* deferred cleanup and reset to protocols[0] */
|
2016-04-08 13:25:34 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
lwsl_info("no hit\n");
|
2016-04-08 18:30:45 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
|
|
|
|
return 1;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
goto after;
|
|
|
|
}
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
s = uri_ptr + hit->mountpoint_len;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
/*
|
|
|
|
* if we have a mountpoint like https://xxx.com/yyy
|
|
|
|
* there is an implied / at the end for our purposes since
|
|
|
|
* we can only mount on a "directory".
|
|
|
|
*
|
|
|
|
* But if we just go with that, the browser cannot understand
|
|
|
|
* that he is actually looking down one "directory level", so
|
|
|
|
* even though we give him /yyy/abc.html he acts like the
|
|
|
|
* current directory level is /. So relative urls like "x.png"
|
|
|
|
* wrongly look outside the mountpoint.
|
|
|
|
*
|
|
|
|
* Therefore if we didn't come in on a url with an explicit
|
|
|
|
* / at the end, we must redirect to add it so the browser
|
|
|
|
* understands he is one "directory level" down.
|
|
|
|
*/
|
|
|
|
if ((hit->mountpoint_len > 1 ||
|
|
|
|
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
|
|
|
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
|
|
|
|
(*s != '/' ||
|
|
|
|
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
|
|
|
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
|
|
|
|
(hit->origin_protocol != LWSMPRO_CGI &&
|
2017-09-23 12:55:21 +08:00
|
|
|
hit->origin_protocol != LWSMPRO_CALLBACK)) {
|
2016-06-26 06:29:20 +08:00
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE,
|
|
|
|
*p = start, *end = p + 512;
|
|
|
|
|
|
|
|
lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
|
|
|
|
|
|
|
|
/* > at start indicates deal with by redirect */
|
|
|
|
if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
|
|
|
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
|
2016-09-15 02:22:57 +08:00
|
|
|
n = lws_snprintf((char *)end, 256, "%s%s",
|
2016-06-26 06:29:20 +08:00
|
|
|
oprot[hit->origin_protocol & 1],
|
|
|
|
hit->origin);
|
2017-10-13 10:33:02 +08:00
|
|
|
else {
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
2017-10-16 12:52:32 +08:00
|
|
|
if (!lws_hdr_total_length(wsi,
|
|
|
|
WSI_TOKEN_HTTP_COLON_AUTHORITY))
|
2017-10-13 10:33:02 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
n = lws_snprintf((char *)end, 256,
|
|
|
|
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_COLON_AUTHORITY),
|
2017-10-13 10:33:02 +08:00
|
|
|
uri_ptr);
|
|
|
|
} else
|
|
|
|
n = lws_snprintf((char *)end, 256,
|
|
|
|
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
|
|
|
|
uri_ptr);
|
|
|
|
}
|
2017-07-21 20:04:02 +08:00
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
lws_clean_url((char *)end);
|
2016-06-26 06:29:20 +08:00
|
|
|
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
|
|
|
|
end, n, &p, end);
|
|
|
|
if ((int)n < 0)
|
|
|
|
goto bail_nuke_ah;
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
}
|
|
|
|
|
2016-12-03 15:13:15 +08:00
|
|
|
/* basic auth? */
|
|
|
|
|
|
|
|
if (hit->basic_auth_login_file) {
|
|
|
|
char b64[160], plain[(sizeof(b64) * 3) / 4];
|
|
|
|
int m;
|
|
|
|
|
|
|
|
/* Did he send auth? */
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION))
|
|
|
|
return lws_unauthorised_basic_auth(wsi);
|
|
|
|
|
|
|
|
n = HTTP_STATUS_FORBIDDEN;
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
m = lws_hdr_copy(wsi, b64, sizeof(b64),
|
|
|
|
WSI_TOKEN_HTTP_AUTHORIZATION);
|
2016-12-03 15:13:15 +08:00
|
|
|
if (m < 7) {
|
|
|
|
lwsl_err("b64 auth too long\n");
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
|
|
|
b64[5] = '\0';
|
|
|
|
if (strcasecmp(b64, "Basic")) {
|
|
|
|
lwsl_err("auth missing basic: %s\n", b64);
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
|
|
|
|
|
|
|
|
m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain));
|
|
|
|
if (m < 0) {
|
|
|
|
lwsl_err("plain auth too long\n");
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
if (!lws_find_string_in_file(hit->basic_auth_login_file,
|
|
|
|
plain, m)) {
|
2016-12-03 15:13:15 +08:00
|
|
|
lwsl_err("basic auth lookup failed\n");
|
|
|
|
return lws_unauthorised_basic_auth(wsi);
|
|
|
|
}
|
|
|
|
|
2018-04-19 08:41:16 +08:00
|
|
|
lwsl_info("basic auth accepted\n");
|
2016-12-03 15:13:15 +08:00
|
|
|
|
|
|
|
/* accept the auth */
|
|
|
|
}
|
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_PROXY)
|
|
|
|
/*
|
|
|
|
* The mount is a reverse proxy?
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (hit->origin_protocol == LWSMPRO_HTTPS ||
|
|
|
|
hit->origin_protocol == LWSMPRO_HTTP) {
|
|
|
|
struct lws_client_connect_info i;
|
|
|
|
char ads[96], rpath[256], *pcolon, *pslash, *p;
|
|
|
|
int n, na;
|
|
|
|
|
|
|
|
memset(&i, 0, sizeof(i));
|
|
|
|
i.context = lws_get_context(wsi);
|
|
|
|
|
|
|
|
pcolon = strchr(hit->origin, ':');
|
|
|
|
pslash = strchr(hit->origin, '/');
|
|
|
|
if (!pslash) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_err("Proxy mount origin '%s' must have /\n",
|
|
|
|
hit->origin);
|
2017-06-12 13:36:24 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pcolon > pslash)
|
|
|
|
pcolon = NULL;
|
|
|
|
|
|
|
|
if (pcolon)
|
|
|
|
n = pcolon - hit->origin;
|
|
|
|
else
|
|
|
|
n = pslash - hit->origin;
|
|
|
|
|
2017-10-20 17:45:02 +08:00
|
|
|
if (n >= (int)sizeof(ads) - 2)
|
2017-06-12 13:36:24 +08:00
|
|
|
n = sizeof(ads) - 2;
|
|
|
|
|
|
|
|
memcpy(ads, hit->origin, n);
|
|
|
|
ads[n] = '\0';
|
|
|
|
|
|
|
|
i.address = ads;
|
|
|
|
i.port = 80;
|
|
|
|
if (hit->origin_protocol == LWSMPRO_HTTPS) {
|
|
|
|
i.port = 443;
|
|
|
|
i.ssl_connection = 1;
|
|
|
|
}
|
|
|
|
if (pcolon)
|
|
|
|
i.port = atoi(pcolon + 1);
|
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1,
|
|
|
|
uri_ptr + hit->mountpoint_len);
|
2017-06-12 13:36:24 +08:00
|
|
|
lws_clean_url(rpath);
|
|
|
|
na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
|
|
|
|
if (na) {
|
|
|
|
p = rpath + strlen(rpath);
|
|
|
|
*p++ = '?';
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_hdr_copy(wsi, p, &rpath[sizeof(rpath) - 1] - p,
|
|
|
|
WSI_TOKEN_HTTP_URI_ARGS);
|
2017-06-12 13:36:24 +08:00
|
|
|
while (--na) {
|
|
|
|
if (*p == '\0')
|
|
|
|
*p = '&';
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i.path = rpath;
|
|
|
|
i.host = i.address;
|
|
|
|
i.origin = NULL;
|
|
|
|
i.method = "GET";
|
|
|
|
i.parent_wsi = wsi;
|
|
|
|
i.uri_replace_from = hit->origin;
|
|
|
|
i.uri_replace_to = hit->mountpoint;
|
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
lwsl_notice("proxying to %s port %d url %s, ssl %d, "
|
|
|
|
"from %s, to %s\n",
|
|
|
|
i.address, i.port, i.path, i.ssl_connection,
|
|
|
|
i.uri_replace_from, i.uri_replace_to);
|
2017-06-12 13:36:24 +08:00
|
|
|
|
|
|
|
if (!lws_client_connect_via_info(&i)) {
|
|
|
|
lwsl_err("proxy connect fail\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
/*
|
|
|
|
* A particular protocol callback is mounted here?
|
|
|
|
*
|
|
|
|
* For the duration of this http transaction, bind us to the
|
|
|
|
* associated protocol
|
|
|
|
*/
|
|
|
|
if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
|
|
|
|
const struct lws_protocols *pp;
|
|
|
|
const char *name = hit->origin;
|
|
|
|
if (hit->protocol)
|
|
|
|
name = hit->protocol;
|
|
|
|
|
|
|
|
pp = lws_vhost_name_to_protocol(wsi->vhost, name);
|
|
|
|
if (!pp) {
|
|
|
|
n = -1;
|
|
|
|
lwsl_err("Unable to find plugin '%s'\n",
|
|
|
|
hit->origin);
|
|
|
|
return 1;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-11-26 20:46:04 +08:00
|
|
|
args.p = uri_ptr;
|
|
|
|
args.len = uri_len;
|
|
|
|
args.max_len = hit->auth_mask;
|
|
|
|
args.final = 0; /* used to signal callback dealt with it */
|
2018-01-14 20:09:41 +08:00
|
|
|
args.chunked = 0;
|
2016-11-26 20:46:04 +08:00
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
n = wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
|
2016-11-26 20:46:04 +08:00
|
|
|
wsi->user_space, &args, 0);
|
|
|
|
if (n) {
|
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
|
|
|
|
NULL);
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
if (args.final) /* callback completely handled it well */
|
|
|
|
return 0;
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (hit->cgienv && wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_PMO,
|
|
|
|
wsi->user_space, (void *)hit->cgienv, 0))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
|
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space,
|
|
|
|
uri_ptr + hit->mountpoint_len,
|
|
|
|
uri_len - hit->mountpoint_len);
|
2016-05-09 09:37:01 +08:00
|
|
|
goto after;
|
|
|
|
}
|
2016-06-26 06:29:20 +08:00
|
|
|
}
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
2016-06-26 06:29:20 +08:00
|
|
|
/* did we hit something with a cgi:// origin? */
|
|
|
|
if (hit->origin_protocol == LWSMPRO_CGI) {
|
|
|
|
const char *cmd[] = {
|
|
|
|
NULL, /* replace with cgi path */
|
|
|
|
NULL
|
|
|
|
};
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
lwsl_debug("%s: cgi\n", __func__);
|
|
|
|
cmd[0] = hit->origin;
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = 5;
|
|
|
|
if (hit->cgi_timeout)
|
|
|
|
n = hit->cgi_timeout;
|
|
|
|
|
|
|
|
n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
|
|
|
|
hit->cgienv);
|
|
|
|
if (n) {
|
2017-02-05 22:07:34 +08:00
|
|
|
lwsl_err("%s: cgi failed\n", __func__);
|
2016-06-26 06:29:20 +08:00
|
|
|
return -1;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
goto deal_body;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2017-10-25 08:00:23 +08:00
|
|
|
n = (int)strlen(s);
|
2016-06-26 06:29:20 +08:00
|
|
|
if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
|
|
|
|
s = (char *)hit->def;
|
|
|
|
if (!s)
|
|
|
|
s = "index.html";
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
wsi->cache_secs = hit->cache_max_age;
|
|
|
|
wsi->cache_reuse = hit->cache_reusable;
|
|
|
|
wsi->cache_revalidate = hit->cache_revalidate;
|
|
|
|
wsi->cache_intermediaries = hit->cache_intermediaries;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2018-03-20 10:31:53 +08:00
|
|
|
n = 1;
|
|
|
|
if (hit->origin_protocol == LWSMPRO_FILE)
|
|
|
|
n = lws_http_serve(wsi, s, hit->origin, hit);
|
2016-06-26 06:29:20 +08:00
|
|
|
if (n) {
|
|
|
|
/*
|
2017-11-06 10:05:04 +08:00
|
|
|
* lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
2016-06-26 06:29:20 +08:00
|
|
|
*/
|
|
|
|
if (hit->protocol) {
|
2017-11-06 10:05:04 +08:00
|
|
|
const struct lws_protocols *pp =
|
|
|
|
lws_vhost_name_to_protocol(
|
|
|
|
wsi->vhost, hit->protocol);
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = pp->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space,
|
|
|
|
uri_ptr + hit->mountpoint_len,
|
|
|
|
uri_len - hit->mountpoint_len);
|
|
|
|
} else
|
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
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
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
2016-05-09 09:37:01 +08:00
|
|
|
}
|
2016-06-26 06:29:20 +08:00
|
|
|
|
2016-05-09 09:37:01 +08:00
|
|
|
after:
|
2016-02-25 20:27:10 +08:00
|
|
|
if (n) {
|
|
|
|
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
deal_body:
|
|
|
|
#endif
|
2015-12-14 08:52:03 +08:00
|
|
|
/*
|
2014-10-08 12:00:53 +08:00
|
|
|
* If we're not issuing a file, check for content_length or
|
|
|
|
* HTTP keep-alive. No keep-alive header allocation for
|
2015-12-14 08:52:03 +08:00
|
|
|
* ISSUING_FILE, as this uses HTTP/1.0.
|
|
|
|
*
|
2015-12-04 08:43:54 +08:00
|
|
|
* In any case, return 0 and let lws_read decide how to
|
2014-10-08 12:00:53 +08:00
|
|
|
* proceed based on state
|
|
|
|
*/
|
2018-04-02 11:55:17 +08:00
|
|
|
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
|
2014-10-08 12:00:53 +08:00
|
|
|
/* Prepare to read body if we have a content length: */
|
2017-12-01 11:09:32 +08:00
|
|
|
lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
|
|
|
|
(long long)wsi->http.rx_content_length,
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->upgraded_to_http2, wsi->http2_substream);
|
2017-12-01 11:09:32 +08:00
|
|
|
if (wsi->http.rx_content_length > 0) {
|
2018-04-17 15:35:15 +08:00
|
|
|
struct lws_tokens ebuf;
|
|
|
|
int m;
|
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_BODY);
|
|
|
|
lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
|
|
|
|
__func__, wsi, wsi->wsistate);
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.rx_content_remain =
|
|
|
|
wsi->http.rx_content_length;
|
2018-04-17 15:35:15 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we have transitioned from deferred
|
|
|
|
* action to expecting BODY on the stream wsi, if it's
|
|
|
|
* in a bundle like h2. So if the stream wsi has its
|
|
|
|
* own buflist, we need to deal with that first.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ebuf.len = lws_buflist_next_segment_len(
|
|
|
|
&wsi->buflist, (uint8_t **)&ebuf.token);
|
|
|
|
if (!ebuf.len)
|
|
|
|
break;
|
|
|
|
lwsl_notice("%s: consuming %d\n", __func__, (int)ebuf.len);
|
|
|
|
m = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
|
|
|
|
if (m < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
lws_buflist_aware_consume(wsi, &ebuf, m, 1);
|
|
|
|
}
|
2017-10-13 10:33:02 +08:00
|
|
|
}
|
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bail_nuke_ah:
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_detach(wsi, 1);
|
2014-12-05 00:09:20 +01:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
return 1;
|
2017-09-23 12:55:21 +08:00
|
|
|
|
2016-12-03 15:13:15 +08:00
|
|
|
transaction_result_n:
|
|
|
|
lws_return_http_status(wsi, n, NULL);
|
2016-12-22 11:32:34 +08:00
|
|
|
|
2016-12-03 15:13:15 +08:00
|
|
|
return lws_http_transaction_completed(wsi);
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
int
|
|
|
|
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
2014-10-08 12:00:53 +08:00
|
|
|
{
|
2015-12-17 18:25:25 +08:00
|
|
|
struct lws_context *context = lws_get_context(wsi);
|
2017-03-07 16:06:05 +08:00
|
|
|
unsigned char *obuf = *buf;
|
2018-03-11 11:26:06 +08:00
|
|
|
#if defined(LWS_WITH_HTTP2)
|
|
|
|
char tbuf[128], *p;
|
|
|
|
#endif
|
2017-03-07 16:06:05 +08:00
|
|
|
size_t olen = len;
|
2018-03-11 11:26:06 +08:00
|
|
|
int n = 0, m, i;
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2016-05-13 10:27:48 +08:00
|
|
|
if (len >= 10000000) {
|
|
|
|
lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
if (!wsi->http.ah) {
|
2016-05-13 10:27:48 +08:00
|
|
|
lwsl_err("%s: assert: NULL ah\n", __func__);
|
|
|
|
assert(0);
|
|
|
|
}
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2018-03-07 18:15:17 +08:00
|
|
|
while (len) {
|
2018-04-11 13:39:42 +08:00
|
|
|
if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsl_err("%s: bad wsi role 0x%x\n", __func__,
|
|
|
|
lwsi_role(wsi));
|
2016-04-13 11:42:53 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2016-01-21 10:54:14 +08:00
|
|
|
|
2018-03-07 18:15:17 +08:00
|
|
|
i = (int)len;
|
|
|
|
m = lws_parse(wsi, *buf, &i);
|
2018-04-11 13:39:42 +08:00
|
|
|
lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
|
2018-03-07 18:15:17 +08:00
|
|
|
(*buf) += (int)len - i;
|
2018-03-11 11:26:06 +08:00
|
|
|
len = i;
|
2017-03-07 16:06:05 +08:00
|
|
|
if (m) {
|
|
|
|
if (m == 2) {
|
|
|
|
/*
|
|
|
|
* we are transitioning from http with
|
|
|
|
* an AH, to raw. Drop the ah and set
|
|
|
|
* the mode.
|
|
|
|
*/
|
|
|
|
raw_transition:
|
|
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
|
|
|
lws_bind_protocol(wsi, &wsi->vhost->protocols[
|
|
|
|
wsi->vhost->
|
|
|
|
raw_protocol_index]);
|
|
|
|
lwsl_info("transition to raw vh %s prot %d\n",
|
|
|
|
wsi->vhost->name,
|
|
|
|
wsi->vhost->raw_protocol_index);
|
|
|
|
if ((wsi->protocol->callback)(wsi,
|
|
|
|
LWS_CALLBACK_RAW_ADOPT,
|
|
|
|
wsi->user_space, NULL, 0))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(wsi, 0, LRS_ESTABLISHED,
|
|
|
|
&role_ops_raw_skt);
|
2017-03-07 16:06:05 +08:00
|
|
|
lws_header_table_detach(wsi, 1);
|
|
|
|
|
|
|
|
if (m == 2 && (wsi->protocol->callback)(wsi,
|
|
|
|
LWS_CALLBACK_RAW_RX,
|
|
|
|
wsi->user_space, obuf, olen))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-04 09:23:56 +08:00
|
|
|
lwsl_info("lws_parse failed\n");
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2018-04-27 15:20:56 +08:00
|
|
|
if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
|
2014-04-03 09:03:37 +08:00
|
|
|
continue;
|
|
|
|
|
2016-02-28 10:55:31 +08:00
|
|
|
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2017-09-06 09:30:32 +08:00
|
|
|
/* select vhost */
|
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
|
|
|
struct lws_vhost *vhost = lws_select_vhost(
|
|
|
|
context, wsi->vhost->listen_port,
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
|
|
|
|
|
|
|
if (vhost)
|
|
|
|
wsi->vhost = vhost;
|
|
|
|
} else
|
|
|
|
lwsl_info("no host\n");
|
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
|
2017-10-13 10:33:02 +08:00
|
|
|
wsi->vhost->conn_stats.h1_trans++;
|
|
|
|
if (!wsi->conn_stat_done) {
|
|
|
|
wsi->vhost->conn_stats.h1_conn++;
|
|
|
|
wsi->conn_stat_done = 1;
|
|
|
|
}
|
2017-09-06 09:30:32 +08:00
|
|
|
}
|
|
|
|
|
2016-10-13 06:32:57 +08:00
|
|
|
/* check for unwelcome guests */
|
|
|
|
|
|
|
|
if (wsi->context->reject_service_keywords) {
|
|
|
|
const struct lws_protocol_vhost_options *rej =
|
|
|
|
wsi->context->reject_service_keywords;
|
|
|
|
char ua[384], *msg = NULL;
|
|
|
|
|
|
|
|
if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
|
2017-09-06 09:30:32 +08:00
|
|
|
WSI_TOKEN_HTTP_USER_AGENT) > 0) {
|
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
2017-11-06 10:05:04 +08:00
|
|
|
char *uri_ptr = NULL;
|
|
|
|
int meth, uri_len;
|
2017-09-06 09:30:32 +08:00
|
|
|
#endif
|
2017-11-06 10:05:04 +08:00
|
|
|
ua[sizeof(ua) - 1] = '\0';
|
|
|
|
while (rej) {
|
|
|
|
if (!strstr(ua, rej->name)) {
|
|
|
|
rej = rej->next;
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-06 09:30:32 +08:00
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
msg = strchr(rej->value, ' ');
|
|
|
|
if (msg)
|
|
|
|
msg++;
|
|
|
|
lws_return_http_status(wsi,
|
|
|
|
atoi(rej->value), msg);
|
2017-09-06 09:30:32 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
2017-11-06 10:05:04 +08:00
|
|
|
meth = lws_http_get_uri_and_method(wsi,
|
|
|
|
&uri_ptr, &uri_len);
|
|
|
|
if (meth >= 0)
|
|
|
|
lws_prepare_access_log_info(wsi,
|
2017-09-06 09:30:32 +08:00
|
|
|
uri_ptr, meth);
|
2016-10-13 06:32:57 +08:00
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
/* wsi close will do the log */
|
2017-09-06 09:30:32 +08:00
|
|
|
#endif
|
2017-11-06 10:05:04 +08:00
|
|
|
wsi->vhost->conn_stats.rejected++;
|
|
|
|
/*
|
|
|
|
* We don't want anything from
|
|
|
|
* this rejected guy. Follow
|
|
|
|
* the close flow, not the
|
|
|
|
* transaction complete flow.
|
|
|
|
*/
|
|
|
|
goto bail_nuke_ah;
|
2016-10-13 06:32:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 14:01:29 +08:00
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
|
|
|
|
lwsl_info("Changing to RAW mode\n");
|
2017-03-07 16:06:05 +08:00
|
|
|
m = 0;
|
|
|
|
goto raw_transition;
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
|
|
|
/* is this websocket protocol or normal http 1.0? */
|
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
|
2017-11-06 10:05:04 +08:00
|
|
|
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_UPGRADE),
|
2016-01-21 10:54:14 +08:00
|
|
|
"websocket")) {
|
2018-04-25 08:42:18 +08:00
|
|
|
#if defined(LWS_ROLE_WS)
|
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
|
|
|
wsi->vhost->conn_stats.ws_upg++;
|
2016-01-21 10:54:14 +08:00
|
|
|
lwsl_info("Upgrade to ws\n");
|
|
|
|
goto upgrade_ws;
|
2018-04-25 08:42:18 +08:00
|
|
|
#endif
|
2016-01-21 10:54:14 +08:00
|
|
|
}
|
2017-11-14 07:35:05 +08:00
|
|
|
#if defined(LWS_WITH_HTTP2)
|
2017-11-06 10:05:04 +08:00
|
|
|
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_UPGRADE),
|
2016-04-10 09:33:54 +08:00
|
|
|
"h2c")) {
|
2017-10-13 10:33:02 +08:00
|
|
|
wsi->vhost->conn_stats.h2_upg++;
|
2016-04-10 09:33:54 +08:00
|
|
|
lwsl_info("Upgrade to h2c\n");
|
2016-01-21 10:54:14 +08:00
|
|
|
goto upgrade_h2c;
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("Unknown upgrade\n");
|
2016-01-21 10:54:14 +08:00
|
|
|
/* dunno what he wanted to upgrade to */
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
/* no upgrade ack... he remained as HTTP */
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
lwsl_info("No upgrade\n");
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.fop_fd = NULL;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
|
2018-04-27 15:20:56 +08:00
|
|
|
(void *)wsi->http.ah);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
n = lws_http_action(wsi);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
return n;
|
2014-09-30 09:43:14 +08:00
|
|
|
|
2017-11-14 07:35:05 +08:00
|
|
|
#if defined(LWS_WITH_HTTP2)
|
2014-10-08 12:00:53 +08:00
|
|
|
upgrade_h2c:
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("missing http2_settings\n");
|
2014-09-30 09:43:14 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("h2c upgrade...\n");
|
2014-09-30 09:43:14 +08:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
|
|
|
|
/* convert the peer's HTTP-Settings */
|
2018-03-11 11:26:06 +08:00
|
|
|
n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
|
2014-10-08 12:00:53 +08:00
|
|
|
if (n < 0) {
|
|
|
|
lwsl_parser("HTTP2_SETTINGS too long\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* adopt the header info */
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if (!wsi->h2.h2n) {
|
|
|
|
wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
|
2017-11-06 10:05:04 +08:00
|
|
|
"h2n");
|
2017-12-01 11:09:32 +08:00
|
|
|
if (!wsi->h2.h2n)
|
2017-10-13 10:33:02 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_h2_init(wsi);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* HTTP2 union */
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2018-03-11 11:26:06 +08:00
|
|
|
lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
|
2017-10-16 12:52:32 +08:00
|
|
|
H2SET_HEADER_TABLE_SIZE]);
|
2017-10-13 10:33:02 +08:00
|
|
|
|
2018-03-11 11:26:06 +08:00
|
|
|
strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
|
|
|
"Connection: Upgrade\x0d\x0a"
|
|
|
|
"Upgrade: h2c\x0d\x0a\x0d\x0a");
|
|
|
|
m = (int)strlen(tbuf);
|
|
|
|
n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
|
|
|
|
if (n != m) {
|
2014-10-08 12:00:53 +08:00
|
|
|
lwsl_debug("http2 switch: ERROR writing to socket\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_H2_AWAIT_PREFACE);
|
2017-11-14 07:35:05 +08:00
|
|
|
wsi->upgraded_to_http2 = 1;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-09-30 09:43:14 +08:00
|
|
|
return 0;
|
2014-10-08 12:00:53 +08:00
|
|
|
#endif
|
2018-04-25 08:42:18 +08:00
|
|
|
#if defined(LWS_ROLE_WS)
|
2014-09-30 08:15:49 +08:00
|
|
|
upgrade_ws:
|
2018-03-11 11:26:06 +08:00
|
|
|
if (lws_process_ws_upgrade(wsi))
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
return 0;
|
2018-04-25 08:42:18 +08:00
|
|
|
#endif
|
2014-04-03 09:03:37 +08:00
|
|
|
} /* while all chars are handled */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bail_nuke_ah:
|
|
|
|
/* drop the header info */
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_detach(wsi, 1);
|
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
|
|
|
|
2014-04-03 09:03:37 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-09-14 13:14:11 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
static int
|
|
|
|
lws_get_idlest_tsi(struct lws_context *context)
|
|
|
|
{
|
|
|
|
unsigned int lowest = ~0;
|
|
|
|
int n = 0, hit = -1;
|
|
|
|
|
|
|
|
for (; n < context->count_threads; n++) {
|
2016-02-21 21:25:48 +08:00
|
|
|
if ((unsigned int)context->pt[n].fds_count !=
|
|
|
|
context->fd_limit_per_thread - 1 &&
|
2016-01-26 20:56:56 +08:00
|
|
|
(unsigned int)context->pt[n].fds_count < lowest) {
|
|
|
|
lowest = context->pt[n].fds_count;
|
|
|
|
hit = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hit;
|
|
|
|
}
|
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *
|
2018-04-27 12:49:42 +08:00
|
|
|
lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
|
2013-01-18 11:43:21 +08:00
|
|
|
{
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *new_wsi;
|
2018-04-27 12:49:42 +08:00
|
|
|
int n = fixed_tsi;
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
n = lws_get_idlest_tsi(vhost->context);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
lwsl_err("no space for new conn\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2017-10-04 07:10:39 +08:00
|
|
|
new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi");
|
2013-01-18 11:43:21 +08:00
|
|
|
if (new_wsi == NULL) {
|
|
|
|
lwsl_err("Out of memory for new connection\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
new_wsi->tsi = n;
|
2017-10-13 10:33:02 +08:00
|
|
|
lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
|
|
|
|
vhost->name, new_wsi->tsi);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->vhost = vhost;
|
|
|
|
new_wsi->context = vhost->context;
|
2013-01-18 11:43:21 +08:00
|
|
|
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
2014-10-08 12:00:53 +08:00
|
|
|
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2016-10-02 02:21:03 +03:00
|
|
|
/* initialize the instance struct */
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(new_wsi, LRS_UNCONNECTED);
|
2013-02-11 21:43:41 +08:00
|
|
|
new_wsi->hdr_parsing_completed = 0;
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
#ifdef LWS_WITH_TLS
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
|
2015-03-28 10:20:50 +08:00
|
|
|
#endif
|
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
/*
|
|
|
|
* these can only be set once the protocol is known
|
2017-10-13 10:33:02 +08:00
|
|
|
* we set an un-established connection's protocol pointer
|
2013-01-18 11:43:21 +08:00
|
|
|
* to the start of the supported list, so it can look
|
|
|
|
* for matching ones during the handshake
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->protocol = vhost->protocols;
|
2013-01-18 11:43:21 +08:00
|
|
|
new_wsi->user_space = NULL;
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
|
2017-07-19 04:39:14 +08:00
|
|
|
new_wsi->position_in_fds_table = -1;
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->context->count_wsi_allocated++;
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2014-02-15 19:25:50 +08:00
|
|
|
/*
|
|
|
|
* outermost create notification for wsi
|
|
|
|
* no user_space because no protocol selection
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
|
2016-01-26 20:56:56 +08:00
|
|
|
NULL, NULL, 0);
|
2014-02-15 19:25:50 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
return new_wsi;
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:57:39 +08:00
|
|
|
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
|
|
|
|
lws_http_transaction_completed(struct lws *wsi)
|
2014-10-17 08:38:44 +08:00
|
|
|
{
|
2016-04-12 16:26:03 +08:00
|
|
|
int n = NO_PENDING_TIMEOUT;
|
|
|
|
|
2017-10-13 10:33:02 +08:00
|
|
|
lwsl_info("%s: wsi %p\n", __func__, wsi);
|
2018-04-17 15:35:15 +08:00
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_access_log(wsi);
|
|
|
|
|
2017-06-28 09:58:44 +08:00
|
|
|
if (!wsi->hdr_parsing_completed) {
|
|
|
|
lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
/* if we can't go back to accept new headers, drop the connection */
|
2017-10-13 10:33:02 +08:00
|
|
|
if (wsi->http2_substream)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (wsi->seen_zero_length_recv)
|
|
|
|
return 1;
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if (wsi->http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
|
2016-01-26 20:56:56 +08:00
|
|
|
lwsl_info("%s: %p: close connection\n", __func__, wsi);
|
2014-10-17 08:38:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-18 09:00:04 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
|
|
|
|
return 1;
|
2016-06-08 10:07:02 +08:00
|
|
|
|
2017-12-07 18:48:21 +08:00
|
|
|
/*
|
|
|
|
* otherwise set ourselves up ready to go again, but because we have no
|
|
|
|
* idea about the wsi writability, we make put it in a holding state
|
|
|
|
* until we can verify POLLOUT. The part of this that confirms POLLOUT
|
|
|
|
* with no partials is in lws_server_socket_service() below.
|
|
|
|
*/
|
2018-04-17 11:43:20 +08:00
|
|
|
lwsl_debug("%s: setting DEF_ACT from 0x%x\n", __func__, wsi->wsistate);
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.tx_content_length = 0;
|
|
|
|
wsi->http.tx_content_remain = 0;
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
wsi->hdr_parsing_completed = 0;
|
2016-04-20 06:10:56 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
2018-04-27 19:16:50 +08:00
|
|
|
wsi->http.access_log.sent = 0;
|
2016-04-20 06:10:56 +08:00
|
|
|
#endif
|
2016-04-12 16:26:03 +08:00
|
|
|
|
|
|
|
if (wsi->vhost->keepalive_timeout)
|
|
|
|
n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
|
|
|
|
lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
|
2015-10-21 08:16:34 +08:00
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
/*
|
|
|
|
* We already know we are on http1.1 / keepalive and the next thing
|
|
|
|
* coming will be another header set.
|
|
|
|
*
|
|
|
|
* If there is no pending rx and we still have the ah, drop it and
|
|
|
|
* reacquire a new ah when the new headers start to arrive. (Otherwise
|
|
|
|
* we needlessly hog an ah indefinitely.)
|
|
|
|
*
|
|
|
|
* However if there is pending rx and we know from the keepalive state
|
|
|
|
* that is already at least the start of another header set, simply
|
|
|
|
* reset the existing header table and keep it.
|
2016-01-29 09:06:22 +08:00
|
|
|
*/
|
2018-04-27 15:20:56 +08:00
|
|
|
if (wsi->http.ah) {
|
2018-04-17 15:35:15 +08:00
|
|
|
// lws_buflist_describe(&wsi->buflist, wsi);
|
|
|
|
if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
|
|
|
|
lwsl_debug("%s: nothing in buflist so detaching ah\n", __func__);
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_detach(wsi, 1);
|
2018-04-11 13:39:42 +08:00
|
|
|
#ifdef LWS_WITH_TLS
|
2017-03-16 10:46:31 +08:00
|
|
|
/*
|
|
|
|
* additionally... if we are hogging an SSL instance
|
|
|
|
* with no pending pipelined headers (or ah now), and
|
|
|
|
* SSL is scarce, drop this connection without waiting
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (wsi->vhost->use_ssl &&
|
2017-04-07 20:51:44 +08:00
|
|
|
wsi->context->simultaneous_ssl_restriction &&
|
|
|
|
wsi->context->simultaneous_ssl ==
|
|
|
|
wsi->context->simultaneous_ssl_restriction) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_info("%s: simultaneous_ssl_restriction\n",
|
|
|
|
__func__);
|
2017-03-16 10:46:31 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
2017-08-15 07:58:53 +08:00
|
|
|
} else {
|
2018-04-17 15:35:15 +08:00
|
|
|
lwsl_debug("%s: resetting and keeping ah as more pipeline stuff available\n", __func__);
|
2018-01-14 10:18:32 +08:00
|
|
|
lws_header_table_reset(wsi, 0);
|
2017-08-15 07:58:53 +08:00
|
|
|
/*
|
|
|
|
* If we kept the ah, we should restrict the amount
|
|
|
|
* of time we are willing to keep it. Otherwise it
|
|
|
|
* will be bound the whole time the connection remains
|
|
|
|
* open.
|
|
|
|
*/
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
|
|
|
|
wsi->vhost->keepalive_timeout);
|
|
|
|
}
|
2017-12-01 11:09:32 +08:00
|
|
|
/* If we're (re)starting on headers, need other implied init */
|
2018-04-27 15:20:56 +08:00
|
|
|
if (wsi->http.ah)
|
|
|
|
wsi->http.ah->ues = URIES_IDLE;
|
2018-01-14 11:32:45 +08:00
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
2018-01-14 10:18:32 +08:00
|
|
|
} else
|
2018-04-17 15:35:15 +08:00
|
|
|
if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
|
2018-01-14 10:18:32 +08:00
|
|
|
if (lws_header_table_attach(wsi, 0))
|
|
|
|
lwsl_debug("acquired ah\n");
|
|
|
|
|
2015-10-21 08:16:34 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
|
2017-12-07 18:48:21 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
/* if not a socket, it's a raw, non-ssl file descriptor */
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
2017-03-03 07:36:08 +08:00
|
|
|
lws_sock_file_fd_type fd, const char *vh_prot_name,
|
|
|
|
struct lws *parent)
|
2016-01-19 03:34:24 +08:00
|
|
|
{
|
2016-04-08 18:30:45 +08:00
|
|
|
struct lws_context *context = vh->context;
|
2017-09-14 13:14:11 +08:00
|
|
|
struct lws *new_wsi;
|
2017-05-07 10:02:03 +08:00
|
|
|
struct lws_context_per_thread *pt;
|
2017-02-27 12:55:56 +08:00
|
|
|
int n, ssl = 0;
|
2016-01-29 21:18:54 +08:00
|
|
|
|
2017-09-14 13:14:11 +08:00
|
|
|
#if defined(LWS_WITH_PEER_LIMITS)
|
|
|
|
struct lws_peer *peer = NULL;
|
|
|
|
|
|
|
|
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
|
|
|
|
peer = lws_get_or_create_peer(vh, fd.sockfd);
|
|
|
|
|
2018-03-24 08:07:00 +08:00
|
|
|
if (peer && context->ip_limit_wsi &&
|
2017-09-14 13:14:11 +08:00
|
|
|
peer->count_wsi >= context->ip_limit_wsi) {
|
|
|
|
lwsl_notice("Peer reached wsi limit %d\n",
|
|
|
|
context->ip_limit_wsi);
|
|
|
|
lws_stats_atomic_bump(context, &context->pt[0],
|
|
|
|
LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-27 12:49:42 +08:00
|
|
|
n = -1;
|
|
|
|
if (parent)
|
|
|
|
n = parent->tsi;
|
|
|
|
new_wsi = lws_create_new_server_wsi(vh, n);
|
2016-01-26 20:56:56 +08:00
|
|
|
if (!new_wsi) {
|
2017-07-19 04:39:14 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO))
|
2017-02-27 12:55:56 +08:00
|
|
|
compatible_close(fd.sockfd);
|
2016-01-26 20:56:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2017-09-14 13:14:11 +08:00
|
|
|
#if defined(LWS_WITH_PEER_LIMITS)
|
|
|
|
if (peer)
|
|
|
|
lws_peer_add_wsi(context, peer, new_wsi);
|
|
|
|
#endif
|
2017-05-07 10:02:03 +08:00
|
|
|
pt = &context->pt[(int)new_wsi->tsi];
|
|
|
|
lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1);
|
2016-01-19 03:34:24 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (parent) {
|
|
|
|
new_wsi->parent = parent;
|
|
|
|
new_wsi->sibling_list = parent->child_list;
|
|
|
|
parent->child_list = new_wsi;
|
2017-07-19 04:39:14 +08:00
|
|
|
|
|
|
|
if (type & LWS_ADOPT_WS_PARENTIO)
|
|
|
|
new_wsi->parent_carries_io = 1;
|
2017-03-03 07:36:08 +08:00
|
|
|
}
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->desc = fd;
|
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (vh_prot_name) {
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
|
2017-03-03 07:36:08 +08:00
|
|
|
vh_prot_name);
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!new_wsi->protocol) {
|
|
|
|
lwsl_err("Protocol %s not enabled on vhost %s\n",
|
|
|
|
vh_prot_name, new_wsi->vhost->name);
|
2017-03-03 07:36:08 +08:00
|
|
|
goto bail;
|
2017-02-27 12:55:56 +08:00
|
|
|
}
|
2017-05-15 08:10:08 +08:00
|
|
|
if (lws_ensure_user_space(new_wsi)) {
|
|
|
|
lwsl_notice("OOM trying to get user_space\n");
|
2017-03-03 07:36:08 +08:00
|
|
|
goto bail;
|
2017-05-15 08:10:08 +08:00
|
|
|
}
|
2018-04-25 08:42:18 +08:00
|
|
|
#if defined(LWS_ROLE_WS)
|
2017-07-19 04:39:14 +08:00
|
|
|
if (type & LWS_ADOPT_WS_PARENTIO) {
|
|
|
|
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
|
|
|
|
lwsl_debug("binding to %s\n", new_wsi->protocol->name);
|
|
|
|
lws_bind_protocol(new_wsi, new_wsi->protocol);
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, LWSIFR_SERVER,
|
|
|
|
LRS_ESTABLISHED, &role_ops_ws);
|
2017-12-01 11:09:32 +08:00
|
|
|
/* allocate the ws struct for the wsi */
|
|
|
|
new_wsi->ws = lws_zalloc(sizeof(*new_wsi->ws), "ws struct");
|
|
|
|
if (!new_wsi->ws) {
|
|
|
|
lwsl_notice("OOM\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
2017-07-19 04:39:14 +08:00
|
|
|
lws_server_init_wsi_for_ws(new_wsi);
|
|
|
|
|
|
|
|
return new_wsi;
|
|
|
|
}
|
2018-04-25 08:42:18 +08:00
|
|
|
#endif
|
2017-03-03 07:36:08 +08:00
|
|
|
} else
|
2018-04-27 19:16:50 +08:00
|
|
|
#if defined(LWS_ROLE_H1)
|
2018-04-11 13:39:42 +08:00
|
|
|
if (type & LWS_ADOPT_HTTP) {/* he will transition later */
|
2017-04-06 12:47:42 +08:00
|
|
|
new_wsi->protocol =
|
|
|
|
&vh->protocols[vh->default_protocol_index];
|
2018-04-11 13:39:42 +08:00
|
|
|
new_wsi->role_ops = &role_ops_h1;
|
|
|
|
}
|
2018-04-27 19:16:50 +08:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{ /* this is the only time he will transition */
|
2017-04-06 12:47:42 +08:00
|
|
|
lws_bind_protocol(new_wsi,
|
|
|
|
&vh->protocols[vh->raw_protocol_index]);
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, 0, LRS_ESTABLISHED,
|
|
|
|
&role_ops_raw_skt);
|
2017-04-06 12:47:42 +08:00
|
|
|
}
|
2017-02-27 12:55:56 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET) { /* socket desc */
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
|
2017-07-07 08:32:04 +08:00
|
|
|
(int)(lws_intptr_t)fd.sockfd);
|
2018-04-12 15:56:38 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2018-03-24 08:07:00 +08:00
|
|
|
if (type & LWS_ADOPT_FLAG_UDP)
|
|
|
|
/*
|
|
|
|
* these can be >128 bytes, so just alloc for UDP
|
|
|
|
*/
|
|
|
|
new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp),
|
|
|
|
"udp struct");
|
2018-04-12 15:56:38 +08:00
|
|
|
#endif
|
2018-03-24 08:07:00 +08:00
|
|
|
|
2017-04-06 12:47:42 +08:00
|
|
|
if (type & LWS_ADOPT_HTTP)
|
|
|
|
/* the transport is accepted...
|
|
|
|
* give him time to negotiate */
|
|
|
|
lws_set_timeout(new_wsi,
|
|
|
|
PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
|
|
|
|
context->timeout_secs);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
} else /* file desc */
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
|
2017-07-07 08:32:04 +08:00
|
|
|
(int)(lws_intptr_t)fd.filefd);
|
2017-02-27 12:55:56 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
/*
|
|
|
|
* A new connection was accepted. Give the user a chance to
|
|
|
|
* set properties of the newly created wsi. There's no protocol
|
2017-02-12 20:32:49 +08:00
|
|
|
* selected yet so we issue this to the vhosts's default protocol,
|
|
|
|
* itself by default protocols[0]
|
2016-01-26 20:56:56 +08:00
|
|
|
*/
|
2017-02-12 20:32:49 +08:00
|
|
|
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!(type & LWS_ADOPT_HTTP)) {
|
|
|
|
if (!(type & LWS_ADOPT_SOCKET))
|
|
|
|
n = LWS_CALLBACK_RAW_ADOPT_FILE;
|
|
|
|
else
|
|
|
|
n = LWS_CALLBACK_RAW_ADOPT;
|
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) ||
|
|
|
|
!(type & LWS_ADOPT_SOCKET)) {
|
|
|
|
/* non-SSL */
|
|
|
|
if (!(type & LWS_ADOPT_HTTP)) {
|
|
|
|
if (!(type & LWS_ADOPT_SOCKET))
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, 0, LRS_UNCONNECTED,
|
|
|
|
&role_ops_raw_file);
|
2017-02-27 12:55:56 +08:00
|
|
|
else
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, 0, LRS_UNCONNECTED,
|
|
|
|
&role_ops_raw_skt);
|
2018-04-27 19:16:50 +08:00
|
|
|
}
|
|
|
|
#if defined(LWS_ROLE_H1)
|
|
|
|
else
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, LWSIFR_SERVER,
|
|
|
|
LRS_HEADERS, &role_ops_h1);
|
2018-04-27 19:16:50 +08:00
|
|
|
#endif
|
2016-01-26 20:56:56 +08:00
|
|
|
} else {
|
2017-02-27 12:55:56 +08:00
|
|
|
/* SSL */
|
|
|
|
if (!(type & LWS_ADOPT_HTTP))
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, 0, LRS_SSL_INIT,
|
|
|
|
&role_ops_raw_skt);
|
2018-04-27 19:16:50 +08:00
|
|
|
#if defined(LWS_ROLE_H1)
|
2017-02-12 20:32:49 +08:00
|
|
|
else
|
2018-04-11 13:39:42 +08:00
|
|
|
lws_role_transition(new_wsi, LWSIFR_SERVER,
|
|
|
|
LRS_SSL_INIT, &role_ops_h1);
|
2018-04-27 19:16:50 +08:00
|
|
|
#endif
|
2017-02-27 12:55:56 +08:00
|
|
|
ssl = 1;
|
|
|
|
}
|
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_libev_accept(new_wsi, new_wsi->desc);
|
|
|
|
lws_libuv_accept(new_wsi, new_wsi->desc);
|
2017-03-15 19:41:11 +05:30
|
|
|
lws_libevent_accept(new_wsi, new_wsi->desc);
|
2017-02-27 12:55:56 +08:00
|
|
|
|
|
|
|
if (!ssl) {
|
2018-03-05 16:49:28 +08:00
|
|
|
lws_pt_lock(pt, __func__);
|
|
|
|
if (__insert_wsi_socket_into_fds(context, new_wsi)) {
|
|
|
|
lws_pt_unlock(pt);
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_err("%s: fail inserting socket\n", __func__);
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-03-05 16:49:28 +08:00
|
|
|
lws_pt_unlock(pt);
|
2017-02-27 12:55:56 +08:00
|
|
|
} else
|
|
|
|
if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
|
2017-09-19 17:30:06 +08:00
|
|
|
lwsl_info("%s: fail ssl negotiation\n", __func__);
|
2016-01-26 20:56:56 +08:00
|
|
|
goto fail;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2017-04-07 11:25:55 +08:00
|
|
|
/*
|
|
|
|
* by deferring callback to this point, after insertion to fds,
|
|
|
|
* lws_callback_on_writable() can work from the callback
|
|
|
|
*/
|
|
|
|
if ((new_wsi->protocol->callback)(
|
|
|
|
new_wsi, n, new_wsi->user_space, NULL, 0))
|
|
|
|
goto fail;
|
|
|
|
|
2017-05-15 07:30:06 +08:00
|
|
|
if (type & LWS_ADOPT_HTTP) {
|
2017-07-28 07:04:47 +08:00
|
|
|
if (!lws_header_table_attach(new_wsi, 0))
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_debug("Attached ah immediately\n");
|
2017-07-28 07:04:47 +08:00
|
|
|
else
|
|
|
|
lwsl_info("%s: waiting for ah\n", __func__);
|
2017-05-15 07:30:06 +08:00
|
|
|
}
|
2016-04-19 10:10:53 +08:00
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
lws_cancel_service_pt(new_wsi);
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
return new_wsi;
|
|
|
|
|
|
|
|
fail:
|
2017-02-27 12:55:56 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET)
|
2018-02-03 13:48:18 +08:00
|
|
|
lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt fail");
|
2016-01-26 20:56:56 +08:00
|
|
|
|
|
|
|
return NULL;
|
2017-03-03 07:36:08 +08:00
|
|
|
|
|
|
|
bail:
|
2017-05-15 08:10:08 +08:00
|
|
|
lwsl_notice("%s: exiting on bail\n", __func__);
|
2017-03-03 07:36:08 +08:00
|
|
|
if (parent)
|
|
|
|
parent->child_list = new_wsi->sibling_list;
|
|
|
|
if (new_wsi->user_space)
|
|
|
|
lws_free(new_wsi->user_space);
|
|
|
|
lws_free(new_wsi);
|
2017-05-15 08:10:08 +08:00
|
|
|
compatible_close(fd.sockfd);
|
2017-03-03 07:36:08 +08:00
|
|
|
|
|
|
|
return NULL;
|
2016-01-19 03:34:24 +08:00
|
|
|
}
|
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
|
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_sock_file_fd_type fd;
|
|
|
|
|
|
|
|
fd.sockfd = accept_fd;
|
|
|
|
return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
|
2017-03-03 07:36:08 +08:00
|
|
|
LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
|
2016-04-08 18:30:45 +08:00
|
|
|
}
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
/* Common read-buffer adoption for lws_adopt_*_readbuf */
|
|
|
|
static struct lws*
|
|
|
|
adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
|
2016-02-24 11:05:56 +08:00
|
|
|
{
|
2016-02-27 11:42:22 +08:00
|
|
|
struct lws_context_per_thread *pt;
|
|
|
|
struct lws_pollfd *pfd;
|
2016-02-24 11:05:56 +08:00
|
|
|
|
|
|
|
if (!wsi)
|
|
|
|
return NULL;
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
if (!readbuf || len == 0)
|
2016-02-24 11:05:56 +08:00
|
|
|
return wsi;
|
|
|
|
|
2018-04-17 15:35:15 +08:00
|
|
|
if (lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len) < 0)
|
2016-02-24 11:05:56 +08:00
|
|
|
goto bail;
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-02-24 11:05:56 +08:00
|
|
|
/*
|
|
|
|
* we can't process the initial read data until we can attach an ah.
|
|
|
|
*
|
|
|
|
* if one is available, get it and place the data in his ah rxbuf...
|
|
|
|
* wsi with ah that have pending rxbuf get auto-POLLIN service.
|
2016-02-27 11:42:22 +08:00
|
|
|
*
|
|
|
|
* no autoservice because we didn't get a chance to attach the
|
|
|
|
* readbuf data to wsi or ah yet, and we will do it next if we get
|
|
|
|
* the ah.
|
2016-02-24 11:05:56 +08:00
|
|
|
*/
|
2018-04-27 15:20:56 +08:00
|
|
|
if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
|
2016-02-24 11:05:56 +08:00
|
|
|
|
2016-02-27 11:42:22 +08:00
|
|
|
lwsl_notice("%s: calling service on readbuf ah\n", __func__);
|
2016-12-21 09:32:16 +08:00
|
|
|
pt = &wsi->context->pt[(int)wsi->tsi];
|
2016-02-27 11:42:22 +08:00
|
|
|
|
|
|
|
/* unlike a normal connect, we have the headers already
|
|
|
|
* (or the first part of them anyway).
|
|
|
|
* libuv won't come back and service us without a network
|
|
|
|
* event, so we need to do the header service right here.
|
|
|
|
*/
|
|
|
|
pfd = &pt->fds[wsi->position_in_fds_table];
|
|
|
|
pfd->revents |= LWS_POLLIN;
|
|
|
|
lwsl_err("%s: calling service\n", __func__);
|
2016-12-21 09:32:16 +08:00
|
|
|
if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi))
|
2016-02-27 11:42:22 +08:00
|
|
|
/* service closed us */
|
|
|
|
return NULL;
|
|
|
|
|
2016-02-24 11:05:56 +08:00
|
|
|
return wsi;
|
|
|
|
}
|
2016-02-27 11:42:22 +08:00
|
|
|
lwsl_err("%s: deferring handling ah\n", __func__);
|
2016-02-24 11:05:56 +08:00
|
|
|
|
|
|
|
return wsi;
|
|
|
|
|
|
|
|
bail:
|
2018-02-03 13:48:18 +08:00
|
|
|
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt readbuf fail");
|
2016-02-24 11:05:56 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
|
|
|
|
const char *readbuf, size_t len)
|
|
|
|
{
|
2017-10-16 12:52:32 +08:00
|
|
|
return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd),
|
|
|
|
readbuf, len);
|
2016-12-21 09:32:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE struct lws *
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
|
|
|
|
lws_sockfd_type accept_fd,
|
|
|
|
const char *readbuf, size_t len)
|
2016-12-21 09:32:16 +08:00
|
|
|
{
|
2017-10-16 12:52:32 +08:00
|
|
|
return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd),
|
|
|
|
readbuf, len);
|
2016-12-21 09:32:16 +08:00
|
|
|
}
|
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
LWS_VISIBLE int
|
|
|
|
lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|
|
|
const char *other_headers, int other_headers_len)
|
2013-11-11 07:30:33 +08:00
|
|
|
{
|
2016-04-22 08:53:49 +08:00
|
|
|
static const char * const intermediates[] = { "private", "public" };
|
2015-12-17 18:25:25 +08:00
|
|
|
struct lws_context *context = lws_get_context(wsi);
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2017-12-01 11:09:32 +08:00
|
|
|
struct lws_range_parsing *rp = &wsi->http.range;
|
2016-12-12 13:36:25 +08:00
|
|
|
#endif
|
2016-04-22 08:53:49 +08:00
|
|
|
char cache_control[50], *cc = "no-store";
|
2016-01-19 03:34:24 +08:00
|
|
|
unsigned char *response = pt->serv_buf + LWS_PRE;
|
2014-10-08 12:00:53 +08:00
|
|
|
unsigned char *p = response;
|
2016-05-19 12:34:35 +08:00
|
|
|
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
|
2017-11-06 10:05:04 +08:00
|
|
|
lws_filepos_t total_content_length;
|
2017-02-25 12:42:45 +08:00
|
|
|
int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
|
2017-03-03 12:38:10 +08:00
|
|
|
lws_fop_flags_t fflags = LWS_O_RDONLY;
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
int ranges;
|
|
|
|
#endif
|
2017-03-03 12:38:10 +08:00
|
|
|
const struct lws_plat_file_ops *fops;
|
|
|
|
const char *vpath;
|
2013-11-11 07:30:33 +08:00
|
|
|
|
2018-03-07 19:57:34 +08:00
|
|
|
if (wsi->handling_404)
|
|
|
|
n = HTTP_STATUS_NOT_FOUND;
|
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
/*
|
|
|
|
* We either call the platform fops .open with first arg platform fops,
|
|
|
|
* or we call fops_zip .open with first arg platform fops, and fops_zip
|
|
|
|
* open will decide whether to switch to fops_zip or stay with fops_def.
|
|
|
|
*
|
2017-12-01 11:09:32 +08:00
|
|
|
* If wsi->http.fop_fd is already set, the caller already opened it
|
2017-03-03 12:38:10 +08:00
|
|
|
*/
|
2017-12-01 11:09:32 +08:00
|
|
|
if (!wsi->http.fop_fd) {
|
2017-03-03 12:38:10 +08:00
|
|
|
fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
|
|
|
|
fflags |= lws_vfs_prepare_flags(wsi);
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
|
2017-03-03 12:38:10 +08:00
|
|
|
file, vpath, &fflags);
|
2017-12-01 11:09:32 +08:00
|
|
|
if (!wsi->http.fop_fd) {
|
2018-04-13 06:43:11 +08:00
|
|
|
lwsl_info("%s: Unable to open: '%s': errno %d\n",
|
|
|
|
__func__, file, errno);
|
|
|
|
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
|
|
|
|
return -1;
|
|
|
|
return !wsi->http2_substream;
|
2017-03-03 12:38:10 +08:00
|
|
|
}
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
|
|
|
|
total_content_length = wsi->http.filelen;
|
2017-02-12 20:32:49 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2017-12-01 11:09:32 +08:00
|
|
|
ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
|
2013-11-11 07:30:33 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
lwsl_debug("Range count %d\n", ranges);
|
|
|
|
/*
|
|
|
|
* no ranges -> 200;
|
|
|
|
* 1 range -> 206 + Content-Type: normal; Content-Range;
|
|
|
|
* more -> 206 + Content-Type: multipart/byteranges
|
|
|
|
* Repeat the true Content-Type in each multipart header
|
|
|
|
* along with Content-Range
|
|
|
|
*/
|
|
|
|
if (ranges < 0) {
|
|
|
|
/* it means he expressed a range in Range:, but it was illegal */
|
2017-10-16 12:52:32 +08:00
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE,
|
|
|
|
NULL);
|
2016-12-12 13:36:25 +08:00
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
return -1; /* <0 means just hang up */
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
lws_vfs_file_close(&wsi->http.fop_fd);
|
2017-03-03 12:38:10 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
return 0; /* == 0 means we dealt with the transaction complete */
|
|
|
|
}
|
|
|
|
if (ranges)
|
|
|
|
n = HTTP_STATUS_PARTIAL_CONTENT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (lws_add_http_header_status(wsi, n, &p, end))
|
2014-10-17 08:38:44 +08:00
|
|
|
return -1;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
|
2017-03-03 12:38:10 +08:00
|
|
|
LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
|
|
|
|
(LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_ENCODING,
|
|
|
|
(unsigned char *)"gzip", 4, &p, end))
|
|
|
|
return -1;
|
|
|
|
lwsl_info("file is being provided in gzip\n");
|
|
|
|
}
|
|
|
|
|
2017-08-12 20:47:23 +08:00
|
|
|
if (
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2017-08-12 20:47:23 +08:00
|
|
|
ranges < 2 &&
|
|
|
|
#endif
|
|
|
|
content_type && content_type[0])
|
2017-11-06 10:05:04 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
2016-08-13 11:17:53 +02:00
|
|
|
(unsigned char *)content_type,
|
2017-11-06 10:05:04 +08:00
|
|
|
(int)strlen(content_type),
|
|
|
|
&p, end))
|
2016-08-13 11:17:53 +02:00
|
|
|
return -1;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-08-12 20:47:23 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2016-12-12 13:36:25 +08:00
|
|
|
if (ranges >= 2) { /* multipart byteranges */
|
2018-03-12 09:28:26 +08:00
|
|
|
lws_strncpy(wsi->http.multipart_content_type, content_type,
|
|
|
|
sizeof(wsi->http.multipart_content_type));
|
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
|
|
(unsigned char *)
|
|
|
|
"multipart/byteranges; "
|
|
|
|
"boundary=_lws",
|
|
|
|
20, &p, end))
|
2016-12-12 13:36:25 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* our overall content length has to include
|
|
|
|
*
|
|
|
|
* - (n + 1) x "_lws\r\n"
|
|
|
|
* - n x Content-Type: xxx/xxx\r\n
|
|
|
|
* - n x Content-Range: bytes xxx-yyy/zzz\r\n
|
|
|
|
* - n x /r/n
|
|
|
|
* - the actual payloads (aggregated in rp->agg)
|
|
|
|
*
|
|
|
|
* Precompute it for the main response header
|
|
|
|
*/
|
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
total_content_length = (lws_filepos_t)rp->agg +
|
|
|
|
6 /* final _lws\r\n */;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
|
|
|
lws_ranges_reset(rp);
|
|
|
|
while (lws_ranges_next(rp)) {
|
|
|
|
n = lws_snprintf(cache_control, sizeof(cache_control),
|
|
|
|
"bytes %llu-%llu/%llu",
|
|
|
|
rp->start, rp->end, rp->extent);
|
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
total_content_length +=
|
2017-10-16 12:52:32 +08:00
|
|
|
6 /* header _lws\r\n */ +
|
|
|
|
/* Content-Type: xxx/xxx\r\n */
|
|
|
|
14 + strlen(content_type) + 2 +
|
|
|
|
/* Content-Range: xxxx\r\n */
|
|
|
|
15 + n + 2 +
|
|
|
|
2; /* /r/n */
|
2016-12-12 13:36:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
lws_ranges_reset(rp);
|
|
|
|
lws_ranges_next(rp);
|
2016-08-13 11:17:53 +02:00
|
|
|
}
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
if (ranges == 1) {
|
2017-11-06 10:05:04 +08:00
|
|
|
total_content_length = (lws_filepos_t)rp->agg;
|
2017-10-16 12:52:32 +08:00
|
|
|
n = lws_snprintf(cache_control, sizeof(cache_control),
|
|
|
|
"bytes %llu-%llu/%llu",
|
|
|
|
rp->start, rp->end, rp->extent);
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-11-06 10:05:04 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_RANGE,
|
2016-12-12 13:36:25 +08:00
|
|
|
(unsigned char *)cache_control,
|
|
|
|
n, &p, end))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.range.inside = 0;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
|
|
|
|
(unsigned char *)"bytes", 5, &p, end))
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
|
2018-01-14 20:09:41 +08:00
|
|
|
if (!wsi->http2_substream) {
|
|
|
|
if (!wsi->sending_chunked) {
|
|
|
|
if (lws_add_http_header_content_length(wsi,
|
|
|
|
total_content_length,
|
|
|
|
&p, end))
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
2017-10-16 12:52:32 +08:00
|
|
|
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
2016-05-19 15:28:31 +08:00
|
|
|
(unsigned char *)"chunked",
|
|
|
|
7, &p, end))
|
2018-01-14 20:09:41 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2016-05-19 15:28:31 +08:00
|
|
|
}
|
2014-10-12 14:31:47 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (wsi->cache_secs && wsi->cache_reuse) {
|
|
|
|
if (wsi->cache_revalidate) {
|
|
|
|
cc = cache_control;
|
|
|
|
cclen = sprintf(cache_control, "%s max-age: %u",
|
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
wsi->cache_secs);
|
|
|
|
} else {
|
|
|
|
cc = "no-cache";
|
|
|
|
cclen = 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
|
|
|
|
(unsigned char *)cc, cclen, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if (wsi->http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
|
2016-07-23 14:18:25 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
|
|
|
|
(unsigned char *)"keep-alive", 10, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2013-11-11 07:30:33 +08:00
|
|
|
if (other_headers) {
|
2014-10-12 14:31:47 +08:00
|
|
|
if ((end - p) < other_headers_len)
|
|
|
|
return -1;
|
|
|
|
memcpy(p, other_headers, other_headers_len);
|
|
|
|
p += other_headers_len;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2015-12-16 18:19:08 +08:00
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
2014-10-17 08:38:44 +08:00
|
|
|
return -1;
|
2015-12-14 08:52:03 +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
|
|
|
ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
|
2014-10-08 12:00:53 +08:00
|
|
|
if (ret != (p - response)) {
|
2017-10-16 12:52:32 +08:00
|
|
|
lwsl_err("_write returned %d from %ld\n", ret,
|
|
|
|
(long)(p - response));
|
2013-11-11 07:30:33 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.filepos = 0;
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_ISSUING_FILE);
|
2013-11-11 07:30:33 +08:00
|
|
|
|
2017-10-13 10:33:02 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
|
|
|
|
return 0;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 10:33:23 +08:00
|
|
|
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_context *context = wsi->context;
|
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
|
|
|
struct lws_process_html_args args;
|
|
|
|
lws_filepos_t amount, poss;
|
|
|
|
unsigned char *p, *pstart;
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
unsigned char finished = 0;
|
|
|
|
#endif
|
|
|
|
int n, m;
|
|
|
|
|
|
|
|
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
|
|
|
if (wsi->trunc_len) {
|
|
|
|
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
|
|
|
wsi->trunc_offset,
|
|
|
|
wsi->trunc_len) < 0) {
|
|
|
|
lwsl_info("%s: closing\n", __func__);
|
|
|
|
goto file_had_it;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wsi->http.filepos == wsi->http.filelen)
|
|
|
|
goto all_sent;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
|
|
|
|
|
|
|
|
p = pstart;
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
|
|
|
|
|
|
|
|
lwsl_notice("%s: doing range start %llu\n", __func__,
|
|
|
|
wsi->http.range.start);
|
|
|
|
|
|
|
|
if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
|
|
|
|
wsi->http.range.start -
|
|
|
|
wsi->http.filepos) < 0)
|
|
|
|
goto file_had_it;
|
|
|
|
|
|
|
|
wsi->http.filepos = wsi->http.range.start;
|
|
|
|
|
|
|
|
if (wsi->http.range.count_ranges > 1) {
|
|
|
|
n = lws_snprintf((char *)p,
|
|
|
|
context->pt_serv_buf_size -
|
|
|
|
LWS_H2_FRAME_HEADER_LENGTH,
|
|
|
|
"_lws\x0d\x0a"
|
|
|
|
"Content-Type: %s\x0d\x0a"
|
|
|
|
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
|
|
|
|
"\x0d\x0a",
|
|
|
|
wsi->http.multipart_content_type,
|
|
|
|
wsi->http.range.start,
|
|
|
|
wsi->http.range.end,
|
|
|
|
wsi->http.range.extent);
|
|
|
|
p += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
wsi->http.range.budget = wsi->http.range.end -
|
|
|
|
wsi->http.range.start + 1;
|
|
|
|
wsi->http.range.inside = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
|
|
|
|
|
|
|
|
if (wsi->http.tx_content_length)
|
|
|
|
if (poss > wsi->http.tx_content_remain)
|
|
|
|
poss = wsi->http.tx_content_remain;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if there is a hint about how much we will do well to send at
|
|
|
|
* one time, restrict ourselves to only trying to send that.
|
|
|
|
*/
|
|
|
|
if (wsi->protocol->tx_packet_size &&
|
|
|
|
poss > wsi->protocol->tx_packet_size)
|
|
|
|
poss = wsi->protocol->tx_packet_size;
|
|
|
|
|
|
|
|
if (wsi->role_ops->tx_credit) {
|
|
|
|
lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
|
|
|
|
|
|
|
|
if (!txc) {
|
|
|
|
lwsl_info("%s: came here with no tx credit\n",
|
|
|
|
__func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (txc < poss)
|
|
|
|
poss = txc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* consumption of the actual payload amount sent will be
|
|
|
|
* handled when the role data frame is sent
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
if (wsi->http.range.count_ranges) {
|
|
|
|
if (wsi->http.range.count_ranges > 1)
|
|
|
|
poss -= 7; /* allow for final boundary */
|
|
|
|
if (poss > wsi->http.range.budget)
|
|
|
|
poss = wsi->http.range.budget;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (wsi->sending_chunked) {
|
|
|
|
/* we need to drop the chunk size in here */
|
|
|
|
p += 10;
|
|
|
|
/* allow for the chunk to grow by 128 in translation */
|
|
|
|
poss -= 10 + 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
|
|
|
|
goto file_had_it; /* caller will close */
|
|
|
|
|
|
|
|
if (wsi->sending_chunked)
|
|
|
|
n = (int)amount;
|
|
|
|
else
|
|
|
|
n = lws_ptr_diff(p, pstart) + (int)amount;
|
|
|
|
|
|
|
|
lwsl_debug("%s: sending %d\n", __func__, n);
|
|
|
|
|
|
|
|
if (n) {
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
|
|
|
context->timeout_secs);
|
|
|
|
|
|
|
|
if (wsi->interpreting) {
|
|
|
|
args.p = (char *)p;
|
|
|
|
args.len = n;
|
|
|
|
args.max_len = (unsigned int)poss + 128;
|
|
|
|
args.final = wsi->http.filepos + n ==
|
|
|
|
wsi->http.filelen;
|
|
|
|
args.chunked = wsi->sending_chunked;
|
|
|
|
if (user_callback_handle_rxflow(
|
|
|
|
wsi->vhost->protocols[
|
|
|
|
(int)wsi->protocol_interpret_idx].callback,
|
|
|
|
wsi, LWS_CALLBACK_PROCESS_HTML,
|
|
|
|
wsi->user_space, &args, 0) < 0)
|
|
|
|
goto file_had_it;
|
|
|
|
n = args.len;
|
|
|
|
p = (unsigned char *)args.p;
|
|
|
|
} else
|
|
|
|
p = pstart;
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
if (wsi->http.range.send_ctr + 1 ==
|
|
|
|
wsi->http.range.count_ranges && // last range
|
|
|
|
wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
|
|
|
|
wsi->http.range.budget - amount == 0) {// final part
|
|
|
|
n += lws_snprintf((char *)pstart + n, 6,
|
|
|
|
"_lws\x0d\x0a"); // append trailing boundary
|
|
|
|
lwsl_debug("added trailing boundary\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
m = lws_write(wsi, p, n,
|
|
|
|
wsi->http.filepos + amount == wsi->http.filelen ?
|
|
|
|
LWS_WRITE_HTTP_FINAL :
|
|
|
|
LWS_WRITE_HTTP
|
|
|
|
);
|
|
|
|
if (m < 0)
|
|
|
|
goto file_had_it;
|
|
|
|
|
|
|
|
wsi->http.filepos += amount;
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
if (wsi->http.range.count_ranges >= 1) {
|
|
|
|
wsi->http.range.budget -= amount;
|
|
|
|
if (wsi->http.range.budget == 0) {
|
|
|
|
lwsl_notice("range budget exhausted\n");
|
|
|
|
wsi->http.range.inside = 0;
|
|
|
|
wsi->http.range.send_ctr++;
|
|
|
|
|
|
|
|
if (lws_ranges_next(&wsi->http.range) < 1) {
|
|
|
|
finished = 1;
|
|
|
|
goto all_sent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (m != n) {
|
|
|
|
/* adjust for what was not sent */
|
|
|
|
if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
|
|
|
|
m - n) ==
|
|
|
|
(lws_fileofs_t)-1)
|
|
|
|
goto file_had_it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
all_sent:
|
|
|
|
if ((!wsi->trunc_len && wsi->http.filepos >= wsi->http.filelen)
|
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
|| finished)
|
|
|
|
#else
|
|
|
|
)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
|
|
|
/* we might be in keepalive, so close it off here */
|
|
|
|
lws_vfs_file_close(&wsi->http.fop_fd);
|
|
|
|
|
|
|
|
lwsl_debug("file completed\n");
|
|
|
|
|
|
|
|
if (wsi->protocol->callback &&
|
|
|
|
user_callback_handle_rxflow(wsi->protocol->callback,
|
|
|
|
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
|
|
|
wsi->user_space, NULL,
|
|
|
|
0) < 0) {
|
|
|
|
/*
|
|
|
|
* For http/1.x, the choices from
|
|
|
|
* transaction_completed are either
|
|
|
|
* 0 to use the connection for pipelined
|
|
|
|
* or nonzero to hang it up.
|
|
|
|
*
|
|
|
|
* However for http/2. while we are
|
|
|
|
* still interested in hanging up the
|
|
|
|
* nwsi if there was a network-level
|
|
|
|
* fatal error, simply completing the
|
|
|
|
* transaction is a matter of the stream
|
|
|
|
* state, not the root connection at the
|
|
|
|
* network level
|
|
|
|
*/
|
|
|
|
if (wsi->http2_substream)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1; /* >0 indicates completed */
|
|
|
|
}
|
|
|
|
} while (0); // while (!lws_send_pipe_choked(wsi))
|
|
|
|
|
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
|
|
|
|
return 0; /* indicates further processing must be done */
|
|
|
|
|
|
|
|
file_had_it:
|
|
|
|
lws_vfs_file_close(&wsi->http.fop_fd);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-12 10:07:02 +08:00
|
|
|
LWS_VISIBLE void
|
2015-12-04 11:08:32 +08:00
|
|
|
lws_server_get_canonical_hostname(struct lws_context *context,
|
2018-04-27 08:27:16 +08:00
|
|
|
const struct lws_context_creation_info *info)
|
2014-04-12 10:07:02 +08:00
|
|
|
{
|
2017-10-16 12:52:32 +08:00
|
|
|
if (lws_check_opt(info->options,
|
|
|
|
LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
|
2014-04-12 10:07:02 +08:00
|
|
|
return;
|
2018-04-11 13:39:42 +08:00
|
|
|
#if !defined(LWS_WITH_ESP32)
|
2014-04-12 10:07:02 +08:00
|
|
|
/* find canonical hostname */
|
|
|
|
gethostname((char *)context->canonical_hostname,
|
2016-01-29 21:18:54 +08:00
|
|
|
sizeof(context->canonical_hostname) - 1);
|
2014-04-12 10:07:02 +08:00
|
|
|
|
2017-09-19 17:30:06 +08:00
|
|
|
lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
|
2015-11-02 20:34:12 +08:00
|
|
|
#else
|
|
|
|
(void)context;
|
|
|
|
#endif
|
2014-04-27 13:28:22 +02:00
|
|
|
}
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
|
2016-06-12 09:56:39 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_chunked_html_process(struct lws_process_html_args *args,
|
|
|
|
struct lws_process_html_state *s)
|
|
|
|
{
|
|
|
|
char *sp, buffer[32];
|
|
|
|
const char *pc;
|
|
|
|
int old_len, n;
|
|
|
|
|
|
|
|
/* do replacements */
|
|
|
|
sp = args->p;
|
|
|
|
old_len = args->len;
|
|
|
|
args->len = 0;
|
|
|
|
s->start = sp;
|
|
|
|
while (sp < args->p + old_len) {
|
|
|
|
|
|
|
|
if (args->len + 7 >= args->max_len) {
|
|
|
|
lwsl_err("Used up interpret padding\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!s->pos && *sp == '$') || s->pos) {
|
2016-06-27 05:53:38 +08:00
|
|
|
int hits = 0, hit = 0;
|
2016-06-12 09:56:39 +08:00
|
|
|
|
|
|
|
if (!s->pos)
|
|
|
|
s->start = sp;
|
|
|
|
s->swallow[s->pos++] = *sp;
|
2016-08-28 09:28:55 +08:00
|
|
|
if (s->pos == sizeof(s->swallow) - 1)
|
2016-06-12 09:56:39 +08:00
|
|
|
goto skip;
|
|
|
|
for (n = 0; n < s->count_vars; n++)
|
|
|
|
if (!strncmp(s->swallow, s->vars[n], s->pos)) {
|
|
|
|
hits++;
|
|
|
|
hit = n;
|
|
|
|
}
|
|
|
|
if (!hits) {
|
|
|
|
skip:
|
|
|
|
s->swallow[s->pos] = '\0';
|
|
|
|
memcpy(s->start, s->swallow, s->pos);
|
|
|
|
args->len++;
|
|
|
|
s->pos = 0;
|
|
|
|
sp = s->start + 1;
|
|
|
|
continue;
|
|
|
|
}
|
2017-10-20 17:45:02 +08:00
|
|
|
if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
|
2016-06-12 09:56:39 +08:00
|
|
|
pc = s->replace(s->data, hit);
|
|
|
|
if (!pc)
|
|
|
|
pc = "NULL";
|
2017-10-25 08:00:23 +08:00
|
|
|
n = (int)strlen(pc);
|
2016-06-12 09:56:39 +08:00
|
|
|
s->swallow[s->pos] = '\0';
|
|
|
|
if (n != s->pos) {
|
|
|
|
memmove(s->start + n,
|
|
|
|
s->start + s->pos,
|
|
|
|
old_len - (sp - args->p));
|
|
|
|
old_len += (n - s->pos) + 1;
|
|
|
|
}
|
|
|
|
memcpy(s->start, pc, n);
|
|
|
|
args->len++;
|
|
|
|
sp = s->start + 1;
|
|
|
|
|
|
|
|
s->pos = 0;
|
|
|
|
}
|
|
|
|
sp++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
args->len++;
|
|
|
|
sp++;
|
|
|
|
}
|
|
|
|
|
2018-01-14 20:09:41 +08:00
|
|
|
if (args->chunked) {
|
|
|
|
/* no space left for final chunk trailer */
|
|
|
|
if (args->final && args->len + 7 >= args->max_len)
|
|
|
|
return -1;
|
2016-06-12 09:56:39 +08:00
|
|
|
|
2018-01-14 20:09:41 +08:00
|
|
|
n = sprintf(buffer, "%X\x0d\x0a", args->len);
|
|
|
|
|
|
|
|
args->p -= n;
|
|
|
|
memcpy(args->p, buffer, n);
|
|
|
|
args->len += n;
|
|
|
|
|
|
|
|
if (args->final) {
|
|
|
|
sp = args->p + args->len;
|
|
|
|
*sp++ = '\x0d';
|
|
|
|
*sp++ = '\x0a';
|
|
|
|
*sp++ = '0';
|
|
|
|
*sp++ = '\x0d';
|
|
|
|
*sp++ = '\x0a';
|
|
|
|
*sp++ = '\x0d';
|
|
|
|
*sp++ = '\x0a';
|
|
|
|
args->len += 7;
|
|
|
|
} else {
|
|
|
|
sp = args->p + args->len;
|
|
|
|
*sp++ = '\x0d';
|
|
|
|
*sp++ = '\x0a';
|
|
|
|
args->len += 2;
|
|
|
|
}
|
2016-06-12 09:56:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|