From c7b9e005e8f155032af2b530b2ac3216b18d940d Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Tue, 18 Feb 2020 17:56:15 +0100 Subject: [PATCH] rest server wip --- lib/include/{restd.h => restd-old.h} | 0 lib/include/restd_http_handler.h | 4 +- src/CMakeLists.txt | 2 + src/rest/http_resp.c | 68 ++++ src/rest/http_resp.h | 94 ++++++ src/rest/rest_devices_handlers.c | 2 +- src/rest/rest_server.c | 2 +- src/rest/rest_server.h | 2 +- src/rest/restd.c | 447 ++++++++++++++++++++++++++- src/rest/restd.h | 28 +- src/tests/test_main.c | 2 +- src/tests/test_rest.c | 113 +++---- 12 files changed, 681 insertions(+), 83 deletions(-) rename lib/include/{restd.h => restd-old.h} (100%) create mode 100644 src/rest/http_resp.c create mode 100644 src/rest/http_resp.h diff --git a/lib/include/restd.h b/lib/include/restd-old.h similarity index 100% rename from lib/include/restd.h rename to lib/include/restd-old.h diff --git a/lib/include/restd_http_handler.h b/lib/include/restd_http_handler.h index 26e20c3..ecaf26e 100644 --- a/lib/include/restd_http_handler.h +++ b/lib/include/restd_http_handler.h @@ -77,7 +77,7 @@ typedef struct restd_http_s restd_http_t; #define RESTD_HOOK_ON_BODY (1 << 4) /*!< call on every time body data received */ #define RESTD_HOOK_ON_REQUEST (1 << 5) /*!< call with complete request */ #define RESTD_HOOK_ON_CLOSE (1 << 6) /*!< call right before closing or next request */ - +#if 0 enum restd_http_request_status_e { RESTD_HTTP_REQ_INIT = 0, /*!< initial state */ @@ -87,7 +87,7 @@ enum restd_http_request_status_e RESTD_HTTP_ERROR, /*!< unrecoverable error found. */ }; - +#endif /*----------------------------------------------------------------------------*\ | PUBLIC FUNCTIONS | \*----------------------------------------------------------------------------*/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fe4c22..933312c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ file( devices/shutter.c devices/sprinkler.c rest/restd.c + rest/http_resp.c rest/rest_devices_handlers.c rest/rest_server.c ) @@ -62,6 +63,7 @@ endif() # Tests if(DOMO_BUILD_TEST) add_definitions("-fprofile-arcs -ftest-coverage") +add_definitions (-g -DBUILD_DEBUG) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov --coverage") add_executable (test_device tests/test_main.c ${source_files}) diff --git a/src/rest/http_resp.c b/src/rest/http_resp.c new file mode 100644 index 0000000..36bbf88 --- /dev/null +++ b/src/rest/http_resp.c @@ -0,0 +1,68 @@ +/*! + * http_resp.c + * + * Copyright (c) 2015-2020, NADAL Jean-Baptiste. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * @Author: NADAL Jean-Baptiste + * @Date: 07/02/2020 + * + */ + +/*------------------------------- INCLUDES ----------------------------------*/ + +#include + +#include "http_resp.h" + +#include "macro.h" + +/*--------------------------------------------------------------------------*/ + +http_resp_t *http_resp_new(void) +{ + http_resp_t *resp = NEW_OBJECT(http_resp_t); + if (resp == NULL) + { + return NULL; + } + + resp->ev_req = NULL; + resp->ev_uri = NULL; + //resp->path_params = 0; + resp->head_params = NULL; + //resp->post_params = {0}; + resp->post_param_parsed = false; + resp->str_body = NULL; + resp->resp_headers = NULL; + resp->resp_buf = NULL; + resp->resp_code = HTTP_OK; + + DEBUG("Created an http response object."); + return resp; +} + +/*--------------------------------------------------------------------------*/ + +void http_resp_free(http_resp_t *resp) +{ + if (resp == NULL) + { + return; + } + +} diff --git a/src/rest/http_resp.h b/src/rest/http_resp.h new file mode 100644 index 0000000..33ae0df --- /dev/null +++ b/src/rest/http_resp.h @@ -0,0 +1,94 @@ +/*! + * http_resp.h + * + * Copyright (c) 2015-2020, NADAL Jean-Baptiste. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * @Author: NADAL Jean-Baptiste + * @Date: 07/02/2020 + * + */ + +#ifndef _HTTP_RESP_H +#define _HTTP_RESP_H + +/*------------------------------- INCLUDES ----------------------------------*/ + +#include + +#include + +typedef struct http_resp_s http_resp_t; + +/** + * Server info container. + */ +struct http_resp_s +{ + struct evhttp_request *ev_req; + const struct evhttp_uri *ev_uri; + struct evkeyvalq path_params; + struct evkeyvalq *head_params; + struct evkeyvalq post_params; + bool post_param_parsed; + char *str_body; + struct evkeyvalq *resp_headers; + struct evbuffer *resp_buf; + int resp_code; +}; + + +extern http_resp_t *http_resp_new(void); +extern void http_resp_free(http_resp_t *resp); + +#if 0 + EvHttpResp(struct evhttp_request *req) throw(EvHttpRespRTEXCP); + ~EvHttpResp(); + + std::string GetRequestUri(); + std::string GetUriHost(); //#TODO add const of this + /// It will return -1 if no port set + int GetUriPort(); + std::string GetUriPath(); + std::string GetUriQuery(); + /// Useless to get from a request url, fragment is only for browser to locate sth. + std::string GetUriFragment(); + + std::string GetHeadParam(std::string const &strKey); + std::string GetPathParam(std::string const &strKey); + std::string GetPostParam(std::string const &strKey); + + std::string GetPostMsg(); + + bool AddRespHeadParam(std::string const &key, std::string const &val); + void AddRespHeaders(HttpHeaders& headers); + bool AddRespString(std::string const &str); + /// This will cause data memcpy, if not so, user have to make sure data lifetime last until be read + /// #TODO: This func is dangerious, NOT RECOMMEND to use, for if len is larger than actual, cause overflow + bool AddRespBuf(void const *data, std::size_t len); + bool AddRespFile(std::string const &fileName); + + void SetRespCode(int code); + /// Make sure code and all response body has finished set + void SendResponse(); + void QuickResponse(int code, std::string const &strBody); + void SimpleResponse(int code, HttpHeaders &headers, std::string const &strBody); + /// If strMsg is empty, libevent will use default error code message instead + void RespError(int nCode, std::string const &strMsg); +#endif + +#endif /* _HTTP_RESP_H */ diff --git a/src/rest/rest_devices_handlers.c b/src/rest/rest_devices_handlers.c index 88901e9..8280198 100644 --- a/src/rest/rest_devices_handlers.c +++ b/src/rest/rest_devices_handlers.c @@ -31,7 +31,7 @@ #include "devices/devices_manager.h" #include "domo.h" -#include "rest_devices_handlers.h" +//#include "rest_devices_handlers.h" // Code Description // 204 Success. No content. diff --git a/src/rest/rest_server.c b/src/rest/rest_server.c index 1bb2158..b8f11d7 100644 --- a/src/rest/rest_server.c +++ b/src/rest/rest_server.c @@ -28,7 +28,7 @@ /*------------------------------- INCLUDES ----------------------------------*/ -#include "rest_devices_handlers.h" +//#include "rest_devices_handlers.h" #include "rest_server.h" diff --git a/src/rest/rest_server.h b/src/rest/rest_server.h index f93c3c9..fb0dc02 100644 --- a/src/rest/rest_server.h +++ b/src/rest/rest_server.h @@ -28,7 +28,7 @@ /*------------------------------- INCLUDES ----------------------------------*/ -#include +#include extern int setup_rest_server(restd_server_t *rest_server, const char *port, const char *root_path, void *dm); diff --git a/src/rest/restd.c b/src/rest/restd.c index 63c62c6..c09b92b 100644 --- a/src/rest/restd.c +++ b/src/rest/restd.c @@ -35,12 +35,25 @@ #include #include #include -#include +#include #include +#ifdef __linux__ +#include +#else +#include +#endif + #include "macro.h" #include "restd.h" + +struct mimetype_s +{ + const char *extn; + const char *mime; +}; + /* * Local variables. */ @@ -51,6 +64,77 @@ static bool initialized = false; */ int _restd_log_level = RESTD_LOG_WARN; +/*--------------------------------------------------------------------------*/ + +static const struct mimetype_s g_mime_types[] = { + + {"txt", "text/plain"}, + {"log", "text/plain"}, + {"js", "text/javascript"}, + {"css", "text/css"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"diff", "text/x-patch"}, + {"patch", "text/x-patch"}, + {"c", "text/x-csrc"}, + {"h", "text/x-chdr"}, + {"o", "text/x-object"}, + {"ko", "text/x-object"}, + + {"bmp", "image/bmp"}, + {"gif", "image/gif"}, + {"png", "image/png"}, + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, + {"svg", "image/svg+xml"}, + + {"json", "application/json"}, + {"jsonp", "application/javascript"}, + {"zip", "application/zip"}, + {"pdf", "application/pdf"}, + {"xml", "application/xml"}, + {"xsl", "application/xml"}, + {"doc", "application/msword"}, + {"ppt", "application/vnd.ms-powerpoint"}, + {"xls", "application/vnd.ms-excel"}, + {"odt", "application/vnd.oasis.opendocument.text"}, + {"odp", "application/vnd.oasis.opendocument.presentation"}, + {"pl", "application/x-perl"}, + {"sh", "application/x-shellscript"}, + {"php", "application/x-php"}, + {"deb", "application/x-deb"}, + {"iso", "application/x-cd-image"}, + {"tar.gz", "application/x-compressed-tar"}, + {"tgz", "application/x-compressed-tar"}, + {"gz", "application/x-gzip"}, + {"tar.bz2", "application/x-bzip-compressed-tar"}, + {"tbz", "application/x-bzip-compressed-tar"}, + {"bz2", "application/x-bzip"}, + {"tar", "application/x-tar"}, + {"rar", "application/x-rar-compressed"}, + + {"mp3", "audio/mpeg"}, + {"ogg", "audio/x-vorbis+ogg"}, + {"wav", "audio/x-wav"}, + + {"mpg", "video/mpeg"}, + {"mpeg", "video/mpeg"}, + {"avi", "video/x-msvideo"}, + + {"README", "text/plain"}, + {"log", "text/plain"}, + {"cfg", "text/plain"}, + {"conf", "text/plain"}, + + {"pac", "application/x-ns-proxy-autoconfig"}, + {"wpad.dat", "application/x-ns-proxy-autoconfig"}, + {"appcache", "text/cache-manifest"}, + {"manifest", "text/cache-manifest"}, + + {NULL, NULL}}; + +/*--------------------------------------------------------------------------*/ + /** * Server option names and default values. */ @@ -85,8 +169,11 @@ 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 *server_loop(void *instance); static void libevent_log_cb(int severity, const char *msg); static int notify_loopexit(restd_server_t *server); +static void notify_cb(struct bufferevent *buffer, void *userdata); +void rest_request_cb(struct evhttp_request *req, void *arg); /*--------------------------- PUBLIC FUNCTIONS -------------------------------*/ @@ -160,16 +247,16 @@ void restd_server_free(restd_server_t *server) close_server(server); } - if (server->evbase) - { - event_base_free(server->evbase); - } - if (server->evhttp) { evhttp_free(server->evhttp); } + if (server->evbase) + { + event_base_free(server->evbase); + } + if (server->options) { server->options->free(server->options); @@ -184,8 +271,6 @@ void restd_server_free(restd_server_t *server) restd_hook_t *hook; while ((hook = tbl->popfirst(tbl, NULL))) { - if (hook->method) - free(hook->method); if (hook->path) free(hook->path); free(hook); @@ -283,7 +368,8 @@ int restd_server_start(restd_server_t *server) return -2; } -#if 0 + evhttp_set_gencb(server->evhttp, rest_request_cb, server); + ret = evhttp_bind_socket(server->evhttp, addr, port); if (ret != 0) @@ -291,8 +377,41 @@ int restd_server_start(restd_server_t *server) ERROR("Http bind server addr:%s & port:%d failed!\n", addr, port); return -3; } + + // Listen + INFO("Listening on %s:%d", addr, port); + + // Create a eventfd for notification channel. +#ifdef __linux__ + int notifyfd = eventfd(0, 0); +#else + int notifyfd = kqueue(); #endif - return 0; + server->notify_buffer = bufferevent_socket_new(server->evbase, notifyfd, BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(server->notify_buffer, NULL, notify_cb, NULL, server); + + int exitstatus = 0; + if (restd_server_get_option_int(server, "server.thread")) + { + DEBUG("Launching server as a thread.\n"); + server->thread = NEW_OBJECT(pthread_t); + pthread_create(server->thread, NULL, &server_loop, (void *)server); + //pthread_detach(server->thread); + } + else + { + int *retval = server_loop(server); + exitstatus = *retval; + free(retval); + + close_server(server); + if (restd_server_get_option_int(server, "server.free_on_stop")) + { + restd_server_free(server); + } + } + + return exitstatus; } /*--------------------------------------------------------------------------*/ @@ -315,12 +434,12 @@ void restd_server_set_option(restd_server_t *server, const char *key, const char /*--------------------------------------------------------------------------*/ -void restd_server_register_hook_on_path(restd_server_t *server, const char *method, const char *path, +void restd_server_register_hook_on_path(restd_server_t *server, enum evhttp_cmd_type 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.method = method; hook.path = (path) ? strdup(path) : NULL; hook.cb = cb; hook.userdata = userdata; @@ -328,6 +447,27 @@ void restd_server_register_hook_on_path(restd_server_t *server, const char *meth server->hooks->addlast(server->hooks, (void *)&hook, sizeof(restd_hook_t)); } +/*--------------------------------------------------------------------------*/ + +void restd_http_response(struct evhttp_request *req, int code, const char *contenttype, const char *data) +{ + struct evbuffer *resp_buf; + struct evkeyvalq *resp_headers; + + resp_buf = evhttp_request_get_output_buffer(req); + resp_headers = evhttp_request_get_output_headers(req); + + if (data != NULL) + { + evbuffer_add_printf(resp_buf, "%s", data); + } + + evhttp_add_header(resp_headers, "Content-Type", contenttype); + + evhttp_send_reply(req, code, NULL ,resp_buf); +} + + /*--------------------------- LOCAL FUNCTIONS -------------------------------*/ /*--------------------------------------------------------------------------*/ @@ -398,6 +538,21 @@ static void close_server(restd_server_t *server) /*--------------------------------------------------------------------------*/ +static void *server_loop(void *instance) +{ + restd_server_t *server = (restd_server_t *)instance; + + int *retval = NEW_OBJECT(int); + DEBUG("Loop start\n"); + event_base_loop(server->evbase, 0); + DEBUG("Loop finished\n"); + *retval = (event_base_got_break(server->evbase)) ? -1 : 0; + + return retval; +} + +/*--------------------------------------------------------------------------*/ + static void libevent_log_cb(int severity, const char *msg) { switch (severity) @@ -435,3 +590,271 @@ static int notify_loopexit(restd_server_t *server) uint64_t x = 0; return bufferevent_write(server->notify_buffer, &x, sizeof(uint64_t)); } + +/*--------------------------------------------------------------------------*/ + +static void notify_cb(struct bufferevent *buffer, void *userdata) +{ + restd_server_t *server = (restd_server_t *)userdata; + event_base_loopexit(server->evbase, NULL); + DEBUG("Existing loop."); +} + +/*--------------------------------------------------------------------------*/ + +static const char *file_mime_lookup(const char *path) +{ + const struct mimetype_s *m = &g_mime_types[0]; + const char *e; + + while (m->extn) + { + e = &path[strlen(path) - 1]; + + while (e >= path) + { + if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn)) + return m->mime; + + e--; + } + + m++; + } + + return "application/octet-stream"; +} + +/*--------------------------------------------------------------------------*/ + +static bool contain(const char *src, const char *dest, int len) +{ + int len_src, len_dest; + int i = 0; + + len_src = strlen(src); + if (len_src < len) + return false; + + len_dest = strlen(dest); + if (len_dest < len) + return false; + + while (i < len) + { + if (src[i] != dest[i]) + return false; + i++; + } + + return true; +} + +/*--------------------------------------------------------------------------*/ + +void rest_request_cb(struct evhttp_request *req, void *arg) +{ + const char *cmdtype; + restd_server_t *server = (restd_server_t *)arg; + + printf("request.\n"); + switch (evhttp_request_get_command(req)) + { + case EVHTTP_REQ_GET: + cmdtype = "GET"; + break; + case EVHTTP_REQ_POST: + cmdtype = "POST"; + break; + case EVHTTP_REQ_HEAD: + cmdtype = "HEAD"; + break; + case EVHTTP_REQ_PUT: + cmdtype = "PUT"; + break; + case EVHTTP_REQ_DELETE: + cmdtype = "DELETE"; + break; + case EVHTTP_REQ_OPTIONS: + cmdtype = "OPTIONS"; + break; + case EVHTTP_REQ_TRACE: + cmdtype = "TRACE"; + break; + case EVHTTP_REQ_CONNECT: + cmdtype = "CONNECT"; + break; + case EVHTTP_REQ_PATCH: + cmdtype = "PATCH"; + break; + default: + cmdtype = "unknown"; + break; + } + + printf("Received a %s request for %s\nHeaders:\n", + cmdtype, evhttp_request_get_uri(req)); + + qlist_t *hooks = server->hooks; + //int reason = RESTD_ERROR_PATH_NOT_FOUND; + qlist_obj_t obj; + bzero((void *)&obj, sizeof(qlist_obj_t)); + while (hooks->getnext(hooks, &obj, false) == true) + { + restd_hook_t *hook = (restd_hook_t *)obj.data; + if (hook->cb) + { + const char *request_path = evhttp_request_get_uri(req); + printf("==== call_hooks: method: %d - %d \n", hook->method, evhttp_request_get_command(req)); + printf("==== call_hooks: path: %s - %s\n", hook->path, request_path); + printf("==== HOOK FOUND !!!!\n"); + if (hook->method != evhttp_request_get_command(req)) + { + printf("==== Hook found but method failed -> next.\n"); + //reason = RESTD_ERROR_METHOD_NOT_ALLOWED; + continue; + } + + if ((hook->path != NULL) && (request_path != NULL)) + { + int i = 0; + int pos = -1; + while (hook->path[i]) + { + if (hook->path[i] == ':') + pos = i; + i++; + } + if (pos != -1 && contain(hook->path, request_path, pos)) + { + const char *buffer = &request_path[pos]; + // printf("buffer: <%s>\n", buffer); + // TODO conn->id = atoi(buffer); + hook->cb(req, hook->userdata); + return; + } + else + { + int rett = strcmp(hook->path, request_path); + if (rett == 0) + { + hook->cb(req, hook->userdata); + return; + } + + } + } + } + } + + evhttp_send_reply(req, 200, "OK", NULL); +} +#if 0 +int restd_rest_handler(short event, restd_conn_t *conn) +{ + if (event & RESTD_EVENT_INIT) + { + DEBUG("==> HTTP INIT"); + restd_http_t *http = http_new(conn->out); + if (http == NULL) + return RESTD_CLOSE; + restd_conn_set_extra(conn, http, http_free_cb); + return RESTD_OK; + } + else if (event & RESTD_EVENT_CLOSE) + { + DEBUG("==> HTTP CLOSE=%x (TIMEOUT=%d, SHUTDOWN=%d)", + event, event & RESTD_EVENT_TIMEOUT, event & RESTD_EVENT_SHUTDOWN); + return RESTD_OK; + } + else if ((event & RESTD_EVENT_READ) || (event & RESTD_EVENT_WRITE)) + { + restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); + int status = http_parser(http, conn->in); + if (conn->method == NULL && http->request.method != NULL) + { + restd_conn_set_method(conn, http->request.method); + } + DEBUG("==> HTTP READ || HTTP WRITE"); + int reason = RESTD_ERROR_PATH_NOT_FOUND; + DEBUG("********restd_rest_handler: event 0x%x", event); + char *root_path; + qlist_t *hooks = conn->server->hooks; + + qlist_obj_t obj; + bzero((void *)&obj, sizeof(qlist_obj_t)); + while (hooks->getnext(hooks, &obj, false) == true) + { + restd_hook_t *hook = (restd_hook_t *)obj.data; + if (hook->cb) + { + printf("==== call_hooks: method: %s - %s \n", hook->method, conn->method); + printf("==== call_hooks: path: %s - %s\n", hook->path, http->request.path); + printf("==== HOOK FOUND !!!!\n"); + if ((hook->method == NULL) || (conn->method == NULL) || (strcmp(hook->method, conn->method) != 0)) + { + printf("==== Hook found but method failed -> next.\n"); + reason = RESTD_ERROR_METHOD_NOT_ALLOWED; + continue; + } + + if ((hook->path != NULL) && (http->request.path != NULL)) + { + int i = 0; + int pos = -1; + while (hook->path[i]) + { + if (hook->path[i] == ':') + pos = i; + i++; + } + if (pos != -1 && contain(hook->path, http->request.path, pos)) + { + const char *buffer = &http->request.path[pos]; + // printf("buffer: <%s>\n", buffer); + conn->id = atoi(buffer); + return hook->cb(event, conn, hook->userdata); + } + else + { + int rett = strcmp(hook->path, http->request.path); + if (rett == 0) + return hook->cb(event, conn, hook->userdata); + } + } + } + } + // No Hook Found check if it's a real file into document root. + root_path = restd_server_get_option(conn->server, "server.root_path"); + if ((root_path != NULL) && (strlen(root_path) != 0)) + { + FILE *file; + char buf[1024] = ""; + qstrcatf(buf, "%s%s", root_path, http->request.path); + if ((file = fopen(buf, "r"))) + { + long fsize; + char *file_buf; + fseek(file, 0, SEEK_END); + fsize = ftell(file); + fseek(file, 0, SEEK_SET); + file_buf = malloc(fsize + 1); + fread(file_buf, 1, fsize, file); + fclose(file); + file_buf[fsize] = 0; + + printf("mime type:%s\n", file_mime_lookup(buf)); + restd_http_response(conn, 200, file_mime_lookup(buf), file_buf, fsize); + return RESTD_CLOSE; + } + } + + if (conn->server->error_handler != NULL) + return conn->server->error_handler(reason, conn, NULL); + return RESTD_CLOSE; + } + + BUG_EXIT(); + return RESTD_CLOSE; +} +#endif \ No newline at end of file diff --git a/src/rest/restd.h b/src/rest/restd.h index c541089..e2228eb 100644 --- a/src/rest/restd.h +++ b/src/rest/restd.h @@ -30,6 +30,8 @@ #include +#include + #include /*------------------------------- TYPEDEFS ----------------------------------*/ @@ -37,7 +39,12 @@ typedef struct restd_server_s restd_server_t; typedef struct restd_hook_s restd_hook_t; -typedef int (*restd_callback)(short event, void *conn, void *userdata); +/** + * User callback(hook) prototype. + */ +typedef int (*restd_call_hook_cb)(short event, void *conn); +typedef int (*restd_callback)(struct evhttp_request *req, void *arg); +typedef void (*restd_userdata_free_cb)(void *conn, void *userdata); /*------------------------------- INCLUDES ----------------------------------*/ @@ -47,9 +54,12 @@ extern int restd_server_start(restd_server_t *server); extern int restd_server_attach_event_loop(restd_server_t *server, struct event_base *ev_base); extern void restd_server_set_option(restd_server_t *server, const char *key, const char *value); -extern void restd_server_register_hook_on_path(restd_server_t *server, const char *method, const char *path, +extern void restd_server_register_hook_on_path(restd_server_t *server, enum evhttp_cmd_type method, const char *path, restd_callback cb, void *userdata); +extern void restd_http_response(struct evhttp_request *req, int code, const char *contenttype, const char *data); + + /*------------------------------- DEFINES ------------------------------------*/ /* @@ -65,16 +75,16 @@ enum restd_log_e RESTD_LOG_DEBUG2, }; + + +#define RESTD_OK (0) /*!< I'm done with this request. Escalate to other hooks. */ +#define RESTD_FAILED (1) /*!< I'm done with this request. But the Process failed. */ + /*---------------------------------------------------------------------------*\ | USER-CALLBACK | \*---------------------------------------------------------------------------*/ -/** - * User callback(hook) prototype. - */ -typedef int (*restd_call_hook_cb)(short event, void *conn); -typedef int (*restd_callback)(short event, void *conn, void *userdata); -typedef void (*restd_userdata_free_cb)(void *conn, void *userdata); + /*---------------------------------------------------------------------------*\ | DATA STRUCTURES | @@ -107,7 +117,7 @@ struct restd_server_s */ struct restd_hook_s { - char *method; + enum evhttp_cmd_type method; char *path; restd_callback cb; void *userdata; diff --git a/src/tests/test_main.c b/src/tests/test_main.c index 6e163a1..90e7f72 100644 --- a/src/tests/test_main.c +++ b/src/tests/test_main.c @@ -34,7 +34,7 @@ #include -#include +#include #include "qunit.h" diff --git a/src/tests/test_rest.c b/src/tests/test_rest.c index c48513c..47838e5 100644 --- a/src/tests/test_rest.c +++ b/src/tests/test_rest.c @@ -39,7 +39,7 @@ /*--------------------------------------------------------------------------*/ -int my_error_handler(short event, restd_conn_t *conn, void *userdata) +int my_error_handler(struct evhttp_request *req, void *arg) { //restd_http_response(conn, 200, "application/json", "{\"status\":\"error\"}", 18); //return RESTD_CLOSE; // Close connection. @@ -47,43 +47,35 @@ int my_error_handler(short event, restd_conn_t *conn, void *userdata) /*--------------------------------------------------------------------------*/ -int my_success_http_handler(short event, restd_conn_t *conn, void *userdata) +int my_success_http_handler(struct evhttp_request *req, void *arg) { -#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. - } + restd_http_response(req, 200, "application/json", ksuccess_get_body); return RESTD_OK; -#endif } -#if 0 + /*--------------------------------------------------------------------------*/ -int my_success_rest_get_handler(short event, restd_conn_t *conn, void *userdata) +int my_success_rest_get_handler(struct evhttp_request *req, void *arg) { - restd_http_response(conn, 200, "application/json", ksuccess_get_body, strlen(ksuccess_get_body)); - return RESTD_CLOSE; // Close connection. + restd_http_response(req, 200, "application/json", ksuccess_get_body); + return RESTD_OK; } /*--------------------------------------------------------------------------*/ -int my_success_rest_delete_handler(short event, restd_conn_t *conn, void *userdata) +int my_success_rest_delete_handler(struct evhttp_request *req, void *arg) { - restd_http_response(conn, 200, "application/json", ksuccess_delete_body, strlen(ksuccess_delete_body)); - return RESTD_CLOSE; // Close connection. + restd_http_response(req, 200, "application/json", ksuccess_delete_body); + return RESTD_OK; } /*--------------------------------------------------------------------------*/ -int my_success_rest_put_handler(short event, restd_conn_t *conn, void *userdata) +int my_success_rest_put_handler(struct evhttp_request *req, void *arg) { - printf("put event: %d\n", event); - restd_http_response(conn, 200, "application/json", ksuccess_put_body, strlen(ksuccess_put_body)); - return RESTD_CLOSE; // Close connection. + restd_http_response(req, 200, "application/json", ksuccess_put_body); + return RESTD_OK; } /*--------------------------------------------------------------------------*/ @@ -154,6 +146,8 @@ int exec_request(const char *request, const char *path, int expected_code, const curl_easy_setopt(curl_handle, CURLOPT_POST, 0); } + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 2); + // Debug Only //curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1); @@ -169,11 +163,18 @@ int exec_request(const char *request, const char *path, int expected_code, const printf("Error: %d\n", res); } ASSERT_EQUAL_INT(res, CURLE_OK); - + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_result_code); ASSERT_EQUAL_INT(http_result_code, expected_code); - res = strcmp(expected_body, body); + if (body == NULL) + { + res = 1; + } + else + { + res = strcmp(expected_body, body); + } //printf ("body: %s != expected_body: %s\n", body, expected_body); if (res != 0) { @@ -193,10 +194,10 @@ exit: return ret; } -#endif + /*--------------------------------------------------------------------------*/ -bool found_route(restd_server_t *server, const char *method, const char *path) +bool found_route(restd_server_t *server, enum evhttp_cmd_type method, const char *path) { qlist_t *hooks = server->hooks; @@ -205,7 +206,7 @@ bool found_route(restd_server_t *server, const char *method, const char *path) while (hooks->getnext(hooks, &obj, false) == true) { restd_hook_t *hook = (restd_hook_t *)obj.data; - if (hook->method && method && strcmp(hook->method, method) == 0) + if (hook->method == method) { if (hook->path && path && strcmp(hook->path, path) == 0) return true; @@ -242,7 +243,7 @@ bool contain(const char *src, const char *dest, int len) /*--------------------------------------------------------------------------*/ -bool found_special_route(restd_server_t *server, const char *method, const char *path, int *id) +bool found_special_route(restd_server_t *server, enum evhttp_cmd_type method, const char *path, int *id) { qlist_t *hooks = server->hooks; @@ -251,7 +252,7 @@ bool found_special_route(restd_server_t *server, const char *method, const char while (hooks->getnext(hooks, &obj, false) == true) { restd_hook_t *hook = (restd_hook_t *)obj.data; - if (hook->method && method && strcmp(hook->method, method) == 0) + if (hook->method == method) { if (hook->path && path) { @@ -293,7 +294,7 @@ TEST("Rest - create free\t") restd_server_set_option(rest_server, "server.port", kserver_port); - restd_server_register_hook_on_path(rest_server, "POST", "/api/v1/test", my_error_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_POST, "/api/v1/test", my_error_handler, NULL); restd_server_free(rest_server); } @@ -309,21 +310,21 @@ TEST("Rest - create access regular route free\t") restd_server_set_option(rest_server, "server.port", kserver_port); - restd_server_register_hook_on_path(rest_server, "POST", "/api/v1/test", my_success_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_POST, "/api/v1/test", my_success_http_handler, NULL); - ASSERT_TRUE(found_route(rest_server, "POST", "/api/v1/test")); - ASSERT_FALSE(found_route(rest_server, "POST", "/api/v1/notfound")); + ASSERT_TRUE(found_route(rest_server, EVHTTP_REQ_POST, "/api/v1/test")); + ASSERT_FALSE(found_route(rest_server, EVHTTP_REQ_POST, "/api/v1/notfound")); - restd_server_register_hook_on_path(rest_server, "GET", "/api/v1/test", my_success_http_handler, NULL); - ASSERT_TRUE(found_route(rest_server, kget_method, "/api/v1/test")); - restd_server_register_hook_on_path(rest_server, "GET", "/api/v1/test", my_success_http_handler, NULL); - ASSERT_TRUE(found_route(rest_server, kget_method, "/api/v1/test")); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/test", my_success_http_handler, NULL); + ASSERT_TRUE(found_route(rest_server, EVHTTP_REQ_GET, "/api/v1/test")); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/test", my_success_http_handler, NULL); + ASSERT_TRUE(found_route(rest_server, EVHTTP_REQ_GET, "/api/v1/test")); - restd_server_register_hook_on_path(rest_server, "PUT", "/api/v1/test", my_success_http_handler, NULL); - ASSERT_TRUE(found_route(rest_server, "PUT", "/api/v1/test")); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_PUT, "/api/v1/test", my_success_http_handler, NULL); + ASSERT_TRUE(found_route(rest_server, EVHTTP_REQ_PUT, "/api/v1/test")); - restd_server_register_hook_on_path(rest_server, kdelete_method, "/api/v1/test", my_success_http_handler, NULL); - ASSERT_TRUE(found_route(rest_server, kdelete_method, "/api/v1/test")); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_DELETE, "/api/v1/test", my_success_http_handler, NULL); + ASSERT_TRUE(found_route(rest_server, EVHTTP_REQ_DELETE, "/api/v1/test")); restd_server_free(rest_server); } @@ -336,13 +337,13 @@ TEST("Rest - create access route with param free\t") rest_server = restd_server_new(); ASSERT_NOT_NULL(rest_server); - restd_server_register_hook_on_path(rest_server, kget_method, "/api/v1/klong/:id", my_success_http_handler, NULL); - restd_server_register_hook_on_path(rest_server, kget_method, "/api/v1/donkey", my_success_http_handler, NULL); - restd_server_register_hook_on_path(rest_server, kget_method, "/api/v1/test/:id", my_success_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/klong/:id", my_success_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/donkey", my_success_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/test/:id", my_success_http_handler, NULL); - ASSERT_FALSE(found_special_route(rest_server, kget_method, "/api/v1/notfound/77", &id)); + ASSERT_FALSE(found_special_route(rest_server, EVHTTP_REQ_GET, "/api/v1/notfound/77", &id)); - ASSERT_TRUE(found_special_route(rest_server, kget_method, "/api/v1/test/77", &id)); + ASSERT_TRUE(found_special_route(rest_server, EVHTTP_REQ_GET, "/api/v1/test/77", &id)); ASSERT_EQUAL_INT(id, 77); restd_server_free(rest_server); @@ -361,7 +362,7 @@ TEST("Rest - create start free\t") restd_server_set_option(rest_server, "server.port", kserver_port); restd_server_set_option(rest_server, "server.thread", "1"); - restd_server_register_hook_on_path(rest_server, kget_method, "/api/v1/test/:id", my_success_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, "/api/v1/test/:id", my_success_http_handler, NULL); ret = restd_server_start(rest_server); ASSERT_EQUAL_INT(ret, 0); @@ -370,7 +371,7 @@ TEST("Rest - create start free\t") } /*--------------------------------------------------------------------------*/ -#if 0 + TEST("Rest - create start access http hook free\t") { int ret; @@ -382,8 +383,8 @@ TEST("Rest - create start access http hook free\t") restd_server_set_option(rest_server, "server.port", kserver_port); restd_server_set_option(rest_server, "server.thread", "1"); - restd_server_register_hook(rest_server, restd_http_handler, NULL); - restd_server_register_hook_on_path(rest_server, "GET", kapi_test_get, my_success_http_handler, NULL); + // restd_server_register_hook(rest_server, restd_http_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, kapi_test_get, my_success_http_handler, NULL); ret = restd_server_start(rest_server); ASSERT_EQUAL_INT(ret, 0); @@ -409,11 +410,11 @@ TEST("Rest - create start access rest hook free\t") restd_server_set_option(rest_server, "server.port", kserver_port); restd_server_set_option(rest_server, "server.thread", "1"); - restd_server_register_call_hooks_handler(rest_server, restd_rest_handler); + //restd_server_register_call_hooks_handler(rest_server, restd_rest_handler); - restd_server_register_hook_on_path(rest_server, kget_method, kapi_test_get, my_success_rest_get_handler, NULL); - restd_server_register_hook_on_path(rest_server, kput_method, kapi_test_get, my_success_rest_put_handler, NULL); - restd_server_register_hook_on_path(rest_server, kdelete_method, kapi_test_get, my_success_rest_delete_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_GET, kapi_test_get, my_success_rest_get_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_PUT, kapi_test_get, my_success_rest_put_handler, NULL); + restd_server_register_hook_on_path(rest_server, EVHTTP_REQ_DELETE, kapi_test_get, my_success_rest_delete_handler, NULL); ret = restd_server_start(rest_server); ASSERT_EQUAL_INT(ret, 0); @@ -423,17 +424,17 @@ TEST("Rest - create start access rest hook free\t") PRINTLN("\n - GET"); ret = exec_request(kget_method, "http://localhost:"kserver_port kapi_test_get, 200, ksuccess_get_body); ASSERT_EQUAL_INT(ret, 0); - + PRINTLN("\n - DELETE"); ret = exec_request(kdelete_method, "http://localhost:"kserver_port kapi_test_get, 200, ksuccess_delete_body); ASSERT_EQUAL_INT(ret, 0); + #if 0 PRINTLN("\n - PUT"); ret = exec_request(kput_method, "http://localhost:"kserver_port kapi_test_get, 200, ksuccess_put_body); ASSERT_EQUAL_INT(ret, 0); - +#endif // TODO POST restd_server_free(rest_server); } -#endif \ No newline at end of file