1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

POST: handle http/1 pipelined after body

Re-use wsi->preamble_rx to also hold leftover rx after dealing with POST
body.  Ensure ah->rx is always big enough to cope with what may have
been read into the pt->serv_buf.

Update the check for forced needed to also accept non-NULL wsi->preamble
as well as ah->rxpos != ah->rxlen as indication forced needed.

Disable autoservice on ah reset during transaction completed... it may
close the wsi underneath us when it sees and processes the pending
wsi->preamble_rx recursively otherwise.
This commit is contained in:
Andy Green 2018-01-14 10:18:32 +08:00
parent 0e24969f53
commit 93bc409ca1
5 changed files with 89 additions and 24 deletions

View file

@ -1117,6 +1117,14 @@ lws_create_context(struct lws_context_creation_info *info)
info->max_http_header_data2;
else
context->max_http_header_data = LWS_DEF_HEADER_LEN;
/*
* HTTP/1 piplining after POST gets read in pt_serv_buf_size but
* may need stashing in ah->rx, so ensure it's always big enough
*/
if ((int)context->max_http_header_data < (int)context->pt_serv_buf_size)
context->max_http_header_data = context->pt_serv_buf_size;
if (info->max_http_header_pool)
context->max_http_header_pool = info->max_http_header_pool;
else

View file

@ -152,6 +152,8 @@ lws_header_table_reset(struct lws *wsi, int autoservice)
memcpy(ah->rx, wsi->preamble_rx, wsi->preamble_rx_len);
ah->rxlen = wsi->preamble_rx_len;
lws_free_set_NULL(wsi->preamble_rx);
wsi->preamble_rx_len = 0;
ah->rxpos = 0;
if (autoservice) {
lwsl_debug("%s: service on readbuf ah\n", __func__);
@ -345,8 +347,10 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
(void *)wsi, (void *)ah, wsi->tsi,
pt->ah_count_in_use);
if (wsi->preamble_rx)
if (wsi->preamble_rx) {
lws_free_set_NULL(wsi->preamble_rx);
wsi->preamble_rx_len = 0;
}
/* may not be detached while he still has unprocessed rx */
if (!lws_header_table_is_in_detachable_state(wsi)) {

View file

@ -1842,7 +1842,8 @@ lws_http_transaction_completed(struct lws *wsi)
int n = NO_PENDING_TIMEOUT;
lwsl_info("%s: wsi %p\n", __func__, wsi);
if (wsi->ah)
lwsl_info("ah attached, pos %d, len %d\n", wsi->ah->rxpos, wsi->ah->rxlen);
lws_access_log(wsi);
if (!wsi->hdr_parsing_completed) {
@ -1850,7 +1851,6 @@ lws_http_transaction_completed(struct lws *wsi)
return 0;
}
lwsl_debug("%s: wsi %p\n", __func__, wsi);
/* if we can't go back to accept new headers, drop the connection */
if (wsi->http2_substream)
return 0;
@ -1917,7 +1917,7 @@ lws_http_transaction_completed(struct lws *wsi)
}
#endif
} else {
lws_header_table_reset(wsi, 1);
lws_header_table_reset(wsi, 0);
/*
* If we kept the ah, we should restrict the amount
* of time we are willing to keep it. Otherwise it
@ -1930,7 +1930,11 @@ lws_http_transaction_completed(struct lws *wsi)
/* If we're (re)starting on headers, need other implied init */
if (wsi->ah)
wsi->ah->ues = URIES_IDLE;
}
} else
if (wsi->preamble_rx)
if (lws_header_table_attach(wsi, 0))
lwsl_debug("acquired ah\n");
lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
lws_callback_on_writable(wsi);
@ -2313,10 +2317,19 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
}
ah = wsi->ah;
assert(ah->rxpos <= ah->rxlen);
/* if nothing in ah rx buffer, get some fresh rx */
if (ah->rxpos == ah->rxlen) {
ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
if (wsi->preamble_rx) {
memcpy(ah->rx, wsi->preamble_rx, wsi->preamble_rx_len);
lws_free_set_NULL(wsi->preamble_rx);
ah->rxlen = wsi->preamble_rx_len;
wsi->preamble_rx_len = 0;
} else {
ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
sizeof(ah->rx));
}
ah->rxpos = 0;
switch (ah->rxlen) {
case 0:
@ -2377,23 +2390,36 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
break;
}
len = lws_ssl_capable_read(wsi, pt->serv_buf,
context->pt_serv_buf_size);
lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
switch (len) {
case 0:
lwsl_info("%s: read 0 len b\n", __func__);
if (wsi->preamble_rx && wsi->preamble_rx_len) {
memcpy(pt->serv_buf, wsi->preamble_rx, wsi->preamble_rx_len);
lws_free_set_NULL(wsi->preamble_rx);
len = wsi->preamble_rx_len;
lwsl_debug("bringing %d out of stash\n", wsi->preamble_rx_len);
wsi->preamble_rx_len = 0;
} else {
/* fallthru */
case LWS_SSL_CAPABLE_ERROR:
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto try_pollout;
/*
* ... in the case of pipelined HTTP, this may be
* POST data followed by next headers...
*/
len = lws_ssl_capable_read(wsi, pt->serv_buf,
context->pt_serv_buf_size);
lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
switch (len) {
case 0:
lwsl_info("%s: read 0 len b\n", __func__);
/* fallthru */
case LWS_SSL_CAPABLE_ERROR:
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto try_pollout;
}
if (len < 0) /* coverity */
goto fail;
}
if (len < 0) /* coverity */
goto fail;
if (wsi->mode == LWSCM_RAW) {
n = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RAW_RX,
@ -2412,10 +2438,31 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
/*
* this may want to send
* (via HTTP callback for example)
*
* returns number of bytes used
*/
n = lws_read(wsi, pt->serv_buf, len);
if (n < 0) /* we closed wsi */
return 1;
if (n != len) {
if (wsi->preamble_rx) {
lwsl_err("DISCARDING %d (ah %p)\n", len - n, wsi->ah);
goto fail;
}
assert(n < len);
wsi->preamble_rx = lws_malloc(len - n, "preamble_rx");
if (!wsi->preamble_rx) {
lwsl_err("OOM\n");
goto fail;
}
memcpy(wsi->preamble_rx, pt->serv_buf + n, len - n);
wsi->preamble_rx_len = (int)len - n;
lwsl_debug("stashed %d\n", (int)wsi->preamble_rx_len);
}
/*
* he may have used up the
* writability above, if we will defer POLLOUT
@ -2452,6 +2499,10 @@ try_pollout:
if (wsi->state == LWSS_HTTP_DEFERRING_ACTION) {
lwsl_debug("%s: LWSS_HTTP_DEFERRING_ACTION now writable\n",
__func__);
if (wsi->ah)
lwsl_debug(" existing ah rxpos %d / rxlen %d\n",
wsi->ah->rxpos, wsi->ah->rxlen);
wsi->state = LWSS_HTTP;
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");

View file

@ -770,10 +770,11 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
/* 3) if any ah has pending rx, do not wait in poll */
ah = pt->ah_list;
while (ah) {
if (ah->rxpos != ah->rxlen) {
if (ah->rxpos != ah->rxlen || (ah->wsi && ah->wsi->preamble_rx)) {
if (!ah->wsi) {
assert(0);
}
// lwsl_debug("ah pending force\n");
return 0;
}
ah = ah->next;
@ -850,7 +851,8 @@ lws_service_flag_pending(struct lws_context *context, int tsi)
*/
ah = pt->ah_list;
while (ah) {
if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) {
if ((ah->rxpos != ah->rxlen &&
!ah->wsi->hdr_parsing_completed) || ah->wsi->preamble_rx) {
pt->fds[ah->wsi->position_in_fds_table].revents |=
pt->fds[ah->wsi->position_in_fds_table].events &
LWS_POLLIN;

View file

@ -107,7 +107,7 @@ function check {
rm -rf $LOG
killall libwebsockets-test-server 2>/dev/null
libwebsockets-test-server -d127 2>> $LOG &
libwebsockets-test-server -d1023 2>> $LOG &
CPID=$!
echo "Started server on PID $CPID"