Newer
Older
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* 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"
int
lws_context_init_server(struct lws_context_creation_info *info,
Andy Green
committed
{
struct lws *wsi;
Andy Green
committed
/* set up our external listening socket we serve on */
if (info->port == CONTEXT_PORT_NO_LISTEN)
return 0;
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;
}
Andy Green
committed
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context))
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
#endif
sockfd = socket(AF_INET, SOCK_STREAM, 0);
#else
sockfd = mbed3_create_tcp_stream_socket();
if (!lws_sockfd_valid(sockfd)) {
#endif
Andy Green
committed
lwsl_err("ERROR opening socket\n");
return 1;
}
Andy Green
committed
/*
* allow us to restart even if old sockets in TIME_WAIT
*/
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
#if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
(const void *)&opt, sizeof(opt)) < 0) {
compatible_close(sockfd);
return 1;
}
Andy Green
committed
n = lws_socket_bind(vhost->context, sockfd, info->port, info->iface);
vhost->listen_port = info->port;
vhost->iface = info->iface;
Andy Green
committed
wsi = lws_zalloc(sizeof(struct lws));
Andy Green
committed
if (wsi == NULL) {
lwsl_err("Out of mem\n");
Andy Green
committed
}
Andy Green
committed
wsi->sock = sockfd;
Andy Green
committed
vhost->context->pt[m].wsi_listening = wsi;
if (insert_wsi_socket_into_fds(vhost->context, wsi))
Andy Green
committed
vhost->context->count_wsi_allocated++;
vhost->lserv_wsi = wsi;
Andy Green
committed
listen(wsi->sock, LWS_SOMAXCONN);
} /* for each thread able to independently lister */
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
lwsl_notice(" Listening on port %d\n", info->port);
Andy Green
committed
return 0;
bail:
compatible_close(sockfd);
return 1;
Andy Green
committed
}
_lws_server_listen_accept_flow_control(struct lws *twsi, int on)
struct lws_context_per_thread *pt = &twsi->context->pt[(int)twsi->tsi];
struct lws *wsi = pt->wsi_listening;
lwsl_debug("%s: Thr %d: LISTEN wsi %p: state %d\n",
__func__, twsi->tsi, (void *)wsi, on);
if (on)
n = lws_change_pollfd(wsi, 0, LWS_POLLIN);
else
n = lws_change_pollfd(wsi, LWS_POLLIN, 0);
return n;
}
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
struct lws_vhost *
lws_select_vhost(struct lws_context *context, int port, const char *servername)
{
struct lws_vhost *vhost = context->vhost_list;
while (vhost) {
if (port == vhost->listen_port &&
!strcmp(vhost->name, servername)) {
lwsl_info("SNI: Found: %s\n", servername);
return vhost;
}
vhost = vhost->vhost_next;
}
return NULL;
}
static const char * get_mimetype(const char *file)
{
int n = strlen(file);
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 - 5], ".html"))
return "text/html";
if (!strcmp(&file[n - 4], ".css"))
return "text/css";
if (!strcmp(&file[n - 4], ".ttf"))
return "application/x-font-ttf";
return NULL;
}
int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
{
const char *mimetype;
struct stat st;
char path[256], sym[256];
int n, spin = 0;
lwsl_notice("%s: %s %s\n", __func__, uri, origin);
snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
do {
spin++;
if (stat(path, &st)) {
lwsl_err("unable to stat %s\n", path);
goto bail;
}
lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode);
if ((S_IFMT & st.st_mode) == S_IFLNK) {
if (readlink(path, sym, sizeof(sym))) {
lwsl_err("Failed to read link %s\n", path);
goto bail;
}
lwsl_debug("symlink %s -> %s\n", path, sym);
snprintf(path, sizeof(path) - 1, "%s", sym);
}
if ((S_IFMT & st.st_mode) == S_IFDIR) {
lwsl_debug("default filename append to dir\n");
snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
origin, uri);
}
} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
if (spin == 5) {
lwsl_err("symlink loop %s \n", path);
}
mimetype = get_mimetype(path);
if (!mimetype) {
lwsl_err("unknown mimetype for %s", path);
}
n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
return -1; /* error or can't reuse connection: close the socket */
return 0;
bail:
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
return -1;
int
lws_http_action(struct lws *wsi)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
enum http_connection_type connection_type;
char content_length_str[32];
char http_version_str[10];
char http_conn_str[20];
int http_version_len;
char *uri_ptr = NULL;
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,
static const char * const method_names[] = {
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
#ifdef LWS_USE_HTTP2
":path",
#endif
};
/* 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) {
lwsl_warn("Missing URI in HTTP request\n");
goto bail_nuke_ah;
}
if (count != 1) {
lwsl_warn("multiple methods?\n");
if (lws_ensure_user_space(wsi))
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);
break;
}
/* we insist on absolute paths */
if (uri_ptr[0] != '/') {
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
goto bail_nuke_ah;
}
/* HTTP header had a content length? */
wsi->u.http.content_length = 0;
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->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,
sizeof(content_length_str) - 1,
WSI_TOKEN_HTTP_CONTENT_LENGTH);
wsi->u.http.content_length = atoi(content_length_str);
}
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;
/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
if (request_version == HTTP_VERSION_1_1)
connection_type = HTTP_CONNECTION_KEEP_ALIVE;
else
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;
n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
* if there is content supposed to be coming,
* put a timeout on it having arrived
#ifdef LWS_OPENSSL_SUPPORT
* we accepted http:// only so we could redirect to
* 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;
if (lws_add_http_header_status(wsi, 301, &p, end))
goto bail_nuke_ah;
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
end, n, &p, end))
goto bail_nuke_ah;
if (lws_finalize_http_header(wsi, &p, end))
goto bail_nuke_ah;
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
goto bail_nuke_ah;
return lws_http_transaction_completed(wsi);
}
/* can we serve it from the mount list? */
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->mountpoint_len > best) {
best = hm->mountpoint_len;
hit = hm;
}
if (hit) {
char *s = uri_ptr + hit->mountpoint_len;
lwsl_debug("*** hit %d %d %s\n", hit->mountpoint_len,
hit->origin_protocol , hit->origin);
/*
* 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 & 4)) &&
(*s != '/' || (hit->origin_protocol & 4))) {
unsigned char *start = pt->serv_buf + LWS_PRE,
*p = start, *end = p + 512;
static const char *oprot[] = {
"http://", "https://"
};
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
goto bail_nuke_ah;
if (lws_add_http_header_status(wsi, 301, &p, end))
goto bail_nuke_ah;
lwsl_debug("**** %s", hit->origin);
/* > at start indicates deal with by redirect */
if (hit->origin_protocol & 4)
n = snprintf((char *)end, 256, "%s%s",
oprot[hit->origin_protocol & 1],
hit->origin);
else
n = snprintf((char *)end, 256,
"https://%s/%s/",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
uri_ptr);
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_LOCATION,
end, n, &p, end))
goto bail_nuke_ah;
if (lws_finalize_http_header(wsi, &p, end))
goto bail_nuke_ah;
n = lws_write(wsi, start, p - start,
LWS_WRITE_HTTP_HEADERS);
if ((int)n < 0)
goto bail_nuke_ah;
return lws_http_transaction_completed(wsi);
}
#ifdef LWS_WITH_CGI
/* did we hit something with a cgi:// origin? */
if (hit->origin_protocol == LWSMPRO_CGI) {
const char *cmd[] = {
NULL, /* replace with cgi path */
NULL
};
unsigned char *p, *end, buffer[256];
lwsl_debug("%s: cgi\n", __func__);
cmd[0] = hit->origin;
n = lws_cgi(wsi, cmd, hit->mountpoint_len, 5,
hit->cgienv);
if (n) {
lwsl_err("%s: cgi failed\n");
return -1;
p = buffer + LWS_PRE;
end = p + sizeof(buffer) - LWS_PRE;
if (lws_add_http_header_status(wsi, 200, &p, end))
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);
return 0;
}
#endif
n = strlen(s);
if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
s = (char *)hit->def;
if (!s)
s = "index.html";
if (n) {
lwsl_info("LWS_CALLBACK_HTTP closing\n");
return 1;
}
* If we're not issuing a file, check for content_length or
* HTTP keep-alive. No keep-alive header allocation for
* ISSUING_FILE, as this uses HTTP/1.0.
*
* In any case, return 0 and let lws_read decide how to
/* Prepare to read body if we have a content length: */
if (wsi->u.http.content_length > 0)
/* we're closing, losing some rx is OK */
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct _lws_header_related hdr;
char protocol_list[128];
char protocol_name[32];
char *p;
while (len--) {
assert(wsi->mode == LWSCM_HTTP_SERVING);
if (lws_parse(wsi, *(*buf)++)) {
lwsl_info("lws_parse failed\n");
goto bail_nuke_ah;
}
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
continue;
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
wsi->more_rx_waiting);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* is this websocket protocol or normal http 1.0? */
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
"websocket")) {
lwsl_info("Upgrade to ws\n");
goto upgrade_ws;
}
#ifdef LWS_USE_HTTP2
if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
goto upgrade_h2c;
}
#endif
lwsl_err("Unknown upgrade\n");
/* dunno what he wanted to upgrade to */
goto bail_nuke_ah;
}
/* no upgrade ack... he remained as HTTP */
lwsl_info("No upgrade\n");
ah = wsi->u.hdr.ah;
/* 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;
}
lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
wsi->state = LWSS_HTTP;
wsi->u.http.fd = LWS_INVALID_FILE;
/* expose it at the same offset as u.hdr */
wsi->u.http.ah = ah;
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
(void *)wsi->u.hdr.ah);
#ifdef LWS_USE_HTTP2
upgrade_h2c:
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
lwsl_err("missing http2_settings\n");
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
/* convert the peer's HTTP-Settings */
n = lws_b64_decode_string(p, protocol_list,
sizeof(protocol_list));
if (n < 0) {
lwsl_parser("HTTP2_SETTINGS too long\n");
return 1;
}
/* adopt the header info */
ah = wsi->u.hdr.ah;
/* http2 union member has http union struct at start */
wsi->u.http.ah = ah;
lws_http2_init(&wsi->u.http2.peer_settings);
lws_http2_init(&wsi->u.http2.my_settings);
lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
(unsigned char *)protocol_list, n);
strcpy(protocol_list,
"HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Upgrade: h2c\x0d\x0a\x0d\x0a");
n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
strlen(protocol_list));
if (n != strlen(protocol_list)) {
lwsl_debug("http2 switch: ERROR writing to socket\n");
return 1;
}
if (!wsi->protocol)
lwsl_err("NULL protocol at lws_read\n");
/*
* It's websocket
*
* Select the first protocol we support from the list
* the client sent us.
*
* Copy it to remove header fragmentation
*/
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;
}
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
protocol_list[protocol_len] = '\0';
p = protocol_list;
hit = 0;
while (*p && !hit) {
while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
protocol_name[n++] = *p++;
protocol_name[n] = '\0';
if (*p)
p++;
lwsl_info("checking %s\n", protocol_name);
n = 0;
while (wsi->vhost->protocols[n].callback) {
if (wsi->vhost->protocols[n].name &&
!strcmp(wsi->vhost->protocols[n].name,
protocol_name)) {
lwsl_info("prot match %d\n", n);
break;
}
/* we didn't find a protocol he wanted? */
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
lwsl_err("No protocol from \"%s\" supported\n",
goto bail_nuke_ah;
}
/*
* some clients only have one protocol and
* do not sent the protocol list header...
* allow it and match to protocol 0
*/
lwsl_info("defaulting to prot 0 handler\n");
}
/* allocate wsi->user storage */
if (lws_ensure_user_space(wsi))
goto bail_nuke_ah;
/*
* Give the user code a chance to study the request and
* have the opportunity to deny it
*/
if ((wsi->protocol->callback)(wsi,
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:
lwsl_warn("Unknown client spec version %d\n",
goto bail_nuke_ah;
}
/* 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.
*/
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);
/*
* 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)
*
* Because rxpos/rxlen shows something in the ah, we will get
* 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);
/*
* 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)
n = LWS_MAX_SOCKET_IO_BUF;
n += LWS_PRE;
wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
if (!wsi->u.ws.rx_ubuf) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
return 1;
}
lwsl_info("Allocating RX buffer %d\n", n);
if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
(const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
return 1;
}
lwsl_parser("accepted v%02d connection\n",
wsi->ietf_spec_revision);
} /* while all chars are handled */
return 0;
bail_nuke_ah:
/* drop the header info */
/* we're closing, losing some rx is OK */
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
return 1;
}
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++) {
if ((unsigned int)context->pt[n].fds_count !=
context->fd_limit_per_thread - 1 &&
(unsigned int)context->pt[n].fds_count < lowest) {
lowest = context->pt[n].fds_count;
hit = n;
}
}
return hit;
}
struct lws *new_wsi;
if (n < 0) {
lwsl_err("no space for new conn\n");
return NULL;
}
new_wsi = lws_zalloc(sizeof(struct lws));
if (new_wsi == NULL) {
lwsl_err("Out of memory for new connection\n");
return NULL;
}
new_wsi->tsi = n;
lwsl_info("Accepted %p to tsi %d\n", new_wsi, new_wsi->tsi);
new_wsi->vhost = vhost;
new_wsi->context = vhost->context;
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
/* intialize the instance struct */
new_wsi->state = LWSS_HTTP;
new_wsi->mode = LWSCM_HTTP_SERVING;
new_wsi->hdr_parsing_completed = 0;
/*
* 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
*/
new_wsi->user_space = NULL;
new_wsi->ietf_spec_revision = 0;
/*
* outermost create notification for wsi
* no user_space because no protocol selection
*/
vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
return new_wsi;
}
/**
* lws_http_transaction_completed() - wait for new http transaction or close
* @wsi: websocket connection
*
* Returns 1 if the HTTP connection must close now
* Returns 0 and resets connection to wait for new HTTP header /
* transaction if possible
*/
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws *wsi)
lwsl_debug("%s: wsi %p\n", __func__, wsi);
/* if we can't go back to accept new headers, drop the connection */
if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
lwsl_info("%s: %p: close connection\n", __func__, wsi);
return 1;
}
/* otherwise set ourselves up ready to go again */