diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d0b13fe..9fe59ec 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,4 +12,5 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) add_library(restd-static STATIC src/restd_server.c src/restd_http_handler.c - ) + src/restd_rest_handler.c + ) diff --git a/lib/include/restd.h b/lib/include/restd.h index aae55d0..5a05a67 100644 --- a/lib/include/restd.h +++ b/lib/include/restd.h @@ -30,6 +30,7 @@ #include "restd_server.h" #include "restd_http_handler.h" +#include "restd_rest_handler.h" #ifdef __cplusplus extern "C" diff --git a/lib/include/restd_http_handler.h b/lib/include/restd_http_handler.h index 9d52b41..6855ac7 100644 --- a/lib/include/restd_http_handler.h +++ b/lib/include/restd_http_handler.h @@ -124,7 +124,7 @@ struct restd_http_s struct { enum restd_http_request_status_e status; /*!< request status. */ - struct evbuffer *inbuf; /*!< input data buffer. */ + struct evbuffer *inbuf; /*!< input data buffer. */ // request line - available on REQ_REQUESTLINE_DONE. char *method; /*!< request method ex) GET */ diff --git a/lib/include/restd_rest_handler.h b/lib/include/restd_rest_handler.h new file mode 100644 index 0000000..96761ce --- /dev/null +++ b/lib/include/restd_rest_handler.h @@ -0,0 +1,33 @@ +/*! + * restd_rest_handler.h + * + * Copyright (c) 2015-2019, 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: 23/12/2019 + * + */ + +#ifndef _RESTD_REST_HANDLER_H +#define _RESTD_REST_HANDLER_H + +/*------------------------------- INCLUDES ----------------------------------*/ + +extern int restd_rest_handler(short event, restd_conn_t *conn, void *userdata); + +#endif /* _RESTD_REST_HANDLER_H */ \ No newline at end of file diff --git a/lib/include/restd_server.h b/lib/include/restd_server.h index 9369d4b..3ecc6c5 100644 --- a/lib/include/restd_server.h +++ b/lib/include/restd_server.h @@ -47,6 +47,9 @@ typedef struct restd_conn_s restd_conn_t; #define RESTD_DONE (2) /*!< We're done with this request but keep the connection open. */ #define RESTD_CLOSE (3) /*!< We're done with this request. Close as soon as we sent all data out. */ +#define RESTD_NOT_ALLOWED (4) +#define RESTD_NOT_FOUND (5) + /* * These flags are used for ad_log_level(); */ @@ -73,17 +76,17 @@ typedef void (*restd_userdata_free_cb)(restd_conn_t *conn, void *userdata); /** * Event types */ -#define RESTD_EVENT_INIT (1) /*!< Call once upon new connection. */ -#define RESTD_EVENT_READ (1 << 1) /*!< Call on read */ -#define RESTD_EVENT_WRITE (1 << 2) /*!< Call on write. */ -#define RESTD_EVENT_CLOSE (1 << 3) /*!< Call before closing. */ -#define RESTD_EVENT_TIMEOUT (1 << 4) /*!< Timeout indicator, this flag will be set with AD_EVENT_CLOSE. */ -#define RESTD_EVENT_SHUTDOWN (1 << 5) /*!< Shutdown indicator, this flag will be set with AD_EVENT_CLOSE. */ +#define RESTD_EVENT_INIT (1) /*!< Call once upon new connection. */ +#define RESTD_EVENT_READ (1 << 1) /*!< Call on read */ +#define RESTD_EVENT_WRITE (1 << 2) /*!< Call on write. */ +#define RESTD_EVENT_CLOSE (1 << 3) /*!< Call before closing. */ +#define RESTD_EVENT_TIMEOUT (1 << 4) /*!< Timeout indicator, this flag will be set with AD_EVENT_CLOSE. */ +#define RESTD_EVENT_SHUTDOWN (1 << 5) /*!< Shutdown indicator, this flag will be set with AD_EVENT_CLOSE. */ /** * Defaults */ -#define RESTD_NUM_USERDATA (2) /*!< Number of userdata. Currently 0 is for userdata, 1 is for extra. */ +#define RESTD_NUM_USERDATA (2) /*!< Number of userdata. Currently 0 is for userdata, 1 is for extra. */ /*---------------------------------------------------------------------------*\ | DATA STRUCTURES | @@ -104,6 +107,8 @@ struct restd_server_s struct event_base *evbase; /*!< event base */ struct bufferevent *notify_buffer; /*!< internal notification channel */ + restd_callback request_handler; + restd_callback error_handler; }; /** @@ -122,6 +127,18 @@ struct restd_conn_s char *method; /*!< request method. set by protocol handler */ }; +/* + * User callback hook container. + */ +typedef struct restd_hook_s restd_hook_t; +struct restd_hook_s +{ + char *method; + char *path; + restd_callback cb; + void *userdata; +}; + /*----------------------------------------------------------------------------*\ | PUBLIC FUNCTIONS | \*----------------------------------------------------------------------------*/ @@ -135,10 +152,16 @@ extern void restd_server_set_option(restd_server_t *server, const char *key, con extern char *restd_server_get_option(restd_server_t *server, const char *key); extern int restd_server_get_option_int(restd_server_t *server, const char *key); +extern void restd_server_register_request_handler(restd_server_t *server, restd_callback cb); +extern void restd_server_register_error_handler(restd_server_t *server, restd_callback cb); + extern void restd_server_register_hook(restd_server_t *server, restd_callback cb, void *userdata); extern void restd_server_register_hook_on_method(restd_server_t *server, const char *method, restd_callback cb, void *userdata); +extern void restd_server_register_hook_on_path(restd_server_t *server, const char *method, const char *path, + restd_callback cb, void *userdata); + extern void *restd_conn_set_extra(restd_conn_t *conn, const void *extra, restd_userdata_free_cb free_cb); extern void *restd_conn_get_extra(restd_conn_t *conn); extern void restd_conn_set_method(restd_conn_t *conn, char *method); diff --git a/lib/src/restd_http_handler.c b/lib/src/restd_http_handler.c index e1cbc43..33a11f6 100644 --- a/lib/src/restd_http_handler.c +++ b/lib/src/restd_http_handler.c @@ -29,18 +29,13 @@ /*------------------------------- INCLUDES ----------------------------------*/ #include -#include #include -#include -#include #include #include -#include + #include -#include #include "restd_server.h" - #include "restd_http_handler.h" #include "macro.h" @@ -100,6 +95,11 @@ int restd_http_handler(short event, restd_conn_t *conn, void *userdata) { restd_conn_set_method(conn, http->request.method); } + DEBUG("==> HTTP PATH: %s METHOD: %s", http->request.path, http->request.method); + if (conn->server->request_handler != NULL) + { + status = conn->server->request_handler(event, conn, NULL); + } return status; } else if (event & RESTD_EVENT_WRITE) @@ -251,7 +251,7 @@ int restd_http_is_keepalive_request(restd_conn_t *conn) * @return 0 on success, -1 if we already sent it out. */ int restd_http_set_response_header(restd_conn_t *conn, const char *name, - const char *value) + const char *value) { restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); if (http->response.frozen_header) @@ -309,7 +309,7 @@ int restd_http_set_response_code(restd_conn_t *conn, int code, const char *reaso * @return 0 on success, -1 if we already sent it out. */ int restd_http_set_response_content(restd_conn_t *conn, const char *contenttype, - off_t size) + off_t size) { restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); if (http->response.frozen_header) @@ -341,7 +341,7 @@ int restd_http_set_response_content(restd_conn_t *conn, const char *contenttype, * @return total bytes sent, 0 on error. */ size_t restd_http_response(restd_conn_t *conn, int code, const char *contenttype, - const void *data, off_t size) + const void *data, off_t size) { restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); if (http->response.frozen_header) @@ -544,10 +544,9 @@ static restd_http_t *http_new(struct evbuffer *out) // Allocate additional resources. http->request.inbuf = evbuffer_new(); - http->request.headers = qlisttbl( - QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE); - http->response.headers = qlisttbl( - QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE); + http->request.headers = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE); + http->response.headers = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE); + if (http->request.inbuf == NULL || http->request.headers == NULL || http->response.headers == NULL) { http_free(http); diff --git a/lib/src/restd_rest_handler.c b/lib/src/restd_rest_handler.c new file mode 100644 index 0000000..b3f632f --- /dev/null +++ b/lib/src/restd_rest_handler.c @@ -0,0 +1,93 @@ +/*! + * restd_rest_handler.c + * + * Copyright (c) 2015-2019, 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: 23/12/2019 + * + */ + +// This is an independent project of an individual developer. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com + +/*------------------------------- INCLUDES ----------------------------------*/ + +#include +#include + +#include + +#include "restd_server.h" +#include "restd_http_handler.h" +#include "restd_rest_handler.h" + +#include "macro.h" + +/*! ---------------------------------------------------------------------------- + * @fn restd_rest_handler + * + * @brief Main function to manage http request. + */ +int restd_rest_handler(short event, restd_conn_t *conn, void *userdata) +{ + bool found = false; + DEBUG("JBN call_hooks: event 0x%x", event); + restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); + qlist_t *hooks = conn->server->hooks; + + qlist_obj_t obj; + bzero((void *)&obj, sizeof(qlist_obj_t)); + while (hooks->getnext(hooks, &obj, false) == true) + { + DEBUG(">>>>>>>>>\n"); + restd_hook_t *hook = (restd_hook_t *)obj.data; + if (hook->cb) + { + DEBUG("JBN call_hooks: method: %s - %s \n", hook->method, conn->method); + DEBUG("JBN call_hooks: path: %s - %s\n", hook->path, http->request.path); + if ((hook->method == NULL) || (conn->method == NULL) || (strcmp(hook->method, conn->method) != 0)) + { + DEBUG("JBN - ca va pas 1 \n"); + continue; + } + if ((hook->path == NULL) || (http->request.path == NULL) || (strcmp(hook->path, http->request.path) != 0)) + { + DEBUG("JBN - ca va pas 2 \n"); + continue; + } + DEBUG("JBN FOUND !!!!\n"); + + return RESTD_OK; + //int status = hook->cb(event, conn, hook->userdata); + //if (status != RESTD_OK) + //{ + // return status; + //} + } + } + + if (found == false) + { + printf("Call error handler...\n"); + return RESTD_NOT_ALLOWED; + } + + DEBUG("JBN call_hooks - DONE\n"); + return RESTD_OK; +} diff --git a/lib/src/restd_server.c b/lib/src/restd_server.c index 018f8e3..a602bcb 100644 --- a/lib/src/restd_server.c +++ b/lib/src/restd_server.c @@ -57,16 +57,6 @@ static bool initialized = false; */ int _restd_log_level = RESTD_LOG_WARN; -/* - * User callback hook container. - */ -typedef struct restd_hook_s restd_hook_t; -struct restd_hook_s -{ - char *method; - restd_callback cb; - void *userdata; -}; /* * Local functions. @@ -686,6 +676,20 @@ static void conn_cb(restd_conn_t *conn, int event) /*--------------------------------------------------------------------------*/ +void restd_server_register_request_handler(restd_server_t *server, restd_callback cb) +{ + server->request_handler = cb; +} + +/*--------------------------------------------------------------------------*/ + +void restd_server_register_error_handler(restd_server_t *server, restd_callback cb) +{ + server->error_handler = cb; +} + +/*--------------------------------------------------------------------------*/ + /** * Register user hook. */ @@ -700,10 +704,18 @@ void restd_server_register_hook(restd_server_t *server, restd_callback cb, void * Register user hook on method name. */ void restd_server_register_hook_on_method(restd_server_t *server, const char *method, restd_callback cb, void *userdata) +{ + restd_server_register_hook_on_path(server, method, NULL, cb, userdata); +} + +/*--------------------------------------------------------------------------*/ + +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; diff --git a/src/main.c b/src/main.c index 4dc8853..5458d4d 100644 --- a/src/main.c +++ b/src/main.c @@ -36,7 +36,7 @@ int my_http_get_handler(short event, restd_conn_t *conn, void *userdata) { if (restd_http_get_status(conn) == RESTD_HTTP_REQ_DONE) { - restd_http_response(conn, 200, "text/html", "Hello World", 11); + restd_http_response(conn, 200, "application/json", "{\"status\": false}", 17); return restd_http_is_keepalive_request(conn) ? RESTD_DONE : RESTD_CLOSE; } } @@ -45,11 +45,12 @@ int my_http_get_handler(short event, restd_conn_t *conn, void *userdata) int my_http_default_handler(short event, restd_conn_t *conn, void *userdata) { + printf("Error Handler...\n"); if (event & RESTD_EVENT_READ) { if (restd_http_get_status(conn) == RESTD_HTTP_REQ_DONE) { - restd_http_response(conn, 501, "text/html", "Not implemented", 15); + restd_http_response(conn, 500, "application/json", "{\"status\":\"error\"}", 18); return RESTD_CLOSE; // Close connection. } } @@ -61,8 +62,14 @@ int main(int argc, char **argv) restd_log_level(RESTD_LOG_DEBUG); restd_server_t *server = restd_server_new(); restd_server_set_option(server, "server.port", "8888"); - restd_server_register_hook(server, restd_http_handler, NULL); // HTTP Parser is also a hook. - restd_server_register_hook_on_method(server, "GET", my_http_get_handler, NULL); - restd_server_register_hook(server, my_http_default_handler, NULL); + + restd_server_register_request_handler(server, restd_rest_handler); + restd_server_register_error_handler(server, my_http_default_handler); + + restd_server_register_hook(server, restd_http_handler, NULL); + restd_server_register_hook_on_path(server, "GET", "/api/zob", my_http_get_handler, NULL); + restd_server_register_hook_on_path(server, "GET", "/api/zob2", my_http_get_handler, NULL); + restd_server_register_hook_on_path(server, "PUT", "/api/zob/25", my_http_get_handler, NULL); + return restd_server_start(server); }