Newer
Older
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
if (wsi->tls.redirect_to_https) {
* 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 + wsi->context->pt_serv_buf_size - LWS_PRE;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
goto bail_nuke_ah;
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
end, n, &p, end);
goto bail_nuke_ah;
return lws_http_transaction_completed(wsi);
}
lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
hit = lws_find_mount(wsi, uri_ptr, uri_len);
if (!hit) {
/* deferred cleanup and reset to protocols[0] */
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], "no mount hit"))
m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
/*
* 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 &&
*p = start, *end = p + wsi->context->pt_serv_buf_size -
LWS_PRE - 512;
lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
/* > at start indicates deal with by redirect */
if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
oprot[hit->origin_protocol & 1],
hit->origin);
else {
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
if (!lws_hdr_total_length(wsi,
WSI_TOKEN_HTTP_COLON_AUTHORITY))
goto bail_nuke_ah;
n = lws_snprintf((char *)end, 256,
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_COLON_AUTHORITY),
uri_ptr);
} else
n = lws_snprintf((char *)end, 256,
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
uri_ptr);
}
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
end, n, &p, end);
if ((int)n < 0)
goto bail_nuke_ah;
return lws_http_transaction_completed(wsi);
}
char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
int m, ml, fi;
ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
if (!ml)
/* Disallow fragmentation monkey business */
fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
if (wsi->http.ah->frags[fi].nfrag) {
lwsl_err("fragmented basic auth header not allowed\n");
return lws_unauthorised_basic_auth(wsi);
}
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) - 1);
if (m < 0) {
lwsl_err("plain auth too long\n");
goto transaction_result_n;
}
plain[m] = '\0';
pcolon = strchr(plain, ':');
if (!pcolon) {
lwsl_err("basic auth format broken\n");
return lws_unauthorised_basic_auth(wsi);
}
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);
}
/*
* Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
* authorized username
*/
*pcolon = '\0';
wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain);
pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
strncpy(pcolon, plain, ml - 1);
pcolon[ml - 1] = '\0';
lwsl_info("%s: basic auth accepted for %s\n", __func__,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
#if defined(LWS_WITH_HTTP_PROXY)
/*
* The mount is a reverse proxy?
*/
// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
if (hit->origin_protocol == LWSMPRO_HTTPS ||
hit->origin_protocol == LWSMPRO_HTTP) {
struct lws_client_connect_info i;
char ads[96], rpath[256], *pcolon, *pslash, unix_skt = 0;
int n, na;
memset(&i, 0, sizeof(i));
i.context = lws_get_context(wsi);
if (hit->origin[0] == '+')
unix_skt = 1;
pcolon = strchr(hit->origin, ':');
pslash = strchr(hit->origin, '/');
if (!pslash) {
lwsl_err("Proxy mount origin '%s' must have /\n",
hit->origin);
if (unix_skt) {
if (!pcolon) {
lwsl_err("Proxy mount origin for unix skt must "
"have address delimited by :\n");
return -1;
}
n = lws_ptr_diff(pcolon, hit->origin);
pslash = pcolon;
} else {
if (pcolon > pslash)
pcolon = NULL;
if (pcolon)
if (n >= (int)sizeof(ads) - 2)
n = sizeof(ads) - 2;
}
memcpy(ads, hit->origin, n);
ads[n] = '\0';
i.address = ads;
i.port = 80;
if (hit->origin_protocol == LWSMPRO_HTTPS) {
i.port = 443;
i.ssl_connection = 1;
}
if (pcolon)
i.port = atoi(pcolon + 1);
n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
pslash + 1, uri_ptr +
lws_clean_url(rpath);
na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
if (na) {
char *p = rpath + n;
if (na >= (int)sizeof(rpath) - n - 2) {
lwsl_info("%s: query string %d longer "
"than we can handle\n", __func__,
na);
return -1;
}
if (lws_hdr_copy(wsi, p,
WSI_TOKEN_HTTP_URI_ARGS) > 0)
while (na--) {
if (*p == '\0')
*p = '&';
p++;
}
*p = '\0';
if (i.address[0] != '+' ||
!lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
i.host = i.address;
else
i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
// i.uri_replace_from = hit->origin;
// i.uri_replace_to = hit->mountpoint;
lwsl_info("proxying to %s port %d url %s, ssl %d, "
"from %s, to %s\n",
i.address, i.port, i.path, i.ssl_connection,
i.uri_replace_from, i.uri_replace_to);
if (!lws_client_connect_via_info(&i)) {
lwsl_err("proxy connect fail\n");
/*
* ... we can't do the proxy action, but we can
* cleanly return him a 503 and a description
*/
lws_return_http_status(wsi,
HTTP_STATUS_SERVICE_UNAVAILABLE,
"<h1>Service Temporarily Unavailable</h1>"
"The server is temporarily unable to service "
"your request due to maintenance downtime or "
"capacity problems. Please try again later.");
lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
__func__, cwsi, lws_get_parent(cwsi));
cwsi->http.proxy_clientside = 1;
/*
* 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;
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;
if (hit->cgienv && wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_PMO,
wsi->user_space, (void *)hit->cgienv, 0))
return 1;
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space,
uri_ptr + hit->mountpoint_len,
uri_len - hit->mountpoint_len);
/* did we hit something with a cgi:// origin? */
if (hit->origin_protocol == LWSMPRO_CGI) {
const char *cmd[] = {
NULL, /* replace with cgi path */
NULL
};
lwsl_debug("%s: cgi\n", __func__);
cmd[0] = hit->origin;
n = 5;
if (hit->cgi_timeout)
n = hit->cgi_timeout;
n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
hit->cgienv);
if (n) {
lwsl_err("%s: cgi failed\n", __func__);
n = uri_len - lws_ptr_diff(s, uri_ptr); // (int)strlen(s);
if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
s = (char *)hit->def;
if (!s)
s = "index.html";
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;
m = lws_http_serve(wsi, s, hit->origin, hit);
if (m > 0) {
const struct lws_protocols *pp =
lws_vhost_name_to_protocol(
wsi->vhost, hit->protocol);
lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
m = pp->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space,
uri_ptr + hit->mountpoint_len,
uri_len - hit->mountpoint_len);
} else
m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
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
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
/* Prepare to read body if we have a content length: */
lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
(long long)wsi->http.rx_content_length,
wsi->upgraded_to_http2, wsi->http2_substream);
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
if (wsi->http.content_length_explicitly_zero &&
lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
/*
* POST with an explicit content-length of zero
*
* If we don't give the user code the empty HTTP_BODY
* callback, he may become confused to hear the
* HTTP_BODY_COMPLETION (due to, eg, instantiation of
* lws_spa never happened).
*
* HTTP_BODY_COMPLETION is responsible for sending the
* result status code and result body if any, and
* do the transaction complete processing.
*/
if (wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY,
wsi->user_space, NULL, 0))
return 1;
if (wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0))
return 1;
return 0;
}
lwsi_set_state(wsi, LRS_BODY);
lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
__func__, wsi, wsi->wsistate);
wsi->http.rx_content_remain =
wsi->http.rx_content_length;
/*
* At this point we have transitioned from deferred
* action to expecting BODY on the stream wsi, if it's
* in a bundle like h2. So if the stream wsi has its
* own buflist, we need to deal with that first.
*/
while (1) {
ebuf.len = (int)lws_buflist_next_segment_len(
lwsl_notice("%s: consuming %d\n", __func__,
(int)ebuf.len);
m = lws_read_h1(wsi, (uint8_t *)ebuf.token,
ebuf.len);
if (m < 0)
return -1;
if (lws_buflist_aware_consume(wsi, &ebuf, m, 1))
return -1;
return 0;
bail_nuke_ah:
transaction_result_n:
lws_return_http_status(wsi, n, NULL);
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
int
lws_confirm_host_header(struct lws *wsi)
{
struct lws_tokenize ts;
lws_tokenize_elem e;
char buf[128];
int port = 80;
/*
* this vhost wants us to validate what the
* client sent against our vhost name
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
lwsl_info("%s: missing host on upgrade\n", __func__);
return 1;
}
#if defined(LWS_WITH_TLS)
if (wsi->tls.ssl)
port = 443;
#endif
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
if (ts.len <= 0) {
lwsl_info("%s: missing or oversize host header\n", __func__);
return 1;
}
if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
goto bad_format;
if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
buf[(ts.token - buf) + ts.token_len] = '\0';
lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
__func__, ts.token, wsi->vhost->name);
return 1;
}
e = lws_tokenize(&ts);
if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
goto bad_format;
else
port = atoi(ts.token);
} else
if (e != LWS_TOKZE_ENDED)
goto bad_format;
if (wsi->vhost->listen_port != port) {
lwsl_info("%s: host port %d mismatches vhost port %d\n",
__func__, port, wsi->vhost->listen_port);
return 1;
}
lwsl_debug("%s: host header OK\n", __func__);
return 0;
bad_format:
lwsl_info("%s: bad host header format\n", __func__);
return 1;
}
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
struct lws_context *context = lws_get_context(wsi);
#if defined(LWS_WITH_HTTP2)
char tbuf[128], *p;
#endif
if (len >= 10000000) {
lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
assert(0);
}
Andy Green
committed
if (!wsi->http.ah) {
lwsl_err("%s: assert: NULL ah\n", __func__);
assert(0);
}
lwsl_err("%s: bad wsi role 0x%x\n", __func__,
lwsi_role(wsi));
i = (int)len;
m = lws_parse(wsi, *buf, &i);
lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
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->
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;
lws_role_transition(wsi, 0, LRS_ESTABLISHED,
&role_ops_raw_skt);
lws_header_table_detach(wsi, 1);
LWS_CALLBACK_RAW_RX,
wsi->user_space, obuf, olen))
return 1;
return 0;
}
lwsl_info("lws_parse failed\n");
goto bail_nuke_ah;
}
Andy Green
committed
if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
continue;
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
if (wsi->vhost->listen_port &&
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)
lws_vhost_bind_wsi(vhost, wsi);
} else
lwsl_info("no host\n");
wsi->vhost->conn_stats.h1_trans++;
if (!wsi->conn_stat_done) {
wsi->vhost->conn_stats.h1_conn++;
wsi->conn_stat_done = 1;
}
/* 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) {
#ifdef LWS_WITH_ACCESS_LOG
ua[sizeof(ua) - 1] = '\0';
while (rej) {
if (!strstr(ua, rej->name)) {
rej = rej->next;
continue;
}
msg = strchr(rej->value, ' ');
if (msg)
msg++;
lws_return_http_status(wsi,
atoi(rej->value), msg);
#ifdef LWS_WITH_ACCESS_LOG
meth = lws_http_get_uri_and_method(wsi,
&uri_ptr, &uri_len);
if (meth >= 0)
lws_prepare_access_log_info(wsi,
wsi->vhost->conn_stats.rejected++;
/*
* We don't want anything from
* this rejected guy. Follow
* the close flow, not the
* transaction complete flow.
*/
goto bail_nuke_ah;
if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
lwsl_info("Changing to RAW mode\n");
m = 0;
goto raw_transition;
lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
const char *up = lws_hdr_simple_ptr(wsi,
WSI_TOKEN_UPGRADE);
if (strcasecmp(up, "websocket") &&
strcasecmp(up, "h2c")) {
lwsl_info("Unknown upgrade '%s'\n", up);
if (lws_return_http_status(wsi,
HTTP_STATUS_FORBIDDEN, NULL) ||
lws_http_transaction_completed(wsi))
goto bail_nuke_ah;
}
n = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
wsi->user_space, (char *)up, 0);
/* just hang up? */
if (n < 0)
goto bail_nuke_ah;
/* callback returned headers already, do t_c? */
if (n > 0) {
if (lws_http_transaction_completed(wsi))
goto bail_nuke_ah;
/* continue on */
return 0;
}
/* callback said 0, it was allowed */
if (wsi->vhost->options &
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
lws_confirm_host_header(wsi))
goto bail_nuke_ah;
if (!strcasecmp(up, "websocket")) {
lwsl_info("Upgrade to ws\n");
goto upgrade_ws;
goto upgrade_h2c;
}
#endif
}
/* no upgrade ack... he remained as HTTP */
lwsi_set_state(wsi, LRS_ESTABLISHED);
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
lws_http_compression_validate(wsi);
#endif
Andy Green
committed
(void *)wsi->http.ah);
upgrade_h2c:
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
lwsl_info("missing http2_settings\n");
lwsl_info("h2c upgrade...\n");
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
/* convert the peer's HTTP-Settings */
n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
if (n < 0) {
lwsl_parser("HTTP2_SETTINGS too long\n");
return 1;
}
/* adopt the header info */
if (!wsi->h2.h2n) {
wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Upgrade: h2c\x0d\x0a\x0d\x0a");
m = (int)strlen(tbuf);
n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
if (n != m) {
lwsl_debug("http2 switch: ERROR writing to socket\n");
return 1;
}
lwsi_set_state(wsi, LRS_H2_AWAIT_PREFACE);
goto bail_nuke_ah;
} /* while all chars are handled */
return 0;
bail_nuke_ah:
/* drop the header info */
return 1;
}
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws *wsi)
if (lws_has_buffered_out(wsi)
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
|| wsi->http.comp_ctx.buflist_comp ||
wsi->http.comp_ctx.may_have_more
#endif
) {
/*
* ...so he tried to send something large as the http reply,
* it went as a partial, but he immediately said the
* transaction was completed.
*
* Defer the transaction completed until the last part of the
* partial is sent.
*/
lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi);
wsi->http.deferred_transaction_completed = 1;
return 0;
}
lwsl_info("%s: wsi %p\n", __func__, wsi);
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
lws_http_compression_destroy(wsi);
#endif
if (!wsi->hdr_parsing_completed) {
char peer[64];
lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
peer[sizeof(peer) - 1] = '\0';
lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
__func__, peer);
return 0;
}
/* if we can't go back to accept new headers, drop the connection */
if (wsi->seen_zero_length_recv)
return 1;
lwsl_info("%s: %p: close connection\n", __func__, wsi);
if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
/*
* otherwise set ourselves up ready to go again, but because we have no
* idea about the wsi writability, we make put it in a holding state
* until we can verify POLLOUT. The part of this that confirms POLLOUT
* with no partials is in lws_server_socket_service() below.
*/
lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
wsi, wsi->wsistate);
lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
wsi->http.tx_content_length = 0;
wsi->http.tx_content_remain = 0;
wsi->hdr_parsing_completed = 0;
wsi->http.access_log.sent = 0;
if (wsi->vhost->keepalive_timeout)
n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
/*
* 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