diff options
author | Felix Fietkau <nbd@openwrt.org> | 2013-01-03 17:03:49 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2013-01-03 17:03:49 +0100 |
commit | b7c85a2819b3869279a669d5681f574824589c6c (patch) | |
tree | da6639a596bd4d802ce1838582d6a86deac62be6 | |
parent | 12931edab795b7caeb70323e2959b5e5503c5980 (diff) |
implement proper flow control for relaying postdata
-rw-r--r-- | client.c | 63 | ||||
-rw-r--r-- | proc.c | 18 | ||||
-rw-r--r-- | uhttpd.h | 3 |
3 files changed, 57 insertions, 27 deletions
@@ -249,29 +249,37 @@ static void client_parse_header(struct client *cl, char *data) cl->state = CLIENT_STATE_HEADER; } -static bool client_data_cb(struct client *cl, char *buf, int len) +void client_poll_post_data(struct client *cl) { struct dispatch *d = &cl->dispatch; struct http_request *r = &cl->request; - int consumed = 0; - int cur_len = 0; + char *buf; + int len; - if (!d->data_send) - return false; + if (cl->state == CLIENT_STATE_DONE) + return; - while (len) { - int offset = 0; + while (1) { char *sep; + int offset = 0; + int cur_len; - consumed += cur_len; - buf += cur_len; - len -= cur_len; - cur_len = min(r->content_length, 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) { - r->content_length -= cur_len; + if (d->data_blocked) + break; + if (d->data_send) d->data_send(cl, buf, cur_len); + r->content_length -= cur_len; + ustream_consume(cl->us, cur_len); continue; } @@ -286,35 +294,38 @@ static bool client_data_cb(struct client *cl, char *buf, int len) break; *sep = 0; - cur_len = sep + 2 - buf; r->content_length = strtoul(buf + offset, &sep, 16); r->transfer_chunked++; + ustream_consume(cl->us, sep + 2 - buf); /* invalid chunk length */ - if (sep && *sep) - goto abort; + if (sep && *sep) { + r->content_length = 0; + r->transfer_chunked = 0; + break; + } /* empty chunk == eof */ - if (!r->content_length) + if (!r->content_length) { r->transfer_chunked = false; - - continue; - -abort: - consumed = len; - r->content_length = 0; - r->transfer_chunked = 0; - break; + break; + } } - ustream_consume(cl->us, consumed); - if (!r->content_length && !r->transfer_chunked) { + 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; } @@ -235,6 +235,18 @@ static void proc_relay_write_cb(struct ustream *us, int bytes) if (ustream_pending_data(us, true)) return; + cl->dispatch.data_blocked = false; + us->notify_write = NULL; + client_poll_post_data(cl); +} + +static void proc_relay_write_close_cb(struct ustream *us, int bytes) +{ + struct client *cl = container_of(us, struct client, dispatch.proc.r.sfd.stream); + + if (ustream_pending_data(us, true)) + return; + proc_write_close(cl); } @@ -243,6 +255,10 @@ static void proc_data_send(struct client *cl, const char *data, int len) struct ustream *us = &cl->dispatch.proc.r.sfd.stream; ustream_write(us, data, len, false); + if (ustream_pending_data(us, true)) { + cl->dispatch.data_blocked = true; + us->notify_write = proc_relay_write_cb; + } } static void proc_data_done(struct client *cl) @@ -250,7 +266,7 @@ static void proc_data_done(struct client *cl) struct ustream *us = &cl->dispatch.proc.r.sfd.stream; if (ustream_pending_data(us, true)) { - us->notify_write = proc_relay_write_cb; + us->notify_write = proc_relay_write_close_cb; return; } @@ -144,6 +144,8 @@ struct dispatch { void (*write_cb)(struct client *cl); void (*close_fds)(struct client *cl); void (*free)(struct client *cl); + bool data_blocked; + union { struct { struct blob_attr **hdr; @@ -208,6 +210,7 @@ void __printf(4, 5) uh_client_error(struct client *cl, int code, const char *summary, const char *fmt, ...); void uh_handle_request(struct client *cl); +void client_poll_post_data(struct client *cl); void uh_auth_add(const char *path, const char *user, const char *pass); bool uh_auth_check(struct client *cl, struct path_info *pi); |