From cf9decf7b8ddd8fb4c4b75b06be1343e8cc34b1e Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Mon, 17 Feb 2020 14:12:15 +0100 Subject: [PATCH] Rework WIP. --- src/rest/restd.c | 196 +++++++++++++++++++++++++++++++++++++++++- src/rest/restd.h | 1 + src/tests/test_rest.c | 15 ++-- 3 files changed, 203 insertions(+), 9 deletions(-) diff --git a/src/rest/restd.c b/src/rest/restd.c index e10231c..63c62c6 100644 --- a/src/rest/restd.c +++ b/src/rest/restd.c @@ -1,7 +1,5 @@ - - /*! - * restd.h + * restd.c * * Copyright (c) 2015-2020, NADAL Jean-Baptiste. All rights reserved. * @@ -27,9 +25,17 @@ /*------------------------------- INCLUDES ----------------------------------*/ +#include + +#include +#include + +#include + #include #include #include +#include #include #include "macro.h" @@ -45,10 +51,41 @@ static bool initialized = false; */ int _restd_log_level = RESTD_LOG_WARN; +/** + * Server option names and default values. + */ +#define RESTD_SERVER_OPTIONS { \ + {"server.port", "8888"}, \ + \ + {"server.root_path", ""}, \ + \ + /* Addr format IPv4="1.2.3.4", IPv6="1:2:3:4:5:6", Unix="/path" */ \ + {"server.addr", "0.0.0.0"}, \ + \ + {"server.backlog", "128"}, \ + \ + /* Set read timeout seconds. 0 means no timeout. */ \ + {"server.timeout", "0"}, \ + \ + /* Enable or disable request pipelining, this change AD_DONE's behavior */ \ + {"server.request_pipelining", "1"}, \ + \ + /* Run server in a separate thread */ \ + {"server.thread", "0"}, \ + \ + /* Collect resources after stop */ \ + {"server.free_on_stop", "1"}, \ + \ + /* End of array marker. Do not remove */ \ + {"", "_END_"}}; +/*------------------------------- FUNCTIONS ----------------------------------*/ + +static int set_undefined_options(restd_server_t *server); char *restd_server_get_option(restd_server_t *server, const char *key); int restd_server_get_option_int(restd_server_t *server, const char *key); static void close_server(restd_server_t *server); +static void libevent_log_cb(int severity, const char *msg); static int notify_loopexit(restd_server_t *server); /*--------------------------- PUBLIC FUNCTIONS -------------------------------*/ @@ -128,6 +165,11 @@ void restd_server_free(restd_server_t *server) event_base_free(server->evbase); } + if (server->evhttp) + { + evhttp_free(server->evhttp); + } + if (server->options) { server->options->free(server->options); @@ -162,6 +204,95 @@ void restd_server_free(restd_server_t *server) */ int restd_server_start(restd_server_t *server) { + int ret; + DEBUG("Starting a server."); + + if (server == NULL) + return -1; + + // Set default options that were not set by user.. + set_undefined_options(server); + + // Hookup libevent's log message. + if (_restd_log_level >= RESTD_LOG_DEBUG) + { + event_set_log_callback(libevent_log_cb); + if (_restd_log_level >= RESTD_LOG_DEBUG2) + { + event_enable_debug_mode(); + } + } + + // Parse addr + int port = restd_server_get_option_int(server, "server.port"); + char *addr = restd_server_get_option(server, "server.addr"); + struct sockaddr *sockaddr = NULL; + size_t sockaddr_len = 0; + if (addr[0] == '/') + { // Unix socket. + struct sockaddr_un unixaddr; + bzero((void *)&unixaddr, sizeof(struct sockaddr_un)); + if (strlen(addr) >= sizeof(unixaddr.sun_path)) + { + errno = EINVAL; + DEBUG("Too long unix socket name. '%s'", addr); + return -1; + } + unixaddr.sun_family = AF_UNIX; + strcpy(unixaddr.sun_path, addr); // no need of strncpy() + sockaddr = (struct sockaddr *)&unixaddr; + sockaddr_len = sizeof(unixaddr); + } + else if (strstr(addr, ":")) + { // IPv6 + struct sockaddr_in6 ipv6addr; + bzero((void *)&ipv6addr, sizeof(struct sockaddr_in6)); + ipv6addr.sin6_family = AF_INET6; + ipv6addr.sin6_port = htons(port); + evutil_inet_pton(AF_INET6, addr, &ipv6addr.sin6_addr); + sockaddr = (struct sockaddr *)&ipv6addr; + sockaddr_len = sizeof(ipv6addr); + } + else + { // IPv4 + struct sockaddr_in ipv4addr; + bzero((void *)&ipv4addr, sizeof(struct sockaddr_in)); + ipv4addr.sin_family = AF_INET; + ipv4addr.sin_port = htons(port); + ipv4addr.sin_addr.s_addr = + (IS_EMPTY_STR(addr)) ? INADDR_ANY : inet_addr(addr); + sockaddr = (struct sockaddr *)&ipv4addr; + sockaddr_len = sizeof(ipv4addr); + } + + // Bind + if (!server->evbase) + { + server->evbase = event_base_new(); + if (!server->evbase) + { + ERROR("Failed to create a new event base."); + return -1; + } + } + + server->evhttp = evhttp_new(server->evbase); + if (!server->evhttp) + { + ERROR("Event http initialize failed!\n"); + return -2; + } + +#if 0 + ret = evhttp_bind_socket(server->evhttp, addr, port); + + if (ret != 0) + { + ERROR("Http bind server addr:%s & port:%d failed!\n", addr, port); + return -3; + } +#endif + return 0; } /*--------------------------------------------------------------------------*/ @@ -179,15 +310,45 @@ int restd_server_attach_event_loop(restd_server_t *server, struct event_base *ev void restd_server_set_option(restd_server_t *server, const char *key, const char *value) { + server->options->putstr(server->options, key, value); } +/*--------------------------------------------------------------------------*/ + void restd_server_register_hook_on_path(restd_server_t *server, const char *method, const char *path, restd_callback cb, void *userdata) { + restd_hook_t hook; + bzero((void *)&hook, sizeof(restd_hook_t)); + hook.method = (method) ? strdup(method) : NULL; + hook.path = (path) ? strdup(path) : NULL; + hook.cb = cb; + hook.userdata = userdata; + + server->hooks->addlast(server->hooks, (void *)&hook, sizeof(restd_hook_t)); } /*--------------------------- LOCAL FUNCTIONS -------------------------------*/ +/*--------------------------------------------------------------------------*/ + +// Set default options that were not set by user.. +static int set_undefined_options(restd_server_t *server) +{ + int newentries = 0; + char *default_options[][2] = RESTD_SERVER_OPTIONS; + for (int i = 0; !IS_EMPTY_STR(default_options[i][0]); i++) + { + if (!restd_server_get_option(server, default_options[i][0])) + { + restd_server_set_option(server, default_options[i][0], default_options[i][1]); + newentries++; + } + DEBUG("%s=%s", default_options[i][0], restd_server_get_option(server, default_options[i][0])); + } + return newentries; +} + /** * Retrieve server option. */ @@ -235,6 +396,35 @@ static void close_server(restd_server_t *server) INFO("Server closed."); } +/*--------------------------------------------------------------------------*/ + +static void libevent_log_cb(int severity, const char *msg) +{ + switch (severity) + { + case _EVENT_LOG_MSG: + { + INFO("%s", msg); + break; + } + case _EVENT_LOG_WARN: + { + WARN("%s", msg); + break; + } + case _EVENT_LOG_ERR: + { + ERROR("%s", msg); + break; + } + default: + { + DEBUG("%s", msg); + break; + } + } +} + /** * If there's no event, loopbreak or loopexit call won't work until one more * event arrived. So we use eventfd as a internal notification channel to let diff --git a/src/rest/restd.h b/src/rest/restd.h index 4ca23b9..c541089 100644 --- a/src/rest/restd.h +++ b/src/rest/restd.h @@ -93,6 +93,7 @@ struct restd_server_s qlist_t *hooks; /*!< list of registered hooks */ struct evconnlistener *listener; /*!< listener */ struct event_base *evbase; /*!< event base */ + struct evhttp *evhttp; struct bufferevent *notify_buffer; /*!< internal notification channel */ diff --git a/src/tests/test_rest.c b/src/tests/test_rest.c index 5af0b67..c48513c 100644 --- a/src/tests/test_rest.c +++ b/src/tests/test_rest.c @@ -46,17 +46,21 @@ int my_error_handler(short event, restd_conn_t *conn, void *userdata) } /*--------------------------------------------------------------------------*/ -#if 0 + int my_success_http_handler(short event, restd_conn_t *conn, void *userdata) { +#if 0 + if (event & RESTD_EVENT_READ) { restd_http_response(conn, 200, "application/json", ksuccess_get_body, strlen(ksuccess_get_body)); return RESTD_CLOSE; // Close connection. } return RESTD_OK; +#endif } +#if 0 /*--------------------------------------------------------------------------*/ int my_success_rest_get_handler(short event, restd_conn_t *conn, void *userdata) @@ -189,7 +193,7 @@ exit: return ret; } - +#endif /*--------------------------------------------------------------------------*/ bool found_route(restd_server_t *server, const char *method, const char *path) @@ -277,7 +281,7 @@ bool found_special_route(restd_server_t *server, const char *method, const char return false; } -#endif + /*--------------------------------------------------------------------------*/ TEST("Rest - create free\t") @@ -295,7 +299,7 @@ TEST("Rest - create free\t") } /*--------------------------------------------------------------------------*/ -#if 0 + TEST("Rest - create access regular route free\t") { restd_server_t *rest_server; @@ -323,7 +327,6 @@ TEST("Rest - create access regular route free\t") restd_server_free(rest_server); } - /*--------------------------------------------------------------------------*/ TEST("Rest - create access route with param free\t") @@ -367,7 +370,7 @@ TEST("Rest - create start free\t") } /*--------------------------------------------------------------------------*/ - +#if 0 TEST("Rest - create start access http hook free\t") { int ret;