struct ustream_ssl ussl;
bool ssl;
+ bool eof;
+ bool connection_close;
enum request_type req_type;
enum http_state state;
+ long read_chunked;
+
struct blob_buf headers;
struct blob_buf meta;
};
return 0;
}
+static void uclient_http_disconnect(struct uclient *cl)
+{
+ struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
+
+ if (!uh->us)
+ return;
+
+ if (uh->ssl)
+ ustream_free(&uh->ussl.stream);
+ ustream_free(&uh->ufd.stream);
+ close(uh->ufd.fd.fd);
+ uh->us = NULL;
+}
+
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);
+
+ if (uh->connection_close)
+ uclient_http_disconnect(&uh->uc);
+}
+
+static void uclient_http_process_headers(struct uclient_http *uh)
+{
+ enum {
+ HTTP_HDR_TRANSFER_ENCODING,
+ HTTP_HDR_CONNECTION,
+ __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"),
+ [HTTP_HDR_CONNECTION] = hdr("connection"),
+#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;
+
+ cur = tb[HTTP_HDR_CONNECTION];
+ if (cur && strstr(blobmsg_data(cur), "close"))
+ uh->connection_close = true;
}
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);
+
+ if (uh->req_type == REQ_HEAD) {
+ uh->eof = true;
+ uclient_notify_eof(uh);
+ }
+
return;
}
return 0;
}
-static void uclient_http_disconnect(struct uclient_http *uh)
+static void uclient_http_reset_state(struct uclient_http *uh)
{
uclient_backend_reset_state(&uh->uc);
-
- if (!uh->us)
- return;
-
- if (uh->ssl)
- ustream_free(&uh->ussl.stream);
- ustream_free(&uh->ufd.stream);
- close(uh->ufd.fd.fd);
- uh->us = NULL;
+ uh->read_chunked = -1;
+ uh->eof = false;
+ uh->connection_close = false;
+ uh->state = HTTP_STATE_INIT;
}
static int uclient_http_connect(struct uclient *cl)
{
struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
- uclient_http_disconnect(uh);
+ uclient_http_reset_state(uh);
blob_buf_init(&uh->meta, 0);
+ if (uh->us)
+ return 0;
+
uh->ssl = cl->url->prefix == PREFIX_HTTPS;
- uh->state = HTTP_STATE_INIT;
if (uh->ssl)
return uclient_setup_https(uh);
{
struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
- uclient_http_disconnect(uh);
+ uclient_http_disconnect(cl);
blob_buf_free(&uh->headers);
blob_buf_free(&uh->meta);
free(uh);
return 0;
}
-#define ustream_printf(us, ...) do { \
- fprintf(stderr, "send: " __VA_ARGS__); \
- ustream_printf(us, __VA_ARGS__); \
-} while (0)
-
-
static void
uclient_http_send_headers(struct uclient_http *uh)
{
ustream_printf(uh->us,
"%s /%s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Connection: close\r\n",
+ "Host: %s\r\n",
request_types[uh->req_type],
url->location, url->host);
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)
+ if (uh->state < HTTP_STATE_RECV_DATA || !uh->us)
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;
.alloc = uclient_http_alloc,
.free = uclient_http_free,
.connect = uclient_http_connect,
+ .update_url = uclient_http_disconnect,
.read = uclient_http_read,
.write = uclient_http_send_data,