X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuclient.git;a=blobdiff_plain;f=uclient-http.c;h=e2b6c79dc9e4609d1c03f6e876e8027e9159042c;hp=bf0b05170837f6de05ab059d741c13e7a3e462ab;hb=32f153a8dba2ef26ec14dd2b4dff917fbda90f83;hpb=c280d54e1bc79de4424fabc3fad011cc15587b81 diff --git a/uclient-http.c b/uclient-http.c index bf0b051..e2b6c79 100644 --- a/uclient-http.c +++ b/uclient-http.c @@ -44,10 +44,11 @@ struct uclient_http { struct ustream_ssl ussl; bool ssl; + bool eof; enum request_type req_type; enum http_state state; - unsigned int send_len; + long read_chunked; struct blob_buf headers; struct blob_buf meta; @@ -84,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; @@ -106,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; @@ -249,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; @@ -361,8 +388,9 @@ uclient_http_send_headers(struct uclient_http *uh) return; ustream_printf(uh->us, - "%s /%s HTTP/1.0\r\n" - "Host: %s\r\n", + "%s /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n", request_types[uh->req_type], url->location, url->host); @@ -381,31 +409,13 @@ uclient_http_send_headers(struct uclient_http *uh) ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf); } - if (uh->send_len > 0) - ustream_printf(uh->us, "Content-Length: %d", uh->send_len); + if (uh->req_type == REQ_POST) + ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n"); ustream_printf(uh->us, "\r\n"); } static int -uclient_http_set_write_len(struct uclient *cl, unsigned int len) -{ - struct uclient_http *uh = container_of(cl, struct uclient_http, uc); - - if (uh->state >= HTTP_STATE_HEADERS_SENT) - return -1; - - if (uh->req_type == REQ_GET || uh->req_type == REQ_HEAD) { - fprintf(stderr, "Sending data is not supported for %s requests\n", - request_types[uh->req_type]); - return -1; - } - - uh->send_len = len; - return 0; -} - -static int uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); @@ -415,12 +425,9 @@ uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len) uclient_http_send_headers(uh); - if (len > uh->send_len) { - fprintf(stderr, "%s: ignoring %d extra data bytes\n", __func__, uh->send_len - len); - len = uh->send_len; - } - + ustream_printf(uh->us, "%X\r\n", len); ustream_write(uh->us, buf, len, false); + ustream_printf(uh->us, "\r\n"); return len; } @@ -433,9 +440,6 @@ uclient_http_request_done(struct uclient *cl) if (uh->state >= HTTP_STATE_REQUEST_DONE) return -1; - if (uh->send_len > 0) - fprintf(stderr, "%s: missing %d POST data bytes\n", __func__, uh->send_len); - uclient_http_send_headers(uh); uh->state = HTTP_STATE_REQUEST_DONE; @@ -446,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; @@ -476,6 +518,4 @@ const struct uclient_backend uclient_backend_http __hidden = { .read = uclient_http_read, .write = uclient_http_send_data, .request = uclient_http_request_done, - - .set_write_len = uclient_http_set_write_len, };