diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a9488d..aa53f7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,6 +53,7 @@ install( uhttpd.h log/log.h utils.h + list.h buffer/buffer.h http-parser/http_parser.h ${CMAKE_CURRENT_BINARY_DIR}/config.h diff --git a/src/connection.c b/src/connection.c index e11a6ea..521faa3 100644 --- a/src/connection.c +++ b/src/connection.c @@ -510,31 +510,31 @@ static bool match_path(struct uh_str *path, const char *needle, int needlelen, u } } -static bool set_path_handler(struct uh_connection_internal *conn, struct uh_path_handler *h, +static bool set_path_handler(struct uh_connection_internal *conn, struct list_head *head, struct uh_str *path, bool wildcard) { - while (h) { + struct uh_path_handler *h; + + list_for_each_entry(h, head, list) { if (match_path(path, h->path, h->len, h->flags, wildcard)) { conn->handler = h->handler; return true; } - - h = h->next; } return false; } -static bool set_plugin_handler(struct uh_connection_internal *conn, struct uh_plugin *p, +static bool set_plugin_handler(struct uh_connection_internal *conn, struct list_head *head, struct uh_str *path, bool wildcard) { - while (p) { + struct uh_plugin *p; + + list_for_each_entry(p, head, list) { if (match_path(path, p->path, p->len, p->flags, wildcard)) { conn->handler = p->h->handler; return true; } - - p = p->next; } return false; @@ -562,19 +562,19 @@ static int on_headers_complete(struct http_parser *parser) addr_str, (saddr2str(sa, addr_str, sizeof(addr_str), &port) ? port : 0)); /* match non wildcard path handler */ - if (set_path_handler(conn, srv->handlers, &path, false)) + if (set_path_handler(conn, &srv->handlers, &path, false)) goto done; /* match wildcard path handler */ - if (set_path_handler(conn, srv->handlers, &path, true)) + if (set_path_handler(conn, &srv->handlers, &path, true)) goto done; /* match non wildcard plugin */ - if (set_plugin_handler(conn, srv->plugins, &path, false)) + if (set_plugin_handler(conn, &srv->plugins, &path, false)) goto done; /* match wildcard plugin */ - set_plugin_handler(conn, srv->plugins, &path, true); + set_plugin_handler(conn, &srv->plugins, &path, true); done: if (!conn->handler) @@ -684,13 +684,7 @@ void conn_free(struct uh_connection_internal *conn) if (conn->file.fd > 0) close(conn->file.fd); - if (conn->prev) - conn->prev->next = conn->next; - else - conn->srv->conns = conn->next; - - if (conn->next) - conn->next->prev = conn->prev; + list_del(&conn->list); #ifdef SSL_SUPPORT ssl_session_free(conn->ssl); diff --git a/src/connection.h b/src/connection.h index f622026..273f773 100644 --- a/src/connection.h +++ b/src/connection.h @@ -29,6 +29,7 @@ #include "buffer.h" #include "uhttpd.h" +#include "list.h" #define UHTTPD_CONNECTION_TIMEOUT 30.0 #define UHTTPD_MAX_HEADER_NUM 50 @@ -68,6 +69,7 @@ struct uh_request { struct uh_connection_internal { struct uh_connection com; + struct list_head list; int sock; void *ssl; uint8_t flags; @@ -91,8 +93,6 @@ struct uh_connection_internal { } addr; /* peer address */ struct http_parser parser; struct http_parser_url url_parser; - struct uh_connection_internal *prev; - struct uh_connection_internal *next; void (*handler)(struct uh_connection *conn, int event); }; diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..738960f --- /dev/null +++ b/src/list.h @@ -0,0 +1,210 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Copyright (c) 2011 Felix Fietkau + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _LINUX_LIST_H_ +#define _LINUX_LIST_H_ + +#include + +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member)); \ + }) +#endif + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list->prev = list; +} + +static inline bool list_empty(const struct list_head *head) +{ + return (head->next == head); +} + +static inline bool list_is_first(const struct list_head *list, + const struct list_head *head) +{ + return list->prev == head; +} + +static inline bool list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +static inline void _list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +static inline void list_del(struct list_head *entry) +{ + _list_del(entry); + entry->next = entry->prev = NULL; +} + +static inline void _list_add(struct list_head *_new, struct list_head *prev, + struct list_head *next) +{ + + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +static inline void list_del_init(struct list_head *entry) +{ + _list_del(entry); + INIT_LIST_HEAD(entry); +} + +#define list_entry(ptr, type, field) container_of(ptr, type, field) +#define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) +#define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) + +#define list_for_each(p, head) \ + for (p = (head)->next; p != (head); p = p->next) + +#define list_for_each_safe(p, n, head) \ + for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) + +#define list_for_each_entry(p, h, field) \ + for (p = list_first_entry(h, __typeof__(*p), field); &p->field != (h); \ + p = list_entry(p->field.next, __typeof__(*p), field)) + +#define list_for_each_entry_safe(p, n, h, field) \ + for (p = list_first_entry(h, __typeof__(*p), field), \ + n = list_entry(p->field.next, __typeof__(*p), field); &p->field != (h);\ + p = n, n = list_entry(n->field.next, __typeof__(*n), field)) + +#define list_for_each_entry_reverse(p, h, field) \ + for (p = list_last_entry(h, __typeof__(*p), field); &p->field != (h); \ + p = list_entry(p->field.prev, __typeof__(*p), field)) + +#define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) +#define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) + +static inline void list_add(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head, head->next); +} + +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head->prev, head); +} + +static inline void list_move(struct list_head *list, struct list_head *head) +{ + _list_del(list); + list_add(list, head); +} + +static inline void list_move_tail(struct list_head *entry, struct list_head *head) +{ + _list_del(entry); + list_add_tail(entry, head); +} + +static inline void _list_splice(const struct list_head *list, struct list_head *prev, + struct list_head *next) +{ + struct list_head *first; + struct list_head *last; + + if (list_empty(list)) + return; + + first = list->next; + last = list->prev; + first->prev = prev; + prev->next = first; + last->next = next; + next->prev = last; +} + +static inline void list_splice(const struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); +} + +static inline void list_splice_tail(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); +} + +static inline void list_splice_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); + INIT_LIST_HEAD(list); +} + +static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); +} + +#endif /* _LINUX_LIST_H_ */ diff --git a/src/uhttpd.c b/src/uhttpd.c index e7e17de..c685e22 100644 --- a/src/uhttpd.c +++ b/src/uhttpd.c @@ -36,55 +36,68 @@ #include "connection.h" #include "utils.h" - -static void uh_server_free(struct uh_server *srv) +static void uh_server_free_conns(struct list_head *head) { - struct uh_server_internal *srvi = (struct uh_server_internal *)srv; - struct uh_connection_internal *conn = srvi->conns; - struct uh_path_handler *h = srvi->handlers; - struct uh_listener *l = srvi->listeners; -#ifdef HAVE_DLOPEN - struct uh_plugin *p = srvi->plugins; -#endif + struct uh_connection_internal *pos, *n; - if (srvi->docroot) - free(srvi->docroot); + list_for_each_entry_safe(pos, n, head, list) { + conn_free(pos); + } +} - if (srvi->index_page) - free(srvi->index_page); +static void uh_server_free_handlers(struct list_head *head) +{ + struct uh_path_handler *pos, *n; - while (conn) { - struct uh_connection_internal *next = conn->next; - conn_free(conn); - conn = next; + list_for_each_entry_safe(pos, n, head, list) { + list_del(&pos->list); + free(pos); } +} + +static void uh_server_free_plugins(struct list_head *head) +{ +#ifdef HAVE_DLOPEN + struct uh_plugin *pos, *n; - while (h) { - struct uh_path_handler *temp = h; - h = h->next; - free(temp); + list_for_each_entry_safe(pos, n, head, list) { + list_del(&pos->list); + dlclose(pos->dlh); + free(pos); } +#endif +} - while (l) { - struct uh_listener *temp = l; +static void uh_server_free_listeners(struct list_head *head) +{ + struct uh_listener *pos, *n; - ev_io_stop(srvi->loop, &l->ior); + list_for_each_entry_safe(pos, n, head, list) { + ev_io_stop(pos->srv->loop, &pos->ior); - if (l->sock > 0) - close(l->sock); + list_del(&pos->list); - l = l->next; - free(temp); - } + if (pos->sock > 0) + close(pos->sock); -#ifdef HAVE_DLOPEN - while (p) { - struct uh_plugin *temp = p; - dlclose(p->dlh); - p = p->next; - free(temp); + free(pos); } -#endif +} + +static void uh_server_free(struct uh_server *srv) +{ + struct uh_server_internal *srvi = (struct uh_server_internal *)srv; + + if (srvi->docroot) + free(srvi->docroot); + + if (srvi->index_page) + free(srvi->index_page); + + uh_server_free_conns(&srvi->conns); + uh_server_free_handlers(&srvi->handlers); + uh_server_free_plugins(&srvi->plugins); + uh_server_free_listeners(&srvi->listeners); #ifdef SSL_SUPPORT ssl_context_free(srvi->ssl_ctx); @@ -134,14 +147,7 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) if (!conn) return; - if (!srv->conns) { - srv->conns = conn; - return; - } - - conn->next = srv->conns; - srv->conns->prev = conn; - srv->conns = conn; + list_add(&conn->list, &srv->conns); } struct uh_server *uh_server_new(struct ev_loop *loop) @@ -237,13 +243,7 @@ static int uh_load_plugin(struct uh_server *srv, const char *path) } } - if (!srvi->plugins) { - srvi->plugins = p; - return 0; - } - - p->next = srvi->plugins; - srvi->plugins = p; + list_add(&p->list, &srvi->plugins); return 0; #else @@ -286,13 +286,7 @@ static int __uh_add_path_handler(struct uh_server *srv, const char *path, uh_pat strncpy(h->path, path, path_len); - if (!srvi->handlers) { - srvi->handlers = h; - return 0; - } - - h->next = srvi->handlers; - srvi->handlers = h; + list_add(&h->list, &srvi->handlers); return 0; } @@ -465,12 +459,7 @@ static int uh_server_listen(struct uh_server *srv, const char *addr, bool ssl) ev_io_init(&l->ior, uh_accept_cb, sock, EV_READ); ev_io_start(srvi->loop, &l->ior); - if (!srvi->listeners) { - srvi->listeners = l; - } else { - l->next = srvi->listeners; - srvi->listeners = l; - } + list_add(&l->list, &srvi->listeners); if (p->ai_family == AF_INET) { struct sockaddr_in *ina = (struct sockaddr_in *)p->ai_addr; @@ -502,6 +491,11 @@ void uh_server_init(struct uh_server *srv, struct ev_loop *loop) memset(srvi, 0, sizeof(struct uh_server_internal)); + INIT_LIST_HEAD(&srvi->listeners); + INIT_LIST_HEAD(&srvi->handlers); + INIT_LIST_HEAD(&srvi->plugins); + INIT_LIST_HEAD(&srvi->conns); + srvi->loop = loop ? loop : EV_DEFAULT; srv->get_loop = uh_get_loop; diff --git a/src/uhttpd.h b/src/uhttpd.h index fef99fb..7aebfde 100644 --- a/src/uhttpd.h +++ b/src/uhttpd.h @@ -133,23 +133,6 @@ struct uh_plugin_handler { uh_path_handler_prototype handler; }; -struct uh_plugin { - struct uh_plugin_handler *h; - void *dlh; - uint8_t flags; - uint8_t len; - const char *path; - struct uh_plugin *next; -}; - -struct uh_path_handler { - uh_path_handler_prototype handler; - struct uh_path_handler *next; - uint8_t flags; - uint8_t len; - char path[0]; -}; - /* * uh_server_new - creat an uh_server struct and init it * @loop: If NULL will use EV_DEFAULT diff --git a/src/uhttpd_internal.h b/src/uhttpd_internal.h index b6ecca3..af0cfb3 100644 --- a/src/uhttpd_internal.h +++ b/src/uhttpd_internal.h @@ -28,6 +28,7 @@ #include #include "uhttpd.h" +#include "list.h" #ifdef SSL_SUPPORT #include "ssl/ssl.h" @@ -36,12 +37,29 @@ struct uh_server_internal; struct uh_connection_internal; +struct uh_path_handler { + uh_path_handler_prototype handler; + struct list_head list; + uint8_t flags; + uint8_t len; + char path[0]; +}; + +struct uh_plugin { + struct uh_plugin_handler *h; + void *dlh; + uint8_t flags; + uint8_t len; + const char *path; + struct list_head list; +}; + struct uh_listener { int sock; bool ssl; struct ev_io ior; + struct list_head list; struct uh_server_internal *srv; - struct uh_listener *next; }; struct uh_server_internal { @@ -49,15 +67,15 @@ struct uh_server_internal { char *docroot; char *index_page; struct ev_loop *loop; - struct uh_listener *listeners; - struct uh_connection_internal *conns; void (*conn_closed_cb)(struct uh_connection *conn); void (*default_handler)(struct uh_connection *conn, int event); #ifdef SSL_SUPPORT struct ssl_context *ssl_ctx; #endif - struct uh_plugin *plugins; - struct uh_path_handler *handlers; + struct list_head listeners; + struct list_head handlers; + struct list_head plugins; + struct list_head conns; }; #endif diff --git a/src/utils.h b/src/utils.h index 71e9737..d5c0d91 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,14 +30,6 @@ #include #include -#ifndef container_of -#define container_of(ptr, type, member) \ - ({ \ - const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \ - (type *) ((char *) __mptr - offsetof(type, member)); \ - }) -#endif - const char *saddr2str(struct sockaddr *addr, char buf[], int len, int *port); bool support_so_reuseport();