X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=client.c;h=dd3f912247a500a58b3f9de0810ce25262444d93;hp=4d8b17b0d1b9279040daefc1a5581e8a047b1661;hb=6384dde85bb0729137c4de3c1c994edc5d7c8091;hpb=0a6fa62baa6d21455485bc6669f4e5f3b6608487;ds=sidebyside diff --git a/client.c b/client.c index 4d8b17b..dd3f912 100644 --- a/client.c +++ b/client.c @@ -142,9 +142,9 @@ static int client_parse_request(struct client *cl, char *data) if (!type || !path || !version) return CLIENT_STATE_DONE; - memset(&cl->request, 0, sizeof(cl->request)); - req->url = path; + blobmsg_add_string(&cl->hdr, "URL", path); + memset(&cl->request, 0, sizeof(cl->request)); h_method = find_idx(http_methods, ARRAY_SIZE(http_methods), type); h_version = find_idx(http_versions, ARRAY_SIZE(http_versions), version); if (h_method < 0 || h_version < 0) { @@ -168,9 +168,8 @@ static bool client_init_cb(struct client *cl, char *buf, int len) *newline = 0; blob_buf_init(&cl->hdr, 0); - blobmsg_add_string(&cl->hdr, "REQUEST", buf); + cl->state = client_parse_request(cl, buf); ustream_consume(cl->us, newline + 2 - buf); - cl->state = client_parse_request(cl, (char *) blobmsg_data(blob_data(cl->hdr.head))); if (cl->state == CLIENT_STATE_DONE) uh_header_error(cl, 400, "Bad Request"); @@ -204,6 +203,8 @@ static void client_header_complete(struct client *cl) static void client_parse_header(struct client *cl, char *data) { + struct http_request *r = &cl->request; + char *err; char *name; char *val; @@ -224,13 +225,22 @@ static void client_parse_header(struct client *cl, char *data) if (isupper(*name)) *name = tolower(*name); - if (!strcasecmp(data, "Expect")) { + if (!strcmp(data, "expect")) { if (!strcasecmp(val, "100-continue")) - cl->request.expect_cont = true; + r->expect_cont = true; else { uh_header_error(cl, 412, "Precondition Failed"); return; } + } else if (!strcmp(data, "content-length")) { + r->content_length = strtoul(val, &err, 0); + if (err && *err) { + uh_header_error(cl, 400, "Bad Request"); + return; + } + } else if (!strcmp(data, "transfer-encoding")) { + if (!strcmp(val, "chunked")) + r->transfer_chunked = true; } @@ -239,8 +249,84 @@ static void client_parse_header(struct client *cl, char *data) cl->state = CLIENT_STATE_HEADER; } +void client_poll_post_data(struct client *cl) +{ + struct dispatch *d = &cl->dispatch; + struct http_request *r = &cl->request; + char *buf; + int len; + + if (cl->state == CLIENT_STATE_DONE) + return; + + while (1) { + char *sep; + int offset = 0; + int cur_len; + + buf = ustream_get_read_buf(cl->us, &len); + if (!buf || !len) + break; + + if (!d->data_send) + return; + + cur_len = min(r->content_length, len); + if (cur_len) { + if (d->data_blocked) + break; + + if (d->data_send) + cur_len = d->data_send(cl, buf, cur_len); + + r->content_length -= cur_len; + ustream_consume(cl->us, cur_len); + continue; + } + + if (!r->transfer_chunked) + break; + + if (r->transfer_chunked > 1) + offset = 2; + + sep = strstr(buf + offset, "\r\n"); + if (!sep) + break; + + *sep = 0; + + r->content_length = strtoul(buf + offset, &sep, 16); + r->transfer_chunked++; + ustream_consume(cl->us, sep + 2 - buf); + + /* invalid chunk length */ + if (sep && *sep) { + r->content_length = 0; + r->transfer_chunked = 0; + break; + } + + /* empty chunk == eof */ + if (!r->content_length) { + r->transfer_chunked = false; + break; + } + } + + buf = ustream_get_read_buf(cl->us, &len); + if (!r->content_length && !r->transfer_chunked && + cl->state != CLIENT_STATE_DONE) { + if (cl->dispatch.data_done) + cl->dispatch.data_done(cl); + + cl->state = CLIENT_STATE_DONE; + } +} + static bool client_data_cb(struct client *cl, char *buf, int len) { + client_poll_post_data(cl); return false; } @@ -258,7 +344,7 @@ static bool client_header_cb(struct client *cl, char *buf, int len) line_len = newline + 2 - buf; ustream_consume(cl->us, line_len); if (cl->state == CLIENT_STATE_DATA) - client_data_cb(cl, newline + 2, len - line_len); + return client_data_cb(cl, newline + 2, len - line_len); return true; } @@ -278,14 +364,15 @@ static void client_read_cb(struct client *cl) do { str = ustream_get_read_buf(us, &len); - if (!str) + if (!str || !len) break; if (cl->state >= array_size(read_cbs) || !read_cbs[cl->state]) break; if (!read_cbs[cl->state](cl, str, len)) { - if (len == us->r.buffer_len) + if (len == us->r.buffer_len && + cl->state != CLIENT_STATE_DATA) uh_header_error(cl, 413, "Request Entity Too Large"); break; } @@ -294,6 +381,7 @@ static void client_read_cb(struct client *cl) static void client_close(struct client *cl) { + n_clients--; uh_dispatch_done(cl); uloop_timeout_cancel(&cl->timeout); ustream_free(&cl->sfd.stream); @@ -350,7 +438,7 @@ static void set_addr(struct uh_addr *addr, void *src) } } -void uh_accept_client(int fd) +bool uh_accept_client(int fd) { static struct client *next_client; struct client *cl; @@ -367,7 +455,7 @@ void uh_accept_client(int fd) sl = sizeof(addr); sfd = accept(fd, (struct sockaddr *) &addr, &sl); if (sfd < 0) - return; + return false; set_addr(&cl->peer_addr, &addr); sl = sizeof(addr); @@ -388,6 +476,7 @@ void uh_accept_client(int fd) next_client = NULL; n_clients++; cl->id = client_id++; + return true; } void uh_close_fds(void)