2013-01-18 11:43:21 +08:00
|
|
|
/*
|
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
2013-01-18 11:43:21 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
2013-01-18 11:43:21 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
2013-01-18 11:43:21 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
2013-01-18 11:43:21 +08:00
|
|
|
*/
|
|
|
|
|
2019-08-15 10:49:52 +01:00
|
|
|
#include "private-lib-core.h"
|
2013-01-18 11:43:21 +08:00
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
2018-08-13 16:49:58 +08:00
|
|
|
static const char * const intermediates[] = { "private", "public" };
|
2019-08-18 10:35:43 +01:00
|
|
|
#endif
|
2018-08-13 16:49:58 +08:00
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
2019-08-18 05:04:15 +01:00
|
|
|
#if defined(LWS_WITH_SERVER)
|
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-29 10:44:36 +08:00
|
|
|
_lws_vhost_init_server(const struct lws_context_creation_info *info,
|
2018-11-23 08:47:56 +08:00
|
|
|
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...
|
|
|
|
*/
|
2018-11-22 17:58:30 +08:00
|
|
|
is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port,
|
2019-03-08 08:58:56 +08:00
|
|
|
vhost->iface, 1);
|
2018-03-29 13:32:33 +08:00
|
|
|
lwsl_debug("initial if check says %d\n", is);
|
2018-11-22 17:58:30 +08:00
|
|
|
|
|
|
|
if (is == LWS_ITOSA_BUSY)
|
|
|
|
/* treat as fatal */
|
|
|
|
return -1;
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
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 */
|
2018-11-22 17:58:30 +08:00
|
|
|
lwsl_debug("deferred iface: removing vh %s\n",
|
|
|
|
(*pv)->name);
|
2018-03-29 13:32:33 +08:00
|
|
|
*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);
|
2018-11-22 17:58:30 +08:00
|
|
|
vhost->no_listener_vhost_list =
|
|
|
|
vhost->context->no_listener_vhost_list;
|
2018-03-29 13:32:33 +08:00
|
|
|
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);
|
2019-08-17 07:54:17 +01:00
|
|
|
else
|
|
|
|
return -1;
|
2019-06-27 12:14:50 +01:00
|
|
|
return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
|
|
|
|
-1 : 1;
|
2018-03-29 13:32:33 +08:00
|
|
|
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);
|
2019-08-17 07:54:17 +01:00
|
|
|
else
|
|
|
|
return -1;
|
2019-06-27 12:14:50 +01:00
|
|
|
return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
|
|
|
|
-1 : 1;
|
2018-03-29 13:32:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-23 13:08:46 +08:00
|
|
|
(void)n;
|
2016-01-26 20:56:56 +08:00
|
|
|
#if defined(__linux__)
|
2018-07-08 15:33:10 +08:00
|
|
|
#ifdef LWS_WITH_UNIX_SOCK
|
|
|
|
/*
|
|
|
|
* A Unix domain sockets cannot be bound for several times, even if we set
|
|
|
|
* the SO_REUSE* options on.
|
|
|
|
* However, fortunately, each thread is able to independently listen when
|
|
|
|
* running on a reasonably new Linux kernel. So we can safely assume
|
|
|
|
* creating just one listening socket for a multi-threaded environment won't
|
|
|
|
* fail in most cases.
|
|
|
|
*/
|
|
|
|
if (!LWS_UNIX_SOCK_ENABLED(vhost))
|
|
|
|
#endif
|
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;
|
|
|
|
}
|
2019-08-18 10:35:43 +01:00
|
|
|
#if !defined(LWS_PLAT_FREERTOS)
|
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-09-04 08:06:46 +08:00
|
|
|
lws_plat_set_socket_options(vhost, sockfd, 0);
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2019-03-08 08:58:56 +08:00
|
|
|
is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface, 1);
|
2018-11-22 17:58:30 +08:00
|
|
|
if (is == LWS_ITOSA_BUSY) {
|
|
|
|
/* treat as fatal */
|
|
|
|
compatible_close(sockfd);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-29 13:32:33 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
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;
|
|
|
|
}
|
2018-09-04 08:06:46 +08:00
|
|
|
|
|
|
|
#ifdef LWS_WITH_UNIX_SOCK
|
|
|
|
if (!LWS_UNIX_SOCK_ENABLED(vhost))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
wsi->unix_skt = 1;
|
|
|
|
vhost->listen_port = is;
|
|
|
|
|
2018-11-22 17:58:30 +08:00
|
|
|
lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
|
2018-09-04 08:06:46 +08:00
|
|
|
}
|
|
|
|
|
2018-03-08 12:04:13 +08:00
|
|
|
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;
|
vhost_destroy: use vhost wsi reference counting to trigger destroy
This changes the vhost destroy flow to only hand off the listen
socket if another vhost sharing it, and mark the vhost as
being_destroyed.
Each tsi calls lws_check_deferred_free() once a second, if it sees
any vhost being_destroyed there, it closes all wsi on its tsi on
the same vhost, one time.
As the wsi on the vhost complete close (ie, after libuv async close
if on libuv event loop), they decrement a reference count for all
wsi open on the vhost. The tsi who closes the last one then
completes the destroy flow for the vhost itself... it's random
which tsi completes the vhost destroy but since there are no
wsi left on the vhost, and it holds the context lock, nothing
can conflict.
The advantage of this is that owning tsi do the close for wsi
that are bound to the vhost under destruction, at a time when
they are guaranteed to be idle for service, and they do it with
both vhost and context locks owned, so no other service thread
can conflict for stuff protected by those either.
For the situation the user code may have allocations attached to
the vhost, this adds args to lws_vhost_destroy() to allow destroying
the user allocations just before the vhost is freed.
2018-06-16 09:31:07 +08:00
|
|
|
lws_vhost_bind_wsi(vhost, wsi);
|
2018-03-08 12:04:13 +08:00
|
|
|
wsi->listener = 1;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2018-04-29 10:44:36 +08:00
|
|
|
if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
|
|
|
|
wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
|
2016-11-30 07:05:13 +08:00
|
|
|
|
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
|
|
|
}
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
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;
|
2018-10-10 13:54:43 +08:00
|
|
|
int n, colon;
|
2016-07-11 09:44:17 +08:00
|
|
|
|
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) {
|
2018-10-10 13:54:43 +08:00
|
|
|
int m = (int)strlen(vhost->name);
|
2018-09-04 08:06:46 +08:00
|
|
|
if (port && port == vhost->listen_port &&
|
2016-07-11 09:44:17 +08:00
|
|
|
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) {
|
2018-09-04 08:06:46 +08:00
|
|
|
if (port && port == vhost->listen_port) {
|
2018-05-04 12:05:56 +08:00
|
|
|
lwsl_info("%s: vhost match to %s based on port %d\n",
|
|
|
|
__func__, vhost->name, port);
|
2017-03-07 16:06:05 +08:00
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no match */
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-08 21:03:32 +03:00
|
|
|
static const struct lws_mimetype {
|
|
|
|
const char *extension;
|
|
|
|
const char *mimetype;
|
|
|
|
} server_mimetypes[] = {
|
|
|
|
{ ".html", "text/html" },
|
|
|
|
{ ".htm", "text/html" },
|
|
|
|
{ ".js", "text/javascript" },
|
|
|
|
{ ".css", "text/css" },
|
|
|
|
{ ".png", "image/png" },
|
|
|
|
{ ".jpg", "image/jpeg" },
|
|
|
|
{ ".jpeg", "image/jpeg" },
|
|
|
|
{ ".ico", "image/x-icon" },
|
|
|
|
{ ".gif", "image/gif" },
|
|
|
|
{ ".svg", "image/svg+xml" },
|
|
|
|
{ ".ttf", "application/x-font-ttf" },
|
|
|
|
{ ".otf", "application/font-woff" },
|
|
|
|
{ ".woff", "application/font-woff" },
|
|
|
|
{ ".woff2", "application/font-woff2" },
|
|
|
|
{ ".gz", "application/gzip" },
|
|
|
|
{ ".txt", "text/plain" },
|
|
|
|
{ ".xml", "application/xml" },
|
|
|
|
{ ".json", "application/json" },
|
|
|
|
};
|
|
|
|
|
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
|
|
|
{
|
2019-04-08 21:03:32 +03:00
|
|
|
const struct lws_protocol_vhost_options *pvo;
|
|
|
|
size_t n = strlen(file), len, i;
|
|
|
|
const char *fallback_mimetype = NULL;
|
|
|
|
const struct lws_mimetype *mt;
|
|
|
|
|
|
|
|
/* prioritize user-defined mimetypes */
|
|
|
|
for (pvo = m ? m->extra_mimetypes : NULL; pvo; pvo = pvo->next) {
|
|
|
|
/* ie, match anything */
|
|
|
|
if (!fallback_mimetype && pvo->name[0] == '*') {
|
|
|
|
fallback_mimetype = pvo->value;
|
|
|
|
continue;
|
|
|
|
}
|
2016-05-06 08:02:57 +08:00
|
|
|
|
2019-04-08 21:03:32 +03:00
|
|
|
len = strlen(pvo->name);
|
|
|
|
if (n > len && !strcasecmp(&file[n - len], pvo->name)) {
|
|
|
|
lwsl_info("%s: match to user mimetype: %s\n", __func__, pvo->value);
|
2016-08-13 11:17:53 +02:00
|
|
|
return pvo->value;
|
2019-04-08 21:03:32 +03:00
|
|
|
}
|
|
|
|
}
|
2016-08-13 11:17:53 +02:00
|
|
|
|
2019-04-08 21:03:32 +03:00
|
|
|
/* fallback to server-defined mimetypes */
|
|
|
|
for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) {
|
|
|
|
mt = &server_mimetypes[i];
|
|
|
|
|
|
|
|
len = strlen(mt->extension);
|
|
|
|
if (n > len && !strcasecmp(&file[n - len], mt->extension)) {
|
|
|
|
lwsl_info("%s: match to server mimetype: %s\n", __func__, mt->mimetype);
|
|
|
|
return mt->mimetype;
|
|
|
|
}
|
|
|
|
}
|
2016-05-14 10:00:21 +08:00
|
|
|
|
2019-04-08 21:03:32 +03:00
|
|
|
/* fallback to '*' if defined */
|
|
|
|
if (fallback_mimetype) {
|
|
|
|
lwsl_info("%s: match to any mimetype: %s\n", __func__, fallback_mimetype);
|
|
|
|
return fallback_mimetype;
|
2016-05-14 10:00:21 +08:00
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-04-08 21:03:32 +03:00
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
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
|
2018-08-25 12:27:00 +08:00
|
|
|
char path[256], sym[2048];
|
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;
|
2019-08-18 10:35:43 +01:00
|
|
|
#if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
|
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
|
|
|
|
2018-07-16 09:34:39 +08:00
|
|
|
return 1;
|
2017-03-03 12:38:10 +08:00
|
|
|
}
|
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;
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_PLAT_FREERTOS)
|
2017-04-03 14:09:37 +08:00
|
|
|
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);
|
2018-07-16 09:34:39 +08:00
|
|
|
goto notfound;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
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);
|
2018-07-16 09:34:39 +08:00
|
|
|
goto notfound;
|
2017-06-09 20:20:42 +08:00
|
|
|
}
|
2017-03-17 11:43:45 +08:00
|
|
|
#else
|
|
|
|
if (stat(path, &st)) {
|
|
|
|
lwsl_info("unable to stat %s\n", path);
|
2018-07-16 09:34:39 +08:00
|
|
|
goto notfound;
|
2017-03-17 11:43:45 +08:00
|
|
|
}
|
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;
|
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
|
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);
|
2018-07-16 09:34:39 +08:00
|
|
|
goto notfound;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
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
|
|
|
|
2018-08-13 16:49:58 +08:00
|
|
|
char cache_control[50], *cc = "no-store";
|
|
|
|
int cclen = 8;
|
|
|
|
|
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,
|
2018-08-25 12:27:00 +08:00
|
|
|
HTTP_STATUS_NOT_MODIFIED, &p, end)) {
|
|
|
|
lwsl_err("%s: failed adding not modified\n",
|
|
|
|
__func__);
|
2016-04-22 08:53:49 +08:00
|
|
|
return -1;
|
2018-08-25 12:27:00 +08:00
|
|
|
}
|
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
|
|
|
|
2018-08-13 16:49:58 +08:00
|
|
|
/* but we still need to send cache control... */
|
|
|
|
|
2018-08-23 11:48:17 +08:00
|
|
|
if (m->cache_max_age && m->cache_reusable) {
|
2018-08-13 16:49:58 +08:00
|
|
|
if (!m->cache_revalidate) {
|
|
|
|
cc = cache_control;
|
2018-11-23 08:47:56 +08:00
|
|
|
cclen = sprintf(cache_control,
|
|
|
|
"%s, max-age=%u",
|
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
m->cache_max_age);
|
2018-08-13 16:49:58 +08:00
|
|
|
} else {
|
|
|
|
cc = cache_control;
|
2018-11-23 08:47:56 +08:00
|
|
|
cclen = sprintf(cache_control,
|
|
|
|
"must-revalidate, %s, max-age=%u",
|
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
m->cache_max_age);
|
2018-08-13 16:49:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CACHE_CONTROL,
|
2018-08-13 16:49:58 +08:00
|
|
|
(unsigned char *)cc, cclen, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
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
|
|
|
|
2018-07-16 09:34:39 +08:00
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2016-04-22 08:53:49 +08:00
|
|
|
}
|
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) {
|
2018-10-31 10:44:44 +08:00
|
|
|
lwsl_info("unknown mimetype for %s\n", path);
|
|
|
|
if (lws_return_http_status(wsi,
|
|
|
|
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||
|
|
|
|
lws_http_transaction_completed(wsi))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
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;
|
2019-04-05 21:13:59 +08:00
|
|
|
wsi->interpreting = 0;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2019-04-05 21:13:59 +08:00
|
|
|
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->protocol_interpret_idx =
|
2019-04-05 21:13:59 +08:00
|
|
|
lws_vhost_name_to_protocol(wsi->vhost,
|
|
|
|
pvo->value) -
|
|
|
|
&lws_get_vhost(wsi)->protocols[0];
|
|
|
|
|
|
|
|
lwsl_debug("want %s interpreted by %s (pcol is %s)\n", path,
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->vhost->protocols[
|
2019-04-05 21:13:59 +08:00
|
|
|
(int)wsi->protocol_interpret_idx].name,
|
|
|
|
wsi->protocol->name);
|
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[
|
|
|
|
(int)wsi->protocol_interpret_idx], __func__))
|
|
|
|
return -1;
|
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
if (lws_ensure_user_space(wsi))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pvo = pvo->next;
|
|
|
|
}
|
|
|
|
|
2019-04-05 21:13:59 +08:00
|
|
|
if (wsi->sending_chunked) {
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
|
|
|
(unsigned char *)"chunked", 7,
|
|
|
|
&p, end))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
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
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp, __func__))
|
2018-07-16 09:34:39 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-11-15 16:33:54 +08:00
|
|
|
*p = '\0';
|
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;
|
2018-07-16 09:34:39 +08:00
|
|
|
|
|
|
|
notfound:
|
|
|
|
|
|
|
|
return 1;
|
2016-03-28 10:10:43 +08:00
|
|
|
}
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
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) ||
|
2019-03-22 06:22:40 +08:00
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
|
2019-08-01 12:56:29 +01:00
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_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
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if !defined(LWS_PLAT_FREERTOS) && defined(LWS_WITH_FILE_OPS)
|
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;
|
|
|
|
|
2018-06-23 12:56:21 +08:00
|
|
|
fd = lws_open(filename, O_RDONLY);
|
2016-12-03 15:13:15 +08:00
|
|
|
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
|
|
|
|
2018-11-28 12:16:01 +08:00
|
|
|
int
|
2016-12-03 15:13:15 +08:00
|
|
|
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,
|
2018-09-04 08:06:46 +08:00
|
|
|
*p = start, *end = p + 2048;
|
2016-12-03 15:13:15 +08:00
|
|
|
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;
|
|
|
|
|
2019-04-03 10:18:17 +03:00
|
|
|
if (lws_add_http_header_content_length(wsi, 0, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2016-12-03 15:13:15 +08:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2019-02-20 07:47:48 +08:00
|
|
|
int
|
2017-09-06 09:30:32 +08:00
|
|
|
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
|
|
|
|
2018-08-16 19:10:32 +08:00
|
|
|
for (n = 0; n < (int)LWS_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 &&
|
2019-02-20 07:47:48 +08:00
|
|
|
!((wsi->http2_substream || wsi->h2_stream_carries_ws) &&
|
2017-10-13 10:33:02 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-08-16 19:10:32 +08:00
|
|
|
for (n = 0; n < (int)LWS_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;
|
|
|
|
}
|
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
|
|
|
|
|
2018-11-28 12:16:01 +08:00
|
|
|
enum lws_check_basic_auth_results
|
|
|
|
lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file)
|
|
|
|
{
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
2018-11-28 12:16:01 +08:00
|
|
|
char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
|
|
|
|
int m, ml, fi;
|
|
|
|
|
|
|
|
if (!basic_auth_login_file)
|
|
|
|
return LCBA_CONTINUE;
|
|
|
|
|
|
|
|
/* Did he send auth? */
|
|
|
|
ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
|
|
if (!ml)
|
|
|
|
return LCBA_FAILED_AUTH;
|
|
|
|
|
|
|
|
/* Disallow fragmentation monkey business */
|
|
|
|
|
|
|
|
fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
|
|
|
|
if (wsi->http.ah->frags[fi].nfrag) {
|
|
|
|
lwsl_err("fragmented basic auth header not allowed\n");
|
|
|
|
return LCBA_FAILED_AUTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = lws_hdr_copy(wsi, b64, sizeof(b64),
|
|
|
|
WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
|
|
if (m < 7) {
|
|
|
|
lwsl_err("b64 auth too long\n");
|
|
|
|
return LCBA_END_TRANSACTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
b64[5] = '\0';
|
|
|
|
if (strcasecmp(b64, "Basic")) {
|
|
|
|
lwsl_err("auth missing basic: %s\n", b64);
|
|
|
|
return LCBA_END_TRANSACTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
|
|
|
|
|
|
|
|
m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
|
|
|
|
if (m < 0) {
|
|
|
|
lwsl_err("plain auth too long\n");
|
|
|
|
return LCBA_END_TRANSACTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
plain[m] = '\0';
|
|
|
|
pcolon = strchr(plain, ':');
|
|
|
|
if (!pcolon) {
|
|
|
|
lwsl_err("basic auth format broken\n");
|
|
|
|
return LCBA_END_TRANSACTION;
|
|
|
|
}
|
|
|
|
if (!lws_find_string_in_file(basic_auth_login_file, plain, m)) {
|
|
|
|
lwsl_err("basic auth lookup failed\n");
|
|
|
|
return LCBA_FAILED_AUTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
|
|
|
|
* authorized username
|
|
|
|
*/
|
|
|
|
|
|
|
|
*pcolon = '\0';
|
|
|
|
wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain);
|
|
|
|
pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
|
|
strncpy(pcolon, plain, ml - 1);
|
|
|
|
pcolon[ml - 1] = '\0';
|
|
|
|
lwsl_info("%s: basic auth accepted for %s\n", __func__,
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
|
|
|
|
|
|
|
|
return LCBA_CONTINUE;
|
2019-08-18 10:35:43 +01:00
|
|
|
#else
|
|
|
|
return LCBA_FAILED_AUTH;
|
|
|
|
#endif
|
2018-11-28 12:16:01 +08:00
|
|
|
}
|
|
|
|
|
2019-03-19 11:53:57 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_PROXY)
|
|
|
|
/*
|
|
|
|
* Set up an onward http proxy connection according to the mount this
|
|
|
|
* uri falls under. Notice this can also be starting the proxying of what was
|
|
|
|
* originally an incoming h1 upgrade, or an h2 ws "upgrade".
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
|
|
|
|
char *uri_ptr, char ws)
|
|
|
|
{
|
|
|
|
char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0;
|
|
|
|
struct lws_client_connect_info i;
|
|
|
|
struct lws *cwsi;
|
|
|
|
int n, na;
|
|
|
|
|
|
|
|
if (ws)
|
|
|
|
/*
|
|
|
|
* Neither our inbound ws upgrade request side, nor our onward
|
|
|
|
* ws client connection on our side can bind to the actual
|
|
|
|
* protocol that only the remote inbound side and the remote
|
|
|
|
* onward side understand.
|
|
|
|
*
|
|
|
|
* Instead these are both bound to our built-in "lws-ws-proxy"
|
|
|
|
* protocol, which understands how to proxy between the two
|
|
|
|
* sides.
|
|
|
|
*
|
|
|
|
* We bind the parent, inbound part here and our side of the
|
|
|
|
* onward client connection is bound to the same handler using
|
|
|
|
* the .local_protocol_name.
|
|
|
|
*/
|
|
|
|
lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
|
|
|
|
|
|
|
|
memset(&i, 0, sizeof(i));
|
|
|
|
i.context = lws_get_context(wsi);
|
|
|
|
|
|
|
|
if (hit->origin[0] == '+')
|
|
|
|
unix_skt = 1;
|
|
|
|
|
|
|
|
pcolon = strchr(hit->origin, ':');
|
|
|
|
pslash = strchr(hit->origin, '/');
|
|
|
|
if (!pslash) {
|
|
|
|
lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unix_skt) {
|
|
|
|
if (!pcolon) {
|
|
|
|
lwsl_err("Proxy mount origin for unix skt must "
|
|
|
|
"have address delimited by :\n");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
n = lws_ptr_diff(pcolon, hit->origin);
|
|
|
|
pslash = pcolon;
|
|
|
|
} else {
|
|
|
|
if (pcolon > pslash)
|
|
|
|
pcolon = NULL;
|
|
|
|
|
|
|
|
if (pcolon)
|
|
|
|
n = (int)(pcolon - hit->origin);
|
|
|
|
else
|
|
|
|
n = (int)(pslash - hit->origin);
|
|
|
|
|
|
|
|
if (n >= (int)sizeof(ads) - 2)
|
|
|
|
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);
|
|
|
|
|
|
|
|
n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
|
|
|
|
pslash + 1, uri_ptr + hit->mountpoint_len) - 2;
|
|
|
|
lws_clean_url(rpath);
|
|
|
|
na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
|
|
|
|
if (na) {
|
|
|
|
char *p = rpath + n;
|
|
|
|
|
|
|
|
if (na >= (int)sizeof(rpath) - n - 2) {
|
|
|
|
lwsl_info("%s: query string %d longer "
|
|
|
|
"than we can handle\n", __func__,
|
|
|
|
na);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p++ = '?';
|
|
|
|
if (lws_hdr_copy(wsi, p,
|
|
|
|
(int)(&rpath[sizeof(rpath) - 1] - p),
|
|
|
|
WSI_TOKEN_HTTP_URI_ARGS) > 0)
|
|
|
|
while (na--) {
|
|
|
|
if (*p == '\0')
|
|
|
|
*p = '&';
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
i.path = rpath;
|
2019-04-05 21:13:59 +08:00
|
|
|
|
|
|
|
/* incoming may be h1 or h2... if he sends h1 HOST, use that
|
|
|
|
* directly, otherwise we must convert h2 :authority to h1
|
|
|
|
* host */
|
|
|
|
|
|
|
|
i.host = NULL;
|
|
|
|
n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
|
|
|
|
if (n > 0)
|
|
|
|
i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
|
|
|
|
else {
|
|
|
|
n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
|
|
|
|
if (n > 0) {
|
|
|
|
i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
2019-03-19 11:53:57 +08:00
|
|
|
if (i.address[0] != '+' ||
|
|
|
|
!lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
|
|
|
|
i.host = i.address;
|
|
|
|
else
|
|
|
|
i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
|
2019-04-05 21:13:59 +08:00
|
|
|
#endif
|
2019-03-19 11:53:57 +08:00
|
|
|
i.origin = NULL;
|
2019-03-22 06:22:40 +08:00
|
|
|
if (!ws) {
|
|
|
|
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)
|
|
|
|
#if defined(LWS_WITH_HTTP2)
|
|
|
|
|| (
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
|
|
|
|
!strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD), "post")
|
|
|
|
)
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
i.method = "POST";
|
|
|
|
else
|
|
|
|
i.method = "GET";
|
|
|
|
}
|
2019-03-19 11:53:57 +08:00
|
|
|
|
2019-04-05 21:13:59 +08:00
|
|
|
if (i.host)
|
|
|
|
lws_snprintf(host, sizeof(host), "%s:%u", i.host,
|
|
|
|
wsi->vhost->listen_port);
|
|
|
|
else
|
|
|
|
lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
|
|
|
|
|
2019-03-19 11:53:57 +08:00
|
|
|
i.host = host;
|
|
|
|
|
|
|
|
i.alpn = "http/1.1";
|
|
|
|
i.parent_wsi = wsi;
|
|
|
|
i.pwsi = &cwsi;
|
|
|
|
i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
|
|
|
|
if (ws)
|
|
|
|
i.local_protocol_name = "lws-ws-proxy";
|
|
|
|
|
|
|
|
// i.uri_replace_from = hit->origin;
|
|
|
|
// i.uri_replace_to = hit->mountpoint;
|
|
|
|
|
|
|
|
lwsl_info("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);
|
|
|
|
|
|
|
|
if (!lws_client_connect_via_info(&i)) {
|
|
|
|
lwsl_err("proxy connect fail\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ... we can't do the proxy action, but we can
|
|
|
|
* cleanly return him a 503 and a description
|
|
|
|
*/
|
|
|
|
|
|
|
|
lws_return_http_status(wsi,
|
|
|
|
HTTP_STATUS_SERVICE_UNAVAILABLE,
|
|
|
|
"<h1>Service Temporarily Unavailable</h1>"
|
|
|
|
"The server is temporarily unable to service "
|
|
|
|
"your request due to maintenance downtime or "
|
|
|
|
"capacity problems. Please try again later.");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
|
|
|
|
__func__, cwsi, lws_get_parent(cwsi));
|
|
|
|
|
|
|
|
cwsi->http.proxy_clientside = 1;
|
|
|
|
if (ws) {
|
|
|
|
wsi->proxied_ws_parent = 1;
|
|
|
|
cwsi->h1_ws_proxied = 1;
|
|
|
|
if (i.protocol) {
|
|
|
|
lwsl_debug("%s: (requesting '%s')\n", __func__, i.protocol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
static const char * const oprot[] = {
|
|
|
|
"http://", "https://"
|
|
|
|
};
|
|
|
|
|
2017-09-06 09:30:32 +08:00
|
|
|
int
|
|
|
|
lws_http_action(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
2018-11-23 08:47:56 +08:00
|
|
|
const struct lws_http_mount *hit = NULL;
|
2017-09-06 09:30:32 +08:00
|
|
|
enum http_version request_version;
|
|
|
|
struct lws_process_html_args args;
|
2018-11-23 08:47:56 +08:00
|
|
|
enum http_conn_type conn_type;
|
|
|
|
char content_length_str[32];
|
2018-10-26 16:14:30 +08:00
|
|
|
char http_version_str[12];
|
2017-09-06 09:30:32 +08:00
|
|
|
char *uri_ptr = NULL, *s;
|
2018-07-16 09:34:39 +08:00
|
|
|
int uri_len = 0, meth, m;
|
2018-11-23 08:47:56 +08:00
|
|
|
char http_conn_str[25];
|
|
|
|
int http_version_len;
|
|
|
|
unsigned int n;
|
2017-09-06 09:30:32 +08:00
|
|
|
|
|
|
|
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
|
2018-08-16 19:10:32 +08:00
|
|
|
if (meth < 0 || meth >= (int)LWS_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;
|
2018-07-21 10:26:13 +08:00
|
|
|
wsi->http.content_length_explicitly_zero = 0;
|
2015-01-10 19:01:52 -08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
|
2018-11-07 17:02:09 +08:00
|
|
|
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
|
|
|
|
2018-11-07 17:02:09 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
|
|
|
|
lws_hdr_copy(wsi, content_length_str,
|
|
|
|
sizeof(content_length_str) - 1,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) {
|
2019-07-05 06:07:03 +01:00
|
|
|
wsi->http.rx_content_remain = wsi->http.rx_content_length =
|
|
|
|
atoll(content_length_str);
|
2018-11-07 17:02:09 +08:00
|
|
|
if (!wsi->http.rx_content_length) {
|
|
|
|
wsi->http.content_length_explicitly_zero = 1;
|
|
|
|
lwsl_debug("%s: explicit 0 content-length\n", __func__);
|
2018-07-21 10:26:13 +08:00
|
|
|
}
|
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);
|
2018-11-07 17:02:09 +08:00
|
|
|
if (http_version_len > 7 &&
|
|
|
|
lws_hdr_copy(wsi, http_version_str,
|
|
|
|
sizeof(http_version_str) - 1,
|
|
|
|
WSI_TOKEN_HTTP) > 0 &&
|
|
|
|
http_version_str[5] == '1' && http_version_str[7] == '1')
|
|
|
|
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)
|
2018-09-02 14:35:37 +08:00
|
|
|
conn_type = HTTP_CONNECTION_KEEP_ALIVE;
|
2014-10-08 12:00:53 +08:00
|
|
|
else
|
2018-09-02 14:35:37 +08:00
|
|
|
conn_type = HTTP_CONNECTION_CLOSE;
|
2016-04-10 09:33:54 +08:00
|
|
|
|
|
|
|
/* Override default if http "Connection:" header: */
|
2018-10-26 16:14:30 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) &&
|
|
|
|
lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
|
|
|
|
WSI_TOKEN_CONNECTION) > 0) {
|
2016-04-10 09:33:54 +08:00
|
|
|
http_conn_str[sizeof(http_conn_str) - 1] = '\0';
|
|
|
|
if (!strcasecmp(http_conn_str, "keep-alive"))
|
2018-09-02 14:35:37 +08:00
|
|
|
conn_type = HTTP_CONNECTION_KEEP_ALIVE;
|
2016-04-10 09:33:54 +08:00
|
|
|
else
|
|
|
|
if (!strcasecmp(http_conn_str, "close"))
|
2018-09-02 14:35:37 +08:00
|
|
|
conn_type = HTTP_CONNECTION_CLOSE;
|
2016-04-10 09:33:54 +08:00
|
|
|
}
|
2018-09-02 14:35:37 +08:00
|
|
|
wsi->http.conn_type = conn_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
|
|
|
*/
|
2019-09-13 10:33:24 +01:00
|
|
|
if (!wsi->h2_stream_immortal)
|
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
|
|
|
wsi->context->timeout_secs);
|
2018-04-11 13:39:42 +08:00
|
|
|
#ifdef LWS_WITH_TLS
|
2018-05-01 12:41:42 +08:00
|
|
|
if (wsi->tls.redirect_to_https) {
|
2016-03-17 15:26:49 +08:00
|
|
|
/*
|
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,
|
2018-09-04 08:06:46 +08:00
|
|
|
*end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
|
2016-03-17 15:26:49 +08:00
|
|
|
|
2018-11-29 08:47:49 +08:00
|
|
|
n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
|
|
|
|
if (!n || n > 128)
|
2016-03-17 15:26:49 +08:00
|
|
|
goto bail_nuke_ah;
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2018-11-29 08:47:49 +08:00
|
|
|
p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://");
|
|
|
|
memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);
|
|
|
|
p += n;
|
|
|
|
*p++ = '/';
|
|
|
|
*p = '\0';
|
|
|
|
n = lws_ptr_diff(p, start);
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2018-11-29 08:47:49 +08:00
|
|
|
p += LWS_PRE;
|
2016-04-25 10:04:49 +08:00
|
|
|
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
|
2018-11-29 08:47:49 +08:00
|
|
|
start, 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
|
2018-08-14 08:00:25 +08:00
|
|
|
lws_prepare_access_log_info(wsi, uri_ptr, uri_len, 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
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0],
|
|
|
|
"no mount hit"))
|
2016-06-26 06:29:20 +08:00
|
|
|
return 1;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2018-05-04 12:05:56 +08:00
|
|
|
lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
|
|
|
|
|
2018-07-16 09:34:39 +08:00
|
|
|
m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
2016-06-26 06:29:20 +08:00
|
|
|
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)) {
|
2018-11-23 08:47:56 +08:00
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
|
|
|
|
*end = p + wsi->context->pt_serv_buf_size -
|
|
|
|
LWS_PRE - 512;
|
2016-06-26 06:29:20 +08:00
|
|
|
|
2018-09-04 08:06:46 +08:00
|
|
|
lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
|
2016-06-26 06:29:20 +08:00
|
|
|
|
|
|
|
/* > 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? */
|
|
|
|
|
2018-11-28 12:16:01 +08:00
|
|
|
switch(lws_check_basic_auth(wsi, hit->basic_auth_login_file)) {
|
|
|
|
case LCBA_CONTINUE:
|
|
|
|
break;
|
|
|
|
case LCBA_FAILED_AUTH:
|
|
|
|
return lws_unauthorised_basic_auth(wsi);
|
|
|
|
case LCBA_END_TRANSACTION:
|
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
|
|
|
return lws_http_transaction_completed(wsi);
|
2016-12-03 15:13:15 +08:00
|
|
|
}
|
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_PROXY)
|
|
|
|
/*
|
|
|
|
* The mount is a reverse proxy?
|
|
|
|
*/
|
|
|
|
|
2019-03-22 06:22:40 +08:00
|
|
|
// if (hit)
|
2018-09-04 08:06:46 +08:00
|
|
|
// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
|
2019-03-22 06:22:40 +08:00
|
|
|
//else
|
|
|
|
// lwsl_notice("%s: no hit\n", __func__);
|
2018-09-04 08:06:46 +08:00
|
|
|
|
2017-06-12 13:36:24 +08:00
|
|
|
if (hit->origin_protocol == LWSMPRO_HTTPS ||
|
2019-03-22 06:22:40 +08:00
|
|
|
hit->origin_protocol == LWSMPRO_HTTP) {
|
|
|
|
n = lws_http_proxy_start(wsi, hit, uri_ptr, 0);
|
2019-04-05 21:13:59 +08:00
|
|
|
// lwsl_notice("proxy start says %d\n", n);
|
2019-03-22 06:22:40 +08:00
|
|
|
if (n)
|
|
|
|
return n;
|
|
|
|
|
|
|
|
goto deal_body;
|
|
|
|
}
|
2017-06-12 13:36:24 +08:00
|
|
|
#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
|
|
|
}
|
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
|
2016-06-26 06:29:20 +08:00
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2019-08-30 13:08:09 +01:00
|
|
|
lwsl_debug("%s: %s, checking access rights for mask 0x%x\n",
|
2019-04-05 21:13:59 +08:00
|
|
|
__func__, hit->origin, hit->auth_mask);
|
|
|
|
|
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)) {
|
2018-07-16 09:34:39 +08:00
|
|
|
m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
2016-06-26 06:29:20 +08:00
|
|
|
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
|
|
|
|
2018-08-23 11:29:45 +08:00
|
|
|
n = uri_len - lws_ptr_diff(s, uri_ptr); // (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-07-16 09:34:39 +08:00
|
|
|
m = 1;
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
2018-03-20 10:31:53 +08:00
|
|
|
if (hit->origin_protocol == LWSMPRO_FILE)
|
2018-07-16 09:34:39 +08:00
|
|
|
m = lws_http_serve(wsi, s, hit->origin, hit);
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
2018-07-16 09:34:39 +08:00
|
|
|
|
|
|
|
if (m > 0) {
|
2016-06-26 06:29:20 +08:00
|
|
|
/*
|
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
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
|
|
|
|
|
|
|
|
if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
|
2016-06-26 06:29:20 +08:00
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2018-07-16 09:34:39 +08:00
|
|
|
m = pp->callback(wsi, LWS_CALLBACK_HTTP,
|
2016-06-26 06:29:20 +08:00
|
|
|
wsi->user_space,
|
|
|
|
uri_ptr + hit->mountpoint_len,
|
|
|
|
uri_len - hit->mountpoint_len);
|
|
|
|
} else
|
2018-07-16 09:34:39 +08:00
|
|
|
m = 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:
|
2018-07-16 09:34:39 +08:00
|
|
|
if (m) {
|
2016-02-25 20:27:10 +08:00
|
|
|
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2019-03-22 06:22:40 +08:00
|
|
|
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
|
2016-04-13 11:42:53 +08:00
|
|
|
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: */
|
2019-04-05 21:13:59 +08:00
|
|
|
lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
|
2017-12-01 11:09:32 +08:00
|
|
|
(long long)wsi->http.rx_content_length,
|
2017-10-16 12:52:32 +08:00
|
|
|
wsi->upgraded_to_http2, wsi->http2_substream);
|
2018-07-21 10:26:13 +08:00
|
|
|
|
|
|
|
if (wsi->http.content_length_explicitly_zero &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POST with an explicit content-length of zero
|
|
|
|
*
|
|
|
|
* If we don't give the user code the empty HTTP_BODY
|
|
|
|
* callback, he may become confused to hear the
|
|
|
|
* HTTP_BODY_COMPLETION (due to, eg, instantiation of
|
|
|
|
* lws_spa never happened).
|
|
|
|
*
|
|
|
|
* HTTP_BODY_COMPLETION is responsible for sending the
|
|
|
|
* result status code and result body if any, and
|
|
|
|
* do the transaction complete processing.
|
|
|
|
*/
|
|
|
|
if (wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_BODY,
|
|
|
|
wsi->user_space, NULL, 0))
|
|
|
|
return 1;
|
|
|
|
if (wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_BODY_COMPLETION,
|
|
|
|
wsi->user_space, NULL, 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-01 11:09:32 +08:00
|
|
|
if (wsi->http.rx_content_length > 0) {
|
2018-04-17 15:35:15 +08:00
|
|
|
|
2019-07-05 06:07:03 +01:00
|
|
|
if (lwsi_state(wsi) != LRS_DISCARD_BODY) {
|
|
|
|
lwsi_set_state(wsi, LRS_BODY);
|
|
|
|
lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
|
2018-04-02 11:55:17 +08:00
|
|
|
__func__, wsi, wsi->wsistate);
|
2019-07-05 06:07:03 +01:00
|
|
|
}
|
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) {
|
2018-10-10 13:54:43 +08:00
|
|
|
struct lws_tokens ebuf;
|
|
|
|
int m;
|
|
|
|
|
2018-04-30 19:17:32 +08:00
|
|
|
ebuf.len = (int)lws_buflist_next_segment_len(
|
2018-10-10 13:54:43 +08:00
|
|
|
&wsi->buflist,
|
2019-05-30 08:21:33 +08:00
|
|
|
&ebuf.token);
|
2018-04-17 15:35:15 +08:00
|
|
|
if (!ebuf.len)
|
|
|
|
break;
|
2018-12-01 09:20:00 +08:00
|
|
|
lwsl_debug("%s: consuming %d\n", __func__,
|
2018-10-10 13:54:43 +08:00
|
|
|
(int)ebuf.len);
|
2019-05-30 08:21:33 +08:00
|
|
|
m = lws_read_h1(wsi, ebuf.token,
|
2018-10-10 13:54:43 +08:00
|
|
|
ebuf.len);
|
2018-04-17 15:35:15 +08:00
|
|
|
if (m < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-08-27 06:06:13 +01:00
|
|
|
if (lws_buflist_aware_finished_consuming(wsi,
|
|
|
|
&ebuf, m, 1, __func__))
|
2018-04-29 10:44:36 +08:00
|
|
|
return -1;
|
2018-04-17 15:35:15 +08:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2018-11-13 09:33:13 +08:00
|
|
|
int
|
|
|
|
lws_confirm_host_header(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_tokenize ts;
|
|
|
|
lws_tokenize_elem e;
|
|
|
|
char buf[128];
|
|
|
|
int port = 80;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this vhost wants us to validate what the
|
|
|
|
* client sent against our vhost name
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
|
|
|
lwsl_info("%s: missing host on upgrade\n", __func__);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_TLS)
|
|
|
|
if (wsi->tls.ssl)
|
|
|
|
port = 443;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
|
|
|
|
LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
|
|
|
|
LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
|
|
|
|
ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
|
|
|
|
if (ts.len <= 0) {
|
|
|
|
lwsl_info("%s: missing or oversize host header\n", __func__);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
|
|
|
|
goto bad_format;
|
|
|
|
|
|
|
|
if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
|
|
|
|
buf[(ts.token - buf) + ts.token_len] = '\0';
|
|
|
|
lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
|
|
|
|
__func__, ts.token, wsi->vhost->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
e = lws_tokenize(&ts);
|
|
|
|
if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
|
|
|
|
if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
|
|
|
|
goto bad_format;
|
|
|
|
else
|
|
|
|
port = atoi(ts.token);
|
|
|
|
} else
|
|
|
|
if (e != LWS_TOKZE_ENDED)
|
|
|
|
goto bad_format;
|
|
|
|
|
|
|
|
if (wsi->vhost->listen_port != port) {
|
|
|
|
lwsl_info("%s: host port %d mismatches vhost port %d\n",
|
|
|
|
__func__, port, wsi->vhost->listen_port);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_debug("%s: host header OK\n", __func__);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bad_format:
|
|
|
|
lwsl_info("%s: bad host header format\n", __func__);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-18 05:04:15 +01:00
|
|
|
#if defined(LWS_WITH_SERVER)
|
2018-11-29 08:47:49 +08:00
|
|
|
int
|
|
|
|
lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
|
|
|
|
{
|
|
|
|
const struct lws_role_ops *role = &role_ops_raw_skt;
|
|
|
|
const struct lws_protocols *p1, *protocol =
|
|
|
|
&wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
|
2018-12-13 20:05:12 +08:00
|
|
|
char ipbuf[64];
|
2018-11-29 08:29:48 +08:00
|
|
|
int n;
|
2018-11-29 08:47:49 +08:00
|
|
|
|
|
|
|
if (wsi->vhost->listen_accept_role &&
|
|
|
|
lws_role_by_name(wsi->vhost->listen_accept_role))
|
|
|
|
role = lws_role_by_name(wsi->vhost->listen_accept_role);
|
|
|
|
|
|
|
|
if (wsi->vhost->listen_accept_protocol) {
|
|
|
|
p1 = lws_vhost_name_to_protocol(wsi->vhost,
|
|
|
|
wsi->vhost->listen_accept_protocol);
|
|
|
|
if (p1)
|
|
|
|
protocol = p1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_bind_protocol(wsi, protocol, __func__);
|
|
|
|
|
2018-11-29 08:29:48 +08:00
|
|
|
lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
|
2018-11-29 08:47:49 +08:00
|
|
|
|
2018-11-29 08:29:48 +08:00
|
|
|
lws_header_table_detach(wsi, 0);
|
2018-11-29 08:47:49 +08:00
|
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
|
|
|
|
2018-11-29 08:29:48 +08:00
|
|
|
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
|
|
|
|
if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
|
|
|
|
n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
|
|
|
|
|
2018-12-13 20:05:12 +08:00
|
|
|
ipbuf[0] = '\0';
|
|
|
|
#if !defined(LWS_PLAT_OPTEE)
|
|
|
|
lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
lwsl_notice("%s: vh %s, peer: %s, role %s, "
|
|
|
|
"protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name,
|
|
|
|
ipbuf, role->name, protocol->name, n, wsi->http.ah);
|
2018-11-29 08:29:48 +08:00
|
|
|
|
|
|
|
if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
n = LWS_CALLBACK_RAW_RX;
|
|
|
|
if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)])
|
|
|
|
n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)];
|
|
|
|
if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen))
|
2018-11-29 08:47:49 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 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
|
|
|
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);
|
2019-05-17 01:24:52 +01:00
|
|
|
#if defined(LWS_WITH_HTTP2)
|
2019-05-12 08:01:50 +01:00
|
|
|
struct allocated_headers *ah;
|
2019-05-17 01:24:52 +01:00
|
|
|
#endif
|
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
|
|
|
|
2018-11-29 08:47:49 +08:00
|
|
|
if (m == LPR_DO_FALLBACK) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* http parser went off the rails and
|
|
|
|
* LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_
|
|
|
|
* ACCEPT_CONFIG is set on this vhost.
|
|
|
|
*
|
|
|
|
* We are transitioning from http with an AH, to
|
|
|
|
* a backup role (raw-skt, by default). Drop
|
|
|
|
* the ah, bind to the role with mode as
|
|
|
|
* ESTABLISHED.
|
|
|
|
*/
|
|
|
|
raw_transition:
|
2017-03-07 16:06:05 +08:00
|
|
|
|
2018-11-29 08:29:48 +08:00
|
|
|
if (lws_http_to_fallback(wsi, obuf, olen)) {
|
|
|
|
lwsl_info("%s: fallback -> close\n", __func__);
|
2018-11-29 08:47:49 +08:00
|
|
|
goto bail_nuke_ah;
|
2018-11-29 08:29:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
(*buf) = obuf + olen;
|
2017-03-07 16:06:05 +08:00
|
|
|
|
2018-11-29 08:47:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (m) {
|
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 */
|
|
|
|
|
2018-09-04 08:06:46 +08:00
|
|
|
if (wsi->vhost->listen_port &&
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
2017-09-06 09:30:32 +08:00
|
|
|
struct lws_vhost *vhost = lws_select_vhost(
|
|
|
|
context, wsi->vhost->listen_port,
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
|
|
|
|
|
|
|
if (vhost)
|
vhost_destroy: use vhost wsi reference counting to trigger destroy
This changes the vhost destroy flow to only hand off the listen
socket if another vhost sharing it, and mark the vhost as
being_destroyed.
Each tsi calls lws_check_deferred_free() once a second, if it sees
any vhost being_destroyed there, it closes all wsi on its tsi on
the same vhost, one time.
As the wsi on the vhost complete close (ie, after libuv async close
if on libuv event loop), they decrement a reference count for all
wsi open on the vhost. The tsi who closes the last one then
completes the destroy flow for the vhost itself... it's random
which tsi completes the vhost destroy but since there are no
wsi left on the vhost, and it holds the context lock, nothing
can conflict.
The advantage of this is that owning tsi do the close for wsi
that are bound to the vhost under destruction, at a time when
they are guaranteed to be idle for service, and they do it with
both vhost and context locks owned, so no other service thread
can conflict for stuff protected by those either.
For the situation the user code may have allocations attached to
the vhost, this adds args to lws_vhost_destroy() to allow destroying
the user allocations just before the vhost is freed.
2018-06-16 09:31:07 +08:00
|
|
|
lws_vhost_bind_wsi(vhost, wsi);
|
2017-09-06 09:30:32 +08:00
|
|
|
} else
|
|
|
|
lwsl_info("no host\n");
|
|
|
|
|
2018-04-11 13:39:42 +08:00
|
|
|
if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
|
2019-08-18 06:29:34 +01:00
|
|
|
#if defined(LWS_WITH_SERVER_STATUS)
|
2017-10-13 10:33:02 +08:00
|
|
|
wsi->vhost->conn_stats.h1_trans++;
|
2019-08-18 06:29:34 +01:00
|
|
|
#endif
|
2017-10-13 10:33:02 +08:00
|
|
|
if (!wsi->conn_stat_done) {
|
2019-08-18 06:29:34 +01:00
|
|
|
#if defined(LWS_WITH_SERVER_STATUS)
|
2017-10-13 10:33:02 +08:00
|
|
|
wsi->vhost->conn_stats.h1_conn++;
|
2019-08-18 06:29:34 +01:00
|
|
|
#endif
|
2017-10-13 10:33:02 +08:00
|
|
|
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,
|
2018-08-14 08:00:25 +08:00
|
|
|
uri_ptr, uri_len, 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
|
2019-08-18 06:29:34 +01:00
|
|
|
#if defined(LWS_WITH_SERVER_STATUS)
|
2017-11-06 10:05:04 +08:00
|
|
|
wsi->vhost->conn_stats.rejected++;
|
2019-08-18 06:29:34 +01:00
|
|
|
#endif
|
2017-11-06 10:05:04 +08:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
|
2018-10-31 13:38:37 +08:00
|
|
|
|
|
|
|
const char *up = lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_UPGRADE);
|
|
|
|
|
|
|
|
if (strcasecmp(up, "websocket") &&
|
|
|
|
strcasecmp(up, "h2c")) {
|
|
|
|
lwsl_info("Unknown upgrade '%s'\n", up);
|
|
|
|
|
|
|
|
if (lws_return_http_status(wsi,
|
|
|
|
HTTP_STATUS_FORBIDDEN, NULL) ||
|
|
|
|
lws_http_transaction_completed(wsi))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
|
|
|
wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
|
|
|
|
wsi->user_space, (char *)up, 0);
|
|
|
|
|
|
|
|
/* just hang up? */
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
/* callback returned headers already, do t_c? */
|
|
|
|
|
|
|
|
if (n > 0) {
|
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
/* continue on */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* callback said 0, it was allowed */
|
|
|
|
|
2018-11-13 09:33:13 +08:00
|
|
|
if (wsi->vhost->options &
|
|
|
|
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
|
|
|
|
lws_confirm_host_header(wsi))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
2018-10-31 13:38:37 +08:00
|
|
|
if (!strcasecmp(up, "websocket")) {
|
2018-04-25 08:42:18 +08:00
|
|
|
#if defined(LWS_ROLE_WS)
|
2019-08-18 06:29:34 +01:00
|
|
|
#if defined(LWS_WITH_SERVER_STATUS)
|
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++;
|
2019-08-18 06:29:34 +01:00
|
|
|
#endif
|
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)
|
2018-10-31 13:38:37 +08:00
|
|
|
if (!strcasecmp(up, "h2c")) {
|
2019-08-18 06:29:34 +01:00
|
|
|
#if defined(LWS_WITH_SERVER_STATUS)
|
2017-10-13 10:33:02 +08:00
|
|
|
wsi->vhost->conn_stats.h2_upg++;
|
2019-08-18 06:29:34 +01:00
|
|
|
#endif
|
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
|
|
|
|
}
|
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
|
|
|
|
2018-05-04 12:05:56 +08:00
|
|
|
lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2018-04-02 11:55:17 +08:00
|
|
|
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
2017-12-01 11:09:32 +08:00
|
|
|
wsi->http.fop_fd = NULL;
|
2019-08-18 10:35:43 +01:00
|
|
|
#endif
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
lws_http_compression_validate(wsi);
|
|
|
|
#endif
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-05-12 08:01:50 +01:00
|
|
|
wsi->upgraded_to_http2 = 1;
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* adopt the header info */
|
|
|
|
|
2019-05-12 08:01:50 +01:00
|
|
|
ah = wsi->http.ah;
|
|
|
|
lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
|
|
|
|
&role_ops_h2);
|
|
|
|
|
|
|
|
/* http2 union member has http union struct at start */
|
|
|
|
wsi->http.ah = ah;
|
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
}
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
2014-04-03 09:03:37 +08:00
|
|
|
|
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;
|
|
|
|
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
if (lws_has_buffered_out(wsi)
|
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
|| wsi->http.comp_ctx.buflist_comp ||
|
|
|
|
wsi->http.comp_ctx.may_have_more
|
|
|
|
#endif
|
|
|
|
) {
|
2018-06-16 10:38:17 +08:00
|
|
|
/*
|
|
|
|
* ...so he tried to send something large as the http reply,
|
|
|
|
* it went as a partial, but he immediately said the
|
|
|
|
* transaction was completed.
|
|
|
|
*
|
|
|
|
* Defer the transaction completed until the last part of the
|
|
|
|
* partial is sent.
|
|
|
|
*/
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi);
|
2018-06-16 10:38:17 +08:00
|
|
|
wsi->http.deferred_transaction_completed = 1;
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
2018-06-16 10:38:17 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-07-05 06:07:03 +01:00
|
|
|
/*
|
|
|
|
* Are we finishing the transaction before we have consumed any body?
|
|
|
|
*
|
|
|
|
* For h1 this would kill keepalive pipelining, and for h2, considering
|
|
|
|
* it can extend over multiple DATA frames, it would kill the network
|
|
|
|
* connection.
|
|
|
|
*/
|
|
|
|
if (wsi->http.rx_content_length && wsi->http.rx_content_remain) {
|
|
|
|
/*
|
|
|
|
* are we already in LRS_DISCARD_BODY and didn't clear the
|
|
|
|
* remaining before trying to complete the transaction again?
|
|
|
|
*/
|
|
|
|
if (lwsi_state(wsi) == LRS_DISCARD_BODY)
|
|
|
|
return -1;
|
|
|
|
/*
|
|
|
|
* let's defer transaction completed processing until we
|
|
|
|
* discarded the remaining body
|
|
|
|
*/
|
|
|
|
lwsi_set_state(wsi, LRS_DISCARD_BODY);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-06-16 10:38:17 +08:00
|
|
|
|
2018-09-04 08:06:46 +08:00
|
|
|
lwsl_info("%s: wsi %p\n", __func__, wsi);
|
2018-04-17 15:35:15 +08:00
|
|
|
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
lws_http_compression_destroy(wsi);
|
|
|
|
#endif
|
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) {
|
2018-09-02 06:51:16 +08:00
|
|
|
char peer[64];
|
2019-01-11 13:13:19 +08:00
|
|
|
|
|
|
|
#if !defined(LWS_PLAT_OPTEE)
|
2018-09-02 06:51:16 +08:00
|
|
|
lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
|
2019-01-11 13:13:19 +08:00
|
|
|
#else
|
|
|
|
peer[0] = '\0';
|
|
|
|
#endif
|
2018-09-02 06:51:16 +08:00
|
|
|
peer[sizeof(peer) - 1] = '\0';
|
|
|
|
lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
|
|
|
|
__func__, peer);
|
2017-06-28 09:58:44 +08:00
|
|
|
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)
|
2018-08-14 08:00:25 +08:00
|
|
|
return 1;
|
2017-10-13 10:33:02 +08:00
|
|
|
|
|
|
|
if (wsi->seen_zero_length_recv)
|
|
|
|
return 1;
|
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
|
2018-06-16 09:35:07 +08:00
|
|
|
lwsl_info("%s: %p: close connection\n", __func__, wsi);
|
2014-10-17 08:38:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
|
2016-06-18 09:00:04 +08:00
|
|
|
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-05-04 12:05:56 +08:00
|
|
|
lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
|
|
|
|
wsi, 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;
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
wsi->sending_chunked = 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) {
|
2019-09-12 05:41:19 +01:00
|
|
|
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
2018-04-17 15:35:15 +08:00
|
|
|
if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_debug("%s: %p: nothing in buflist, detaching ah\n",
|
2018-05-04 12:05:56 +08:00
|
|
|
__func__, wsi);
|
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
|
|
|
|
*/
|
|
|
|
|
2018-05-01 12:41:42 +08:00
|
|
|
if (wsi->vhost->tls.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-11-23 08:47:56 +08:00
|
|
|
lwsl_info("%s: %p: resetting/keeping ah as pipeline\n",
|
2018-05-04 12:05:56 +08:00
|
|
|
__func__, wsi);
|
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-09-02 14:35:37 +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");
|
|
|
|
|
2018-09-02 14:35:37 +08:00
|
|
|
lwsl_debug("%s: %p: keep-alive await new transaction (state 0x%x)\n",
|
|
|
|
__func__, wsi, wsi->wsistate);
|
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;
|
|
|
|
}
|
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
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
|
|
|
{
|
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];
|
2018-11-23 08:47:56 +08:00
|
|
|
unsigned char *response = pt->serv_buf + LWS_PRE;
|
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
|
2018-11-23 08:47:56 +08:00
|
|
|
int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
|
2016-04-22 08:53:49 +08:00
|
|
|
char cache_control[50], *cc = "no-store";
|
2018-11-23 08:47:56 +08:00
|
|
|
lws_fop_flags_t fflags = LWS_O_RDONLY;
|
|
|
|
const struct lws_plat_file_ops *fops;
|
|
|
|
lws_filepos_t total_content_length;
|
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;
|
2018-11-23 08:47:56 +08:00
|
|
|
const char *vpath;
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
int ranges;
|
|
|
|
#endif
|
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);
|
2018-11-23 08:47:56 +08:00
|
|
|
if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND,
|
|
|
|
NULL))
|
2018-04-13 06:43:11 +08:00
|
|
|
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 */
|
2018-11-23 08:47:56 +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
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
return 0; /* == 0 means we did the transaction complete */
|
2016-12-12 13:36:25 +08:00
|
|
|
}
|
|
|
|
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");
|
|
|
|
}
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* if we know its very compressible, and we can use
|
|
|
|
* compression, then use the most preferred compression
|
|
|
|
* method that the client said he will accept
|
|
|
|
*/
|
|
|
|
|
2019-04-05 21:13:59 +08:00
|
|
|
if (!wsi->interpreting && (
|
|
|
|
!strncmp(content_type, "text/", 5) ||
|
|
|
|
!strcmp(content_type, "application/javascript") ||
|
|
|
|
!strcmp(content_type, "image/svg+xml")))
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
lws_http_compression_apply(wsi, NULL, &p, end, 0);
|
|
|
|
}
|
|
|
|
#endif
|
2017-03-03 12:38:10 +08:00
|
|
|
|
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) {
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
/* for http/1.1 ... */
|
|
|
|
if (!wsi->sending_chunked
|
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
&& !wsi->http.lcs
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
/* ... if not already using chunked and not using an
|
|
|
|
* http compression translation, then send the naive
|
|
|
|
* content length
|
|
|
|
*/
|
2018-01-14 20:09:41 +08:00
|
|
|
if (lws_add_http_header_content_length(wsi,
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
total_content_length, &p, end))
|
2018-01-14 20:09:41 +08:00
|
|
|
return -1;
|
|
|
|
} else {
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
|
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
if (wsi->http.lcs) {
|
2018-09-04 08:06:46 +08:00
|
|
|
|
|
|
|
/* ...otherwise, for http 1 it must go chunked.
|
|
|
|
* For the compression case, the reason is we
|
|
|
|
* compress on the fly and do not know the
|
|
|
|
* compressed content-length until it has all
|
|
|
|
* been sent. Http/1.1 pipelining must be able
|
|
|
|
* to know where the transaction boundaries are
|
|
|
|
* ... so chunking...
|
|
|
|
*/
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
|
|
|
(unsigned char *)"chunked", 7,
|
|
|
|
&p, end))
|
|
|
|
return -1;
|
|
|
|
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
/*
|
|
|
|
* ...this is fun, isn't it :-) For h1 that is
|
|
|
|
* using an http compression translation, the
|
|
|
|
* compressor must chunk its output privately.
|
|
|
|
*
|
|
|
|
* h2 doesn't need (or support) any of this
|
|
|
|
* crap.
|
|
|
|
*/
|
|
|
|
lwsl_debug("setting chunking\n");
|
|
|
|
wsi->http.comp_ctx.chunking = 1;
|
|
|
|
}
|
|
|
|
#endif
|
2018-01-14 20:09:41 +08:00
|
|
|
}
|
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) {
|
2018-08-13 16:49:58 +08:00
|
|
|
if (!wsi->cache_revalidate) {
|
2016-04-22 08:53:49 +08:00
|
|
|
cc = cache_control;
|
2018-08-13 16:49:58 +08:00
|
|
|
cclen = sprintf(cache_control, "%s, max-age=%u",
|
2016-04-22 08:53:49 +08:00
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
wsi->cache_secs);
|
|
|
|
} else {
|
2018-08-13 16:49:58 +08:00
|
|
|
cc = cache_control;
|
2018-09-04 08:06:46 +08:00
|
|
|
cclen = sprintf(cache_control,
|
|
|
|
"must-revalidate, %s, max-age=%u",
|
2018-08-13 16:49:58 +08:00
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
wsi->cache_secs);
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 05:05:39 +08:00
|
|
|
/* Only add cache control if its not specified by any other_headers. */
|
|
|
|
if (!other_headers ||
|
2018-11-15 16:33:54 +08:00
|
|
|
(!strstr(other_headers, "cache-control") &&
|
|
|
|
!strstr(other_headers, "Cache-Control"))) {
|
2018-11-23 08:47:56 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CACHE_CONTROL,
|
2018-10-15 05:05:39 +08:00
|
|
|
(unsigned char *)cc, cclen, &p, end))
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-22 08:53:49 +08:00
|
|
|
|
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
|
|
|
|
2019-08-01 12:56:29 +01:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) {
|
|
|
|
/* we do not emit the body */
|
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-13 10:33:02 +08:00
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
|
|
|
|
return 0;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
2013-11-11 07:30:33 +08:00
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#if defined(LWS_WITH_FILE_OPS)
|
|
|
|
|
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 {
|
|
|
|
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
/* priority 1: buffered output */
|
|
|
|
|
2018-08-20 12:02:26 +08:00
|
|
|
if (lws_has_buffered_out(wsi)) {
|
|
|
|
if (lws_issue_raw(wsi, NULL, 0) < 0) {
|
2018-04-20 10:33:23 +08:00
|
|
|
lwsl_info("%s: closing\n", __func__);
|
|
|
|
goto file_had_it;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
/* priority 2: buffered pre-compression-transform */
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
if (wsi->http.comp_ctx.buflist_comp ||
|
|
|
|
wsi->http.comp_ctx.may_have_more) {
|
|
|
|
enum lws_write_protocol wp = LWS_WRITE_HTTP;
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_info("%s: completing comp partial (buflist %p, may %d)\n",
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
__func__, wsi->http.comp_ctx.buflist_comp,
|
|
|
|
wsi->http.comp_ctx.may_have_more);
|
|
|
|
|
|
|
|
if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) {
|
|
|
|
lwsl_info("%s signalling to close\n", __func__);
|
|
|
|
goto file_had_it;
|
|
|
|
}
|
|
|
|
lws_callback_on_writable(wsi);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-20 10:33:23 +08:00
|
|
|
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"
|
2018-11-23 08:47:56 +08:00
|
|
|
"Content-Range: bytes "
|
|
|
|
"%llu-%llu/%llu\x0d\x0a"
|
2018-04-20 10:33:23 +08:00
|
|
|
"\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
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
poss = context->pt_serv_buf_size - n -
|
|
|
|
LWS_H2_FRAME_HEADER_LENGTH;
|
2018-04-20 10:33:23 +08:00
|
|
|
|
|
|
|
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
|
2018-11-23 08:47:56 +08:00
|
|
|
m = lws_write(wsi, p, n, wsi->http.filepos + amount ==
|
|
|
|
wsi->http.filelen ?
|
|
|
|
LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
|
2018-04-20 10:33:23 +08:00
|
|
|
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:
|
http: compression methods
Add generic http compression layer eanbled at cmake with LWS_WITH_HTTP_STREAM_COMPRESSION.
This is wholly a feature of the HTTP role (used by h1 and h2 roles) and doesn't exist
outside that context.
Currently provides 'deflate' and 'br' compression methods for server side only.
'br' requires also -DLWS_WITH_HTTP_BROTLI=1 at cmake and the brotli libraries (available in
your distro already) and dev package.
Other compression methods can be added nicely using an ops struct.
The built-in file serving stuff will use this is the client says he can handle it, and the
mimetype of the file either starts with "text/" (html and css etc) or is the mimetype of
Javascript.
zlib allocates quite a bit while in use, it seems to be around 256KiB per stream. So this
is only useful on relatively strong servers with lots of memory. However for some usecases
where you are serving a lot of css and js assets, it's a nice help.
The patch performs special treatment for http/1.1 pipelining, since the compression is
performed on the fly the compressed content-length is not known until the end. So for h1
only, chunked transfer-encoding is automatically added so pipelining can continue of the
connection.
For h2 the chunking is neither supported nor required, so it "just works".
User code can also request to add a compression transform before the reply headers were
sent using the new api
LWS_VISIBLE int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp);
... this allows transparent compression of dynamically generated HTTP. The requested
compression (eg, "deflate") is only applied if the client headers indicated it was
supported, otherwise it's a NOP.
Name may be NULL in which case the first compression method in the internal table at
stream.c that is mentioned as acceptable by the client will be used.
NOTE: the compression translation, same as h2 support, relies on the user code using
LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part written. The internal
lws fileserving code already does this.
2018-09-02 14:43:05 +08:00
|
|
|
if ((!lws_has_buffered_out(wsi)
|
|
|
|
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|
|
|
|
&& !wsi->http.comp_ctx.buflist_comp &&
|
|
|
|
!wsi->http.comp_ctx.may_have_more
|
|
|
|
#endif
|
|
|
|
) && (wsi->http.filepos >= wsi->http.filelen
|
2018-04-20 10:33:23 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
|| finished)
|
|
|
|
#else
|
|
|
|
)
|
|
|
|
#endif
|
2018-12-13 20:05:12 +08:00
|
|
|
) {
|
2018-04-20 10:33:23 +08:00
|
|
|
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,
|
2018-11-23 08:47:56 +08:00
|
|
|
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
|
|
|
wsi->user_space, NULL, 0) < 0) {
|
2018-04-20 10:33:23 +08:00
|
|
|
/*
|
|
|
|
* 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 */
|
|
|
|
}
|
2018-12-13 20:05:12 +08:00
|
|
|
/*
|
|
|
|
* while(1) here causes us to spam the whole file contents into
|
|
|
|
* a hugely bloated output buffer if it ever can't send the
|
|
|
|
* whole chunk...
|
|
|
|
*/
|
|
|
|
} while (!lws_send_pipe_choked(wsi));
|
2018-04-20 10:33:23 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-08-18 10:35:43 +01:00
|
|
|
#endif
|
|
|
|
|
2019-08-18 05:04:15 +01:00
|
|
|
#if defined(LWS_WITH_SERVER)
|
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;
|
2019-08-18 10:35:43 +01:00
|
|
|
#if !defined(LWS_PLAT_FREERTOS)
|
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
|
|
|
}
|
2019-06-05 05:04:17 +01:00
|
|
|
#endif
|
2016-06-08 10:07:02 +08:00
|
|
|
|
2019-08-18 06:29:34 +01:00
|
|
|
int
|
2016-06-12 09:56:39 +08:00
|
|
|
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) {
|
2018-11-23 08:47:56 +08:00
|
|
|
memmove(s->start + n, s->start + s->pos,
|
2019-04-05 21:13:59 +08:00
|
|
|
old_len - (sp - args->p) - 1);
|
2016-06-12 09:56:39 +08:00
|
|
|
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;
|
|
|
|
}
|