client chunked transfer encoding

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2016-03-13 16:44:19 +08:00 committed by Andy Green
parent c3c2d6d953
commit 5c8906e931
5 changed files with 128 additions and 12 deletions

View file

@ -79,7 +79,7 @@ just deferred until an ah becomes available.
8) The test client pays attention to if you give it an http:/ or https://
protocol string to its argument in URL format. If so, it stays in http[s]
client mode and doesn't upgrade to ws[s], allowing you to do generic http client
operations.
operations. Receiving transfer-encoding: chunked is supported.
9) The test server has a new URI path http://localhost:7681/proxytest
If you visit here, a client connection to http://example.com:80 is spawned,

View file

@ -628,6 +628,17 @@ lws_client_interpret_server_handshake(struct lws *wsi)
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,
@ -635,8 +646,9 @@ lws_client_interpret_server_handshake(struct lws *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

View file

@ -496,7 +496,7 @@ lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
return 0;
}
static signed char char_to_hex(const char c)
signed char char_to_hex(const char c)
{
if (c >= '0' && c <= '9')
return c - '0';

View file

@ -1054,7 +1054,18 @@ struct lws_cgi {
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 {
@ -1121,6 +1132,9 @@ 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;
@ -1129,6 +1143,7 @@ struct lws {
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;
@ -1161,6 +1176,9 @@ struct lws {
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;

View file

@ -760,24 +760,110 @@ read:
*/
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))
@ -821,7 +907,7 @@ drain:
} 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;