summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2013-01-03 17:03:49 +0100
committerFelix Fietkau <nbd@openwrt.org>2013-01-03 17:03:49 +0100
commitb7c85a2819b3869279a669d5681f574824589c6c (patch)
treeda6639a596bd4d802ce1838582d6a86deac62be6
parent12931edab795b7caeb70323e2959b5e5503c5980 (diff)
implement proper flow control for relaying postdata
-rw-r--r--client.c63
-rw-r--r--proc.c18
-rw-r--r--uhttpd.h3
3 files changed, 57 insertions, 27 deletions
diff --git a/client.c b/client.c
index 2fdc1b2..43c4af8 100644
--- a/client.c
+++ b/client.c
@@ -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;
}
diff --git a/proc.c b/proc.c
index 425576e..3ebfffd 100644
--- a/proc.c
+++ b/proc.c
@@ -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;
}
diff --git a/uhttpd.h b/uhttpd.h
index 5ec0f00..3cd040d 100644
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -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);