new features: path handler and upload large file
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>main
parent
4c75690dfd
commit
e571a34385
|
@ -26,6 +26,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2fb5523d44eb5cc65eb9473ecbf689727097b3cd
|
||||
Subproject commit 55e4d3bddc1cd258766448788f8fa3e406e25065
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
34
src/uhttpd.c
34
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);
|
||||
|
||||
|
|
20
src/uhttpd.h
20
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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue