From e571a34385982cb477c9ee68fe934bb68b10e6b9 Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Thu, 17 Dec 2020 00:53:36 +0800 Subject: [PATCH] new features: path handler and upload large file Signed-off-by: Jianhui Zhao --- example/example.c | 53 ++++++++++++++++++++++-- example/test_plugin.c | 9 ++++- src/buffer | 2 +- src/connection.c | 94 ++++++++++++++++++++++++++++++++----------- src/connection.h | 3 ++ src/uhttpd.c | 34 ++++++++++++++++ src/uhttpd.h | 20 ++++++++- 7 files changed, 183 insertions(+), 32 deletions(-) diff --git a/example/example.c b/example/example.c index 145c852..0ce5fca 100644 --- a/example/example.c +++ b/example/example.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "uhttpd.h" @@ -33,8 +35,11 @@ static bool serve_file = false; static const char *docroot = "."; static const char *index_page = "index.html"; -static void on_request(struct uh_connection *conn) +static void default_handler(struct uh_connection *conn, int event) { + if (event != UH_EV_COMPLETE) + return; + if (!serve_file) { struct uh_str path = conn->get_path(conn); struct uh_str query = conn->get_query(conn); @@ -55,6 +60,46 @@ static void on_request(struct uh_connection *conn) } } +static void upload_handler(struct uh_connection *conn, int event) +{ + static int fd = -1; + + if (event == UH_EV_BODY) { + struct uh_str body = conn->extract_body(conn); + + if (fd < 0) { + fd = open("upload.bin", O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno)); + return; + } + } + + if (write(fd, body.p, body.len) < 0) { + conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno)); + close(fd); + return; + } + } else { + struct stat st; + size_t size = 0; + + conn->send_head(conn, HTTP_STATUS_OK, -1, NULL); + + if (fd > 0) { + fstat(fd, &st); + close(fd); + + fd = -1; + size = st.st_size; + } + + conn->chunk_printf(conn, "Upload size: %zd\n", size); + conn->chunk_end(conn); + conn->done(conn); + } +} + static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { if (w->signum == SIGINT) { @@ -119,9 +164,11 @@ int main(int argc, char **argv) goto err; #endif - srv->on_request = on_request; + srv->default_handler = default_handler; - srv->load_plugin(srv, "/usr/local/lib/uhttpd/test_plugin.so"); + srv->add_path_handler(srv, "/upload", upload_handler); + + srv->load_plugin(srv, "test_plugin.so"); uh_log_info("Listen on: *:%d\n", port); diff --git a/example/test_plugin.c b/example/test_plugin.c index 0200048..85a887c 100644 --- a/example/test_plugin.c +++ b/example/test_plugin.c @@ -24,9 +24,14 @@ #include "uhttpd.h" -static void test_handler(struct uh_connection *conn) +static void test_handler(struct uh_connection *conn, int event) { - struct uh_str path = conn->get_path(conn); + struct uh_str path; + + if (event != UH_EV_COMPLETE) + return; + + path = conn->get_path(conn); conn->send_head(conn, 200, -1, NULL); conn->chunk_printf(conn, "Path: %.*s\n", path.len, path.p); diff --git a/src/buffer b/src/buffer index 2fb5523..55e4d3b 160000 --- a/src/buffer +++ b/src/buffer @@ -1 +1 @@ -Subproject commit 2fb5523d44eb5cc65eb9473ecbf689727097b3cd +Subproject commit 55e4d3bddc1cd258766448788f8fa3e406e25065 diff --git a/src/connection.c b/src/connection.c index d8691fb..e5d2000 100644 --- a/src/connection.c +++ b/src/connection.c @@ -42,6 +42,10 @@ static void conn_done(struct uh_connection *conn) if (!http_should_keep_alive(&conn->parser)) conn->flags |= CONN_F_SEND_AND_CLOSE; + + if (conn->flags & CONN_F_SEND_AND_CLOSE) + ev_io_stop(conn->srv->loop, &conn->ior); + ev_io_start(conn->srv->loop, &conn->iow); } @@ -257,6 +261,21 @@ static struct uh_str conn_get_body(struct uh_connection *conn) return body; } +static struct uh_str conn_extract_body(struct uh_connection *conn) +{ + struct uh_request *req = &conn->req; + struct uh_str body; + + body.p = O2D(conn, req->body.offset); + body.len = req->body.length; + + req->body.length = 0; + + buffer_discard(&conn->rb, body.len); + + return body; +} + static int on_message_begin_cb(struct http_parser *parser) { struct uh_connection *conn = (struct uh_connection *)parser->data; @@ -322,49 +341,74 @@ static int on_header_value_cb(struct http_parser *parser, const char *at, size_t return 0; } +static int on_headers_complete(struct http_parser *parser) +{ + struct uh_connection *conn = (struct uh_connection *)parser->data; + struct uh_server *srv = conn->srv; + struct uh_request *req = &conn->req; + struct uh_path_handler *h = srv->handlers; + struct uh_plugin *p = srv->plugins; + struct uh_str path; + + http_parser_parse_url(O2D(conn, req->url.offset), req->url.length, false, &conn->url_parser); + + if (conn->handler) + return 0; + + path = conn->get_path(conn); + + while (h) { + if (strlen(h->path) == path.len && !strncmp(path.p, h->path, path.len)) { + conn->handler = h->handler; + return 0; + } + h = h->next; + } + + while (p) { + if (strlen(p->h->path) == path.len && !strncmp(path.p, p->h->path, path.len)) { + conn->handler = p->h->handler; + return 0; + } + p = p->next; + } + + return 0; +} + static int on_body_cb(struct http_parser *parser, const char *at, size_t length) { struct uh_connection *conn = (struct uh_connection *)parser->data; struct uh_request *req = &conn->req; + struct uh_server *srv = conn->srv; + int event = UH_EV_BODY; if (req->body.offset == 0) req->body.offset = ROF(conn, at); req->body.length += length; + if (conn->handler) + conn->handler(conn, event); + else if (srv->default_handler) + srv->default_handler(conn, event); + return 0; } -static bool run_plugins(struct uh_connection *conn) -{ - struct uh_plugin *p = conn->srv->plugins; - struct uh_str path = conn->get_path(conn); - - while (p) { - if (strlen(p->h->path) == path.len && !strncmp(path.p, p->h->path, path.len)) { - p->h->handler(conn); - return true; - } - p = p->next; - } - return false; -} - static int on_message_complete_cb(struct http_parser *parser) { struct uh_connection *conn = (struct uh_connection *)parser->data; struct uh_server *srv = conn->srv; - struct uh_request *req = &conn->req; + int event = UH_EV_COMPLETE; ev_timer_stop(srv->loop, &conn->timer); - http_parser_parse_url(O2D(conn, req->url.offset), req->url.length, false, &conn->url_parser); - - if (!run_plugins(conn)) { - if (srv->on_request) - srv->on_request(conn); - else - conn_error(conn, HTTP_STATUS_NOT_FOUND, NULL); - } + if (conn->handler) + conn->handler(conn, event); + else if (srv->default_handler) + srv->default_handler(conn, event); + else + conn_error(conn, HTTP_STATUS_NOT_FOUND, NULL); return 0; } @@ -374,6 +418,7 @@ static struct http_parser_settings settings = { .on_url = on_url_cb, .on_header_field = on_header_field_cb, .on_header_value = on_header_value_cb, + .on_headers_complete = on_headers_complete, .on_body = on_body_cb, .on_message_complete = on_message_complete_cb }; @@ -617,6 +662,7 @@ struct uh_connection *uh_new_connection(struct uh_server *srv, int sock, struct conn->get_query = conn_get_query; conn->get_header = conn_get_header; conn->get_body = conn_get_body; + conn->extract_body = conn_extract_body; return conn; } diff --git a/src/connection.h b/src/connection.h index 2bedeb6..4cd6bac 100644 --- a/src/connection.h +++ b/src/connection.h @@ -94,6 +94,7 @@ struct uh_connection { struct http_parser_url url_parser; struct uh_connection *prev; struct uh_connection *next; + void (*handler)(struct uh_connection *conn, int event); void (*done)(struct uh_connection *conn); /* Must be called at last, if not call 'error', 'redirect' and 'serve_file' */ void (*send)(struct uh_connection *conn, const void *data, ssize_t len); void (*send_file)(struct uh_connection *conn, const char *path); @@ -115,6 +116,8 @@ struct uh_connection { struct uh_str (*get_query)(struct uh_connection *conn); struct uh_str (*get_header)(struct uh_connection *conn, const char *name); struct uh_str (*get_body)(struct uh_connection *conn); + /* The remain body data will be discurd after this function called */ + struct uh_str (*extract_body)(struct uh_connection *conn); }; struct uh_connection *uh_new_connection(struct uh_server *srv, int sock, struct sockaddr_in *addr); diff --git a/src/uhttpd.c b/src/uhttpd.c index 7cba0c3..904586e 100644 --- a/src/uhttpd.c +++ b/src/uhttpd.c @@ -42,6 +42,7 @@ void conn_free(struct uh_connection *conn); static void uh_server_free(struct uh_server *srv) { struct uh_connection *conn = srv->conns; + struct uh_path_handler *h = srv->handlers; #ifdef HAVE_DLOPEN struct uh_plugin *p = srv->plugins; #endif @@ -57,6 +58,12 @@ static void uh_server_free(struct uh_server *srv) conn = next; } + while (h) { + struct uh_path_handler *temp = h; + h = h->next; + free(temp); + } + #ifdef HAVE_DLOPEN while (p) { struct uh_plugin *temp = p; @@ -178,6 +185,31 @@ static int uh_load_plugin(struct uh_server *srv, const char *path) #endif } +static int uh_add_path_handler(struct uh_server *srv, const char *path, uh_path_handler_prototype handler) +{ + struct uh_path_handler *h; + + h = calloc(1, sizeof(struct uh_path_handler) + strlen(path) + 1); + if (!h) { + uh_log_err("calloc: %s\n", strerror(errno)); + return -1; + } + + h->handler = handler; + strcpy(h->path, path); + + if (!srv->handlers) { + srv->handlers = h; + return 0; + } + + h->next = srv->handlers; + srv->handlers->prev = h; + srv->handlers = h; + + return 0; +} + int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port) { struct sockaddr_in addr = { @@ -219,6 +251,8 @@ int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host srv->load_plugin = uh_load_plugin; + srv->add_path_handler = uh_add_path_handler; + ev_io_init(&srv->ior, uh_accept_cb, sock, EV_READ); ev_io_start(srv->loop, &srv->ior); diff --git a/src/uhttpd.h b/src/uhttpd.h index e14dcfd..edbfbf7 100644 --- a/src/uhttpd.h +++ b/src/uhttpd.h @@ -31,9 +31,11 @@ #include "config.h" #include "log.h" +typedef void (*uh_path_handler_prototype)(struct uh_connection *conn, int event); + struct uh_plugin_handler { const char *path; - void (*handler)(struct uh_connection *conn); + uh_path_handler_prototype handler; }; struct uh_plugin { @@ -43,19 +45,33 @@ struct uh_plugin { struct uh_plugin *next; }; +enum { + UH_EV_BODY, + UH_EV_COMPLETE +}; + +struct uh_path_handler { + uh_path_handler_prototype handler; + struct uh_path_handler *prev; + struct uh_path_handler *next; + char path[0]; +}; + struct uh_server { int sock; struct ev_loop *loop; struct ev_io ior; struct uh_connection *conns; void (*free)(struct uh_server *srv); - void (*on_request)(struct uh_connection *conn); + void (*default_handler)(struct uh_connection *conn, int event); #if UHTTPD_SSL_SUPPORT void *ssl_ctx; int (*ssl_init)(struct uh_server *srv, const char *cert, const char *key); #endif struct uh_plugin *plugins; int (*load_plugin)(struct uh_server *srv, const char *path); + struct uh_path_handler *handlers; + int (*add_path_handler)(struct uh_server *srv, const char *path, uh_path_handler_prototype handler); }; /*