2013-01-18 11:43:21 +08:00
|
|
|
/*
|
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
|
|
*
|
2016-01-29 09:20:00 +08:00
|
|
|
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
2013-01-18 11:43:21 +08:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation:
|
|
|
|
* version 2.1 of the License.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "private-libwebsockets.h"
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined (LWS_WITH_ESP8266)
|
|
|
|
#undef memcpy
|
|
|
|
void *memcpy(void *dest, const void *src, size_t n)
|
|
|
|
{
|
|
|
|
return ets_memcpy(dest, src, n);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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_context_init_server(struct lws_context_creation_info *info,
|
2016-03-28 10:10:43 +08:00
|
|
|
struct lws_vhost *vhost)
|
2014-04-03 08:24:29 +08:00
|
|
|
{
|
2016-07-23 14:18:25 +08:00
|
|
|
#if LWS_POSIX
|
2016-01-29 21:18:54 +08:00
|
|
|
int n, opt = 1, limit = 1;
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
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;
|
2016-01-29 21:18:54 +08:00
|
|
|
int m = 0;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2017-02-18 17:26:40 +08:00
|
|
|
(void)opt;
|
2014-04-03 08:24:29 +08:00
|
|
|
/* set up our external listening socket we serve on */
|
|
|
|
|
2016-12-21 09:32:44 +08:00
|
|
|
if (info->port == CONTEXT_PORT_NO_LISTEN || info->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) {
|
|
|
|
if (vh->listen_port == info->port) {
|
|
|
|
if ((!info->iface && !vh->iface) ||
|
|
|
|
(info->iface && vh->iface &&
|
|
|
|
!strcmp(info->iface, vh->iface))) {
|
|
|
|
vhost->listen_port = info->port;
|
|
|
|
vhost->iface = info->iface;
|
|
|
|
lwsl_notice(" using listen skt from vhost %s\n",
|
|
|
|
vh->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vh = vh->vhost_next;
|
|
|
|
}
|
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2016-01-26 20:56:56 +08:00
|
|
|
#if defined(__linux__)
|
2016-03-28 10:10:43 +08:00
|
|
|
limit = vhost->context->count_threads;
|
2016-01-26 20:56:56 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
for (m = 0; m < limit; m++) {
|
2016-03-30 22:47:02 -07:00
|
|
|
#ifdef LWS_USE_UNIX_SOCK
|
2016-04-14 12:11:51 +08:00
|
|
|
if (LWS_UNIX_SOCK_ENABLED(vhost))
|
2016-03-30 22:47:02 -07:00
|
|
|
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
else
|
|
|
|
#endif
|
2014-04-03 08:24:29 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2016-06-03 21:19:40 +08:00
|
|
|
if (LWS_IPV6_ENABLED(vhost))
|
2014-04-03 08:24:29 +08:00
|
|
|
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2015-11-14 13:48:58 +08:00
|
|
|
if (sockfd == -1) {
|
2016-07-23 14:18:25 +08:00
|
|
|
#else
|
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
sockfd = esp8266_create_tcp_listen_socket(vhost);
|
|
|
|
if (!lws_sockfd_valid(sockfd)) {
|
|
|
|
#endif
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2014-04-03 08:24:29 +08:00
|
|
|
lwsl_err("ERROR opening socket\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2017-02-18 17:26:40 +08:00
|
|
|
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
2014-04-03 08:24:29 +08:00
|
|
|
/*
|
|
|
|
* allow us to restart even if old sockets in TIME_WAIT
|
|
|
|
*/
|
2014-11-30 13:07:11 +08:00
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
2015-12-06 08:00:03 +08:00
|
|
|
(const void *)&opt, sizeof(opt)) < 0) {
|
2017-02-18 17:26:40 +08:00
|
|
|
lwsl_err("reuseaddr failed\n");
|
2014-11-30 13:30:57 +08:00
|
|
|
compatible_close(sockfd);
|
2014-11-30 13:07:11 +08:00
|
|
|
return 1;
|
2014-11-30 13:30:57 +08:00
|
|
|
}
|
2016-06-04 08:37:39 +08:00
|
|
|
|
|
|
|
#if defined(LWS_USE_IPV6) && defined(IPV6_V6ONLY)
|
|
|
|
if (LWS_IPV6_ENABLED(vhost)) {
|
|
|
|
if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
|
|
|
|
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,
|
2016-06-04 08:37:39 +08:00
|
|
|
(const void*)&value, sizeof(value)) < 0) {
|
|
|
|
compatible_close(sockfd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-01-29 09:20:00 +08:00
|
|
|
#if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1
|
2016-03-28 10:10:43 +08:00
|
|
|
if (vhost->context->count_threads > 1)
|
2016-01-29 09:20:00 +08:00
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
|
|
|
|
(const void *)&opt, sizeof(opt)) < 0) {
|
|
|
|
compatible_close(sockfd);
|
|
|
|
return 1;
|
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
#endif
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2016-03-28 10:10:43 +08:00
|
|
|
lws_plat_set_socket_options(vhost, sockfd);
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2016-04-14 12:11:51 +08:00
|
|
|
n = lws_socket_bind(vhost, sockfd, info->port, info->iface);
|
2016-03-12 08:18:58 +08:00
|
|
|
if (n < 0)
|
2015-12-17 17:03:59 +08:00
|
|
|
goto bail;
|
2016-03-12 08:18:58 +08:00
|
|
|
info->port = n;
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->listen_port = info->port;
|
|
|
|
vhost->iface = info->iface;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
wsi = lws_zalloc(sizeof(struct lws));
|
2014-04-03 08:24:29 +08:00
|
|
|
if (wsi == NULL) {
|
|
|
|
lwsl_err("Out of mem\n");
|
2015-12-17 17:03:59 +08:00
|
|
|
goto bail;
|
2014-04-03 08:24:29 +08:00
|
|
|
}
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->context = vhost->context;
|
2017-02-27 12:55:56 +08:00
|
|
|
wsi->desc.sockfd = sockfd;
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_SERVER_LISTENER;
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->protocol = vhost->protocols;
|
2016-01-26 20:56:56 +08:00
|
|
|
wsi->tsi = m;
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->vhost = vhost;
|
|
|
|
wsi->listener = 1;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2016-11-30 07:05:13 +08:00
|
|
|
#ifdef LWS_USE_LIBUV
|
|
|
|
if (LWS_LIBUV_ENABLED(vhost->context))
|
|
|
|
lws_uv_initvhost(vhost, wsi);
|
|
|
|
#endif
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (insert_wsi_socket_into_fds(vhost->context, wsi))
|
2015-12-17 17:03:59 +08:00
|
|
|
goto bail;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->context->count_wsi_allocated++;
|
|
|
|
vhost->lserv_wsi = wsi;
|
2014-04-03 08:24:29 +08:00
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2017-02-27 12:55:56 +08:00
|
|
|
n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
|
2017-01-02 17:33:26 +01:00
|
|
|
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 */
|
2016-07-23 14:18:25 +08:00
|
|
|
#else
|
|
|
|
#if defined(LWS_WITH_ESP8266)
|
2017-02-27 12:55:56 +08:00
|
|
|
esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi);
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2016-03-30 22:47:02 -07:00
|
|
|
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
|
|
|
|
#ifdef LWS_USE_UNIX_SOCK
|
2016-04-14 12:11:51 +08:00
|
|
|
if (LWS_UNIX_SOCK_ENABLED(vhost))
|
2016-03-30 22:47:02 -07:00
|
|
|
lwsl_notice(" Listening on \"%s\"\n", info->iface);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
lwsl_notice(" Listening on port %d\n", info->port);
|
|
|
|
}
|
2014-12-04 23:59:35 +01:00
|
|
|
|
2014-04-03 08:24:29 +08:00
|
|
|
return 0;
|
2015-12-17 17:03:59 +08:00
|
|
|
|
|
|
|
bail:
|
|
|
|
compatible_close(sockfd);
|
|
|
|
|
|
|
|
return 1;
|
2014-04-03 08:24:29 +08:00
|
|
|
}
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
|
|
|
#undef strchr
|
|
|
|
#define strchr ets_strchr
|
|
|
|
#endif
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
struct lws_vhost *
|
|
|
|
lws_select_vhost(struct lws_context *context, int port, const char *servername)
|
|
|
|
{
|
|
|
|
struct lws_vhost *vhost = context->vhost_list;
|
2016-07-11 09:44:17 +08:00
|
|
|
const char *p;
|
|
|
|
int n, m, colon;
|
|
|
|
|
|
|
|
n = strlen(servername);
|
|
|
|
colon = n;
|
|
|
|
p = strchr(servername, ':');
|
|
|
|
if (p)
|
|
|
|
colon = p - servername;
|
|
|
|
|
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) {
|
|
|
|
m = strlen(vhost->name);
|
|
|
|
if (port == vhost->listen_port &&
|
|
|
|
m <= (colon - 2) &&
|
|
|
|
servername[colon - m - 1] == '.' &&
|
|
|
|
!strncmp(vhost->name, servername + colon - m, m)) {
|
|
|
|
lwsl_info("SNI: Found %s on wildcard: %s\n",
|
|
|
|
servername, vhost->name);
|
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
/* Priority 3: match the first vhost on our port */
|
|
|
|
|
|
|
|
vhost = context->vhost_list;
|
|
|
|
while (vhost) {
|
|
|
|
if (port == vhost->listen_port) {
|
|
|
|
lwsl_info("vhost match to %s based on port %d\n",
|
|
|
|
vhost->name, port);
|
|
|
|
return vhost;
|
|
|
|
}
|
|
|
|
vhost = vhost->vhost_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no match */
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-07-03 09:20:11 +08:00
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_get_mimetype(const char *file, const struct lws_http_mount *m)
|
2016-03-28 10:10:43 +08:00
|
|
|
{
|
|
|
|
int n = strlen(file);
|
2016-05-14 10:00:21 +08:00
|
|
|
const struct lws_protocol_vhost_options *pvo = NULL;
|
|
|
|
|
|
|
|
if (m)
|
|
|
|
pvo = m->extra_mimetypes;
|
2016-03-28 10:10:43 +08:00
|
|
|
|
|
|
|
if (n < 5)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".ico"))
|
|
|
|
return "image/x-icon";
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".gif"))
|
|
|
|
return "image/gif";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 3], ".js"))
|
|
|
|
return "text/javascript";
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".png"))
|
|
|
|
return "image/png";
|
|
|
|
|
2016-04-06 16:15:40 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".jpg"))
|
|
|
|
return "image/jpeg";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 3], ".gz"))
|
|
|
|
return "application/gzip";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".JPG"))
|
|
|
|
return "image/jpeg";
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!strcmp(&file[n - 5], ".html"))
|
|
|
|
return "text/html";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".css"))
|
|
|
|
return "text/css";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".txt"))
|
|
|
|
return "text/plain";
|
|
|
|
|
2016-09-23 00:04:40 +02:00
|
|
|
if (!strcmp(&file[n - 4], ".svg"))
|
|
|
|
return "image/svg+xml";
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
if (!strcmp(&file[n - 4], ".ttf"))
|
|
|
|
return "application/x-font-ttf";
|
|
|
|
|
2016-05-06 08:02:57 +08:00
|
|
|
if (!strcmp(&file[n - 5], ".woff"))
|
|
|
|
return "application/font-woff";
|
|
|
|
|
|
|
|
if (!strcmp(&file[n - 4], ".xml"))
|
|
|
|
return "application/xml";
|
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
while (pvo) {
|
2016-08-13 11:17:53 +02:00
|
|
|
if (pvo->name[0] == '*') /* ie, match anything */
|
|
|
|
return pvo->value;
|
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
if (!strcmp(&file[n - strlen(pvo->name)], pvo->name))
|
|
|
|
return pvo->value;
|
|
|
|
|
|
|
|
pvo = pvo->next;
|
|
|
|
}
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2017-03-03 12:38:10 +08:00
|
|
|
static lws_fop_flags_t
|
|
|
|
lws_vfs_prepare_flags(struct lws *wsi)
|
|
|
|
{
|
|
|
|
lws_fop_flags_t f = 0;
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
|
|
|
|
return f;
|
|
|
|
|
|
|
|
if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
|
|
|
|
"gzip")) {
|
|
|
|
lwsl_info("client indicates GZIP is acceptable\n");
|
|
|
|
f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2016-05-14 10:00:21 +08:00
|
|
|
static int
|
|
|
|
lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
|
|
|
const struct lws_http_mount *m)
|
2016-03-28 10:10:43 +08:00
|
|
|
{
|
2016-05-19 15:28:31 +08:00
|
|
|
const struct lws_protocol_vhost_options *pvo = m->interpret;
|
|
|
|
struct lws_process_html_args args;
|
2017-03-08 11:11:41 +08:00
|
|
|
const char *mimetype;
|
2017-03-03 12:38:10 +08:00
|
|
|
#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && \
|
|
|
|
!defined(LWS_WITH_ESP32)
|
|
|
|
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;
|
2016-04-13 11:49:07 +08:00
|
|
|
struct stat st;
|
2016-07-23 14:18:25 +08:00
|
|
|
int spin = 0;
|
2016-05-05 13:28:04 +02:00
|
|
|
#endif
|
2016-05-19 15:28:31 +08:00
|
|
|
char path[256], sym[512];
|
2016-04-22 08:53:49 +08:00
|
|
|
unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
|
|
|
|
unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
|
2017-03-08 11:11:41 +08:00
|
|
|
#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32)
|
2016-04-26 22:52:16 +02:00
|
|
|
size_t len;
|
2016-04-26 22:38:42 +02:00
|
|
|
#endif
|
2016-07-23 14:18:25 +08:00
|
|
|
int n;
|
2016-03-28 10:10:43 +08:00
|
|
|
|
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-03-03 12:38:10 +08:00
|
|
|
#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) && \
|
|
|
|
!defined(LWS_WITH_ESP32)
|
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);
|
|
|
|
|
|
|
|
if (wsi->u.http.fop_fd)
|
|
|
|
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
|
|
|
|
|
|
|
wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
|
|
|
|
path, vpath, &fflags);
|
|
|
|
if (!wsi->u.http.fop_fd) {
|
|
|
|
lwsl_err("Unable to open '%s'\n", path);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
/* if it can't be statted, don't try */
|
|
|
|
if (fflags & LWS_FOP_FLAG_VIRTUAL)
|
|
|
|
break;
|
2017-03-17 11:44:47 +08:00
|
|
|
#if !defined(WIN32)
|
2017-03-03 12:38:10 +08:00
|
|
|
if (fstat(wsi->u.http.fop_fd->fd, &st)) {
|
2016-04-25 10:04:49 +08:00
|
|
|
lwsl_info("unable to stat %s\n", path);
|
2016-04-13 11:49:07 +08:00
|
|
|
goto bail;
|
|
|
|
}
|
2017-03-17 11:44:47 +08:00
|
|
|
#else
|
|
|
|
if (stat(path, &st)) {
|
|
|
|
lwsl_info("unable to stat %s\n", path);
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
#endif
|
2016-04-13 11:49:07 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime;
|
|
|
|
fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
|
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode);
|
2017-02-12 20:32:49 +08:00
|
|
|
#if !defined(WIN32) && LWS_POSIX
|
2016-04-13 11:49:07 +08:00
|
|
|
if ((S_IFMT & st.st_mode) == S_IFLNK) {
|
2016-04-23 07:49:57 +08:00
|
|
|
len = readlink(path, sym, sizeof(sym) - 1);
|
|
|
|
if (len) {
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_err("Failed to read link %s\n", path);
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-04-23 07:49:57 +08:00
|
|
|
sym[len] = '\0';
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_debug("symlink %s -> %s\n", path, sym);
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(path, sizeof(path) - 1, "%s", sym);
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
#endif
|
2016-04-13 11:49:07 +08:00
|
|
|
if ((S_IFMT & st.st_mode) == S_IFDIR) {
|
|
|
|
lwsl_debug("default filename append to dir\n");
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
|
2016-04-13 11:49:07 +08:00
|
|
|
origin, uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (spin == 5)
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_err("symlink loop %s \n", path);
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
n = sprintf(sym, "%08lX%08lX",
|
|
|
|
(unsigned long)lws_vfs_get_length(wsi->u.http.fop_fd),
|
|
|
|
(unsigned long)lws_vfs_get_mod_time(wsi->u.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: */
|
|
|
|
wsi->u.http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
|
|
|
|
/*
|
|
|
|
* he thinks he has some version of it already,
|
|
|
|
* check if the tag matches
|
|
|
|
*/
|
2017-03-03 12:38:10 +08:00
|
|
|
if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
|
|
|
|
WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2016-04-23 07:14:03 +08:00
|
|
|
lwsl_debug("%s: ETAG match %s %s\n", __func__,
|
|
|
|
uri, origin);
|
2016-04-22 08:53:49 +08:00
|
|
|
|
|
|
|
/* we don't need to send the payload */
|
2017-03-08 07:51:47 +08:00
|
|
|
if (lws_add_http_header_status(wsi,
|
|
|
|
HTTP_STATUS_NOT_MODIFIED, &p, end))
|
2016-04-22 08:53:49 +08:00
|
|
|
return -1;
|
2016-08-27 17:07:06 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_ETAG,
|
|
|
|
(unsigned char *)sym, n, &p, end))
|
|
|
|
return -1;
|
2016-08-27 17:07:06 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = lws_write(wsi, start, p - start,
|
2016-05-19 15:28:31 +08:00
|
|
|
LWS_WRITE_HTTP_HEADERS);
|
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-03-03 12:38:10 +08:00
|
|
|
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
}
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
|
|
|
|
(unsigned char *)sym, n, &p, end))
|
|
|
|
return -1;
|
2016-05-05 13:28:04 +02:00
|
|
|
#endif
|
2016-04-22 08:53:49 +08:00
|
|
|
|
2016-07-03 09:20:11 +08:00
|
|
|
mimetype = lws_get_mimetype(path, m);
|
2016-03-28 10:10:43 +08:00
|
|
|
if (!mimetype) {
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_err("unknown mimetype for %s\n", path);
|
2016-08-13 11:17:53 +02:00
|
|
|
goto bail;
|
2016-03-28 10:10:43 +08:00
|
|
|
}
|
2016-08-13 11:17:53 +02:00
|
|
|
if (!mimetype[0])
|
|
|
|
lwsl_debug("sending no mimetype for %s\n", path);
|
2016-03-28 10:10:43 +08:00
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
wsi->sending_chunked = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check if this is in the list of file suffixes to be interpreted by
|
|
|
|
* a protocol
|
|
|
|
*/
|
|
|
|
while (pvo) {
|
|
|
|
n = strlen(path);
|
|
|
|
if (n > (int)strlen(pvo->name) &&
|
|
|
|
!strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
|
|
|
|
wsi->sending_chunked = 1;
|
|
|
|
wsi->protocol_interpret_idx = (char)(long)pvo->value;
|
|
|
|
lwsl_info("want %s interpreted by %s\n", path,
|
|
|
|
wsi->vhost->protocols[(int)(long)(pvo->value)].name);
|
|
|
|
wsi->protocol = &wsi->vhost->protocols[(int)(long)(pvo->value)];
|
|
|
|
if (lws_ensure_user_space(wsi))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pvo = pvo->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->protocol) {
|
|
|
|
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
|
|
|
wsi->vhost, m->protocol);
|
|
|
|
|
2016-06-18 09:00:04 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-19 15:28:31 +08:00
|
|
|
args.p = (char *)p;
|
|
|
|
args.max_len = end - p;
|
|
|
|
if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
|
|
|
|
wsi->user_space, &args, 0))
|
|
|
|
return -1;
|
|
|
|
p = (unsigned char *)args.p;
|
|
|
|
}
|
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start);
|
2016-04-06 16:15:40 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
|
|
|
return -1; /* error or can't reuse connection: close the socket */
|
|
|
|
|
|
|
|
return 0;
|
2016-04-13 11:49:07 +08:00
|
|
|
bail:
|
|
|
|
|
|
|
|
return -1;
|
2016-03-28 10:10:43 +08:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
hm = wsi->vhost->mount_list;
|
|
|
|
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) ||
|
|
|
|
hm->protocol) &&
|
|
|
|
hm->mountpoint_len > best)) {
|
|
|
|
best = hm->mountpoint_len;
|
|
|
|
hit = hm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hm = hm->mount_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hit;
|
|
|
|
}
|
|
|
|
|
2016-12-22 11:32:34 +08:00
|
|
|
#if LWS_POSIX
|
2016-12-03 15:13:15 +08:00
|
|
|
|
|
|
|
static int
|
|
|
|
lws_find_string_in_file(const char *filename, const char *string, int stringlen)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
int fd, match = 0, pos = 0, n = 0, hit = 0;
|
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
lwsl_err("can't open auth file: %s\n", filename);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_unauthorised_basic_auth(struct lws *wsi)
|
|
|
|
{
|
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE,
|
|
|
|
*p = start, *end = p + 512;
|
|
|
|
char buf[64];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/* no auth... tell him it is required */
|
|
|
|
|
|
|
|
if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
|
|
|
|
(unsigned char *)buf, n, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
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_http_action(struct lws *wsi)
|
2014-04-03 09:03:37 +08:00
|
|
|
{
|
2016-03-17 15:26:49 +08:00
|
|
|
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
HTTP Version, Keep-alive support, No-copy POST
This is a squashed commit from https://github.com/andrew-canaday/libwebsockets,
dev/http_keepalive branch (strategies changed a few times, so the commit
history is clutteread). This branch is submitted for clarity, but the other
can be used as a reference or alternative.
* added **enum http_version** to track HTTP/1.0 vs HTTP/1.1 requests
* added **enum http_connection_type** to track keep-alive vs close
* replaced content_length_seen and body_index with **content_remain**
* removed **post_buffer** (see handshake.c modifications)
* removed post_buffer free
* switch state to WSI_TOKEN_SKIPPING after URI is complete to store version
* delete *spill* label (unused)
* add vars to track HTTP version and connection type
* HTTP version defaults to 1.0
* connection type defaults to 'close' for 1.0, keep-alive for 1.1
* additional checks in **cleanup:** label:
* if HTTP version string is present and valid, set enum val appropriately
* override connection default with the "Connection:" header, if present
* set state to WSI_STATE_HTTP_BODY if content_length > 0
* return 0 on HTTP requests, unless LWS_CALLBACK_HTTP indicates otherwise
* add vars to track remaining content_length and body chunk size
* re-arrange switch case order to facilitate creation of jump-table
* added new labels:
* **read_ok**: normal location reach on break from switch; just return 0
* **http_complete**: check for keep-alive + init state, mode, hdr table
* **http_new**: jump location for keep-alive when http_complete sees len>0
* after libwebsocket_parse, jump to one of those labels based on state
* POST body handling:
* don't bother iterating over input byte-by-byte or using memcpy
* just pass the relevant portion of the context->service_buffer to callback
2014-07-13 01:07:36 -04:00
|
|
|
enum http_connection_type connection_type;
|
2015-12-06 08:00:03 +08:00
|
|
|
enum http_version request_version;
|
2014-04-03 09:03:37 +08:00
|
|
|
char content_length_str[32];
|
2016-05-19 15:28:31 +08:00
|
|
|
struct lws_process_html_args args;
|
2016-06-26 06:29:20 +08:00
|
|
|
const struct lws_http_mount *hit = NULL;
|
2015-12-06 08:00:03 +08:00
|
|
|
unsigned int n, count = 0;
|
2014-10-08 12:00:53 +08:00
|
|
|
char http_version_str[10];
|
|
|
|
char http_conn_str[20];
|
2015-12-06 08:00:03 +08:00
|
|
|
int http_version_len;
|
2016-06-26 06:29:20 +08:00
|
|
|
char *uri_ptr = NULL, *s;
|
|
|
|
int uri_len = 0;
|
2016-04-15 12:00:23 +08:00
|
|
|
int meth = -1;
|
2015-12-06 08:00:03 +08:00
|
|
|
|
2015-01-10 19:01:52 -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,
|
2017-02-12 20:32:49 +08:00
|
|
|
WSI_TOKEN_CONNECT,
|
2014-10-12 08:38:16 +08:00
|
|
|
#ifdef LWS_USE_HTTP2
|
2015-01-10 19:01:52 -08:00
|
|
|
WSI_TOKEN_HTTP_COLON_PATH,
|
2014-10-12 08:38:16 +08:00
|
|
|
#endif
|
2015-01-10 19:01:52 -08:00
|
|
|
};
|
2016-04-15 12:00:23 +08:00
|
|
|
#if defined(_DEBUG) || defined(LWS_WITH_ACCESS_LOG)
|
2015-01-10 19:01:52 -08:00
|
|
|
static const char * const method_names[] = {
|
2017-02-12 20:32:49 +08:00
|
|
|
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT",
|
2015-01-10 19:01:52 -08:00
|
|
|
#ifdef LWS_USE_HTTP2
|
|
|
|
":path",
|
|
|
|
#endif
|
|
|
|
};
|
2015-02-20 07:37:20 +08:00
|
|
|
#endif
|
2016-05-19 15:28:31 +08:00
|
|
|
static const char * const oprot[] = {
|
|
|
|
"http://", "https://"
|
|
|
|
};
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2015-01-10 19:01:52 -08:00
|
|
|
/* it's not websocket.... shall we accept it as http? */
|
|
|
|
|
|
|
|
for (n = 0; n < ARRAY_SIZE(methods); n++)
|
|
|
|
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");
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2015-01-10 19:01:52 -08:00
|
|
|
if (count != 1) {
|
|
|
|
lwsl_warn("multiple methods?\n");
|
2014-10-08 12:00:53 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2015-12-04 09:23:56 +08:00
|
|
|
if (lws_ensure_user_space(wsi))
|
2014-10-08 12:00:53 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
2015-01-10 19:01:52 -08:00
|
|
|
for (n = 0; n < ARRAY_SIZE(methods); n++)
|
|
|
|
if (lws_hdr_total_length(wsi, methods[n])) {
|
|
|
|
uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
|
|
|
|
uri_len = lws_hdr_total_length(wsi, methods[n]);
|
|
|
|
lwsl_info("Method: %s request for '%s'\n",
|
|
|
|
method_names[n], uri_ptr);
|
2016-04-15 12:00:23 +08:00
|
|
|
meth = n;
|
2015-01-10 19:01:52 -08:00
|
|
|
break;
|
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
(void)meth;
|
|
|
|
|
2016-04-02 07:36:17 +08:00
|
|
|
/* we insist on absolute paths */
|
|
|
|
|
|
|
|
if (uri_ptr[0] != '/') {
|
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
|
|
|
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* HTTP header had a content length? */
|
|
|
|
|
|
|
|
wsi->u.http.content_length = 0;
|
2015-01-10 19:01:52 -08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
|
|
|
|
lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
|
2014-10-08 12:00:53 +08:00
|
|
|
wsi->u.http.content_length = 100 * 1024 * 1024;
|
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
|
|
|
lws_hdr_copy(wsi, content_length_str,
|
2015-12-16 18:19:08 +08:00
|
|
|
sizeof(content_length_str) - 1,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH);
|
2014-10-08 12:00:53 +08:00
|
|
|
wsi->u.http.content_length = atoi(content_length_str);
|
|
|
|
}
|
|
|
|
|
2016-04-10 09:33:54 +08:00
|
|
|
if (wsi->http2_substream) {
|
|
|
|
wsi->u.http.request_version = HTTP_VERSION_2;
|
|
|
|
} else {
|
|
|
|
/* http_version? Default to 1.0, override with token: */
|
|
|
|
request_version = HTTP_VERSION_1_0;
|
|
|
|
|
|
|
|
/* Works for single digit HTTP versions. : */
|
|
|
|
http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
|
|
|
|
if (http_version_len > 7) {
|
|
|
|
lws_hdr_copy(wsi, http_version_str,
|
|
|
|
sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
|
|
|
|
if (http_version_str[5] == '1' && http_version_str[7] == '1')
|
|
|
|
request_version = HTTP_VERSION_1_1;
|
|
|
|
}
|
|
|
|
wsi->u.http.request_version = request_version;
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2016-04-10 09:33:54 +08:00
|
|
|
/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
|
|
|
|
if (request_version == HTTP_VERSION_1_1)
|
2014-10-08 12:00:53 +08:00
|
|
|
connection_type = HTTP_CONNECTION_KEEP_ALIVE;
|
|
|
|
else
|
2016-04-10 09:33:54 +08:00
|
|
|
connection_type = HTTP_CONNECTION_CLOSE;
|
|
|
|
|
|
|
|
/* Override default if http "Connection:" header: */
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
|
|
|
|
lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
|
|
|
|
WSI_TOKEN_CONNECTION);
|
|
|
|
http_conn_str[sizeof(http_conn_str) - 1] = '\0';
|
|
|
|
if (!strcasecmp(http_conn_str, "keep-alive"))
|
|
|
|
connection_type = HTTP_CONNECTION_KEEP_ALIVE;
|
|
|
|
else
|
|
|
|
if (!strcasecmp(http_conn_str, "close"))
|
|
|
|
connection_type = HTTP_CONNECTION_CLOSE;
|
|
|
|
}
|
|
|
|
wsi->u.http.connection_type = connection_type;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
2015-12-17 07:54:44 +08:00
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
|
2015-12-06 08:00:03 +08:00
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
if (n) {
|
|
|
|
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
2014-10-08 12:00:53 +08:00
|
|
|
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
return 1;
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
2016-02-06 08:44:34 +08:00
|
|
|
/*
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
* if there is content supposed to be coming,
|
|
|
|
* put a timeout on it having arrived
|
2016-02-06 08:44:34 +08:00
|
|
|
*/
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
2016-02-15 20:36:02 +08:00
|
|
|
wsi->context->timeout_secs);
|
2016-03-18 23:55:59 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2016-03-17 15:26:49 +08:00
|
|
|
if (wsi->redirect_to_https) {
|
|
|
|
/*
|
2016-03-18 23:55:59 +08:00
|
|
|
* we accepted http:// only so we could redirect to
|
2016-03-17 15:26:49 +08:00
|
|
|
* https://, so issue the redirect. Create the redirection
|
|
|
|
* URI from the host: header and ignore the path part
|
|
|
|
*/
|
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
|
|
|
|
*end = p + 512;
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
|
|
|
|
goto bail_nuke_ah;
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2016-04-07 10:08:35 +08:00
|
|
|
n = sprintf((char *)end, "https://%s/",
|
2016-03-17 15:26:49 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
2016-04-15 20:09:36 +08:00
|
|
|
|
2016-04-25 10:04:49 +08:00
|
|
|
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
|
|
|
|
end, n, &p, end);
|
2016-03-18 23:55:59 +08:00
|
|
|
if ((int)n < 0)
|
2016-03-17 15:26:49 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
return lws_http_transaction_completed(wsi);
|
|
|
|
}
|
2016-04-13 11:42:53 +08:00
|
|
|
#endif
|
2016-03-17 15:26:49 +08:00
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
|
|
|
/*
|
|
|
|
* Produce Apache-compatible log string for wsi, like this:
|
|
|
|
*
|
|
|
|
* 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
|
|
|
|
* "GET /aep-screen.png HTTP/1.1"
|
|
|
|
* 200 152987 "https://libwebsockets.org/index.html"
|
|
|
|
* "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
static const char * const hver[] = {
|
|
|
|
"http/1.0", "http/1.1", "http/2"
|
|
|
|
};
|
|
|
|
#ifdef LWS_USE_IPV6
|
|
|
|
char ads[INET6_ADDRSTRLEN];
|
|
|
|
#else
|
|
|
|
char ads[INET_ADDRSTRLEN];
|
|
|
|
#endif
|
|
|
|
char da[64];
|
|
|
|
const char *pa, *me;
|
|
|
|
struct tm *tmp;
|
|
|
|
time_t t = time(NULL);
|
|
|
|
int l = 256;
|
|
|
|
|
|
|
|
if (wsi->access_log_pending)
|
|
|
|
lws_access_log(wsi);
|
|
|
|
|
|
|
|
wsi->access_log.header_log = lws_malloc(l);
|
2016-05-12 21:04:33 +08:00
|
|
|
if (wsi->access_log.header_log) {
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2016-05-12 21:04:33 +08:00
|
|
|
tmp = localtime(&t);
|
|
|
|
if (tmp)
|
|
|
|
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp);
|
|
|
|
else
|
|
|
|
strcpy(da, "01/Jan/1970:00:00:00 +0000");
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2016-05-12 21:04:33 +08:00
|
|
|
pa = lws_get_peer_simple(wsi, ads, sizeof(ads));
|
|
|
|
if (!pa)
|
|
|
|
pa = "(unknown)";
|
2016-04-15 12:00:23 +08:00
|
|
|
|
2016-05-12 21:04:33 +08:00
|
|
|
if (meth >= 0)
|
|
|
|
me = method_names[meth];
|
|
|
|
else
|
|
|
|
me = "unknown";
|
|
|
|
|
2016-09-15 02:22:57 +08:00
|
|
|
lws_snprintf(wsi->access_log.header_log, l,
|
2016-05-12 21:04:33 +08:00
|
|
|
"%s - - [%s] \"%s %s %s\"",
|
|
|
|
pa, da, me, uri_ptr,
|
|
|
|
hver[wsi->u.http.request_version]);
|
|
|
|
|
|
|
|
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
|
|
|
|
if (l) {
|
|
|
|
wsi->access_log.user_agent = lws_malloc(l + 2);
|
|
|
|
if (wsi->access_log.user_agent)
|
|
|
|
lws_hdr_copy(wsi, wsi->access_log.user_agent,
|
|
|
|
l + 1, WSI_TOKEN_HTTP_USER_AGENT);
|
|
|
|
else
|
|
|
|
lwsl_err("OOM getting user agent\n");
|
|
|
|
}
|
|
|
|
wsi->access_log_pending = 1;
|
2016-04-15 12:00:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
/* can we serve it from the mount list? */
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
hit = lws_find_mount(wsi, uri_ptr, uri_len);
|
|
|
|
if (!hit) {
|
|
|
|
/* deferred cleanup and reset to protocols[0] */
|
2016-04-08 13:25:34 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
lwsl_info("no hit\n");
|
2016-04-08 18:30:45 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
|
|
|
|
return 1;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
goto after;
|
|
|
|
}
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
s = uri_ptr + hit->mountpoint_len;
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
/*
|
|
|
|
* if we have a mountpoint like https://xxx.com/yyy
|
|
|
|
* there is an implied / at the end for our purposes since
|
|
|
|
* we can only mount on a "directory".
|
|
|
|
*
|
|
|
|
* But if we just go with that, the browser cannot understand
|
|
|
|
* that he is actually looking down one "directory level", so
|
|
|
|
* even though we give him /yyy/abc.html he acts like the
|
|
|
|
* current directory level is /. So relative urls like "x.png"
|
|
|
|
* wrongly look outside the mountpoint.
|
|
|
|
*
|
|
|
|
* Therefore if we didn't come in on a url with an explicit
|
|
|
|
* / at the end, we must redirect to add it so the browser
|
|
|
|
* understands he is one "directory level" down.
|
|
|
|
*/
|
|
|
|
if ((hit->mountpoint_len > 1 ||
|
|
|
|
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
|
|
|
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
|
|
|
|
(*s != '/' ||
|
|
|
|
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
|
|
|
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
|
|
|
|
(hit->origin_protocol != LWSMPRO_CGI &&
|
|
|
|
hit->origin_protocol != LWSMPRO_CALLBACK //&&
|
|
|
|
//hit->protocol == NULL
|
|
|
|
)) {
|
|
|
|
unsigned char *start = pt->serv_buf + LWS_PRE,
|
|
|
|
*p = start, *end = p + 512;
|
|
|
|
|
|
|
|
lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
|
|
|
|
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
|
2016-05-19 15:28:31 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
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);
|
|
|
|
else
|
2016-09-15 02:22:57 +08:00
|
|
|
n = lws_snprintf((char *)end, 256,
|
2016-06-26 06:29:20 +08:00
|
|
|
"%s%s%s/", oprot[lws_is_ssl(wsi)],
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
|
|
|
|
uri_ptr);
|
2016-04-13 11:49:07 +08:00
|
|
|
|
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-22 11:32:34 +08:00
|
|
|
#if LWS_POSIX
|
2016-12-03 15:13:15 +08:00
|
|
|
/* basic auth? */
|
|
|
|
|
|
|
|
if (hit->basic_auth_login_file) {
|
|
|
|
char b64[160], plain[(sizeof(b64) * 3) / 4];
|
|
|
|
int m;
|
|
|
|
|
|
|
|
/* Did he send auth? */
|
|
|
|
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION))
|
|
|
|
return lws_unauthorised_basic_auth(wsi);
|
|
|
|
|
|
|
|
n = HTTP_STATUS_FORBIDDEN;
|
|
|
|
|
|
|
|
m = lws_hdr_copy(wsi, b64, sizeof(b64), WSI_TOKEN_HTTP_AUTHORIZATION);
|
|
|
|
if (m < 7) {
|
|
|
|
lwsl_err("b64 auth too long\n");
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
|
|
|
b64[5] = '\0';
|
|
|
|
if (strcasecmp(b64, "Basic")) {
|
|
|
|
lwsl_err("auth missing basic: %s\n", b64);
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
|
|
|
|
|
|
|
|
m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain));
|
|
|
|
if (m < 0) {
|
|
|
|
lwsl_err("plain auth too long\n");
|
|
|
|
goto transaction_result_n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// lwsl_notice(plain);
|
|
|
|
|
|
|
|
if (!lws_find_string_in_file(hit->basic_auth_login_file, plain, m)) {
|
|
|
|
lwsl_err("basic auth lookup failed\n");
|
|
|
|
return lws_unauthorised_basic_auth(wsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_notice("basic auth accepted\n");
|
|
|
|
|
|
|
|
/* accept the auth */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
/*
|
|
|
|
* A particular protocol callback is mounted here?
|
|
|
|
*
|
|
|
|
* For the duration of this http transaction, bind us to the
|
|
|
|
* associated protocol
|
|
|
|
*/
|
|
|
|
if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
|
|
|
|
const struct lws_protocols *pp;
|
|
|
|
const char *name = hit->origin;
|
|
|
|
if (hit->protocol)
|
|
|
|
name = hit->protocol;
|
|
|
|
|
|
|
|
pp = lws_vhost_name_to_protocol(wsi->vhost, name);
|
|
|
|
if (!pp) {
|
|
|
|
n = -1;
|
|
|
|
lwsl_err("Unable to find plugin '%s'\n",
|
|
|
|
hit->origin);
|
|
|
|
return 1;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-11-26 20:46:04 +08:00
|
|
|
args.p = uri_ptr;
|
|
|
|
args.len = uri_len;
|
|
|
|
args.max_len = hit->auth_mask;
|
|
|
|
args.final = 0; /* used to signal callback dealt with it */
|
|
|
|
|
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
|
|
|
|
wsi->user_space, &args, 0);
|
|
|
|
if (n) {
|
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
|
|
|
|
NULL);
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
if (args.final) /* callback completely handled it well */
|
|
|
|
return 0;
|
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (hit->cgienv && wsi->protocol->callback(wsi,
|
|
|
|
LWS_CALLBACK_HTTP_PMO,
|
|
|
|
wsi->user_space, (void *)hit->cgienv, 0))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
|
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space,
|
|
|
|
uri_ptr + hit->mountpoint_len,
|
|
|
|
uri_len - hit->mountpoint_len);
|
2016-05-09 09:37:01 +08:00
|
|
|
goto after;
|
|
|
|
}
|
2016-06-26 06:29:20 +08:00
|
|
|
}
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-04-13 11:49:07 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
2016-06-26 06:29:20 +08:00
|
|
|
/* did we hit something with a cgi:// origin? */
|
|
|
|
if (hit->origin_protocol == LWSMPRO_CGI) {
|
|
|
|
const char *cmd[] = {
|
|
|
|
NULL, /* replace with cgi path */
|
|
|
|
NULL
|
|
|
|
};
|
2016-08-27 17:07:06 +08:00
|
|
|
unsigned char *p, *end, buffer[1024];
|
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
|
|
|
p = buffer + LWS_PRE;
|
|
|
|
end = p + sizeof(buffer) - LWS_PRE;
|
2016-04-08 18:30:45 +08:00
|
|
|
|
2017-03-08 07:51:47 +08:00
|
|
|
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
|
2016-06-26 06:29:20 +08:00
|
|
|
return 1;
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
|
|
|
|
(unsigned char *)"close", 5, &p, end))
|
|
|
|
return 1;
|
|
|
|
n = lws_write(wsi, buffer + LWS_PRE,
|
|
|
|
p - (buffer + LWS_PRE),
|
|
|
|
LWS_WRITE_HTTP_HEADERS);
|
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
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = strlen(s);
|
|
|
|
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
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = lws_http_serve(wsi, s, hit->origin, hit);
|
|
|
|
if (n) {
|
|
|
|
/*
|
|
|
|
* lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
|
|
|
*/
|
|
|
|
if (hit->protocol) {
|
|
|
|
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
|
|
|
wsi->vhost, hit->protocol);
|
2016-05-19 15:28:31 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
if (lws_bind_protocol(wsi, pp))
|
|
|
|
return 1;
|
2016-05-09 09:37:01 +08:00
|
|
|
|
2016-06-26 06:29:20 +08:00
|
|
|
n = pp->callback(wsi, LWS_CALLBACK_HTTP,
|
|
|
|
wsi->user_space,
|
|
|
|
uri_ptr + hit->mountpoint_len,
|
|
|
|
uri_len - hit->mountpoint_len);
|
|
|
|
} else
|
|
|
|
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
wsi->user_space, uri_ptr, uri_len);
|
2016-05-09 09:37:01 +08:00
|
|
|
}
|
2016-06-26 06:29:20 +08:00
|
|
|
|
2016-05-09 09:37:01 +08:00
|
|
|
after:
|
2016-02-25 20:27:10 +08:00
|
|
|
if (n) {
|
|
|
|
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2014-10-08 12:00:53 +08:00
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
#ifdef LWS_WITH_CGI
|
|
|
|
deal_body:
|
|
|
|
#endif
|
2015-12-14 08:52:03 +08:00
|
|
|
/*
|
2014-10-08 12:00:53 +08:00
|
|
|
* If we're not issuing a file, check for content_length or
|
|
|
|
* HTTP keep-alive. No keep-alive header allocation for
|
2015-12-14 08:52:03 +08:00
|
|
|
* ISSUING_FILE, as this uses HTTP/1.0.
|
|
|
|
*
|
2015-12-04 08:43:54 +08:00
|
|
|
* In any case, return 0 and let lws_read decide how to
|
2014-10-08 12:00:53 +08:00
|
|
|
* proceed based on state
|
|
|
|
*/
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->state != LWSS_HTTP_ISSUING_FILE)
|
2014-10-08 12:00:53 +08:00
|
|
|
/* Prepare to read body if we have a content length: */
|
|
|
|
if (wsi->u.http.content_length > 0)
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_HTTP_BODY;
|
2014-10-08 12:00:53 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bail_nuke_ah:
|
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
|
|
|
/* we're closing, losing some rx is OK */
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
2016-07-23 14:18:25 +08:00
|
|
|
// lwsl_notice("%s: drop1\n", __func__);
|
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;
|
2016-12-22 11:32:34 +08:00
|
|
|
#if LWS_POSIX
|
2016-12-03 15:13:15 +08:00
|
|
|
transaction_result_n:
|
|
|
|
lws_return_http_status(wsi, n, NULL);
|
2016-12-22 11:32:34 +08:00
|
|
|
|
2016-12-03 15:13:15 +08:00
|
|
|
return lws_http_transaction_completed(wsi);
|
2016-12-22 11:32:34 +08:00
|
|
|
#endif
|
2014-10-08 12:00:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
int
|
|
|
|
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
2014-10-08 12:00:53 +08:00
|
|
|
{
|
2017-03-07 16:06:05 +08:00
|
|
|
int protocol_len, n = 0, hit, non_space_char_found = 0, m;
|
2015-12-17 18:25:25 +08:00
|
|
|
struct lws_context *context = lws_get_context(wsi);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
|
|
|
struct _lws_header_related hdr;
|
2014-10-08 12:00:53 +08:00
|
|
|
struct allocated_headers *ah;
|
2017-03-07 16:06:05 +08:00
|
|
|
unsigned char *obuf = *buf;
|
2014-07-19 06:52:39 +08:00
|
|
|
char protocol_list[128];
|
2016-08-11 05:36:08 +08:00
|
|
|
char protocol_name[64];
|
2017-03-07 16:06:05 +08:00
|
|
|
size_t olen = len;
|
2014-07-19 06:52:39 +08:00
|
|
|
char *p;
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wsi->u.hdr.ah) {
|
|
|
|
lwsl_err("%s: assert: NULL ah\n", __func__);
|
|
|
|
assert(0);
|
|
|
|
}
|
2014-04-03 09:03:37 +08:00
|
|
|
|
|
|
|
while (len--) {
|
2016-02-28 10:55:31 +08:00
|
|
|
wsi->more_rx_waiting = !!len;
|
2016-01-21 10:54:14 +08:00
|
|
|
|
2016-04-13 11:42:53 +08:00
|
|
|
if (wsi->mode != LWSCM_HTTP_SERVING &&
|
|
|
|
wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
|
|
|
|
lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2016-01-21 10:54:14 +08:00
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
m = lws_parse(wsi, *(*buf)++);
|
|
|
|
if (m) {
|
|
|
|
if (m == 2) {
|
|
|
|
/*
|
|
|
|
* we are transitioning from http with
|
|
|
|
* an AH, to raw. Drop the ah and set
|
|
|
|
* the mode.
|
|
|
|
*/
|
|
|
|
raw_transition:
|
|
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
|
|
|
lws_bind_protocol(wsi, &wsi->vhost->protocols[
|
|
|
|
wsi->vhost->
|
|
|
|
raw_protocol_index]);
|
|
|
|
lwsl_info("transition to raw vh %s prot %d\n",
|
|
|
|
wsi->vhost->name,
|
|
|
|
wsi->vhost->raw_protocol_index);
|
|
|
|
if ((wsi->protocol->callback)(wsi,
|
|
|
|
LWS_CALLBACK_RAW_ADOPT,
|
|
|
|
wsi->user_space, NULL, 0))
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
|
|
|
lws_union_transition(wsi, LWSCM_RAW);
|
|
|
|
lws_header_table_detach(wsi, 1);
|
|
|
|
|
|
|
|
if (m == 2 && (wsi->protocol->callback)(wsi,
|
|
|
|
LWS_CALLBACK_RAW_RX,
|
|
|
|
wsi->user_space, obuf, olen))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-04 09:23:56 +08:00
|
|
|
lwsl_info("lws_parse failed\n");
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
|
|
|
|
continue;
|
|
|
|
|
2016-02-28 10:55:31 +08:00
|
|
|
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
|
|
|
|
lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
|
|
|
|
wsi->more_rx_waiting);
|
2014-04-03 09:03:37 +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,
|
|
|
|
WSI_TOKEN_HTTP_USER_AGENT) > 0) {
|
|
|
|
ua[sizeof(ua) - 1] = '\0';
|
|
|
|
while (rej) {
|
|
|
|
if (strstr(ua, rej->name)) {
|
|
|
|
msg = strchr(rej->value, ' ');
|
|
|
|
if (msg)
|
|
|
|
msg++;
|
|
|
|
lws_return_http_status(wsi, atoi(rej->value), msg);
|
|
|
|
|
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.rejected++;
|
|
|
|
|
2016-10-13 06:32:57 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
rej = rej->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 14:01:29 +08:00
|
|
|
/* select vhost */
|
|
|
|
|
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
|
|
|
struct lws_vhost *vhost = lws_select_vhost(
|
|
|
|
context, wsi->vhost->listen_port,
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
|
|
|
|
|
|
|
if (vhost)
|
|
|
|
wsi->vhost = vhost;
|
2016-07-11 09:44:17 +08:00
|
|
|
} else
|
|
|
|
lwsl_info("no host\n");
|
2016-04-15 14:01:29 +08:00
|
|
|
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
wsi->vhost->conn_stats.trans++;
|
2016-04-15 14:01:29 +08:00
|
|
|
if (!wsi->conn_stat_done) {
|
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.conn++;
|
2016-04-15 14:01:29 +08:00
|
|
|
wsi->conn_stat_done = 1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
|
|
|
/* is this websocket protocol or normal http 1.0? */
|
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
|
|
|
|
if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
|
|
|
|
"websocket")) {
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
wsi->vhost->conn_stats.ws_upg++;
|
2016-01-21 10:54:14 +08:00
|
|
|
lwsl_info("Upgrade to ws\n");
|
|
|
|
goto upgrade_ws;
|
|
|
|
}
|
|
|
|
#ifdef LWS_USE_HTTP2
|
|
|
|
if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
|
2016-04-10 09:33:54 +08:00
|
|
|
"h2c")) {
|
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.http2_upg++;
|
2016-04-10 09:33:54 +08:00
|
|
|
lwsl_info("Upgrade to h2c\n");
|
2016-01-21 10:54:14 +08:00
|
|
|
goto upgrade_h2c;
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("Unknown upgrade\n");
|
2016-01-21 10:54:14 +08:00
|
|
|
/* dunno what he wanted to upgrade to */
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
/* no upgrade ack... he remained as HTTP */
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
lwsl_info("No upgrade\n");
|
|
|
|
ah = wsi->u.hdr.ah;
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
|
|
|
|
wsi->state = LWSS_HTTP;
|
2017-02-25 12:42:45 +08:00
|
|
|
wsi->u.http.fop_fd = NULL;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
/* expose it at the same offset as u.hdr */
|
|
|
|
wsi->u.http.ah = ah;
|
2016-03-28 10:10:43 +08:00
|
|
|
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
|
|
|
|
(void *)wsi->u.hdr.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
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
#ifdef LWS_USE_HTTP2
|
|
|
|
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 */
|
2015-12-06 08:00:03 +08:00
|
|
|
n = lws_b64_decode_string(p, protocol_list,
|
|
|
|
sizeof(protocol_list));
|
2014-10-08 12:00:53 +08:00
|
|
|
if (n < 0) {
|
|
|
|
lwsl_parser("HTTP2_SETTINGS too long\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* adopt the header info */
|
|
|
|
|
|
|
|
ah = wsi->u.hdr.ah;
|
2014-09-30 09:43:14 +08:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
/* http2 union member has http union struct at start */
|
|
|
|
wsi->u.http.ah = ah;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-08 12:00:53 +08:00
|
|
|
lws_http2_init(&wsi->u.http2.peer_settings);
|
|
|
|
lws_http2_init(&wsi->u.http2.my_settings);
|
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
|
|
|
|
2015-12-06 08:00:03 +08:00
|
|
|
lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
|
|
|
|
(unsigned char *)protocol_list, n);
|
2014-10-08 12:00:53 +08:00
|
|
|
|
|
|
|
strcpy(protocol_list,
|
|
|
|
"HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
|
|
|
"Connection: Upgrade\x0d\x0a"
|
|
|
|
"Upgrade: h2c\x0d\x0a\x0d\x0a");
|
2014-10-29 09:39:08 +08:00
|
|
|
n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
|
|
|
|
strlen(protocol_list));
|
|
|
|
if (n != strlen(protocol_list)) {
|
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
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
|
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
|
2014-09-30 09:43:14 +08:00
|
|
|
|
2014-09-30 08:15:49 +08:00
|
|
|
upgrade_ws:
|
2014-04-03 09:03:37 +08:00
|
|
|
if (!wsi->protocol)
|
2015-12-04 08:43:54 +08:00
|
|
|
lwsl_err("NULL protocol at lws_read\n");
|
2014-04-03 09:03:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* It's websocket
|
|
|
|
*
|
2014-07-19 06:52:39 +08:00
|
|
|
* Select the first protocol we support from the list
|
|
|
|
* the client sent us.
|
|
|
|
*
|
|
|
|
* Copy it to remove header fragmentation
|
2014-04-03 09:03:37 +08:00
|
|
|
*/
|
|
|
|
|
2014-07-19 06:52:39 +08:00
|
|
|
if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
|
|
|
|
WSI_TOKEN_PROTOCOL) < 0) {
|
|
|
|
lwsl_err("protocol list too long");
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2014-07-19 06:52:39 +08:00
|
|
|
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
|
|
|
|
protocol_list[protocol_len] = '\0';
|
|
|
|
p = protocol_list;
|
|
|
|
hit = 0;
|
|
|
|
|
|
|
|
while (*p && !hit) {
|
2016-04-16 08:40:35 +08:00
|
|
|
n = 0;
|
2017-01-16 12:01:25 +01:00
|
|
|
non_space_char_found = 0;
|
|
|
|
while (n < sizeof(protocol_name) - 1 && *p &&
|
|
|
|
*p != ',') {
|
|
|
|
// ignore leading spaces
|
|
|
|
if (!non_space_char_found && *p == ' ') {
|
|
|
|
n++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
non_space_char_found = 1;
|
2014-07-19 06:52:39 +08:00
|
|
|
protocol_name[n++] = *p++;
|
2017-01-16 12:01:25 +01:00
|
|
|
}
|
2014-07-19 06:52:39 +08:00
|
|
|
protocol_name[n] = '\0';
|
|
|
|
if (*p)
|
|
|
|
p++;
|
|
|
|
|
|
|
|
lwsl_info("checking %s\n", protocol_name);
|
|
|
|
|
|
|
|
n = 0;
|
2016-03-28 10:10:43 +08:00
|
|
|
while (wsi->vhost->protocols[n].callback) {
|
2016-12-03 15:13:15 +08:00
|
|
|
lwsl_info("try %s\n", wsi->vhost->protocols[n].name);
|
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
if (wsi->vhost->protocols[n].name &&
|
|
|
|
!strcmp(wsi->vhost->protocols[n].name,
|
2014-07-19 06:52:39 +08:00
|
|
|
protocol_name)) {
|
2016-03-28 10:10:43 +08:00
|
|
|
wsi->protocol = &wsi->vhost->protocols[n];
|
2014-07-19 06:52:39 +08:00
|
|
|
hit = 1;
|
2014-04-03 09:03:37 +08:00
|
|
|
break;
|
2014-07-19 06:52:39 +08:00
|
|
|
}
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2014-07-19 06:52:39 +08:00
|
|
|
n++;
|
|
|
|
}
|
2014-04-03 09:03:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* we didn't find a protocol he wanted? */
|
|
|
|
|
2014-07-19 06:52:39 +08:00
|
|
|
if (!hit) {
|
2015-12-21 16:39:59 +01:00
|
|
|
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("No protocol from \"%s\" supported\n",
|
2014-07-19 06:52:39 +08:00
|
|
|
protocol_list);
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
2015-12-21 16:39:59 +01:00
|
|
|
/*
|
|
|
|
* some clients only have one protocol and
|
2016-04-16 08:40:35 +08:00
|
|
|
* do not send the protocol list header...
|
2016-05-06 14:24:59 +08:00
|
|
|
* allow it and match to the vhost's default
|
|
|
|
* protocol (which itself defaults to zero)
|
2015-12-21 16:39:59 +01:00
|
|
|
*/
|
2016-05-06 14:24:59 +08:00
|
|
|
lwsl_info("defaulting to prot handler %d\n",
|
|
|
|
wsi->vhost->default_protocol_index);
|
2016-04-16 08:40:35 +08:00
|
|
|
n = 0;
|
2016-05-06 14:24:59 +08:00
|
|
|
wsi->protocol = &wsi->vhost->protocols[
|
|
|
|
(int)wsi->vhost->default_protocol_index];
|
2014-04-03 09:03:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate wsi->user storage */
|
2015-12-04 09:23:56 +08:00
|
|
|
if (lws_ensure_user_space(wsi))
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Give the user code a chance to study the request and
|
|
|
|
* have the opportunity to deny it
|
|
|
|
*/
|
2015-12-17 07:54:44 +08:00
|
|
|
if ((wsi->protocol->callback)(wsi,
|
2014-04-03 09:03:37 +08:00
|
|
|
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
|
|
|
|
wsi->user_space,
|
|
|
|
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
|
|
|
|
lwsl_warn("User code denied connection\n");
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform the handshake according to the protocol version the
|
|
|
|
* client announced
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (wsi->ietf_spec_revision) {
|
|
|
|
case 13:
|
|
|
|
lwsl_parser("lws_parse calling handshake_04\n");
|
|
|
|
if (handshake_0405(context, wsi)) {
|
|
|
|
lwsl_info("hs0405 has failed the connection\n");
|
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-05-09 13:31:43 +08:00
|
|
|
lwsl_info("Unknown client spec version %d\n",
|
2016-02-16 18:47:24 +08:00
|
|
|
wsi->ietf_spec_revision);
|
2014-04-03 09:03:37 +08:00
|
|
|
goto bail_nuke_ah;
|
|
|
|
}
|
|
|
|
|
2016-04-16 08:40:35 +08:00
|
|
|
/*
|
|
|
|
* stitch protocol choice into the vh protocol linked list
|
|
|
|
* We always insert ourselves at the start of the list
|
|
|
|
*
|
|
|
|
* X <-> B
|
|
|
|
* X <-> pAn <-> pB
|
|
|
|
*/
|
|
|
|
//lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n",
|
|
|
|
// __func__,
|
|
|
|
// wsi->vhost->same_vh_protocol_list[n],
|
|
|
|
// wsi->same_vh_protocol_prev);
|
|
|
|
wsi->same_vh_protocol_prev = /* guy who points to us */
|
|
|
|
&wsi->vhost->same_vh_protocol_list[n];
|
|
|
|
wsi->same_vh_protocol_next = /* old first guy is our next */
|
|
|
|
wsi->vhost->same_vh_protocol_list[n];
|
|
|
|
/* we become the new first guy */
|
|
|
|
wsi->vhost->same_vh_protocol_list[n] = wsi;
|
|
|
|
|
|
|
|
if (wsi->same_vh_protocol_next)
|
|
|
|
/* old first guy points back to us now */
|
|
|
|
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
|
|
|
&wsi->same_vh_protocol_next;
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
/* we are upgrading to ws, so http/1.1 and keepalive +
|
|
|
|
* pipelined header considerations about keeping the ah around
|
|
|
|
* no longer apply. However it's common for the first ws
|
|
|
|
* protocol data to have been coalesced with the browser
|
|
|
|
* upgrade request and to already be in the ah rx buffer.
|
|
|
|
*/
|
|
|
|
|
2016-02-16 18:47:24 +08:00
|
|
|
lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n",
|
|
|
|
__func__, wsi, wsi->u.hdr.ah->rxpos,
|
|
|
|
wsi->u.hdr.ah->rxlen);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
lws_pt_lock(pt);
|
|
|
|
hdr = wsi->u.hdr;
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
lws_union_transition(wsi, LWSCM_WS_SERVING);
|
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
|
|
|
/*
|
|
|
|
* first service is WS mode will notice this, use the RX and
|
|
|
|
* then detach the ah (caution: we are not in u.hdr union
|
|
|
|
* mode any more then... ah_temp member is at start the same
|
|
|
|
* though)
|
|
|
|
*
|
2016-02-16 15:19:36 +02:00
|
|
|
* Because rxpos/rxlen shows something in the ah, we will get
|
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
|
|
|
* service guaranteed next time around the event loop
|
|
|
|
*
|
|
|
|
* All union members begin with hdr, so we can use it even
|
|
|
|
* though we transitioned to ws union mode (the ah detach
|
|
|
|
* code uses it anyway).
|
|
|
|
*/
|
|
|
|
wsi->u.hdr = hdr;
|
|
|
|
lws_pt_unlock(pt);
|
2014-04-03 09:03:37 +08:00
|
|
|
|
2016-07-15 13:41:38 +08:00
|
|
|
lws_restart_ws_ping_pong_timer(wsi);
|
|
|
|
|
2014-04-03 09:03:37 +08:00
|
|
|
/*
|
|
|
|
* create the frame buffer for this connection according to the
|
|
|
|
* size mentioned in the protocol definition. If 0 there, use
|
|
|
|
* a big default for compatibility
|
|
|
|
*/
|
|
|
|
|
|
|
|
n = wsi->protocol->rx_buffer_size;
|
|
|
|
if (!n)
|
2016-05-19 12:34:35 +08:00
|
|
|
n = context->pt_serv_buf_size;
|
2016-01-11 11:34:01 +08:00
|
|
|
n += LWS_PRE;
|
|
|
|
wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
|
|
|
|
if (!wsi->u.ws.rx_ubuf) {
|
2014-04-03 09:03:37 +08:00
|
|
|
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
|
|
|
return 1;
|
|
|
|
}
|
2016-01-12 23:05:02 +08:00
|
|
|
wsi->u.ws.rx_ubuf_alloc = n;
|
2016-06-26 06:29:20 +08:00
|
|
|
lwsl_debug("Allocating RX buffer %d\n", n);
|
2017-02-18 17:26:40 +08:00
|
|
|
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
2017-02-27 12:55:56 +08:00
|
|
|
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
|
2015-12-06 08:00:03 +08:00
|
|
|
(const char *)&n, sizeof n)) {
|
2014-04-03 09:03:37 +08:00
|
|
|
lwsl_warn("Failed to set SNDBUF to %d", n);
|
|
|
|
return 1;
|
|
|
|
}
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2016-06-26 06:29:20 +08:00
|
|
|
|
2016-02-21 21:25:48 +08:00
|
|
|
lwsl_parser("accepted v%02d connection\n",
|
|
|
|
wsi->ietf_spec_revision);
|
2016-01-21 10:54:14 +08:00
|
|
|
|
2016-07-06 17:42:19 +08:00
|
|
|
/* notify user code that we're ready to roll */
|
|
|
|
|
|
|
|
if (wsi->protocol->callback)
|
|
|
|
if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
|
|
|
|
wsi->user_space,
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
wsi->ssl,
|
|
|
|
#else
|
|
|
|
NULL,
|
|
|
|
#endif
|
|
|
|
0))
|
|
|
|
return 1;
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
/* !!! drop ah unreservedly after ESTABLISHED */
|
|
|
|
if (!wsi->more_rx_waiting) {
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
|
|
|
|
|
|
|
//lwsl_notice("%p: dropping ah EST\n", wsi);
|
|
|
|
lws_header_table_detach(wsi, 1);
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:54:14 +08:00
|
|
|
return 0;
|
2014-04-03 09:03:37 +08:00
|
|
|
} /* while all chars are handled */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bail_nuke_ah:
|
|
|
|
/* drop the header info */
|
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
|
|
|
/* we're closing, losing some rx is OK */
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
2016-07-23 14:18:25 +08:00
|
|
|
//lwsl_notice("%s: drop2\n", __func__);
|
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;
|
|
|
|
}
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
static int
|
|
|
|
lws_get_idlest_tsi(struct lws_context *context)
|
|
|
|
{
|
|
|
|
unsigned int lowest = ~0;
|
|
|
|
int n = 0, hit = -1;
|
|
|
|
|
|
|
|
for (; n < context->count_threads; n++) {
|
2016-02-21 21:25:48 +08:00
|
|
|
if ((unsigned int)context->pt[n].fds_count !=
|
|
|
|
context->fd_limit_per_thread - 1 &&
|
2016-01-26 20:56:56 +08:00
|
|
|
(unsigned int)context->pt[n].fds_count < lowest) {
|
|
|
|
lowest = context->pt[n].fds_count;
|
|
|
|
hit = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hit;
|
|
|
|
}
|
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *
|
2016-03-28 10:10:43 +08:00
|
|
|
lws_create_new_server_wsi(struct lws_vhost *vhost)
|
2013-01-18 11:43:21 +08:00
|
|
|
{
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *new_wsi;
|
2016-03-28 10:10:43 +08:00
|
|
|
int n = lws_get_idlest_tsi(vhost->context);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
lwsl_err("no space for new conn\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
new_wsi = lws_zalloc(sizeof(struct lws));
|
2013-01-18 11:43:21 +08:00
|
|
|
if (new_wsi == NULL) {
|
|
|
|
lwsl_err("Out of memory for new connection\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
new_wsi->tsi = n;
|
2017-02-18 17:26:40 +08:00
|
|
|
lwsl_debug("Accepted wsi %p to context %p, tsi %d\n", new_wsi,
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
vhost->context, new_wsi->tsi);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->vhost = vhost;
|
|
|
|
new_wsi->context = vhost->context;
|
2013-01-18 11:43:21 +08:00
|
|
|
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
2014-10-08 12:00:53 +08:00
|
|
|
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2016-10-02 02:21:03 +03:00
|
|
|
/* initialize the instance struct */
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
new_wsi->state = LWSS_HTTP;
|
|
|
|
new_wsi->mode = LWSCM_HTTP_SERVING;
|
2013-02-11 21:43:41 +08:00
|
|
|
new_wsi->hdr_parsing_completed = 0;
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2015-03-28 10:20:50 +08:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
|
2015-03-28 10:20:50 +08:00
|
|
|
#endif
|
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
/*
|
|
|
|
* these can only be set once the protocol is known
|
|
|
|
* we set an unestablished connection's protocol pointer
|
|
|
|
* to the start of the supported list, so it can look
|
|
|
|
* for matching ones during the handshake
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
new_wsi->protocol = vhost->protocols;
|
2013-01-18 11:43:21 +08:00
|
|
|
new_wsi->user_space = NULL;
|
|
|
|
new_wsi->ietf_spec_revision = 0;
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->context->count_wsi_allocated++;
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2014-02-15 19:25:50 +08:00
|
|
|
/*
|
|
|
|
* outermost create notification for wsi
|
|
|
|
* no user_space because no protocol selection
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
|
2016-01-26 20:56:56 +08:00
|
|
|
NULL, NULL, 0);
|
2014-02-15 19:25:50 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
return new_wsi;
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:57:39 +08:00
|
|
|
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
|
|
|
|
lws_http_transaction_completed(struct lws *wsi)
|
2014-10-17 08:38:44 +08:00
|
|
|
{
|
2016-04-12 16:26:03 +08:00
|
|
|
int n = NO_PENDING_TIMEOUT;
|
|
|
|
|
2016-04-15 12:00:23 +08:00
|
|
|
lws_access_log(wsi);
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
lwsl_info("%s: wsi %p\n", __func__, wsi);
|
2014-10-17 08:38:44 +08:00
|
|
|
/* if we can't go back to accept new headers, drop the connection */
|
|
|
|
if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
|
2016-01-26 20:56:56 +08:00
|
|
|
lwsl_info("%s: %p: close connection\n", __func__, wsi);
|
2014-10-17 08:38:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-06-18 09:00:04 +08:00
|
|
|
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
|
|
|
|
return 1;
|
2016-06-08 10:07:02 +08:00
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
/* otherwise set ourselves up ready to go again */
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_HTTP;
|
|
|
|
wsi->mode = LWSCM_HTTP_SERVING;
|
2015-10-21 08:16:34 +08:00
|
|
|
wsi->u.http.content_length = 0;
|
2016-05-19 15:28:31 +08:00
|
|
|
wsi->u.http.content_remain = 0;
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
wsi->hdr_parsing_completed = 0;
|
2016-04-20 06:10:56 +08:00
|
|
|
#ifdef LWS_WITH_ACCESS_LOG
|
2016-04-20 05:58:01 +08:00
|
|
|
wsi->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
|
|
|
*/
|
2016-02-15 19:05:43 +08:00
|
|
|
if (wsi->u.hdr.ah) {
|
2016-02-28 10:55:31 +08:00
|
|
|
lwsl_info("%s: wsi->more_rx_waiting=%d\n", __func__,
|
|
|
|
wsi->more_rx_waiting);
|
|
|
|
|
|
|
|
if (!wsi->more_rx_waiting) {
|
2016-02-19 11:47:52 +08:00
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_detach(wsi, 1);
|
2017-08-15 08:06:32 +08:00
|
|
|
} else {
|
2016-02-27 11:42:22 +08:00
|
|
|
lws_header_table_reset(wsi, 1);
|
2017-08-15 08:06:32 +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_HTTP_KEEPALIVE_IDLE,
|
|
|
|
wsi->vhost->keepalive_timeout);
|
|
|
|
}
|
2016-02-15 19:05:43 +08:00
|
|
|
}
|
2015-10-21 08:16:34 +08:00
|
|
|
|
|
|
|
/* If we're (re)starting on headers, need other implied init */
|
|
|
|
wsi->u.hdr.ues = URIES_IDLE;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
/* if not a socket, it's a raw, non-ssl file descriptor */
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
2017-03-03 07:36:08 +08:00
|
|
|
lws_sock_file_fd_type fd, const char *vh_prot_name,
|
|
|
|
struct lws *parent)
|
2016-01-19 03:34:24 +08:00
|
|
|
{
|
2016-04-08 18:30:45 +08:00
|
|
|
struct lws_context *context = vh->context;
|
|
|
|
struct lws *new_wsi = lws_create_new_server_wsi(vh);
|
2017-02-27 12:55:56 +08:00
|
|
|
int n, ssl = 0;
|
2016-01-29 21:18:54 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
if (!new_wsi) {
|
2017-02-27 12:55:56 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET)
|
|
|
|
compatible_close(fd.sockfd);
|
2016-01-26 20:56:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-01-19 03:34:24 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (parent) {
|
|
|
|
new_wsi->parent = parent;
|
|
|
|
new_wsi->sibling_list = parent->child_list;
|
|
|
|
parent->child_list = new_wsi;
|
|
|
|
}
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->desc = fd;
|
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (vh_prot_name) {
|
2017-02-27 12:55:56 +08:00
|
|
|
new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
|
2017-03-03 07:36:08 +08:00
|
|
|
vh_prot_name);
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!new_wsi->protocol) {
|
|
|
|
lwsl_err("Protocol %s not enabled on vhost %s\n",
|
|
|
|
vh_prot_name, new_wsi->vhost->name);
|
2017-03-03 07:36:08 +08:00
|
|
|
goto bail;
|
2017-02-27 12:55:56 +08:00
|
|
|
}
|
2017-03-03 07:36:08 +08:00
|
|
|
if (lws_ensure_user_space(new_wsi))
|
|
|
|
goto bail;
|
|
|
|
} else
|
|
|
|
new_wsi->protocol = &context->vhost_list->
|
|
|
|
protocols[vh->default_protocol_index];
|
2017-02-27 12:55:56 +08:00
|
|
|
|
2017-03-03 07:36:08 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET) { /* socket desc */
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
|
|
|
|
(int)(size_t)fd.sockfd);
|
2016-02-14 09:27:41 +08:00
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
/* the transport is accepted... give him time to negotiate */
|
|
|
|
lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
|
|
|
|
context->timeout_secs);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
|
|
|
#if LWS_POSIX == 0
|
2016-07-23 14:18:25 +08:00
|
|
|
#if defined(LWS_WITH_ESP8266)
|
2017-02-27 12:55:56 +08:00
|
|
|
esp8266_tcp_stream_accept(accept_fd, new_wsi);
|
2016-01-26 20:56:56 +08:00
|
|
|
#endif
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2017-03-03 07:36:08 +08:00
|
|
|
} else /* file desc */
|
2017-02-27 12:55:56 +08:00
|
|
|
lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi,
|
|
|
|
(int)(size_t)fd.filefd);
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
/*
|
|
|
|
* A new connection was accepted. Give the user a chance to
|
|
|
|
* set properties of the newly created wsi. There's no protocol
|
2017-02-12 20:32:49 +08:00
|
|
|
* selected yet so we issue this to the vhosts's default protocol,
|
|
|
|
* itself by default protocols[0]
|
2016-01-26 20:56:56 +08:00
|
|
|
*/
|
2017-02-12 20:32:49 +08:00
|
|
|
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!(type & LWS_ADOPT_HTTP)) {
|
|
|
|
if (!(type & LWS_ADOPT_SOCKET))
|
|
|
|
n = LWS_CALLBACK_RAW_ADOPT_FILE;
|
|
|
|
else
|
|
|
|
n = LWS_CALLBACK_RAW_ADOPT;
|
|
|
|
}
|
|
|
|
if ((new_wsi->protocol->callback)(
|
2017-02-12 20:32:49 +08:00
|
|
|
new_wsi, n, NULL, NULL, 0)) {
|
2017-02-27 12:55:56 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET) {
|
|
|
|
/* force us off the timeout list by hand */
|
|
|
|
lws_set_timeout(new_wsi, NO_PENDING_TIMEOUT, 0);
|
|
|
|
compatible_close(new_wsi->desc.sockfd);
|
|
|
|
}
|
2017-03-03 07:36:08 +08:00
|
|
|
goto bail;
|
2016-01-26 20:56:56 +08:00
|
|
|
}
|
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) ||
|
|
|
|
!(type & LWS_ADOPT_SOCKET)) {
|
|
|
|
/* non-SSL */
|
|
|
|
if (!(type & LWS_ADOPT_HTTP)) {
|
|
|
|
if (!(type & LWS_ADOPT_SOCKET))
|
|
|
|
new_wsi->mode = LWSCM_RAW_FILEDESC;
|
|
|
|
else
|
|
|
|
new_wsi->mode = LWSCM_RAW;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
} else {
|
2017-02-27 12:55:56 +08:00
|
|
|
/* SSL */
|
|
|
|
if (!(type & LWS_ADOPT_HTTP))
|
2017-02-12 20:32:49 +08:00
|
|
|
new_wsi->mode = LWSCM_SSL_INIT_RAW;
|
|
|
|
else
|
|
|
|
new_wsi->mode = LWSCM_SSL_INIT;
|
2017-02-27 12:55:56 +08:00
|
|
|
|
|
|
|
ssl = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_libev_accept(new_wsi, new_wsi->desc);
|
|
|
|
lws_libuv_accept(new_wsi, new_wsi->desc);
|
|
|
|
|
|
|
|
if (!ssl) {
|
|
|
|
if (insert_wsi_socket_into_fds(context, new_wsi)) {
|
|
|
|
lwsl_err("%s: fail inserting socket\n", __func__);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) {
|
2016-04-13 11:49:07 +08:00
|
|
|
lwsl_err("%s: fail ssl negotiation\n", __func__);
|
2016-01-26 20:56:56 +08:00
|
|
|
goto fail;
|
2016-04-13 11:49:07 +08:00
|
|
|
}
|
2016-01-26 20:56:56 +08:00
|
|
|
|
2017-02-27 12:55:56 +08:00
|
|
|
if (type & LWS_ADOPT_HTTP)
|
|
|
|
if (!lws_header_table_attach(new_wsi, 0))
|
|
|
|
lwsl_debug("Attached ah immediately\n");
|
2016-04-19 10:10:53 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
return new_wsi;
|
|
|
|
|
|
|
|
fail:
|
2017-02-27 12:55:56 +08:00
|
|
|
if (type & LWS_ADOPT_SOCKET)
|
|
|
|
lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
2016-01-26 20:56:56 +08:00
|
|
|
|
|
|
|
return NULL;
|
2017-03-03 07:36:08 +08:00
|
|
|
|
|
|
|
bail:
|
|
|
|
if (parent)
|
|
|
|
parent->child_list = new_wsi->sibling_list;
|
|
|
|
if (new_wsi->user_space)
|
|
|
|
lws_free(new_wsi->user_space);
|
|
|
|
lws_free(new_wsi);
|
|
|
|
|
|
|
|
return NULL;
|
2016-01-19 03:34:24 +08:00
|
|
|
}
|
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
|
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
lws_sock_file_fd_type fd;
|
|
|
|
|
|
|
|
fd.sockfd = accept_fd;
|
|
|
|
return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET |
|
2017-03-03 07:36:08 +08:00
|
|
|
LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL);
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
|
|
|
{
|
2017-02-27 12:55:56 +08:00
|
|
|
return lws_adopt_socket_vhost(context->vhost_list, accept_fd);
|
2016-04-08 18:30:45 +08:00
|
|
|
}
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
/* Common read-buffer adoption for lws_adopt_*_readbuf */
|
|
|
|
static struct lws*
|
|
|
|
adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
|
2016-02-24 11:05:56 +08:00
|
|
|
{
|
2016-02-27 11:42:22 +08:00
|
|
|
struct lws_context_per_thread *pt;
|
2016-02-24 11:05:56 +08:00
|
|
|
struct allocated_headers *ah;
|
2016-02-27 11:42:22 +08:00
|
|
|
struct lws_pollfd *pfd;
|
2016-02-24 11:05:56 +08:00
|
|
|
|
|
|
|
if (!wsi)
|
|
|
|
return NULL;
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
if (!readbuf || len == 0)
|
2016-02-24 11:05:56 +08:00
|
|
|
return wsi;
|
|
|
|
|
|
|
|
if (len > sizeof(ah->rx)) {
|
|
|
|
lwsl_err("%s: rx in too big\n", __func__);
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-02-24 11:05:56 +08:00
|
|
|
/*
|
|
|
|
* we can't process the initial read data until we can attach an ah.
|
|
|
|
*
|
|
|
|
* if one is available, get it and place the data in his ah rxbuf...
|
|
|
|
* wsi with ah that have pending rxbuf get auto-POLLIN service.
|
2016-02-27 11:42:22 +08:00
|
|
|
*
|
|
|
|
* no autoservice because we didn't get a chance to attach the
|
|
|
|
* readbuf data to wsi or ah yet, and we will do it next if we get
|
|
|
|
* the ah.
|
2016-02-24 11:05:56 +08:00
|
|
|
*/
|
2016-04-19 10:10:53 +08:00
|
|
|
if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) {
|
2016-02-24 11:05:56 +08:00
|
|
|
ah = wsi->u.hdr.ah;
|
|
|
|
memcpy(ah->rx, readbuf, len);
|
|
|
|
ah->rxpos = 0;
|
|
|
|
ah->rxlen = len;
|
|
|
|
|
2016-02-27 11:42:22 +08:00
|
|
|
lwsl_notice("%s: calling service on readbuf ah\n", __func__);
|
2016-12-21 09:32:16 +08:00
|
|
|
pt = &wsi->context->pt[(int)wsi->tsi];
|
2016-02-27 11:42:22 +08:00
|
|
|
|
|
|
|
/* unlike a normal connect, we have the headers already
|
|
|
|
* (or the first part of them anyway).
|
|
|
|
* libuv won't come back and service us without a network
|
|
|
|
* event, so we need to do the header service right here.
|
|
|
|
*/
|
|
|
|
pfd = &pt->fds[wsi->position_in_fds_table];
|
|
|
|
pfd->revents |= LWS_POLLIN;
|
|
|
|
lwsl_err("%s: calling service\n", __func__);
|
2016-12-21 09:32:16 +08:00
|
|
|
if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi))
|
2016-02-27 11:42:22 +08:00
|
|
|
/* service closed us */
|
|
|
|
return NULL;
|
|
|
|
|
2016-02-24 11:05:56 +08:00
|
|
|
return wsi;
|
|
|
|
}
|
2016-02-27 11:42:22 +08:00
|
|
|
lwsl_err("%s: deferring handling ah\n", __func__);
|
2016-02-24 11:05:56 +08:00
|
|
|
/*
|
|
|
|
* hum if no ah came, we are on the wait list and must defer
|
|
|
|
* dealing with this until the ah arrives.
|
|
|
|
*
|
|
|
|
* later successful lws_header_table_attach() will apply the
|
2016-02-27 11:42:22 +08:00
|
|
|
* below to the rx buffer (via lws_header_table_reset()).
|
2016-02-24 11:05:56 +08:00
|
|
|
*/
|
|
|
|
wsi->u.hdr.preamble_rx = lws_malloc(len);
|
2016-05-12 21:04:33 +08:00
|
|
|
if (!wsi->u.hdr.preamble_rx) {
|
|
|
|
lwsl_err("OOM\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-02-26 09:22:29 +08:00
|
|
|
memcpy(wsi->u.hdr.preamble_rx, readbuf, len);
|
2016-02-24 11:05:56 +08:00
|
|
|
wsi->u.hdr.preamble_rx_len = len;
|
|
|
|
|
|
|
|
return wsi;
|
|
|
|
|
|
|
|
bail:
|
|
|
|
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-12-21 09:32:16 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
|
|
|
|
const char *readbuf, size_t len)
|
|
|
|
{
|
|
|
|
return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), readbuf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd,
|
|
|
|
const char *readbuf, size_t len)
|
|
|
|
{
|
|
|
|
return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), readbuf, len);
|
|
|
|
}
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
LWS_VISIBLE int
|
|
|
|
lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|
|
|
struct lws_pollfd *pollfd)
|
2013-01-18 11:43:21 +08:00
|
|
|
{
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
2016-01-29 21:18:54 +08:00
|
|
|
lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
|
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
|
|
|
struct allocated_headers *ah;
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2017-06-01 06:57:59 +08:00
|
|
|
struct sockaddr_storage cli_addr;
|
2015-12-06 06:39:51 +08:00
|
|
|
socklen_t clilen;
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2015-12-06 06:39:51 +08:00
|
|
|
int n, len;
|
2016-07-23 14:18:25 +08:00
|
|
|
|
|
|
|
// lwsl_notice("%s: mode %d\n", __func__, wsi->mode);
|
2013-01-18 11:43:21 +08:00
|
|
|
|
|
|
|
switch (wsi->mode) {
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
case LWSCM_HTTP_SERVING:
|
|
|
|
case LWSCM_HTTP_SERVING_ACCEPTED:
|
|
|
|
case LWSCM_HTTP2_SERVING:
|
2017-03-07 16:06:05 +08:00
|
|
|
case LWSCM_RAW:
|
2013-01-18 11:43:21 +08:00
|
|
|
|
|
|
|
/* handle http headers coming in */
|
|
|
|
|
2013-12-09 14:16:17 +08:00
|
|
|
/* pending truncated sends have uber priority */
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->trunc_len) {
|
|
|
|
if (!(pollfd->revents & LWS_POLLOUT))
|
|
|
|
break;
|
|
|
|
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
|
|
|
wsi->trunc_offset,
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->trunc_len) < 0)
|
|
|
|
goto fail;
|
2013-12-09 14:16:17 +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
|
|
|
* we can't afford to allow input processing to send
|
2013-12-09 14:16:17 +08:00
|
|
|
* something new, so spin around he event loop until
|
|
|
|
* he doesn't have any partials
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
/* any incoming data ready? */
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
2015-12-17 17:03:59 +08:00
|
|
|
goto try_pollout;
|
|
|
|
|
2016-04-23 09:26:11 +08:00
|
|
|
/*
|
|
|
|
* If we previously just did POLLIN when IN and OUT were
|
|
|
|
* signalled (because POLLIN processing may have used up
|
|
|
|
* the POLLOUT), don't let that happen twice in a row...
|
|
|
|
* next time we see the situation favour POLLOUT
|
|
|
|
*/
|
2016-07-23 14:18:25 +08:00
|
|
|
#if !defined(LWS_WITH_ESP8266)
|
2016-04-23 09:26:11 +08:00
|
|
|
if (wsi->favoured_pollin &&
|
|
|
|
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
|
|
|
|
wsi->favoured_pollin = 0;
|
|
|
|
goto try_pollout;
|
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
#endif
|
2017-02-12 20:32:49 +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
|
|
|
/* these states imply we MUST have an ah attached */
|
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_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->state == LWSS_HTTP_ISSUING_FILE ||
|
2017-03-07 16:06:05 +08:00
|
|
|
wsi->state == LWSS_HTTP_HEADERS)) {
|
2016-07-23 14:18:25 +08:00
|
|
|
if (!wsi->u.hdr.ah) {
|
|
|
|
|
|
|
|
//lwsl_err("wsi %p: missing ah\n", wsi);
|
2016-02-27 11:42:22 +08:00
|
|
|
/* no autoservice beacuse we will do it next */
|
2016-07-23 14:18:25 +08:00
|
|
|
if (lws_header_table_attach(wsi, 0)) {
|
2017-04-02 13:02:28 +08:00
|
|
|
lwsl_info("wsi %p: failed to acquire ah\n", wsi);
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
goto try_pollout;
|
2016-07-23 14:18:25 +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
|
|
|
ah = wsi->u.hdr.ah;
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
//lwsl_notice("%s: %p: rxpos:%d rxlen:%d\n", __func__, wsi,
|
|
|
|
// ah->rxpos, ah->rxlen);
|
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 nothing in ah rx buffer, get some fresh rx */
|
|
|
|
if (ah->rxpos == ah->rxlen) {
|
|
|
|
ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
|
|
|
|
sizeof(ah->rx));
|
|
|
|
ah->rxpos = 0;
|
2016-07-23 14:18:25 +08:00
|
|
|
//lwsl_notice("%s: wsi %p, ah->rxlen = %d\r\n",
|
|
|
|
// __func__, wsi, ah->rxlen);
|
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
|
|
|
switch (ah->rxlen) {
|
|
|
|
case 0:
|
|
|
|
lwsl_info("%s: read 0 len\n", __func__);
|
|
|
|
/* lwsl_info(" state=%d\n", wsi->state); */
|
|
|
|
// if (!wsi->hdr_parsing_completed)
|
|
|
|
// lws_header_table_detach(wsi);
|
|
|
|
/* fallthru */
|
|
|
|
case LWS_SSL_CAPABLE_ERROR:
|
|
|
|
goto fail;
|
|
|
|
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
|
|
|
ah->rxlen = ah->rxpos = 0;
|
|
|
|
goto try_pollout;
|
|
|
|
}
|
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2016-05-13 10:27:48 +08:00
|
|
|
if (!(ah->rxpos != ah->rxlen && ah->rxlen)) {
|
|
|
|
lwsl_err("%s: assert: rxpos %d, rxlen %d\n",
|
|
|
|
__func__, ah->rxpos, ah->rxlen);
|
|
|
|
|
|
|
|
assert(0);
|
|
|
|
}
|
2016-07-23 14:18:25 +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
|
|
|
/* just ignore incoming if waiting for close */
|
|
|
|
if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
|
|
|
|
n = lws_read(wsi, ah->rx + ah->rxpos,
|
|
|
|
ah->rxlen - ah->rxpos);
|
|
|
|
if (n < 0) /* we closed wsi */
|
|
|
|
return 1;
|
|
|
|
if (wsi->u.hdr.ah) {
|
|
|
|
if ( wsi->u.hdr.ah->rxlen)
|
|
|
|
wsi->u.hdr.ah->rxpos += n;
|
|
|
|
|
2016-09-29 10:31:06 +08:00
|
|
|
lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n", __func__, wsi, wsi->u.hdr.ah->rxpos, wsi->u.hdr.ah->rxlen);
|
|
|
|
|
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 (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen &&
|
|
|
|
(wsi->mode != LWSCM_HTTP_SERVING &&
|
|
|
|
wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
|
|
|
|
wsi->mode != LWSCM_HTTP2_SERVING))
|
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
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-01-26 20:56:56 +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
|
|
|
goto try_pollout;
|
|
|
|
}
|
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
|
|
|
|
2016-01-19 03:34:24 +08:00
|
|
|
len = lws_ssl_capable_read(wsi, pt->serv_buf,
|
2016-05-19 12:34:35 +08:00
|
|
|
context->pt_serv_buf_size);
|
2017-03-07 16:06:05 +08:00
|
|
|
lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
|
2015-12-17 17:03:59 +08:00
|
|
|
switch (len) {
|
|
|
|
case 0:
|
2016-01-26 20:56:56 +08:00
|
|
|
lwsl_info("%s: read 0 len\n", __func__);
|
2015-12-17 17:03:59 +08:00
|
|
|
/* lwsl_info(" state=%d\n", wsi->state); */
|
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 (!wsi->hdr_parsing_completed)
|
|
|
|
// lws_header_table_detach(wsi);
|
2015-12-17 17:03:59 +08:00
|
|
|
/* fallthru */
|
|
|
|
case LWS_SSL_CAPABLE_ERROR:
|
|
|
|
goto fail;
|
|
|
|
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
|
|
|
goto try_pollout;
|
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
if (wsi->mode == LWSCM_RAW) {
|
|
|
|
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
|
|
|
wsi, LWS_CALLBACK_RAW_RX,
|
|
|
|
wsi->user_space, pt->serv_buf, len);
|
|
|
|
if (n < 0) {
|
2017-03-07 16:06:05 +08:00
|
|
|
lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
|
2017-02-12 20:32:49 +08:00
|
|
|
goto fail;
|
|
|
|
}
|
2017-03-07 16:06:05 +08:00
|
|
|
goto try_pollout;
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
/* just ignore incoming if waiting for close */
|
|
|
|
if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
|
|
|
|
/*
|
2016-04-23 09:26:11 +08:00
|
|
|
* this may want to send
|
2015-12-17 17:03:59 +08:00
|
|
|
* (via HTTP callback for example)
|
|
|
|
*/
|
2016-01-19 03:34:24 +08:00
|
|
|
n = lws_read(wsi, pt->serv_buf, len);
|
2015-12-17 17:03:59 +08:00
|
|
|
if (n < 0) /* we closed wsi */
|
|
|
|
return 1;
|
2016-04-23 09:26:11 +08:00
|
|
|
/*
|
|
|
|
* he may have used up the
|
|
|
|
* writability above, if we will defer POLLOUT
|
|
|
|
* processing in favour of POLLIN, note it
|
|
|
|
*/
|
|
|
|
if (pollfd->revents & LWS_POLLOUT)
|
|
|
|
wsi->favoured_pollin = 1;
|
2015-12-17 17:03:59 +08:00
|
|
|
break;
|
2013-01-18 11:43:21 +08:00
|
|
|
}
|
|
|
|
|
2014-10-09 08:37:12 +08:00
|
|
|
try_pollout:
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
/* this handles POLLOUT for http serving fragments */
|
|
|
|
|
2014-03-30 09:18:05 +02:00
|
|
|
if (!(pollfd->revents & LWS_POLLOUT))
|
2013-01-18 11:43:21 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* one shot */
|
2016-01-26 20:56:56 +08:00
|
|
|
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
|
|
|
lwsl_notice("%s a\n", __func__);
|
2014-04-02 14:25:10 +08:00
|
|
|
goto fail;
|
2016-01-26 20:56:56 +08:00
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2017-03-07 16:06:05 +08:00
|
|
|
if (wsi->mode == LWSCM_RAW) {
|
|
|
|
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
|
|
|
wsi, LWS_CALLBACK_RAW_WRITEABLE,
|
|
|
|
wsi->user_space, NULL, 0);
|
|
|
|
if (n < 0) {
|
|
|
|
lwsl_info("writeable_fail\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-27 11:42:22 +08:00
|
|
|
if (!wsi->hdr_parsing_completed)
|
|
|
|
break;
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
|
2016-01-29 21:18:54 +08:00
|
|
|
n = user_callback_handle_rxflow(wsi->protocol->callback,
|
2013-02-14 22:23:54 +08:00
|
|
|
wsi, LWS_CALLBACK_HTTP_WRITEABLE,
|
2015-12-17 07:54:44 +08:00
|
|
|
wsi->user_space, NULL, 0);
|
2016-01-26 20:56:56 +08:00
|
|
|
if (n < 0) {
|
|
|
|
lwsl_info("writeable_fail\n");
|
2014-11-30 13:35:24 +08:00
|
|
|
goto fail;
|
2016-01-26 20:56:56 +08:00
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
break;
|
2013-02-14 22:23:54 +08:00
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2014-10-17 08:38:44 +08:00
|
|
|
/* >0 == completion, <0 == error */
|
2015-12-16 18:19:08 +08:00
|
|
|
n = lws_serve_http_file_fragment(wsi);
|
2016-01-26 20:56:56 +08:00
|
|
|
if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) {
|
|
|
|
lwsl_info("completed\n");
|
2014-11-30 13:35:24 +08:00
|
|
|
goto fail;
|
2016-01-26 20:56:56 +08:00
|
|
|
}
|
2016-07-23 14:18:25 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
break;
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
case LWSCM_SERVER_LISTENER:
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2015-11-02 20:34:12 +08:00
|
|
|
#if LWS_POSIX
|
2013-01-18 11:43:21 +08:00
|
|
|
/* pollin means a client has connected to us then */
|
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
do {
|
|
|
|
if (!(pollfd->revents & LWS_POLLIN) || !(pollfd->events & LWS_POLLIN))
|
|
|
|
break;
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
/* listen socket got an unencrypted connection... */
|
|
|
|
|
|
|
|
clilen = sizeof(cli_addr);
|
|
|
|
lws_latency_pre(context, wsi);
|
|
|
|
accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
|
|
|
|
&clilen);
|
|
|
|
lws_latency(context, wsi, "listener accept", accept_fd,
|
|
|
|
accept_fd >= 0);
|
|
|
|
if (accept_fd < 0) {
|
|
|
|
if (LWS_ERRNO == LWS_EAGAIN ||
|
|
|
|
LWS_ERRNO == LWS_EWOULDBLOCK) {
|
2017-03-20 19:34:49 +08:00
|
|
|
// lwsl_err("accept asks to try again\n");
|
2016-01-26 20:56:56 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO));
|
2013-01-28 12:19:10 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2016-03-28 10:10:43 +08:00
|
|
|
lws_plat_set_socket_options(wsi->vhost, accept_fd);
|
2013-01-18 11:43:21 +08:00
|
|
|
|
2017-06-01 06:57:59 +08:00
|
|
|
#if defined(LWS_USE_IPV6)
|
|
|
|
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
|
|
|
((cli_addr.ss_family == AF_INET6) ?
|
|
|
|
ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
|
|
|
|
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
|
|
|
|
accept_fd);
|
|
|
|
#else
|
|
|
|
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
|
|
|
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
|
|
|
|
accept_fd);
|
|
|
|
#endif
|
2014-02-15 14:36:02 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
#else
|
|
|
|
/* not very beautiful... */
|
|
|
|
accept_fd = (lws_sockfd_type)pollfd;
|
2015-11-02 20:34:12 +08:00
|
|
|
#endif
|
2016-01-26 20:56:56 +08:00
|
|
|
/*
|
|
|
|
* look at who we connected to and give user code a chance
|
|
|
|
* to reject based on client IP. There's no protocol selected
|
|
|
|
* yet so we issue this to protocols[0]
|
|
|
|
*/
|
2016-03-28 10:10:43 +08:00
|
|
|
if ((wsi->vhost->protocols[0].callback)(wsi,
|
2016-01-26 20:56:56 +08:00
|
|
|
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
|
|
|
|
NULL, (void *)(long)accept_fd, 0)) {
|
|
|
|
lwsl_debug("Callback denied network connection\n");
|
|
|
|
compatible_close(accept_fd);
|
|
|
|
break;
|
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-04-08 18:30:45 +08:00
|
|
|
if (!lws_adopt_socket_vhost(wsi->vhost, accept_fd))
|
2016-01-26 20:56:56 +08:00
|
|
|
/* already closed cleanly as necessary */
|
|
|
|
return 1;
|
2014-02-06 23:15:51 -02:00
|
|
|
|
2015-11-08 12:10:26 +08:00
|
|
|
#if LWS_POSIX
|
2016-01-26 20:56:56 +08:00
|
|
|
} while (pt->fds_count < context->fd_limit_per_thread - 1 &&
|
|
|
|
lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
|
2015-11-08 12:10:26 +08:00
|
|
|
#endif
|
2016-01-26 20:56:56 +08:00
|
|
|
return 0;
|
2013-01-28 12:19:10 +08:00
|
|
|
|
2013-01-18 11:43:21 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2014-04-12 10:07:02 +08:00
|
|
|
|
2016-01-26 20:56:56 +08:00
|
|
|
if (!lws_server_socket_service_ssl(wsi, accept_fd))
|
2015-12-06 06:39:51 +08:00
|
|
|
return 0;
|
2015-04-23 05:37:46 +08:00
|
|
|
|
2014-04-02 14:25:10 +08:00
|
|
|
fail:
|
2015-12-15 21:15:58 +08:00
|
|
|
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
2015-12-05 21:51:47 +08:00
|
|
|
|
2014-04-02 14:25:10 +08:00
|
|
|
return 1;
|
2013-01-18 11:43:21 +08:00
|
|
|
}
|
2013-02-11 17:52:23 +01:00
|
|
|
|
ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.
However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears. It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.
But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it. Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.
But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.
NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem. With this patch attack.sh can complete well.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
|
|
|
LWS_VISIBLE int
|
|
|
|
lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|
|
|
const char *other_headers, int other_headers_len)
|
2013-11-11 07:30:33 +08:00
|
|
|
{
|
2016-04-22 08:53:49 +08:00
|
|
|
static const char * const intermediates[] = { "private", "public" };
|
2015-12-17 18:25:25 +08:00
|
|
|
struct lws_context *context = lws_get_context(wsi);
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
struct lws_range_parsing *rp = &wsi->u.http.range;
|
|
|
|
#endif
|
2016-04-22 08:53:49 +08:00
|
|
|
char cache_control[50], *cc = "no-store";
|
2016-01-19 03:34:24 +08:00
|
|
|
unsigned char *response = pt->serv_buf + LWS_PRE;
|
2014-10-08 12:00:53 +08:00
|
|
|
unsigned char *p = response;
|
2016-05-19 12:34:35 +08:00
|
|
|
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
|
2016-12-12 13:36:25 +08:00
|
|
|
unsigned long computed_total_content_length;
|
2017-02-25 12:42:45 +08:00
|
|
|
int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
|
2017-03-03 12:38:10 +08:00
|
|
|
lws_fop_flags_t fflags = LWS_O_RDONLY;
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
int ranges;
|
|
|
|
#endif
|
2017-03-03 12:38:10 +08:00
|
|
|
const struct lws_plat_file_ops *fops;
|
|
|
|
const char *vpath;
|
2013-11-11 07:30:33 +08:00
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* If wsi->u.http.fop_fd is already set, the caller already opened it
|
|
|
|
*/
|
2017-02-25 12:42:45 +08:00
|
|
|
if (!wsi->u.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);
|
|
|
|
wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
|
|
|
|
file, vpath, &fflags);
|
|
|
|
if (!wsi->u.http.fop_fd) {
|
|
|
|
lwsl_err("Unable to open '%s'\n", file);
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-02-12 20:32:49 +08:00
|
|
|
return -1;
|
2017-03-03 12:38:10 +08:00
|
|
|
}
|
2017-02-12 20:32:49 +08:00
|
|
|
}
|
2017-03-03 12:38:10 +08:00
|
|
|
wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd);
|
|
|
|
computed_total_content_length = wsi->u.http.filelen;
|
2017-02-12 20:32:49 +08:00
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
|
|
|
ranges = lws_ranges_init(wsi, rp, wsi->u.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 */
|
|
|
|
lws_return_http_status(wsi, HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL);
|
|
|
|
if (lws_http_transaction_completed(wsi))
|
|
|
|
return -1; /* <0 means just hang up */
|
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
|
|
|
|
2016-12-12 13:36:25 +08:00
|
|
|
return 0; /* == 0 means we dealt with the transaction complete */
|
|
|
|
}
|
|
|
|
if (ranges)
|
|
|
|
n = HTTP_STATUS_PARTIAL_CONTENT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (lws_add_http_header_status(wsi, n, &p, end))
|
2014-10-17 08:38:44 +08:00
|
|
|
return -1;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-03-03 12:38:10 +08:00
|
|
|
if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
|
|
|
|
LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
|
|
|
|
(LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
|
|
|
|
if (lws_add_http_header_by_token(wsi,
|
|
|
|
WSI_TOKEN_HTTP_CONTENT_ENCODING,
|
|
|
|
(unsigned char *)"gzip", 4, &p, end))
|
|
|
|
return -1;
|
|
|
|
lwsl_info("file is being provided in gzip\n");
|
|
|
|
}
|
|
|
|
|
2017-08-12 20:55:14 +08:00
|
|
|
if (
|
2016-12-12 13:36:25 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2017-08-12 20:55:14 +08:00
|
|
|
ranges < 2 &&
|
|
|
|
#endif
|
|
|
|
content_type && content_type[0])
|
2016-08-13 11:17:53 +02:00
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
|
|
(unsigned char *)content_type,
|
|
|
|
strlen(content_type), &p, end))
|
|
|
|
return -1;
|
2016-12-12 13:36:25 +08:00
|
|
|
|
2017-08-12 20:55:14 +08:00
|
|
|
#if defined(LWS_WITH_RANGES)
|
2016-12-12 13:36:25 +08:00
|
|
|
if (ranges >= 2) { /* multipart byteranges */
|
|
|
|
strncpy(wsi->u.http.multipart_content_type, content_type,
|
|
|
|
sizeof(wsi->u.http.multipart_content_type) - 1);
|
|
|
|
wsi->u.http.multipart_content_type[
|
|
|
|
sizeof(wsi->u.http.multipart_content_type) - 1] = '\0';
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
|
|
(unsigned char *)"multipart/byteranges; boundary=_lws",
|
|
|
|
20, &p, end))
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
computed_total_content_length = (unsigned long)rp->agg +
|
|
|
|
6 /* final _lws\r\n */;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
computed_total_content_length +=
|
|
|
|
6 /* header _lws\r\n */ +
|
|
|
|
14 + strlen(content_type) + 2 + /* Content-Type: xxx/xxx\r\n */
|
|
|
|
15 + n + 2 + /* Content-Range: xxxx\r\n */
|
|
|
|
2; /* /r/n */
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
computed_total_content_length = (unsigned long)rp->agg;
|
|
|
|
n = lws_snprintf(cache_control, sizeof(cache_control), "bytes %llu-%llu/%llu",
|
|
|
|
rp->start, rp->end, rp->extent);
|
|
|
|
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_RANGE,
|
|
|
|
(unsigned char *)cache_control,
|
|
|
|
n, &p, end))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wsi->u.http.range.inside = 0;
|
|
|
|
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
|
|
|
|
(unsigned char *)"bytes", 5, &p, end))
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
|
2016-05-19 15:28:31 +08:00
|
|
|
if (!wsi->sending_chunked) {
|
2016-12-12 13:36:25 +08:00
|
|
|
if (lws_add_http_header_content_length(wsi,
|
|
|
|
computed_total_content_length,
|
|
|
|
&p, end))
|
2016-05-19 15:28:31 +08:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
|
|
|
(unsigned char *)"chunked",
|
|
|
|
7, &p, end))
|
|
|
|
return -1;
|
|
|
|
}
|
2014-10-12 14:31:47 +08:00
|
|
|
|
2016-04-22 08:53:49 +08:00
|
|
|
if (wsi->cache_secs && wsi->cache_reuse) {
|
|
|
|
if (wsi->cache_revalidate) {
|
|
|
|
cc = cache_control;
|
|
|
|
cclen = sprintf(cache_control, "%s max-age: %u",
|
|
|
|
intermediates[wsi->cache_intermediaries],
|
|
|
|
wsi->cache_secs);
|
|
|
|
} else {
|
|
|
|
cc = "no-cache";
|
|
|
|
cclen = 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
|
|
|
|
(unsigned char *)cc, cclen, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2016-07-23 14:18:25 +08:00
|
|
|
if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
|
|
|
|
(unsigned char *)"keep-alive", 10, &p, end))
|
|
|
|
return -1;
|
|
|
|
|
2013-11-11 07:30:33 +08:00
|
|
|
if (other_headers) {
|
2014-10-12 14:31:47 +08:00
|
|
|
if ((end - p) < other_headers_len)
|
|
|
|
return -1;
|
|
|
|
memcpy(p, other_headers, other_headers_len);
|
|
|
|
p += other_headers_len;
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2015-12-16 18:19:08 +08:00
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
2014-10-17 08:38:44 +08:00
|
|
|
return -1;
|
2015-12-14 08:52:03 +08:00
|
|
|
|
lws_plat_fd implement platform default handlers
This is a rewrite of the patch from Soapyman here
https://github.com/warmcat/libwebsockets/pull/363
The main changes compared to Soapyman's original patch are
- There's no new stuff in the info struct user code does any overrides
it may want to do explicitly after lws_context_create returns
- User overrides for file ops can call through (subclass) to the original
platform implementation using lws_get_fops_plat()
- A typedef is provided for plat-specific fd type
- Public helpers are provided to allow user code to be platform-independent
about file access, using the lws platform file operations underneath:
static inline lws_filefd_type
lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
unsigned long *filelen, int flags)
static inline int
lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)
static inline unsigned long
lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
long offset_from_cur_pos)
static inline int
lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
static inline int
lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
unsigned long *amount, unsigned char *buf, unsigned long len)
There's example documentation and implementation in the test server.
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:58:58 +08:00
|
|
|
ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
|
2014-10-08 12:00:53 +08:00
|
|
|
if (ret != (p - response)) {
|
2017-02-04 13:09:00 +01:00
|
|
|
lwsl_err("_write returned %d from %ld\n", ret, (long)(p - response));
|
2013-11-11 07:30:33 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wsi->u.http.filepos = 0;
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_HTTP_ISSUING_FILE;
|
2013-11-11 07:30:33 +08:00
|
|
|
|
2015-12-16 18:19:08 +08:00
|
|
|
return lws_serve_http_file_fragment(wsi);
|
2013-11-11 07:30:33 +08:00
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
int
|
2016-01-11 11:34:01 +08:00
|
|
|
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
2014-04-10 14:08:10 +08:00
|
|
|
{
|
|
|
|
int m;
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
|
2014-04-10 14:08:10 +08:00
|
|
|
#if 0
|
2016-01-11 11:34:01 +08:00
|
|
|
lwsl_hexdump(*buf, len);
|
2014-04-10 14:08:10 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* let the rx protocol state machine have as much as it needs */
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
while (len) {
|
2014-04-10 14:08:10 +08:00
|
|
|
/*
|
|
|
|
* we were accepting input but now we stopped doing so
|
|
|
|
*/
|
2014-10-08 12:00:53 +08:00
|
|
|
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
2016-01-11 11:34:01 +08:00
|
|
|
lws_rxflow_cache(wsi, *buf, 0, len);
|
2017-02-05 22:07:34 +08:00
|
|
|
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
|
2014-04-10 14:08:10 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (wsi->u.ws.rx_draining_ext) {
|
2017-03-20 19:34:49 +08:00
|
|
|
// lwsl_notice("draining with 0\n");
|
2016-01-11 11:34:01 +08:00
|
|
|
m = lws_rx_sm(wsi, 0);
|
|
|
|
if (m < 0)
|
|
|
|
return -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-04-10 14:08:10 +08:00
|
|
|
/* account for what we're using in rxflow buffer */
|
2014-10-08 12:00:53 +08:00
|
|
|
if (wsi->rxflow_buffer)
|
|
|
|
wsi->rxflow_pos++;
|
2014-04-10 14:08:10 +08:00
|
|
|
|
2016-03-17 09:34:15 +08:00
|
|
|
/* consume payload bytes efficiently */
|
2017-03-20 19:34:49 +08:00
|
|
|
if (
|
|
|
|
wsi->lws_rx_parse_state ==
|
2016-09-10 04:43:07 +08:00
|
|
|
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
|
|
|
|
m = lws_payload_until_length_exhausted(wsi, buf, &len);
|
|
|
|
if (wsi->rxflow_buffer)
|
|
|
|
wsi->rxflow_pos += m;
|
|
|
|
}
|
2016-03-17 09:34:15 +08:00
|
|
|
|
2014-04-10 14:08:10 +08:00
|
|
|
/* process the byte */
|
2016-01-11 11:34:01 +08:00
|
|
|
m = lws_rx_sm(wsi, *(*buf)++);
|
2014-04-10 14:08:10 +08:00
|
|
|
if (m < 0)
|
|
|
|
return -1;
|
2016-01-11 11:34:01 +08:00
|
|
|
len--;
|
2014-04-10 14:08:10 +08:00
|
|
|
}
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
|
|
|
|
|
2014-04-10 14:08:10 +08:00
|
|
|
return 0;
|
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,
|
2015-12-04 11:30:53 +08:00
|
|
|
struct lws_context_creation_info *info)
|
2014-04-12 10:07:02 +08:00
|
|
|
{
|
2016-03-23 09:22:11 +08:00
|
|
|
if (lws_check_opt(info->options, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
|
2014-04-12 10:07:02 +08:00
|
|
|
return;
|
2017-03-08 11:11:41 +08:00
|
|
|
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
|
2014-04-12 10:07:02 +08:00
|
|
|
/* find canonical hostname */
|
|
|
|
gethostname((char *)context->canonical_hostname,
|
2016-01-29 21:18:54 +08:00
|
|
|
sizeof(context->canonical_hostname) - 1);
|
2014-04-12 10:07:02 +08:00
|
|
|
|
|
|
|
lwsl_notice(" canonical_hostname = %s\n", context->canonical_hostname);
|
2015-11-02 20:34:12 +08:00
|
|
|
#else
|
|
|
|
(void)context;
|
|
|
|
#endif
|
2014-04-27 13:28:22 +02:00
|
|
|
}
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
#define LWS_MAX_ELEM_NAME 32
|
|
|
|
|
|
|
|
enum urldecode_stateful {
|
|
|
|
US_NAME,
|
|
|
|
US_IDLE,
|
|
|
|
US_PC1,
|
|
|
|
US_PC2,
|
|
|
|
|
|
|
|
MT_LOOK_BOUND_IN,
|
|
|
|
MT_HNAME,
|
|
|
|
MT_DISP,
|
|
|
|
MT_TYPE,
|
|
|
|
MT_IGNORE1,
|
|
|
|
MT_IGNORE2,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const mp_hdr[] = {
|
|
|
|
"content-disposition: ",
|
|
|
|
"content-type: ",
|
|
|
|
"\x0d\x0a"
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef int (*lws_urldecode_stateful_cb)(void *data,
|
|
|
|
const char *name, char **buf, int len, int final);
|
|
|
|
|
|
|
|
struct lws_urldecode_stateful {
|
|
|
|
char *out;
|
|
|
|
void *data;
|
|
|
|
char name[LWS_MAX_ELEM_NAME];
|
|
|
|
char temp[LWS_MAX_ELEM_NAME];
|
|
|
|
char content_type[32];
|
|
|
|
char content_disp[32];
|
|
|
|
char content_disp_filename[256];
|
|
|
|
char mime_boundary[128];
|
|
|
|
int out_len;
|
|
|
|
int pos;
|
|
|
|
int hdr_idx;
|
|
|
|
int mp;
|
2017-02-05 21:25:39 +08:00
|
|
|
int sum;
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
unsigned int multipart_form_data:1;
|
|
|
|
unsigned int inside_quote:1;
|
|
|
|
unsigned int subname:1;
|
|
|
|
unsigned int boundary_real_crlf:1;
|
|
|
|
|
|
|
|
enum urldecode_stateful state;
|
|
|
|
|
|
|
|
lws_urldecode_stateful_cb output;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct lws_urldecode_stateful *
|
|
|
|
lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data,
|
|
|
|
lws_urldecode_stateful_cb output)
|
|
|
|
{
|
|
|
|
struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s));
|
|
|
|
char buf[200], *p;
|
|
|
|
int m = 0;
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s->out = out;
|
|
|
|
s->out_len = out_len;
|
|
|
|
s->output = output;
|
|
|
|
s->pos = 0;
|
2017-02-05 21:25:39 +08:00
|
|
|
s->sum = 0;
|
2016-06-08 10:07:02 +08:00
|
|
|
s->mp = 0;
|
|
|
|
s->state = US_NAME;
|
|
|
|
s->name[0] = '\0';
|
|
|
|
s->data = data;
|
|
|
|
|
|
|
|
if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
|
|
|
|
/* multipart/form-data; boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
|
|
|
|
|
|
|
|
if (!strncmp(buf, "multipart/form-data", 19)) {
|
|
|
|
s->multipart_form_data = 1;
|
|
|
|
s->state = MT_LOOK_BOUND_IN;
|
|
|
|
s->mp = 2;
|
|
|
|
p = strstr(buf, "boundary=");
|
|
|
|
if (p) {
|
|
|
|
p += 9;
|
|
|
|
s->mime_boundary[m++] = '\x0d';
|
|
|
|
s->mime_boundary[m++] = '\x0a';
|
|
|
|
s->mime_boundary[m++] = '-';
|
|
|
|
s->mime_boundary[m++] = '-';
|
|
|
|
while (m < sizeof(s->mime_boundary) - 1 &&
|
|
|
|
*p && *p != ' ')
|
|
|
|
s->mime_boundary[m++] = *p++;
|
|
|
|
|
|
|
|
s->mime_boundary[m] = '\0';
|
|
|
|
|
|
|
|
lwsl_notice("boundary '%s'\n", s->mime_boundary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in, int len)
|
|
|
|
{
|
|
|
|
int n, m, hit = 0;
|
2017-02-05 21:25:39 +08:00
|
|
|
char c, was_end = 0;
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
while (len--) {
|
|
|
|
if (s->pos == s->out_len - s->mp - 1) {
|
|
|
|
if (s->output(s->data, s->name, &s->out, s->pos, 0))
|
|
|
|
return -1;
|
|
|
|
|
2016-11-14 18:13:39 +08:00
|
|
|
was_end = s->pos;
|
2016-06-08 10:07:02 +08:00
|
|
|
s->pos = 0;
|
|
|
|
}
|
|
|
|
switch (s->state) {
|
|
|
|
|
|
|
|
/* states for url arg style */
|
|
|
|
|
|
|
|
case US_NAME:
|
|
|
|
s->inside_quote = 0;
|
|
|
|
if (*in == '=') {
|
|
|
|
s->name[s->pos] = '\0';
|
|
|
|
s->pos = 0;
|
|
|
|
s->state = US_IDLE;
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*in == '&') {
|
|
|
|
s->name[s->pos] = '\0';
|
|
|
|
if (s->output(s->data, s->name, &s->out, s->pos, 1))
|
|
|
|
return -1;
|
|
|
|
s->pos = 0;
|
|
|
|
s->state = US_IDLE;
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s->pos >= sizeof(s->name) - 1) {
|
|
|
|
lwsl_notice("Name too long\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s->name[s->pos++] = *in++;
|
|
|
|
break;
|
|
|
|
case US_IDLE:
|
|
|
|
if (*in == '%') {
|
|
|
|
s->state++;
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*in == '&') {
|
|
|
|
s->out[s->pos] = '\0';
|
|
|
|
if (s->output(s->data, s->name, &s->out, s->pos, 1))
|
|
|
|
return -1;
|
|
|
|
s->pos = 0;
|
|
|
|
s->state = US_NAME;
|
|
|
|
in++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*in == '+') {
|
|
|
|
in++;
|
|
|
|
s->out[s->pos++] = ' ';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
s->out[s->pos++] = *in++;
|
|
|
|
break;
|
|
|
|
case US_PC1:
|
|
|
|
n = char_to_hex(*in);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
in++;
|
2017-02-05 21:25:39 +08:00
|
|
|
s->sum = n << 4;
|
2016-06-08 10:07:02 +08:00
|
|
|
s->state++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case US_PC2:
|
|
|
|
n = char_to_hex(*in);
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
in++;
|
2017-02-05 21:25:39 +08:00
|
|
|
s->out[s->pos++] = s->sum | n;
|
2016-06-08 10:07:02 +08:00
|
|
|
s->state = US_IDLE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* states for multipart / mime style */
|
|
|
|
|
|
|
|
case MT_LOOK_BOUND_IN:
|
2016-10-04 08:24:00 +08:00
|
|
|
retry_as_first:
|
2016-06-08 10:07:02 +08:00
|
|
|
if (*in == s->mime_boundary[s->mp] &&
|
|
|
|
s->mime_boundary[s->mp]) {
|
|
|
|
in++;
|
|
|
|
s->mp++;
|
|
|
|
if (!s->mime_boundary[s->mp]) {
|
|
|
|
s->mp = 0;
|
|
|
|
s->state = MT_IGNORE1;
|
|
|
|
|
2016-11-14 18:13:39 +08:00
|
|
|
if (s->pos || was_end)
|
2016-06-08 10:07:02 +08:00
|
|
|
if (s->output(s->data, s->name,
|
|
|
|
&s->out, s->pos, 1))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
s->pos = 0;
|
|
|
|
|
|
|
|
s->content_disp[0] = '\0';
|
|
|
|
s->name[0] = '\0';
|
|
|
|
s->content_disp_filename[0] = '\0';
|
|
|
|
s->boundary_real_crlf = 1;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s->mp) {
|
|
|
|
n = 0;
|
|
|
|
if (!s->boundary_real_crlf)
|
|
|
|
n = 2;
|
2016-10-04 08:24:00 +08:00
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
memcpy(s->out + s->pos, s->mime_boundary + n, s->mp - n);
|
|
|
|
s->pos += s->mp;
|
2016-10-04 08:24:00 +08:00
|
|
|
s->mp = 0;
|
|
|
|
goto retry_as_first;
|
2016-06-08 10:07:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
s->out[s->pos++] = *in;
|
|
|
|
in++;
|
|
|
|
s->mp = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MT_HNAME:
|
|
|
|
m = 0;
|
2016-06-18 09:00:04 +08:00
|
|
|
c =*in;
|
|
|
|
if (c >= 'A' && c <= 'Z')
|
|
|
|
c += 'a' - 'A';
|
2016-06-08 10:07:02 +08:00
|
|
|
for (n = 0; n < ARRAY_SIZE(mp_hdr); n++)
|
2016-06-18 09:00:04 +08:00
|
|
|
if (c == mp_hdr[n][s->mp]) {
|
2016-06-08 10:07:02 +08:00
|
|
|
m++;
|
|
|
|
hit = n;
|
|
|
|
}
|
|
|
|
in++;
|
|
|
|
if (!m) {
|
|
|
|
s->mp = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->mp++;
|
2016-06-18 09:00:04 +08:00
|
|
|
if (m != 1)
|
2016-06-08 10:07:02 +08:00
|
|
|
continue;
|
|
|
|
|
2016-06-18 09:00:04 +08:00
|
|
|
if (mp_hdr[hit][s->mp])
|
|
|
|
continue;
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
s->mp = 0;
|
|
|
|
s->temp[0] = '\0';
|
|
|
|
s->subname = 0;
|
|
|
|
|
|
|
|
if (hit == 2)
|
|
|
|
s->state = MT_LOOK_BOUND_IN;
|
|
|
|
else
|
|
|
|
s->state += hit + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MT_DISP:
|
|
|
|
/* form-data; name="file"; filename="t.txt" */
|
|
|
|
|
|
|
|
if (*in == '\x0d') {
|
2016-06-18 09:00:04 +08:00
|
|
|
// lwsl_notice("disp: '%s', '%s', '%s'\n",
|
|
|
|
// s->content_disp, s->name,
|
|
|
|
// s->content_disp_filename);
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
if (s->content_disp_filename[0])
|
|
|
|
if (s->output(s->data, s->name,
|
|
|
|
&s->out, s->pos, LWS_UFS_OPEN))
|
|
|
|
return -1;
|
|
|
|
s->state = MT_IGNORE2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if (*in == ';') {
|
|
|
|
s->subname = 1;
|
|
|
|
s->temp[0] = '\0';
|
|
|
|
s->mp = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*in == '\"') {
|
|
|
|
s->inside_quote ^= 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->subname) {
|
|
|
|
if (*in == '=') {
|
|
|
|
s->temp[s->mp] = '\0';
|
|
|
|
s->subname = 0;
|
|
|
|
s->mp = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if (s->mp < sizeof(s->temp) - 1 &&
|
|
|
|
(*in != ' ' || s->inside_quote))
|
|
|
|
s->temp[s->mp++] = *in;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!s->temp[0]) {
|
|
|
|
if (s->mp < sizeof(s->content_disp) - 1)
|
|
|
|
s->content_disp[s->mp++] = *in;
|
|
|
|
s->content_disp[s->mp] = '\0';
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(s->temp, "name")) {
|
|
|
|
if (s->mp < sizeof(s->name) - 1)
|
|
|
|
s->name[s->mp++] = *in;
|
|
|
|
s->name[s->mp] = '\0';
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(s->temp, "filename")) {
|
|
|
|
if (s->mp < sizeof(s->content_disp_filename) - 1)
|
|
|
|
s->content_disp_filename[s->mp++] = *in;
|
|
|
|
s->content_disp_filename[s->mp] = '\0';
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
in++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MT_TYPE:
|
|
|
|
if (*in == '\x0d')
|
|
|
|
s->state = MT_IGNORE2;
|
|
|
|
else {
|
|
|
|
if (s->mp < sizeof(s->content_type) - 1)
|
|
|
|
s->content_type[s->mp++] = *in;
|
|
|
|
s->content_type[s->mp] = '\0';
|
|
|
|
}
|
|
|
|
in++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MT_IGNORE1:
|
|
|
|
if (*in == '\x0d')
|
|
|
|
s->state = MT_IGNORE2;
|
|
|
|
in++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MT_IGNORE2:
|
|
|
|
s->mp = 0;
|
|
|
|
if (*in == '\x0a')
|
|
|
|
s->state = MT_HNAME;
|
|
|
|
in++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (s->state != US_IDLE)
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
if (s->output(s->data, s->name, &s->out, s->pos, 1))
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
lws_free(s);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lws_spa {
|
|
|
|
struct lws_urldecode_stateful *s;
|
|
|
|
lws_spa_fileupload_cb opt_cb;
|
|
|
|
const char * const *param_names;
|
|
|
|
int count_params;
|
|
|
|
char **params;
|
|
|
|
int *param_length;
|
|
|
|
void *opt_data;
|
|
|
|
|
|
|
|
char *storage;
|
|
|
|
char *end;
|
|
|
|
int max_storage;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_urldecode_spa_lookup(struct lws_spa *spa,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < spa->count_params; n++)
|
|
|
|
if (!strcmp(spa->param_names[n], name))
|
|
|
|
return n;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len,
|
|
|
|
int final)
|
|
|
|
{
|
|
|
|
struct lws_spa *spa =
|
|
|
|
(struct lws_spa *)data;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (spa->s->content_disp_filename[0]) {
|
|
|
|
if (spa->opt_cb) {
|
|
|
|
n = spa->opt_cb(spa->opt_data, name,
|
|
|
|
spa->s->content_disp_filename,
|
|
|
|
*buf, len, final);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
n = lws_urldecode_spa_lookup(spa, name);
|
|
|
|
|
|
|
|
if (n == -1 || !len) /* unrecognized */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!spa->params[n])
|
|
|
|
spa->params[n] = *buf;
|
|
|
|
|
|
|
|
if ((*buf) + len >= spa->end) {
|
|
|
|
lwsl_notice("%s: exceeded storage\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spa->param_length[n] += len;
|
|
|
|
|
|
|
|
/* move it on inside storage */
|
|
|
|
(*buf) += len;
|
|
|
|
*((*buf)++) = '\0';
|
|
|
|
|
|
|
|
spa->s->out_len -= len + 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN struct lws_spa *
|
|
|
|
lws_spa_create(struct lws *wsi, const char * const *param_names,
|
|
|
|
int count_params, int max_storage,
|
|
|
|
lws_spa_fileupload_cb opt_cb, void *opt_data)
|
|
|
|
{
|
2016-06-18 09:00:04 +08:00
|
|
|
struct lws_spa *spa = lws_zalloc(sizeof(*spa));
|
2016-06-08 10:07:02 +08:00
|
|
|
|
|
|
|
if (!spa)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
spa->param_names = param_names;
|
|
|
|
spa->count_params = count_params;
|
|
|
|
spa->max_storage = max_storage;
|
|
|
|
spa->opt_cb = opt_cb;
|
|
|
|
spa->opt_data = opt_data;
|
|
|
|
|
|
|
|
spa->storage = lws_malloc(max_storage);
|
|
|
|
if (!spa->storage)
|
|
|
|
goto bail2;
|
|
|
|
spa->end = spa->storage + max_storage - 1;
|
|
|
|
|
|
|
|
spa->params = lws_zalloc(sizeof(char *) * count_params);
|
|
|
|
if (!spa->params)
|
|
|
|
goto bail3;
|
|
|
|
|
|
|
|
spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa,
|
|
|
|
lws_urldecode_spa_cb);
|
|
|
|
if (!spa->s)
|
|
|
|
goto bail4;
|
|
|
|
|
|
|
|
spa->param_length = lws_zalloc(sizeof(int) * count_params);
|
|
|
|
if (!spa->param_length)
|
|
|
|
goto bail5;
|
|
|
|
|
2017-03-08 10:52:49 +08:00
|
|
|
lwsl_info("%s: Created SPA %p\n", __func__, spa);
|
2016-06-18 09:00:04 +08:00
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
return spa;
|
|
|
|
|
|
|
|
bail5:
|
|
|
|
lws_urldecode_s_destroy(spa->s);
|
|
|
|
bail4:
|
|
|
|
lws_free(spa->params);
|
|
|
|
bail3:
|
|
|
|
lws_free(spa->storage);
|
|
|
|
bail2:
|
|
|
|
lws_free(spa);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_spa_process(struct lws_spa *ludspa, const char *in, int len)
|
|
|
|
{
|
2016-06-18 09:00:04 +08:00
|
|
|
if (!ludspa) {
|
2017-02-04 13:09:00 +01:00
|
|
|
lwsl_err("%s: NULL spa\n", __func__);
|
2016-06-18 09:00:04 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2016-06-08 10:07:02 +08:00
|
|
|
return lws_urldecode_s_process(ludspa->s, in, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_spa_get_length(struct lws_spa *ludspa, int n)
|
|
|
|
{
|
|
|
|
if (n >= ludspa->count_params)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return ludspa->param_length[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
|
|
lws_spa_get_string(struct lws_spa *ludspa, int n)
|
|
|
|
{
|
|
|
|
if (n >= ludspa->count_params)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return ludspa->params[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_spa_finalize(struct lws_spa *spa)
|
|
|
|
{
|
|
|
|
if (spa->s) {
|
|
|
|
lws_urldecode_s_destroy(spa->s);
|
|
|
|
spa->s = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_spa_destroy(struct lws_spa *spa)
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
2017-03-08 10:52:49 +08:00
|
|
|
lwsl_info("%s: destroy spa %p\n", __func__, spa);
|
2016-06-18 09:00:04 +08:00
|
|
|
|
2016-06-08 10:07:02 +08:00
|
|
|
if (spa->s)
|
|
|
|
lws_urldecode_s_destroy(spa->s);
|
|
|
|
|
|
|
|
lwsl_debug("%s\n", __func__);
|
|
|
|
|
|
|
|
lws_free(spa->param_length);
|
|
|
|
lws_free(spa->params);
|
|
|
|
lws_free(spa->storage);
|
|
|
|
lws_free(spa);
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
2016-06-12 09:56:39 +08:00
|
|
|
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
|
|
lws_chunked_html_process(struct lws_process_html_args *args,
|
|
|
|
struct lws_process_html_state *s)
|
|
|
|
{
|
|
|
|
char *sp, buffer[32];
|
|
|
|
const char *pc;
|
|
|
|
int old_len, n;
|
|
|
|
|
|
|
|
/* do replacements */
|
|
|
|
sp = args->p;
|
|
|
|
old_len = args->len;
|
|
|
|
args->len = 0;
|
|
|
|
s->start = sp;
|
|
|
|
while (sp < args->p + old_len) {
|
|
|
|
|
|
|
|
if (args->len + 7 >= args->max_len) {
|
|
|
|
lwsl_err("Used up interpret padding\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!s->pos && *sp == '$') || s->pos) {
|
2016-06-27 05:53:38 +08:00
|
|
|
int hits = 0, hit = 0;
|
2016-06-12 09:56:39 +08:00
|
|
|
|
|
|
|
if (!s->pos)
|
|
|
|
s->start = sp;
|
|
|
|
s->swallow[s->pos++] = *sp;
|
2016-08-28 09:28:55 +08:00
|
|
|
if (s->pos == sizeof(s->swallow) - 1)
|
2016-06-12 09:56:39 +08:00
|
|
|
goto skip;
|
|
|
|
for (n = 0; n < s->count_vars; n++)
|
|
|
|
if (!strncmp(s->swallow, s->vars[n], s->pos)) {
|
|
|
|
hits++;
|
|
|
|
hit = n;
|
|
|
|
}
|
|
|
|
if (!hits) {
|
|
|
|
skip:
|
|
|
|
s->swallow[s->pos] = '\0';
|
|
|
|
memcpy(s->start, s->swallow, s->pos);
|
|
|
|
args->len++;
|
|
|
|
s->pos = 0;
|
|
|
|
sp = s->start + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (hits == 1 && s->pos == strlen(s->vars[hit])) {
|
|
|
|
pc = s->replace(s->data, hit);
|
|
|
|
if (!pc)
|
|
|
|
pc = "NULL";
|
|
|
|
n = strlen(pc);
|
|
|
|
s->swallow[s->pos] = '\0';
|
|
|
|
if (n != s->pos) {
|
|
|
|
memmove(s->start + n,
|
|
|
|
s->start + s->pos,
|
|
|
|
old_len - (sp - args->p));
|
|
|
|
old_len += (n - s->pos) + 1;
|
|
|
|
}
|
|
|
|
memcpy(s->start, pc, n);
|
|
|
|
args->len++;
|
|
|
|
sp = s->start + 1;
|
|
|
|
|
|
|
|
s->pos = 0;
|
|
|
|
}
|
|
|
|
sp++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
args->len++;
|
|
|
|
sp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no space left for final chunk trailer */
|
|
|
|
if (args->final && args->len + 7 >= args->max_len)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|