diff --git a/README.md b/README.md index 711d7b5..2a2043b 100755 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Then use the command curl or browser to test static void hello_action(struct uh_client *cl) { + int body_len = 0; cl->send_header(cl, 200, "OK", -1); cl->append_header(cl, "Myheader", "Hello"); cl->header_end(cl); @@ -63,6 +64,7 @@ static void hello_action(struct uh_client *cl) cl->chunk_printf(cl, "

REMOTE_ADDR: %s

", cl->get_peer_addr(cl)); cl->chunk_printf(cl, "

PATH: %s

", cl->get_path(cl)); cl->chunk_printf(cl, "

QUERY: %s

", cl->get_query(cl)); + cl->chunk_printf(cl, "

BODY:%s

", cl->get_body(cl, &body_len)); cl->request_done(cl); } diff --git a/README_ZH.md b/README_ZH.md index 9ebd428..64a164e 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -55,6 +55,7 @@ static void hello_action(struct uh_client *cl) { + int body_len = 0; cl->send_header(cl, 200, "OK", -1); cl->append_header(cl, "Myheader", "Hello"); cl->header_end(cl); @@ -63,6 +64,7 @@ static void hello_action(struct uh_client *cl) cl->chunk_printf(cl, "

REMOTE_ADDR: %s

", cl->get_peer_addr(cl)); cl->chunk_printf(cl, "

PATH: %s

", cl->get_path(cl)); cl->chunk_printf(cl, "

QUERY: %s

", cl->get_query(cl)); + cl->chunk_printf(cl, "

BODY:%s

", cl->get_body(cl, &body_len)); cl->request_done(cl); } diff --git a/example/helloworld.c b/example/helloworld.c index 8a93c9f..2af166d 100755 --- a/example/helloworld.c +++ b/example/helloworld.c @@ -4,6 +4,8 @@ static void hello_action(struct uh_client *cl) { + int body_len = 0; + cl->send_header(cl, 200, "OK", -1); cl->append_header(cl, "Myheader", "Hello"); cl->header_end(cl); @@ -12,6 +14,7 @@ static void hello_action(struct uh_client *cl) cl->chunk_printf(cl, "

REMOTE_ADDR: %s

", cl->get_peer_addr(cl)); cl->chunk_printf(cl, "

PATH: %s

", cl->get_path(cl)); cl->chunk_printf(cl, "

QUERY: %s

", cl->get_query(cl)); + cl->chunk_printf(cl, "

BODY:%s

", cl->get_body(cl, &body_len)); cl->request_done(cl); } diff --git a/src/action.c b/src/action.c index 0925abb..81e052f 100755 --- a/src/action.c +++ b/src/action.c @@ -22,6 +22,9 @@ #include "action.h" #include "uhttpd.h" +#define UH_ACTION_DATA_BUF_SIZE 1024 +#define UH_ACTION_MAX_POST_SIZE 4096 + int uh_add_action(struct uh_server *srv, const char *path, action_cb_t cb) { struct uh_action *a; @@ -48,16 +51,70 @@ err: return -1; } +static int action_data_send(struct uh_client *cl, const char *data, int len) +{ + struct dispatch *d = &cl->dispatch; + d->action.post_len += len; + + if (d->action.post_len > UH_ACTION_MAX_POST_SIZE) + goto err; + + if (d->action.post_len > UH_ACTION_DATA_BUF_SIZE) { + d->action.body = realloc(d->action.body, UH_ACTION_MAX_POST_SIZE); + if (!d->action.body) { + cl->send_error(cl, 500, "Internal Server Error", "No memory"); + return 0; + } + } + + memcpy(d->action.body, data, len); + return len; +err: + cl->send_error(cl, 413, "Request Entity Too Large", NULL); + return 0; +} + +static void action_data_done(struct uh_client *cl) +{ + struct uh_action *a = cl->dispatch.action.a; + a->cb(cl); +} + +static void action_data_free(struct uh_client *cl) +{ + struct dispatch *d = &cl->dispatch; + free(d->action.body); +} + bool handle_action_request(struct uh_client *cl, const char *path) { + struct dispatch *d = &cl->dispatch; struct uh_action *a; a = avl_find_element(&cl->srv->actions, path, a, avl); if (a) { - a->cb(cl); - return true; + switch (cl->request.method) { + case UH_HTTP_MSG_POST: + d->action.a = a; + d->data_send = action_data_send; + d->data_done = action_data_done; + d->free = action_data_free; + d->action.body = calloc(1, UH_ACTION_DATA_BUF_SIZE); + if (!d->action.body) + cl->send_error(cl, 500, "Internal Server Error", "No memory"); + break; + + case UH_HTTP_MSG_GET: + a->cb(cl); + break; + + default: + cl->send_error(cl, 400, "Bad Request", "Invalid Request"); + break; + } } - return false; + + return a ? true : false; } diff --git a/src/client.c b/src/client.c index 0265615..032483c 100755 --- a/src/client.c +++ b/src/client.c @@ -101,6 +101,12 @@ static inline const char *client_get_header(struct uh_client *cl, const char *na return kvlist_get(&cl->request.hdr, name); } +static inline const char *client_get_body(struct uh_client *cl, int *len) +{ + *len = cl->dispatch.action.post_len; + return cl->dispatch.action.body; +} + static void uh_handle_request(struct uh_client *cl) { char *path = kvlist_get(&cl->request.hdr, "path"); @@ -138,8 +144,6 @@ static void dispatch_done(struct uh_client *cl) { if (cl->dispatch.free) cl->dispatch.free(cl); - if (cl->dispatch.req_free) - cl->dispatch.req_free(cl); } static inline int hdr_get_len(struct kvlist *kv, const void *data) @@ -256,13 +260,67 @@ static bool client_init_cb(struct uh_client *cl, char *buf, int len) static void client_poll_post_data(struct uh_client *cl) { + struct dispatch *d = &cl->dispatch; struct http_request *r = &cl->request; + char *buf; + int len; if (cl->state == CLIENT_STATE_DONE) return; - if (!r->content_length && !r->transfer_chunked && - cl->state != CLIENT_STATE_DONE) { + 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_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; + } + } + + if (!r->content_length && !r->transfer_chunked && cl->state != CLIENT_STATE_DONE) { if (cl->dispatch.data_done) cl->dispatch.data_done(cl); @@ -462,6 +520,7 @@ void uh_accept_client(struct uh_server *srv, bool ssl) cl->get_path = client_get_path; cl->get_query = client_get_query; cl->get_header = client_get_header; + cl->get_body = client_get_body; uh_log_debug("new connection: %s:%d", cl->get_peer_addr(cl), addr.sin_port); diff --git a/src/client.h b/src/client.h index dc09d72..1197314 100755 --- a/src/client.h +++ b/src/client.h @@ -61,19 +61,17 @@ struct dispatch { int (*data_send)(struct uh_client *cl, const char *data, int len); void (*data_done)(struct uh_client *cl); void (*write_cb)(struct uh_client *cl); - void (*close_fds)(struct uh_client *cl); void (*free)(struct uh_client *cl); - void *req_data; - void (*req_free)(struct uh_client *cl); - - bool data_blocked; - bool no_cache; - union { struct { int fd; } file; + struct { + int post_len; + char *body; + struct uh_action *a; + } action; }; }; @@ -111,6 +109,7 @@ struct uh_client { const char *(*get_path)(struct uh_client *cl); const char *(*get_query)(struct uh_client *cl); const char *(*get_header)(struct uh_client *cl, const char *name); + const char *(*get_body)(struct uh_client *cl, int *len); }; void uh_client_read_cb(struct uh_client *cl); diff --git a/src/file.c b/src/file.c index feaf890..3dab3df 100755 --- a/src/file.c +++ b/src/file.c @@ -370,7 +370,6 @@ static void uh_file_data(struct uh_client *cl, struct path_info *pi, int fd) cl->dispatch.file.fd = fd; cl->dispatch.write_cb = file_write_cb; cl->dispatch.free = uh_file_free; - cl->dispatch.close_fds = uh_file_free; file_write_cb(cl); }