Newer
Older
/*
* libwebsockets - small server side websockets and web server implementation
*
*
* 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 "core/private.h"
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
static const char * const intermediates[] = { "private", "public" };
/*
* return 0: all done
* 1: nonfatal error
* <0: fatal error
_lws_vhost_init_server(const struct lws_context_creation_info *info,
Andy Green
committed
{
struct lws *wsi;
int m = 0, is;
Andy Green
committed
if (info) {
vhost->iface = info->iface;
vhost->listen_port = info->port;
}
Andy Green
committed
/* set up our external listening socket we serve on */
if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
Andy Green
committed
return 0;
if (vh->listen_port == vhost->listen_port) {
if (((!vhost->iface && !vh->iface) ||
(vhost->iface && vh->iface &&
!strcmp(vhost->iface, vh->iface))) &&
vh->lserv_wsi
) {
lwsl_notice(" using listen skt from vhost %s\n",
vh->name);
return 0;
}
}
vh = vh->vhost_next;
}
if (vhost->iface) {
/*
* let's check before we do anything else about the disposition
* of the interface he wants to bind to...
*/
is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port, vhost->iface);
lwsl_debug("initial if check says %d\n", is);
deal:
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
lws_start_foreach_llp(struct lws_vhost **, pv,
vhost->context->no_listener_vhost_list) {
if (is >= LWS_ITOSA_USABLE && *pv == vhost) {
/* on the list and shouldn't be: remove it */
lwsl_debug("deferred iface: removing vh %s\n", (*pv)->name);
*pv = vhost->no_listener_vhost_list;
vhost->no_listener_vhost_list = NULL;
goto done_list;
}
if (is < LWS_ITOSA_USABLE && *pv == vhost)
goto done_list;
} lws_end_foreach_llp(pv, no_listener_vhost_list);
/* not on the list... */
if (is < LWS_ITOSA_USABLE) {
/* ... but needs to be: so add it */
lwsl_debug("deferred iface: adding vh %s\n", vhost->name);
vhost->no_listener_vhost_list = vhost->context->no_listener_vhost_list;
vhost->context->no_listener_vhost_list = vhost;
}
done_list:
switch (is) {
default:
break;
case LWS_ITOSA_NOT_EXIST:
/* can't add it */
if (info) /* first time */
lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n",
vhost->name, vhost->iface, vhost->listen_port);
return 1;
case LWS_ITOSA_NOT_USABLE:
/* can't add it */
if (info) /* first time */
lwsl_err("VH %s: iface %s port %d NOT USABLE\n",
vhost->name, vhost->iface, vhost->listen_port);
return 1;
}
}
#ifdef LWS_WITH_UNIX_SOCK
/*
* A Unix domain sockets cannot be bound for several times, even if we set
* the SO_REUSE* options on.
* However, fortunately, each thread is able to independently listen when
* running on a reasonably new Linux kernel. So we can safely assume
* creating just one listening socket for a multi-threaded environment won't
* fail in most cases.
*/
if (!LWS_UNIX_SOCK_ENABLED(vhost))
#endif
if (LWS_UNIX_SOCK_ENABLED(vhost))
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
else
if (LWS_IPV6_ENABLED(vhost))
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
Andy Green
committed
#endif
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == LWS_SOCK_INVALID) {
lwsl_err("ERROR opening socket\n");
return 1;
}
#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
/*
* only accept that we are the only listener on the port
* https://msdn.microsoft.com/zh-tw/library/
* windows/desktop/ms740621(v=vs.85).aspx
*
* for lws, to match Linux, we default to exclusive listen
*/
if (!lws_check_opt(vhost->options,
LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
(const void *)&opt, sizeof(opt)) < 0) {
lwsl_err("reuseaddr failed\n");
compatible_close(sockfd);
}
} else
#endif
/*
* allow us to restart even if old sockets in TIME_WAIT
*/
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&opt, sizeof(opt)) < 0) {
lwsl_err("reuseaddr failed\n");
compatible_close(sockfd);
#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
if (LWS_IPV6_ENABLED(vhost) &&
vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
int value = (vhost->options &
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
(const void*)&value, sizeof(value)) < 0) {
compatible_close(sockfd);
#if defined(__linux__) && defined(SO_REUSEPORT)
/* keep coverity happy */
n = lws_check_opt(vhost->options,
LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
if (n && vhost->context->count_threads > 1)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
(const void *)&opt, sizeof(opt)) < 0) {
compatible_close(sockfd);
lws_plat_set_socket_options(vhost, sockfd, 0);
Andy Green
committed
is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface);
/*
* There is a race where the network device may come up and then
* go away and fail here. So correctly handle unexpected failure
* here despite we earlier confirmed it.
*/
if (is < 0) {
lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
compatible_close(sockfd);
goto deal;
}
Andy Green
committed
wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
if (wsi == NULL) {
lwsl_err("Out of mem\n");
goto bail;
}
#ifdef LWS_WITH_UNIX_SOCK
if (!LWS_UNIX_SOCK_ENABLED(vhost))
#endif
{
wsi->unix_skt = 1;
vhost->listen_port = is;
lwsl_debug("%s: lws_socket_bind says %d\n", __func__,
is);
}
wsi->context = vhost->context;
wsi->desc.sockfd = sockfd;
lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
wsi->protocol = vhost->protocols;
wsi->tsi = m;
lws_vhost_bind_wsi(vhost, wsi);
Andy Green
committed
if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
lwsl_notice("inserting wsi socket into fds failed\n");
Andy Green
committed
vhost->context->count_wsi_allocated++;
vhost->lserv_wsi = wsi;
Andy Green
committed
n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
if (n < 0) {
lwsl_err("listen failed with error %d\n", LWS_ERRNO);
vhost->lserv_wsi = NULL;
vhost->context->count_wsi_allocated--;
__remove_wsi_socket_from_fds(wsi);
goto bail;
}
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
lwsl_info(" Listening on \"%s\"\n", vhost->iface);
lwsl_info(" Listening on port %d\n", vhost->listen_port);
// info->port = vhost->listen_port;
Andy Green
committed
return 0;
Andy Green
committed
}
struct lws_vhost *
lws_select_vhost(struct lws_context *context, int port, const char *servername)
{
struct lws_vhost *vhost = context->vhost_list;
n = (int)strlen(servername);
colon = n;
p = strchr(servername, ':');
if (p)
colon = lws_ptr_diff(p, servername);
/* Priotity 1: first try exact matches */
!strncmp(vhost->name, servername, colon)) {
lwsl_info("SNI: Found: %s\n", servername);
return vhost;
}
vhost = vhost->vhost_next;
}
* Priority 2: if no exact matches, try matching *.vhost-name
* 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) {
if (port && 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;
}
/* Priority 3: match the first vhost on our port */
vhost = context->vhost_list;
while (vhost) {
if (port && port == vhost->listen_port) {
lwsl_info("%s: vhost match to %s based on port %d\n",
__func__, vhost->name, port);
return vhost;
}
vhost = vhost->vhost_next;
}
/* no match */
LWS_VISIBLE LWS_EXTERN const char *
lws_get_mimetype(const char *file, const struct lws_http_mount *m)
int n = (int)strlen(file);
const struct lws_protocol_vhost_options *pvo = NULL;
if (m)
pvo = m->extra_mimetypes;
if (n < 5)
return NULL;
if (!strcmp(&file[n - 4], ".ico"))
return "image/x-icon";
if (!strcmp(&file[n - 4], ".gif"))
return "image/gif";
if (!strcmp(&file[n - 3], ".js"))
return "text/javascript";
if (!strcmp(&file[n - 4], ".png"))
return "image/png";
if (!strcmp(&file[n - 3], ".gz"))
return "application/gzip";
if (!strcmp(&file[n - 4], ".JPG"))
return "image/jpeg";
if (!strcmp(&file[n - 5], ".html"))
return "text/html";
if (!strcmp(&file[n - 4], ".css"))
return "text/css";
if (!strcmp(&file[n - 4], ".txt"))
return "text/plain";
if (!strcmp(&file[n - 4], ".svg"))
return "image/svg+xml";
if (!strcmp(&file[n - 4], ".ttf"))
return "application/x-font-ttf";
if (!strcmp(&file[n - 4], ".otf"))
return "application/font-woff";
if (!strcmp(&file[n - 5], ".woff"))
return "application/font-woff";
if (!strcmp(&file[n - 4], ".xml"))
return "application/xml";
if (pvo->name[0] == '*') /* ie, match anything */
return pvo->value;
if (!strcmp(&file[n - strlen(pvo->name)], pvo->name))
return pvo->value;
pvo = pvo->next;
}
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;
}
static int
lws_http_serve(struct lws *wsi, char *uri, const char *origin,
const struct lws_http_mount *m)
const struct lws_protocol_vhost_options *pvo = m->interpret;
struct lws_process_html_args args;
#if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
struct _stat32i64 st;
#else
OndraCo
committed
#endif
Andy Green
committed
char path[256], sym[2048];
unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
if (!wsi->vhost)
return -1;
Andy Green
committed
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
if (wsi->vhost->http.error_document_404 &&
!strcmp(uri, wsi->vhost->http.error_document_404))
Andy Green
committed
#endif
fflags |= lws_vfs_prepare_flags(wsi);
if (wsi->http.fop_fd)
lws_vfs_file_close(&wsi->http.fop_fd);
wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
lwsl_info("%s: Unable to open '%s': errno %d\n",
__func__, path, errno);
/* if it can't be statted, don't try */
if (fflags & LWS_FOP_FLAG_VIRTUAL)
break;
#if defined(LWS_WITH_ESP32)
break;
#endif
#else
#if defined(LWS_HAVE__STAT32I64)
if (_stat32i64(path, &st)) {
lwsl_info("unable to stat %s\n", path);
#else
if (stat(path, &st)) {
lwsl_info("unable to stat %s\n", path);
wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
len = readlink(path, sym, sizeof(sym) - 1);
if (len) {
if ((S_IFMT & st.st_mode) == S_IFDIR) {
lwsl_debug("default filename append to dir\n");
origin, uri);
}
} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
(unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
(unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
/* 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: */
Andy Green
committed
wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
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
*/
if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
char cache_control[50], *cc = "no-store";
int cclen = 8;
lwsl_debug("%s: ETAG match %s %s\n", __func__,
uri, origin);
if (lws_add_http_header_status(wsi,
Andy Green
committed
HTTP_STATUS_NOT_MODIFIED, &p, end)) {
lwsl_err("%s: failed adding not modified\n",
__func__);
Andy Green
committed
}
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_ETAG,
(unsigned char *)sym, n, &p, end))
return -1;
/* but we still need to send cache control... */
if (!m->cache_revalidate) {
cc = cache_control;
cclen = sprintf(cache_control, "%s, max-age=%u",
intermediates[wsi->cache_intermediaries],
m->cache_max_age);
} else {
cc = cache_control;
cclen = sprintf(cache_control, "must-revalidate, %s, max-age=%u",
intermediates[wsi->cache_intermediaries],
m->cache_max_age);
}
}
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
(unsigned char *)cc, cclen, &p, end))
return -1;
if (lws_finalize_http_header(wsi, &p, end))
return -1;
n = lws_write(wsi, start, p - start,
Martin Milata
committed
lwsl_err("_write returned %d from %ld\n", n,
(long)(p - start));
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
(unsigned char *)sym, n, &p, end))
return -1;
OndraCo
committed
#endif
lwsl_info("unknown mimetype for %s\n", path);
if (lws_return_http_status(wsi,
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||
lws_http_transaction_completed(wsi))
return -1;
return 0;
if (!mimetype[0])
lwsl_debug("sending no mimetype for %s\n", path);
wsi->sending_chunked = 0;
/*
* check if this is in the list of file suffixes to be interpreted by
* a protocol
*/
while (pvo) {
n = (int)strlen(path);
if (n > (int)strlen(pvo->name) &&
!strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
wsi->interpreting = 1;
if (!wsi->http2_substream)
wsi->sending_chunked = 1;
wsi->protocol_interpret_idx =
(char)(lws_intptr_t)pvo->value;
wsi->vhost->protocols[
(int)(lws_intptr_t)(pvo->value)].name);
wsi->protocol = &wsi->vhost->protocols[
(int)(lws_intptr_t)(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(
args.max_len = lws_ptr_diff(end, p);
if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
wsi->user_space, &args, 0))
return -1;
p = (unsigned char *)args.p;
}
*p = '\0';
n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
lws_ptr_diff(p, start));
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
return -1; /* error or can't reuse connection: close the socket */
return 0;
notfound:
return 1;
Andy Green
committed
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
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;
Andy Green
committed
hm = wsi->vhost->http.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) ||
lws_hdr_total_length(wsi,
WSI_TOKEN_HTTP_COLON_PATH)) ||
hm->protocol) &&
hm->mountpoint_len > best)) {
best = hm->mountpoint_len;
hit = hm;
}
}
hm = hm->mount_next;
}
return hit;
}
Andy Green
committed
#endif
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;
Andy Green
committed
fd = lws_open(filename, O_RDONLY);
if (fd < 0) {
lwsl_err("can't open auth file: %s\n", filename);
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
}
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,
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 |
LWS_WRITE_H2_STREAM_END);
if (n < 0)
return -1;
return lws_http_transaction_completed(wsi);
}
if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
p += 4;
if (*p == 's')
p++;
if (*p == ':') {
p++;
if (*p == '/')
p++;
}
}
while (*p) {
if (p[0] == '/' && p[1] == '/') {
char *p1 = p;
while (*p1) {
*p1 = p1[1];
p1++;
}
continue;
}
p++;
}
return 0;
}
static const unsigned char methods[] = {
WSI_TOKEN_GET_URI,
WSI_TOKEN_POST_URI,
WSI_TOKEN_OPTIONS_URI,
WSI_TOKEN_PUT_URI,
WSI_TOKEN_PATCH_URI,
WSI_TOKEN_DELETE_URI,
WSI_TOKEN_CONNECT,
WSI_TOKEN_HTTP_COLON_PATH,
#endif
};
static int
lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
{
int n, count = 0;
for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n]))
count++;
if (!count) {
lwsl_warn("Missing URI in HTTP request\n");
if (count != 1 &&
!(wsi->http2_substream &&
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n])) {
*puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
*puri_len = lws_hdr_total_length(wsi, methods[n]);
return n;
return -1;
}
int
lws_http_action(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
enum http_version request_version;
char content_length_str[32];
struct lws_process_html_args args;
const struct lws_http_mount *hit = NULL;
unsigned int n;
char http_version_str[12];
char http_conn_str[25];
int http_version_len;
char *uri_ptr = NULL, *s;
int uri_len = 0, meth, m;
static const char * const oprot[] = {
"http://", "https://"
};
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
/* we insist on absolute paths */
if (!uri_ptr || uri_ptr[0] != '/') {
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
goto bail_nuke_ah;
}
lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
meth, uri_ptr);
if (wsi->role_ops && wsi->role_ops->check_upgrades)
switch (wsi->role_ops->check_upgrades(wsi)) {
case LWS_UPG_RET_DONE:
return 0;
case LWS_UPG_RET_CONTINUE:
break;
case LWS_UPG_RET_BAIL:
goto bail_nuke_ah;
if (lws_ensure_user_space(wsi))
goto bail_nuke_ah;
/* HTTP header had a content length? */
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))
wsi->http.rx_content_length = 100 * 1024 * 1024;
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
lws_hdr_copy(wsi, content_length_str,
sizeof(content_length_str) - 1,
WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) {
wsi->http.rx_content_length = atoll(content_length_str);
if (!wsi->http.rx_content_length) {
wsi->http.content_length_explicitly_zero = 1;
lwsl_debug("%s: explicit 0 content-length\n", __func__);
} 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) > 0 &&
http_version_str[5] == '1' && http_version_str[7] == '1')
request_version = HTTP_VERSION_1_1;
/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
if (request_version == HTTP_VERSION_1_1)
/* 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) > 0) {
http_conn_str[sizeof(http_conn_str) - 1] = '\0';
if (!strcasecmp(http_conn_str, "keep-alive"))