diff options
author | Felix Fietkau <nbd@openwrt.org> | 2013-01-03 02:25:10 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2013-01-03 02:25:10 +0100 |
commit | 2bef561c890f275ce3b785e6abdb3eda28c96aa8 (patch) | |
tree | 4505ed34b7c89243a13f9dda7efc63fc0fe883be | |
parent | fbfd773aa6f77391d5ea6e2ffef8eb9ad6bb7d1e (diff) |
add post data relaying
-rw-r--r-- | client.c | 70 | ||||
-rw-r--r-- | proc.c | 36 | ||||
-rw-r--r-- | uhttpd.h | 6 |
3 files changed, 106 insertions, 6 deletions
@@ -204,6 +204,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 +226,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; } @@ -241,7 +252,54 @@ static void client_parse_header(struct client *cl, char *data) static bool client_data_cb(struct client *cl, char *buf, int len) { - return false; + struct dispatch *d = &cl->dispatch; + struct http_request *r = &cl->request; + int consumed = 0; + int cur_len = 0; + + if (!d->data_send) + return false; + + while (len) { + char *sep; + + r->content_length -= cur_len; + consumed += cur_len; + buf += cur_len; + len -= cur_len; + cur_len = min(r->content_length, len); + + if (cur_len) { + if (d->data_send) + d->data_send(cl, buf, cur_len); + continue; + } + + if (!r->transfer_chunked) + break; + + sep = strstr(buf, "\r\n"); + if (!sep) + break; + + *sep = 0; + cur_len = sep + 2 - buf; + + r->content_length = strtoul(buf, &sep, 16); + + /* invalid chunk length */ + if (sep && *sep) + return false; + + /* empty chunk == eof */ + if (!r->content_length) { + r->transfer_chunked = false; + continue; + } + } + + ustream_consume(cl->us, consumed); + return r->content_length || r->transfer_chunked; } static bool client_header_cb(struct client *cl, char *buf, int len) @@ -258,7 +316,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,7 +336,7 @@ 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]) @@ -287,6 +345,8 @@ static void client_read_cb(struct client *cl) if (!read_cbs[cl->state](cl, str, len)) { if (len == us->r.buffer_len) uh_header_error(cl, 413, "Request Entity Too Large"); + if (cl->dispatch.data_done) + cl->dispatch.data_done(cl); break; } } while(1); @@ -221,6 +221,40 @@ static void proc_free(struct client *cl) uh_relay_free(&cl->dispatch.proc.r); } +static void proc_write_close(struct client *cl) +{ + shutdown(cl->dispatch.proc.r.sfd.fd.fd, SHUT_WR); +} + +static void proc_relay_write_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); +} + +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); +} + +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; + return; + } + + proc_write_close(cl); +} + bool uh_create_process(struct client *cl, struct path_info *pi, void (*cb)(struct client *cl, struct path_info *pi, int fd)) { @@ -253,6 +287,8 @@ bool uh_create_process(struct client *cl, struct path_info *pi, uh_relay_open(cl, &cl->dispatch.proc.r, fds[0], pid); d->free = proc_free; d->close_fds = proc_close_fds; + d->data_send = proc_data_send; + d->data_done = proc_data_done; d->proc.r.header_cb = proc_handle_header; d->proc.r.header_end = proc_handle_header_end; d->proc.r.close = proc_handle_close; @@ -78,8 +78,10 @@ enum http_version { struct http_request { enum http_method method; enum http_version version; - bool expect_cont; int redirect_status; + int content_length; + bool expect_cont; + bool transfer_chunked; const char *url; const struct auth_realm *realm; }; @@ -138,6 +140,8 @@ struct dispatch_handler { }; struct dispatch { + void (*data_send)(struct client *cl, const char *data, int len); + void (*data_done)(struct client *cl); void (*write_cb)(struct client *cl); void (*close_fds)(struct client *cl); void (*free)(struct client *cl); |