goto bail2;
}
+ /* he may choose to send us stuff in chunked transfer-coding */
+ wsi->chunked = 0;
+ wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
+ wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
+ WSI_TOKEN_HTTP_TRANSFER_ENCODING),
+ "chunked");
+ /* first thing is hex, after payload there is crlf */
+ wsi->chunk_parser = ELCP_HEX;
+ }
+
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
wsi->u.http.content_length =
atoi(lws_hdr_simple_ptr(wsi,
lwsl_notice("%s: incoming content length %d\n", __func__,
wsi->u.http.content_length);
wsi->u.http.content_remain = wsi->u.http.content_length;
- } else /* can't do 1.1 without a content length */
- wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
+ } else /* can't do 1.1 without a content length or chunked */
+ if (!wsi->chunked)
+ wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
/*
* we seem to be good to go, give client last chance to check
unsigned int being_closed:1;
};
+#endif
+
+signed char char_to_hex(const char c);
+#ifndef LWS_NO_CLIENT
+enum lws_chunk_parser {
+ ELCP_HEX,
+ ELCP_CR,
+ ELCP_CONTENT,
+ ELCP_POST_CR,
+ ELCP_POST_LF,
+};
#endif
struct lws {
unsigned int trunc_alloc_len; /* size of malloc */
unsigned int trunc_offset; /* where we are in terms of spilling */
unsigned int trunc_len; /* how much is buffered */
+#ifndef LWS_NO_CLIENT
+ int chunk_remaining;
+#endif
unsigned int hdr_parsing_completed:1;
unsigned int user_space_externally_allocated:1;
unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
#ifndef LWS_NO_CLIENT
unsigned int do_ws:1; /* whether we are doing http or ws flow */
+ unsigned int chunked:1; /* if the clientside connection is chunked */
#endif
#ifndef LWS_NO_EXTENSIONS
unsigned int extension_data_pending:1;
char cgi_channel; /* which of stdin/out/err */
char hdr_state;
#endif
+#ifndef LWS_NO_CLIENT
+ char chunk_parser; /* enum lws_chunk_parser */
+#endif
};
LWS_EXTERN int log_level;
*/
drain:
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
- lwsl_notice("%s: calling LWS_CALLBACK_RECEIVE_CLIENT_HTTP, "
- "rem %d len %d\n", __func__,
- wsi->u.http.content_remain, eff_buf.token_len);
- if ((int)wsi->u.http.content_remain < eff_buf.token_len)
+ /*
+ * server may insist on transfer-encoding: chunked,
+ * so http client must deal with it
+ */
+spin_chunks:
+ while (wsi->chunked &&
+ (wsi->chunk_parser != ELCP_CONTENT) &&
+ eff_buf.token_len) {
+ switch (wsi->chunk_parser) {
+ case ELCP_HEX:
+ if (eff_buf.token[0] == '\x0d') {
+ wsi->chunk_parser = ELCP_CR;
+ break;
+ }
+ n = char_to_hex(eff_buf.token[0]);
+ if (n < 0)
+ goto close_and_handled;
+ wsi->chunk_remaining <<= 4;
+ wsi->chunk_remaining |= n;
+ break;
+ case ELCP_CR:
+ if (eff_buf.token[0] != '\x0a')
+ goto close_and_handled;
+ wsi->chunk_parser = ELCP_CONTENT;
+ lwsl_info("chunk %d\n",
+ wsi->chunk_remaining);
+ if (wsi->chunk_remaining)
+ break;
+ lwsl_info("final chunk\n");
+ if (user_callback_handle_rxflow(
+ wsi->protocol->callback,
+ wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
+ wsi->user_space, NULL, 0))
+ goto close_and_handled;
+ if (lws_http_transaction_completed(wsi))
+ goto close_and_handled;
+ n = 0;
+ goto handled;
+
+ case ELCP_CONTENT:
+ break;
+
+ case ELCP_POST_CR:
+ if (eff_buf.token[0] == '\x0d') {
+ wsi->chunk_parser = ELCP_POST_LF;
+ break;
+ }
+ goto close_and_handled;
+
+ case ELCP_POST_LF:
+ if (eff_buf.token[0] == '\x0a') {
+ wsi->chunk_parser = ELCP_HEX;
+ wsi->chunk_remaining = 0;
+ break;
+ }
+ goto close_and_handled;
+ }
+ eff_buf.token++;
+ eff_buf.token_len--;
+ }
+
+ if (wsi->chunked && !wsi->chunk_remaining) {
+ n = 0;
+ goto handled;
+ }
+
+ if (wsi->u.http.content_remain &&
+ wsi->u.http.content_remain < eff_buf.token_len)
n = wsi->u.http.content_remain;
else
n = eff_buf.token_len;
+
+ if (wsi->chunked && wsi->chunk_remaining &&
+ wsi->chunk_remaining < n)
+ n = wsi->chunk_remaining;
+
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
wsi->user_space, (void *)eff_buf.token,
- eff_buf.token_len))
+ n))
goto close_and_handled;
+
+ if (wsi->chunked && wsi->chunk_remaining) {
+ eff_buf.token += n;
+ wsi->chunk_remaining -= n;
+ eff_buf.token_len -= n;
+ }
+
+ if (wsi->chunked && !wsi->chunk_remaining)
+ wsi->chunk_parser = ELCP_POST_CR;
+
+ if (wsi->chunked && eff_buf.token_len) {
+ goto spin_chunks;
+ }
+
+ if (wsi->chunked) {
+ n = 0;
+ goto handled;
+ }
+
wsi->u.http.content_remain -= n;
if (wsi->u.http.content_remain)
goto handled;
- lwsl_notice("%s: client http receved all content\n",
- __func__);
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
wsi->user_space, NULL, 0))
} while (more);
if (wsi->u.hdr.ah) {
- lwsl_err("%s: %p: detaching inherited used ah\n",
+ lwsl_info("%s: %p: detaching inherited used ah\n",
__func__, wsi);
/* show we used all the pending rx up */
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;