summaryrefslogtreecommitdiff
path: root/uclient-http.c
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2014-03-21 14:54:19 +0100
committerFelix Fietkau <nbd@openwrt.org>2014-03-21 14:54:19 +0100
commit1ba29352a6bb657d0351f95ef338b01924632a76 (patch)
tree0476ee207e246e9e37319612f879ffd3bc20d335 /uclient-http.c
parent9099aff296c1fdc4ba49908018b46f913fe7d9e7 (diff)
downloaduclient-1ba29352a6bb657d0351f95ef338b01924632a76.tar.gz
add support for parsing chunked data on read
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Diffstat (limited to 'uclient-http.c')
-rw-r--r--uclient-http.c91
1 files changed, 79 insertions, 12 deletions
diff --git a/uclient-http.c b/uclient-http.c
index 8ff2e0a..e2b6c79 100644
--- a/uclient-http.c
+++ b/uclient-http.c
@@ -44,9 +44,12 @@ struct uclient_http {
struct ustream_ssl ussl;
bool ssl;
+ bool eof;
enum request_type req_type;
enum http_state state;
+ long read_chunked;
+
struct blob_buf headers;
struct blob_buf meta;
};
@@ -82,15 +85,38 @@ static void uclient_notify_eof(struct uclient_http *uh)
{
struct ustream *us = uh->us;
- if (!us->eof && !us->write_error)
- return;
+ if (!uh->eof) {
+ if (!us->eof && !us->write_error)
+ return;
- if (ustream_pending_data(us, false))
- return;
+ if (ustream_pending_data(us, false))
+ return;
+ }
uclient_backend_set_eof(&uh->uc);
}
+static void uclient_http_process_headers(struct uclient_http *uh)
+{
+ enum {
+ HTTP_HDR_TRANSFER_ENCODING,
+ __HTTP_HDR_MAX,
+ };
+ static const struct blobmsg_policy hdr_policy[__HTTP_HDR_MAX] = {
+#define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
+ [HTTP_HDR_TRANSFER_ENCODING] = hdr("transfer-encoding"),
+#undef hdr
+ };
+ struct blob_attr *tb[__HTTP_HDR_MAX];
+ struct blob_attr *cur;
+
+ blobmsg_parse(hdr_policy, __HTTP_HDR_MAX, tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
+
+ cur = tb[HTTP_HDR_TRANSFER_ENCODING];
+ if (cur && strstr(blobmsg_data(cur), "chunked"))
+ uh->read_chunked = 0;
+}
+
static void uclient_parse_http_line(struct uclient_http *uh, char *data)
{
char *name;
@@ -104,6 +130,7 @@ static void uclient_parse_http_line(struct uclient_http *uh, char *data)
if (!*data) {
uh->state = HTTP_STATE_RECV_DATA;
uh->uc.meta = uh->meta.head;
+ uclient_http_process_headers(uh);
if (uh->uc.cb->header_done)
uh->uc.cb->header_done(&uh->uc);
return;
@@ -247,6 +274,8 @@ static int uclient_setup_https(struct uclient_http *uh)
static void uclient_http_disconnect(struct uclient_http *uh)
{
uclient_backend_reset_state(&uh->uc);
+ uh->read_chunked = -1;
+ uh->eof = false;
if (!uh->us)
return;
@@ -421,21 +450,59 @@ static int
uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
{
struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
- int data_len;
- char *data;
+ int read_len = 0;
+ char *data, *data_end;
if (uh->state < HTTP_STATE_RECV_DATA)
return 0;
- data = ustream_get_read_buf(uh->us, &data_len);
- if (!data || !data_len)
+ data = ustream_get_read_buf(uh->us, &read_len);
+ if (!data || !read_len)
return 0;
- if (len > data_len)
- len = data_len;
+ data_end = data + read_len;
+ read_len = 0;
+
+ if (uh->read_chunked == 0) {
+ char *sep;
+
+ if (data[0] == '\r' && data[1] == '\n') {
+ data += 2;
+ read_len += 2;
+ }
+
+ sep = strstr(data, "\r\n");
+ if (!sep)
+ return 0;
+
+ *sep = 0;
+ uh->read_chunked = strtoul(data, NULL, 16);
+
+ read_len += sep + 2 - data;
+ data = sep + 2;
+
+ if (!uh->read_chunked)
+ uh->eof = true;
+ }
+
+ if (len > data_end - data)
+ len = data_end - data;
+
+ if (uh->read_chunked >= 0) {
+ if (len > uh->read_chunked)
+ len = uh->read_chunked;
+
+ uh->read_chunked -= len;
+ }
+
+ if (len > 0) {
+ read_len += len;
+ memcpy(buf, data, len);
+ }
+
+ if (read_len > 0)
+ ustream_consume(uh->us, read_len);
- memcpy(buf, data, len);
- ustream_consume(uh->us, len);
uclient_notify_eof(uh);
return len;