X-Git-Url: http://git.openwrt.org//?a=blobdiff_plain;f=uclient-http.c;h=ac9d50f4b2ad914590bf6517f4fe90b181f7425a;hb=a4e49b4163b28b2d12e718e66c4fcba87acef33e;hp=eb69702fb287470d9abbe2b6d4a6679da48dea73;hpb=19c7fb01ad48b884672b7d8d252d3c290b9e883c;p=project%2Fuclient.git diff --git a/uclient-http.c b/uclient-http.c index eb69702..ac9d50f 100644 --- a/uclient-http.c +++ b/uclient-http.c @@ -15,10 +15,12 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include #include +#include #include #include @@ -73,6 +75,7 @@ struct uclient_http { struct ustream_ssl ussl; struct uloop_timeout disconnect_t; + unsigned int seq; bool ssl_require_validation; bool ssl; @@ -88,6 +91,8 @@ struct uclient_http { long read_chunked; long content_length; + int usock_flags; + uint32_t nc; struct blob_buf headers; @@ -106,6 +111,8 @@ static const char * const uclient_http_prefix[] = { [__PREFIX_MAX] = NULL }; +static int uclient_http_connect(struct uclient *cl); + static int uclient_do_connect(struct uclient_http *uh, const char *port) { socklen_t sl; @@ -114,18 +121,20 @@ static int uclient_do_connect(struct uclient_http *uh, const char *port) if (uh->uc.url->port) port = uh->uc.url->port; - fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port); + memset(&uh->uc.remote_addr, 0, sizeof(uh->uc.remote_addr)); + + fd = usock_inet_timeout(USOCK_TCP | USOCK_NONBLOCK | uh->usock_flags, + uh->uc.url->host, port, &uh->uc.remote_addr, + uh->uc.timeout_msecs); if (fd < 0) return -1; + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); ustream_fd_init(&uh->ufd, fd); - memset(&uh->uc.local_addr, 0, sizeof(uh->uc.local_addr)); - memset(&uh->uc.remote_addr, 0, sizeof(uh->uc.remote_addr)); - sl = sizeof(uh->uc.local_addr); + memset(&uh->uc.local_addr, 0, sl); getsockname(fd, &uh->uc.local_addr.sa, &sl); - getpeername(fd, &uh->uc.remote_addr.sa, &sl); return 0; } @@ -188,6 +197,9 @@ static void uclient_notify_eof(struct uclient_http *uh) return; } + if (uh->content_length < 0 && uh->read_chunked >= 0) + uh->uc.data_eof = true; + uclient_backend_set_eof(&uh->uc); if (uh->connection_close) @@ -196,6 +208,7 @@ static void uclient_notify_eof(struct uclient_http *uh) static void uclient_http_reset_state(struct uclient_http *uh) { + uh->seq++; uclient_backend_reset_state(&uh->uc); uh->read_chunked = -1; uh->content_length = -1; @@ -210,6 +223,7 @@ static void uclient_http_reset_state(struct uclient_http *uh) static void uclient_http_init_request(struct uclient_http *uh) { + uh->seq++; uclient_http_reset_state(uh); blob_buf_init(&uh->meta, 0); } @@ -272,6 +286,18 @@ static void uclient_http_process_headers(struct uclient_http *uh) uh->auth_type = uclient_http_update_auth_type(uh); } +static bool uclient_request_supports_body(enum request_type req_type) +{ + switch (req_type) { + case REQ_POST: + case REQ_PUT: + case REQ_DELETE: + return true; + default: + return false; + } +} + static void uclient_http_add_auth_basic(struct uclient_http *uh) { @@ -353,11 +379,14 @@ get_cnonce(char *dest) { uint32_t val = 0; FILE *f; + size_t n; f = fopen("/dev/urandom", "r"); if (f) { - fread(&val, sizeof(val), 1, f); + n = fread(&val, sizeof(val), 1, f); fclose(f); + if (n != 1) + return; } bin_to_hex(dest, &val, sizeof(val)); @@ -538,16 +567,21 @@ uclient_http_send_headers(struct uclient_http *uh) if (uh->state >= HTTP_STATE_HEADERS_SENT) return; + if (uh->uc.proxy_url) + url = uh->uc.proxy_url; + ustream_printf(uh->us, "%s %s HTTP/1.1\r\n" - "Host: %s\r\n", + "Host: %s%s%s\r\n", request_types[req_type], - url->location, url->host); + url->location, url->host, + url->port ? ":" : "", + url->port ? url->port : ""); blobmsg_for_each_attr(cur, uh->headers.head, rem) - ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur)); + ustream_printf(uh->us, "%s: %s\r\n", blobmsg_name(cur), (char *) blobmsg_data(cur)); - if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT) + if (uclient_request_supports_body(uh->req_type)) ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n"); uclient_http_add_auth_header(uh); @@ -560,6 +594,7 @@ uclient_http_send_headers(struct uclient_http *uh) static void uclient_http_headers_complete(struct uclient_http *uh) { enum auth_type auth_type = uh->auth_type; + int seq = uh->uc.seq; uh->state = HTTP_STATE_RECV_DATA; uh->uc.meta = uh->meta.head; @@ -567,7 +602,7 @@ static void uclient_http_headers_complete(struct uclient_http *uh) if (auth_type == AUTH_TYPE_UNKNOWN && uh->uc.status_code == 401 && (uh->req_type == REQ_HEAD || uh->req_type == REQ_GET)) { - uclient_http_init_request(uh); + uclient_http_connect(&uh->uc); uclient_http_send_headers(uh); uh->state = HTTP_STATE_REQUEST_DONE; return; @@ -576,7 +611,7 @@ static void uclient_http_headers_complete(struct uclient_http *uh) if (uh->uc.cb->header_done) uh->uc.cb->header_done(&uh->uc); - if (uh->eof) + if (uh->eof || seq != uh->uc.seq) return; if (uh->req_type == REQ_HEAD || uh->uc.status_code == 204) { @@ -641,6 +676,7 @@ error: static void __uclient_notify_read(struct uclient_http *uh) { struct uclient *uc = &uh->uc; + unsigned int seq = uh->seq; char *data; int len; @@ -652,29 +688,37 @@ static void __uclient_notify_read(struct uclient_http *uh) return; if (uh->state < HTTP_STATE_RECV_DATA) { - char *sep; + char *sep, *next; int cur_len; do { - sep = strstr(data, "\r\n"); + sep = strchr(data, '\n'); if (!sep) break; + next = sep + 1; + if (sep > data && sep[-1] == '\r') + sep--; + /* Check for multi-line HTTP headers */ if (sep > data) { - if (!sep[2]) + if (!*next) return; - if (isspace(sep[2]) && sep[2] != '\r') { + if (isspace(*next) && *next != '\r' && *next != '\n') { sep[0] = ' '; - sep[1] = ' '; + if (sep + 1 < next) + sep[1] = ' '; continue; } } *sep = 0; - cur_len = sep + 2 - data; + cur_len = next - data; uclient_parse_http_line(uh, data); + if (seq != uh->seq) + return; + ustream_consume(uh->us, cur_len); len -= cur_len; @@ -726,6 +770,10 @@ static void uclient_notify_state(struct ustream *us) { struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream); + if (uh->ufd.stream.write_error) { + uclient_http_error(uh, UCLIENT_ERROR_CONNECT); + return; + } uclient_notify_eof(uh); } @@ -820,6 +868,7 @@ static int uclient_setup_https(struct uclient_http *uh) uh->ussl.notify_error = uclient_ssl_notify_error; uh->ussl.notify_verify_error = uclient_ssl_notify_verify_error; uh->ussl.notify_connected = uclient_ssl_notify_connected; + uh->ussl.server_name = uh->uc.url->host; uh->ssl_ops->init(&uh->ussl, &uh->ufd.stream, uh->ssl_ctx, false); uh->ssl_ops->set_peer_cn(&uh->ussl, uh->uc.url->host); @@ -831,7 +880,7 @@ static int uclient_http_connect(struct uclient *cl) struct uclient_http *uh = container_of(cl, struct uclient_http, uc); int ret; - if (!cl->eof || uh->disconnect) + if (!cl->eof || uh->disconnect || uh->connection_close) uclient_http_disconnect(uh); uclient_http_init_request(uh); @@ -960,7 +1009,7 @@ uclient_http_request_done(struct uclient *cl) return -1; uclient_http_send_headers(uh); - if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT) + if (uclient_request_supports_body(uh->req_type)) ustream_printf(uh->us, "0\r\n\r\n"); uh->state = HTTP_STATE_REQUEST_DONE; @@ -1044,7 +1093,7 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len) return len; } -bool uclient_http_redirect(struct uclient *cl) +int uclient_http_redirect(struct uclient *cl) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); struct blobmsg_policy location = { @@ -1070,13 +1119,15 @@ bool uclient_http_redirect(struct uclient *cl) if (!tb) return false; - url = uclient_get_url(blobmsg_data(tb), url->auth); + url = uclient_get_url_location(url, blobmsg_data(tb)); if (!url) return false; free(cl->url); cl->url = url; - uclient_http_connect(cl); + if (uclient_http_connect(cl)) + return -1; + uclient_http_request_done(cl); return true; @@ -1100,6 +1151,28 @@ int uclient_http_set_ssl_ctx(struct uclient *cl, const struct ustream_ssl_ops *o return 0; } +int uclient_http_set_address_family(struct uclient *cl, int af) +{ + struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + + if (cl->backend != &uclient_backend_http) + return -1; + + switch (af) { + case AF_INET: + uh->usock_flags = USOCK_IPV4ONLY; + break; + case AF_INET6: + uh->usock_flags = USOCK_IPV6ONLY; + break; + default: + uh->usock_flags = 0; + break; + } + + return 0; +} + const struct uclient_backend uclient_backend_http = { .prefix = uclient_http_prefix, @@ -1108,6 +1181,7 @@ const struct uclient_backend uclient_backend_http = { .connect = uclient_http_connect, .disconnect = uclient_http_request_disconnect, .update_url = uclient_http_free_url_state, + .update_proxy_url = uclient_http_free_url_state, .read = uclient_http_read, .write = uclient_http_send_data,