From 9d589955508cf20d096a6ffc72e0290a3c4262fc Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Thu, 20 Feb 2020 12:08:21 +0100 Subject: [PATCH] Remove deprecated files --- lib/include/restd-old.h | 35 -- lib/include/restd_http_handler.h | 162 ----- lib/include/restd_rest_handler.h | 33 - lib/include/restd_server.h | 176 ------ lib/src/restd_http_handler.c | 997 ------------------------------- lib/src/restd_rest_handler.c | 284 --------- lib/src/restd_server.c | 837 -------------------------- lib/tests/http-server.c | 580 ------------------ lib/tests/launcher.sh | 37 -- lib/tests/restd.c | 102 ---- lib/tests/test_restd.c | 15 - src/rest/restd.h | 8 - 12 files changed, 3266 deletions(-) delete mode 100644 lib/include/restd-old.h delete mode 100644 lib/include/restd_http_handler.h delete mode 100644 lib/include/restd_rest_handler.h delete mode 100644 lib/include/restd_server.h delete mode 100644 lib/src/restd_http_handler.c delete mode 100644 lib/src/restd_rest_handler.c delete mode 100644 lib/src/restd_server.c delete mode 100644 lib/tests/http-server.c delete mode 100755 lib/tests/launcher.sh delete mode 100644 lib/tests/restd.c delete mode 100644 lib/tests/test_restd.c diff --git a/lib/include/restd-old.h b/lib/include/restd-old.h deleted file mode 100644 index c34eda9..0000000 --- a/lib/include/restd-old.h +++ /dev/null @@ -1,35 +0,0 @@ -/*! - * restd.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_H -#define _RESTD_H - -/*------------------------------- INCLUDES ----------------------------------*/ - -#include "restd_server.h" -#include "restd_http_handler.h" -#include "restd_rest_handler.h" - -#endif /*_RESTD_H */ diff --git a/lib/include/restd_http_handler.h b/lib/include/restd_http_handler.h deleted file mode 100644 index ecaf26e..0000000 --- a/lib/include/restd_http_handler.h +++ /dev/null @@ -1,162 +0,0 @@ -/*! - * restd_http_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_HTTP_HANDLER_H -#define _RESTD_HTTP_HANDLER_H - -/*------------------------------- INCLUDES ----------------------------------*/ - -/*----------------------------------------------------------------------------*\ -| HTTP PROTOCOL SPECIFICS | -\*----------------------------------------------------------------------------*/ - -/* HTTP PROTOCOL CODES */ -#define HTTP_PROTOCOL_09 "HTTP/0.9" -#define HTTP_PROTOCOL_10 "HTTP/1.0" -#define HTTP_PROTOCOL_11 "HTTP/1.1" - -/* HTTP RESPONSE CODES */ -#define HTTP_NO_RESPONSE (0) -#define HTTP_CODE_CONTINUE (100) -#define HTTP_CODE_OK (200) -#define HTTP_CODE_CREATED (201) -#define HTTP_CODE_NO_CONTENT (204) -#define HTTP_CODE_PARTIAL_CONTENT (206) -#define HTTP_CODE_MULTI_STATUS (207) -#define HTTP_CODE_MOVED_TEMPORARILY (302) -#define HTTP_CODE_NOT_MODIFIED (304) -#define HTTP_CODE_Brestd_REQUEST (400) -#define HTTP_CODE_UNAUTHORIZED (401) -#define HTTP_CODE_FORBIDDEN (403) -#define HTTP_CODE_NOT_FOUND (404) -#define HTTP_CODE_METHOD_NOT_ALLOWED (405) -#define HTTP_CODE_REQUEST_TIME_OUT (408) -#define HTTP_CODE_GONE (410) -#define HTTP_CODE_REQUEST_URI_TOO_LONG (414) -#define HTTP_CODE_LOCKED (423) -#define HTTP_CODE_INTERNAL_SERVER_ERROR (500) -#define HTTP_CODE_NOT_IMPLEMENTED (501) -#define HTTP_CODE_SERVICE_UNAVAILABLE (503) - -/* DEFAULT BEHAVIORS */ -#define HTTP_CRLF "\r\n" -#define HTTP_DEF_CONTENTTYPE "application/octet-stream" - -/*----------------------------------------------------------------------------*\ -| TYPEDEFS | -\*----------------------------------------------------------------------------*/ -typedef struct restd_http_s restd_http_t; - -/*!< Hook type */ -#define RESTD_HOOK_ALL (0) /*!< call on each and every phases */ -#define RESTD_HOOK_ON_CONNECT (1) /*!< call right after the establishment of connection */ -#define RESTD_HOOK_AFTER_REQUESTLINE (1 << 2) /*!< call after parsing request line */ -#define RESTD_HOOK_AFTER_HEADER (1 << 3) /*!< call after parsing all headers */ -#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 */ - RESTD_HTTP_REQ_REQUESTLINE_DONE, /*!< received 1st line */ - RESTD_HTTP_REQ_HEADER_DONE, /*!< received headers completely */ - RESTD_HTTP_REQ_DONE, /*!< received body completely. no more data expected */ - - RESTD_HTTP_ERROR, /*!< unrecoverable error found. */ -}; -#endif -/*----------------------------------------------------------------------------*\ -| PUBLIC FUNCTIONS | -\*----------------------------------------------------------------------------*/ -extern restd_http_t *http_new(struct evbuffer *out); -extern void http_free_cb(restd_conn_t *conn, void *userdata); -extern int restd_http_handler(short event, restd_conn_t *conn, void *userdata); - -extern enum restd_http_request_status_e restd_http_get_status(restd_conn_t *conn); -extern struct evbuffer *restd_http_get_inbuf(restd_conn_t *conn); -extern struct evbuffer *restd_http_get_outbuf(restd_conn_t *conn); - -extern const char *restd_http_get_request_header(restd_conn_t *conn, const char *name); -extern off_t restd_http_get_content_length(restd_conn_t *conn); -extern size_t restd_http_get_content_length_stored(restd_conn_t *conn); -extern void *restd_http_get_content(restd_conn_t *conn, size_t maxsize, size_t *storedsize); -extern int restd_http_is_keepalive_request(restd_conn_t *conn); -extern int http_parser(restd_http_t *http, struct evbuffer *in); - -extern int restd_http_set_response_header(restd_conn_t *conn, const char *name, const char *value); -extern const char *restd_http_get_response_header(restd_conn_t *conn, const char *name); -extern int restd_http_set_response_code(restd_conn_t *conn, int code, const char *reason); -extern int restd_http_set_response_content(restd_conn_t *conn, const char *contenttype, off_t size); - -extern size_t restd_http_response(restd_conn_t *conn, int code, const char *contenttype, const void *data, off_t size); -extern size_t restd_http_send_header(restd_conn_t *conn); -extern size_t restd_http_send_data(restd_conn_t *conn, const void *data, size_t size); -extern size_t restd_http_send_chunk(restd_conn_t *conn, const void *data, size_t size); - -extern const char *restd_http_get_reason(int code); - -/*---------------------------------------------------------------------------*\ -| DATA STRUCTURES | -\*---------------------------------------------------------------------------*/ -struct restd_http_s -{ - // HTTP Request - struct - { - enum restd_http_request_status_e status; /*!< request status. */ - struct evbuffer *inbuf; /*!< input data buffer. */ - - // request line - available on REQ_REQUESTLINE_DONE. - char *method; /*!< request method ex) GET */ - char *uri; /*!< url+query ex) /data%20path?query=the%20value */ - char *httpver; /*!< version ex) HTTP/1.1 */ - char *path; /*!< decoded path ex) /data path */ - char *query; /*!< query string ex) query=the%20value */ - - // request header - available on REQ_HEADER_DONE. - qlisttbl_t *headers; /*!< parsed request header entries */ - char *host; /*!< host ex) www.domain.com or www.domain.com:8080 */ - char *domain; /*!< domain name ex) www.domain.com (no port number) */ - off_t contentlength; /*!< value of Content-Length header.*/ - size_t bodyin; /*!< bytes moved to in-buff */ - } request; - - // HTTP Response - struct - { - struct evbuffer *outbuf; /*!< output data buffer. */ - bool frozen_header; /*!< indicator whether we sent header out or not */ - - // response headers - int code; /*!< response status-code */ - char *reason; /*!< reason-phrase */ - qlisttbl_t *headers; /*!< response header entries */ - off_t contentlength; /*!< content length in response */ - size_t bodyout; /*!< bytes added to out-buffer */ - } response; -}; - -#endif /* _RESTD_HTTP_HANDLER_H */ diff --git a/lib/include/restd_rest_handler.h b/lib/include/restd_rest_handler.h deleted file mode 100644 index 194d6f6..0000000 --- a/lib/include/restd_rest_handler.h +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * 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); - -#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 deleted file mode 100644 index 236bc6b..0000000 --- a/lib/include/restd_server.h +++ /dev/null @@ -1,176 +0,0 @@ -/*! - * restd_server.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_SERVER_H -#define _RESTD_SERVER_H - -/*------------------------------- INCLUDES ----------------------------------*/ - -#include - -#include - -/*------------------------------- TYPEDEFS ----------------------------------*/ - -typedef struct restd_server_s restd_server_t; -typedef struct restd_conn_s restd_conn_t; - -/*------------------------------- DEFINE ----------------------------------*/ - -/* - * Return values of user callback. - */ -#define RESTD_OK (0) /*!< I'm done with this request. Escalate to other hooks. */ -#define RESTD_TAKEOVER (1) /*!< I'll handle the buffer directly this time, skip next hook */ -#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_ERROR_METHOD_NOT_ALLOWED (1) -#define RESTD_ERROR_PATH_NOT_FOUND (2) - -/* - * These flags are used for ad_log_level(); - */ -enum restd_log_e -{ - RESTD_LOG_DISABLE = 0, - RESTD_LOG_ERROR, - RESTD_LOG_WARN, - RESTD_LOG_INFO, - RESTD_LOG_DEBUG, - RESTD_LOG_DEBUG2, -}; - -/*---------------------------------------------------------------------------*\ -| USER-CALLBACK | -\*---------------------------------------------------------------------------*/ - -/** - * User callback(hook) prototype. - */ -typedef int (*restd_call_hook_cb)(short event, restd_conn_t *conn); -typedef int (*restd_callback)(short event, restd_conn_t *conn, void *userdata); -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. */ - -/** - * Defaults - */ -#define RESTD_NUM_USERDATA (2) /*!< Number of userdata. Currently 0 is for userdata, 1 is for extra. */ - -/*---------------------------------------------------------------------------*\ -| DATA STRUCTURES | -\*---------------------------------------------------------------------------*/ - -/** - * Server info container. - */ -struct restd_server_s -{ - int errcode; /*!< exit status. 0 for normal exit, non zero for error. */ - pthread_t *thread; /*!< thread object. not null if server runs as a thread */ - - qhashtbl_t *options; /*!< server options */ - qhashtbl_t *stats; /*!< internal statistics */ - qlist_t *hooks; /*!< list of registered hooks */ - struct evconnlistener *listener; /*!< listener */ - struct event_base *evbase; /*!< event base */ - - struct bufferevent *notify_buffer; /*!< internal notification channel */ - - restd_call_hook_cb call_hooks; - restd_callback request_handler; - restd_callback error_handler; -}; - -/** - * Connection structure. - */ -struct restd_conn_s -{ - restd_server_t *server; /*!< reference pointer to server */ - struct bufferevent *buffer; /*!< reference pointer to buffer */ - struct evbuffer *in; /*!< in buffer */ - struct evbuffer *out; /*!< out buffer */ - int status; /*!< hook status such as AD_OK */ - - void *userdata[2]; /*!< userdata[0] for end user, userdata[1] for extra */ - restd_userdata_free_cb userdata_free_cb[2]; /*!< callback to release user data */ - char *method; /*!< request method. set by protocol handler */ - int id; -}; - -/* - * 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 | -\*----------------------------------------------------------------------------*/ -enum restd_log_e restd_log_level(enum restd_log_e log_level); - -extern restd_server_t *restd_server_new(void); -extern void restd_server_free(restd_server_t *server); -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 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_call_hooks_handler(restd_server_t *server, restd_call_hook_cb 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); - -#endif /* _RESTD_SERVER_H */ diff --git a/lib/src/restd_http_handler.c b/lib/src/restd_http_handler.c deleted file mode 100644 index 826c593..0000000 --- a/lib/src/restd_http_handler.c +++ /dev/null @@ -1,997 +0,0 @@ -/*! - * restd_http_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 - -#include - -#include "restd_server.h" -#include "restd_http_handler.h" - -#include "macro.h" - -#ifndef _DOXYGEN_SKIP -static void http_free(restd_http_t *http); -static size_t http_add_inbuf(struct evbuffer *buffer, restd_http_t *http, - size_t maxsize); - -static int parse_requestline(restd_http_t *http, char *line); -static int parse_headers(restd_http_t *http, struct evbuffer *in); -static int parse_body(restd_http_t *http, struct evbuffer *in); -static ssize_t parse_chunked_body(restd_http_t *http, struct evbuffer *in); - -static bool isValidPathname(const char *path); -static void correctPathname(char *path); -static char *evbuffer_peekln(struct evbuffer *buffer, size_t *n_rerestd_out, - enum evbuffer_eol_style eol_style); -static ssize_t evbuffer_drainln(struct evbuffer *buffer, size_t *n_rerestd_out, - enum evbuffer_eol_style eol_style); - -#endif - -/** - * HTTP protocol handler hook. - * - * This hook provides an easy way to handle HTTP request/response. - * - * @note - * This hook must be registered at the top of hook chain. - * - * @code - * restd_server_t *server = restd_server_new(); - * restd_server_register_hook(server, restd_http_handler, NULL); - * @endcode - */ -int restd_http_handler(short event, restd_conn_t *conn, void *userdata) -{ - 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_READ) - { - DEBUG("==> HTTP READ"); - 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 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) - { - DEBUG("==> HTTP WRITE"); - 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; - } - - BUG_EXIT(); - return RESTD_CLOSE; -} - -/** - * Return the request status. - */ -enum restd_http_request_status_e restd_http_get_status(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http == NULL) - return RESTD_HTTP_ERROR; - return http->request.status; -} - -/*--------------------------------------------------------------------------*/ - -struct evbuffer *restd_http_get_inbuf(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return http->request.inbuf; -} - -/*--------------------------------------------------------------------------*/ - -struct evbuffer *restd_http_get_outbuf(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return http->response.outbuf; -} - -/** - * Get request header. - * - * @param name name of header. - * - * @return value of string if found, otherwise NULL. - */ -const char *restd_http_get_request_header(restd_conn_t *conn, const char *name) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return http->request.headers->getstr(http->request.headers, name, false); -} - -/** - * Return the size of content from the request. - */ -off_t restd_http_get_content_length(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return http->request.contentlength; -} - -/** - * Return the actual size of data stored in in-buffer - */ -size_t restd_http_get_content_length_stored(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return evbuffer_get_length(http->request.inbuf); -} - -/** - * Remove content from the in-buffer. - * - * The return data gets null terminated for convenience. For an example, - * if it reads 3 bytes, it will allocate 4 bytes and the 4th byte will - * be set to null terminator. `storedsized` will still return 3. - * - * @param maxsize maximum length of data to read. 0 to read everything. - * @param storedsize the size of data read and stored in the return. - */ -void *restd_http_get_content(restd_conn_t *conn, size_t maxsize, size_t *storedsize) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - - size_t inbuflen = evbuffer_get_length(http->request.inbuf); - size_t readlen = - (maxsize == 0) ? inbuflen : ((inbuflen < maxsize) ? inbuflen : maxsize); - if (readlen == 0) - return NULL; - - void *data = malloc(readlen + 1); - if (data == NULL) - return NULL; - - size_t removedlen = evbuffer_remove(http->request.inbuf, data, readlen); - ((char *)data)[removedlen] = '\0'; - if (storedsize) - *storedsize = removedlen; - - return data; -} - -/** - * Return whether the request is keep-alive request or not. - * - * @return 1 if keep-alive request, otherwise 0. - */ -int restd_http_is_keepalive_request(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http->request.httpver == NULL) - { - return 0; - } - - const char *connection = restd_http_get_request_header(conn, "Connection"); - if (!strcmp(http->request.httpver, HTTP_PROTOCOL_11)) - { - // In HTTP/1.1, Keep-Alive is on by default unless explicitly specified. - if (connection != NULL && !strcmp(connection, "close")) - { - return 0; - } - return 1; - } - else - { - // In older version, Keep-Alive is off by default unless requested. - if (connection != NULL && (!strcmp(connection, "Keep-Alive") || !strcmp(connection, "TE"))) - { - return 1; - } - return 0; - } -} - -/** - * Set response header. - * - * @param name name of header. - * @param value value string to set. NULL to remove the header. - * - * @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) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http->response.frozen_header) - { - return -1; - } - - if (value != NULL) - { - http->response.headers->putstr(http->response.headers, name, value); - } - else - { - http->response.headers->remove(http->response.headers, name); - } - - return 0; -} - -/** - * Get response header. - * - * @param name name of header. - * - * @return value of string if found, otherwise NULL. - */ -const char *restd_http_get_response_header(restd_conn_t *conn, const char *name) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - return http->response.headers->getstr(http->response.headers, name, false); -} - -/** - * - * @return 0 on success, -1 if we already sent it out. - */ -int restd_http_set_response_code(restd_conn_t *conn, int code, const char *reason) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http->response.frozen_header) - { - return -1; - } - - http->response.code = code; - if (reason) - http->response.reason = strdup(reason); - - return 0; -} - -/** - * - * @param size content size. -1 for chunked transfer encoding. - * @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) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http->response.frozen_header) - { - return -1; - } - - // Set Content-Type header. - restd_http_set_response_header( - conn, "Content-Type", - (contenttype) ? contenttype : HTTP_DEF_CONTENTTYPE); - if (size >= 0) - { - char clenval[20 + 1]; - sprintf(clenval, "%jd", size); - restd_http_set_response_header(conn, "Content-Length", clenval); - http->response.contentlength = size; - } - else - { - restd_http_set_response_header(conn, "Transfer-Encoding", "chunked"); - http->response.contentlength = -1; - } - - return 0; -} - -/** - * @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) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - // Sanity Check - if (http == NULL) - return -1; - if (http->response.frozen_header) - { - return 0; - } - - // Set response headers. - if (restd_http_get_response_header(conn, "Connection") == NULL) - { - restd_http_set_response_header( - conn, "Connection", - (restd_http_is_keepalive_request(conn)) ? "Keep-Alive" : "close"); - } - - restd_http_set_response_code(conn, code, restd_http_get_reason(code)); - restd_http_set_response_content(conn, contenttype, size); - return restd_http_send_data(conn, data, size); -} - -/** - * - * @return 0 total bytes put in out buffer, -1 if we already sent it out. - */ -size_t restd_http_send_header(restd_conn_t *conn) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - if (http->response.frozen_header) - { - return 0; - } - http->response.frozen_header = true; - - // Send status line. - const char *reason = - (http->response.reason) ? http->response.reason : restd_http_get_reason(http->response.code); - evbuffer_add_printf(http->response.outbuf, "%s %d %s" HTTP_CRLF, - http->request.httpver, http->response.code, reason); - - // Send headers. - qlisttbl_obj_t obj; - bzero((void *)&obj, sizeof(obj)); - qlisttbl_t *tbl = http->response.headers; - tbl->lock(tbl); - while (tbl->getnext(tbl, &obj, NULL, false)) - { - evbuffer_add_printf(http->response.outbuf, "%s: %s" HTTP_CRLF, - (char *)obj.name, (char *)obj.data); - } - tbl->unlock(tbl); - - // Send empty line, indicator of end of header. - evbuffer_add(http->response.outbuf, HTTP_CRLF, CONST_STRLEN(HTTP_CRLF)); - - return evbuffer_get_length(http->response.outbuf); -} - -/** - * - * @return 0 on success, -1 if we already sent it out. - */ -size_t restd_http_send_data(restd_conn_t *conn, const void *data, size_t size) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - - if (http->response.contentlength < 0) - { - WARN("Content-Length is not set. Invalid usage."); - return 0; - } - - if ((http->response.bodyout + size) > http->response.contentlength) - { - WARN("Trying to send more data than supposed to"); - return 0; - } - - size_t beforesize = evbuffer_get_length(http->response.outbuf); - if (!http->response.frozen_header) - { - restd_http_send_header(conn); - } - - if (data != NULL && size > 0) - { - if (evbuffer_add(http->response.outbuf, data, size)) - return 0; - } - - http->response.bodyout += size; - return (evbuffer_get_length(http->response.outbuf) - beforesize); -} - -/*--------------------------------------------------------------------------*/ - -size_t restd_http_send_chunk(restd_conn_t *conn, const void *data, size_t size) -{ - restd_http_t *http = (restd_http_t *)restd_conn_get_extra(conn); - - if (http->response.contentlength >= 0) - { - WARN("Content-Length is set. Invalid usage."); - return 0; - } - - if (!http->response.frozen_header) - { - restd_http_send_header(conn); - } - - size_t beforesize = evbuffer_get_length(http->response.outbuf); - int status = 0; - if (size > 0) - { - status += evbuffer_add_printf(http->response.outbuf, "%zu" HTTP_CRLF, - size); - status += evbuffer_add(http->response.outbuf, data, size); - status += evbuffer_add(http->response.outbuf, HTTP_CRLF, - CONST_STRLEN(HTTP_CRLF)); - } - else - { - status += evbuffer_add_printf(http->response.outbuf, - "0" HTTP_CRLF HTTP_CRLF); - } - if (status != 0) - { - WARN("Failed to add data to out-buffer. (size:%jd)", size); - return 0; - } - - size_t bytesout = evbuffer_get_length(http->response.outbuf) - beforesize; - http->response.bodyout += bytesout; - return bytesout; -} - -/*--------------------------------------------------------------------------*/ - -const char *restd_http_get_reason(int code) -{ - switch (code) - { - case HTTP_CODE_CONTINUE: - return "Continue"; - case HTTP_CODE_OK: - return "OK"; - case HTTP_CODE_CREATED: - return "Created"; - case HTTP_CODE_NO_CONTENT: - return "No content"; - case HTTP_CODE_PARTIAL_CONTENT: - return "Partial Content"; - case HTTP_CODE_MULTI_STATUS: - return "Multi Status"; - case HTTP_CODE_MOVED_TEMPORARILY: - return "Moved Temporarily"; - case HTTP_CODE_NOT_MODIFIED: - return "Not Modified"; - case HTTP_CODE_Brestd_REQUEST: - return "Bad Request"; - case HTTP_CODE_UNAUTHORIZED: - return "Authorization Required"; - case HTTP_CODE_FORBIDDEN: - return "Forbidden"; - case HTTP_CODE_NOT_FOUND: - return "Not Found"; - case HTTP_CODE_METHOD_NOT_ALLOWED: - return "Method Not Allowed"; - case HTTP_CODE_REQUEST_TIME_OUT: - return "Request Time Out"; - case HTTP_CODE_GONE: - return "Gone"; - case HTTP_CODE_REQUEST_URI_TOO_LONG: - return "Request URI Too Long"; - case HTTP_CODE_LOCKED: - return "Locked"; - case HTTP_CODE_INTERNAL_SERVER_ERROR: - return "Internal Server Error"; - case HTTP_CODE_NOT_IMPLEMENTED: - return "Not Implemented"; - case HTTP_CODE_SERVICE_UNAVAILABLE: - return "Service Unavailable"; - } - - WARN("Undefined code found. %d", code); - return "-"; -} - -/****************************************************************************** - * Private internal functions. - *****************************************************************************/ -#ifndef _DOXYGEN_SKIP - -restd_http_t *http_new(struct evbuffer *out) -{ - // Create a new connection container. - restd_http_t *http = NEW_OBJECT(restd_http_t); - if (http == NULL) - return NULL; - - // Allocate additional resources. - http->request.inbuf = evbuffer_new(); - 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); - return NULL; - } - - // Initialize structure. - http->request.status = RESTD_HTTP_REQ_INIT; - http->request.contentlength = -1; - http->response.contentlength = -1; - http->response.outbuf = out; - - return http; -} - -/*--------------------------------------------------------------------------*/ - -static void http_free(restd_http_t *http) -{ - if (http) - { - if (http->request.inbuf) - evbuffer_free(http->request.inbuf); - if (http->request.method) - free(http->request.method); - if (http->request.uri) - free(http->request.uri); - if (http->request.httpver) - free(http->request.httpver); - if (http->request.path) - free(http->request.path); - if (http->request.query) - free(http->request.query); - - if (http->request.headers) - http->request.headers->free(http->request.headers); - if (http->request.host) - free(http->request.host); - if (http->request.domain) - free(http->request.domain); - - if (http->response.headers) - http->response.headers->free(http->response.headers); - if (http->response.reason) - free(http->response.reason); - - free(http); - } -} - -/*--------------------------------------------------------------------------*/ - -void http_free_cb(restd_conn_t *conn, void *userdata) -{ - http_free((restd_http_t *)userdata); -} - -/*--------------------------------------------------------------------------*/ - -static size_t http_add_inbuf(struct evbuffer *buffer, restd_http_t *http, - size_t maxsize) -{ - if (maxsize == 0 || evbuffer_get_length(buffer) == 0) - { - return 0; - } - - return evbuffer_remove_buffer(buffer, http->request.inbuf, maxsize); -} - -/*--------------------------------------------------------------------------*/ - -int http_parser(restd_http_t *http, struct evbuffer *in) -{ - ASSERT(http != NULL && in != NULL); - - if (http->request.status == RESTD_HTTP_REQ_INIT) - { - char *line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF); - if (line == NULL) - return http->request.status; - http->request.status = parse_requestline(http, line); - free(line); - // Do not call user callbacks until I reach the next state. - if (http->request.status == RESTD_HTTP_REQ_INIT) - { - return RESTD_TAKEOVER; - } - } - - if (http->request.status == RESTD_HTTP_REQ_REQUESTLINE_DONE) - { - http->request.status = parse_headers(http, in); - // Do not call user callbacks until I reach the next state. - if (http->request.status == RESTD_HTTP_REQ_REQUESTLINE_DONE) - { - return RESTD_TAKEOVER; - } - } - - if (http->request.status == RESTD_HTTP_REQ_HEADER_DONE) - { - http->request.status = parse_body(http, in); - // Do not call user callbacks until I reach the next state. - if (http->request.status == RESTD_HTTP_REQ_HEADER_DONE) - { - return RESTD_TAKEOVER; - } - } - - if (http->request.status == RESTD_HTTP_REQ_DONE) - { - return RESTD_OK; - } - - if (http->request.status == RESTD_HTTP_ERROR) - { - return RESTD_CLOSE; - } - - BUG_EXIT(); - return RESTD_CLOSE; -} - -/*--------------------------------------------------------------------------*/ - -static int parse_requestline(restd_http_t *http, char *line) -{ - // Parse request line. - char *saveptr; - char *method = strtok_r(line, " ", &saveptr); - char *uri = strtok_r(NULL, " ", &saveptr); - char *httpver = strtok_r(NULL, " ", &saveptr); - char *tmp = strtok_r(NULL, " ", &saveptr); - - if (method == NULL || uri == NULL || httpver == NULL || tmp != NULL) - { - DEBUG("Invalid request line. %s", line); - return RESTD_HTTP_ERROR; - } - - // Set request method - http->request.method = qstrupper(strdup(method)); - - // Set HTTP version - http->request.httpver = qstrupper(strdup(httpver)); - if (strcmp(http->request.httpver, HTTP_PROTOCOL_09) && strcmp(http->request.httpver, HTTP_PROTOCOL_10) && strcmp(http->request.httpver, HTTP_PROTOCOL_11)) - { - DEBUG("Unknown protocol: %s", http->request.httpver); - return RESTD_HTTP_ERROR; - } - - // Set URI - if (uri[0] == '/') - { - http->request.uri = strdup(uri); - } - else if ((tmp = strstr(uri, "://"))) - { - // divide URI into host and path - char *path = strstr(tmp + CONST_STRLEN("://"), "/"); - if (path == NULL) - { // URI has no path ex) http://domain.com:80 - http->request.headers->putstr(http->request.headers, "Host", - tmp + CONST_STRLEN("://")); - http->request.uri = strdup("/"); - } - else - { // URI has path, ex) http://domain.com:80/path - *path = '\0'; - http->request.headers->putstr(http->request.headers, "Host", - tmp + CONST_STRLEN("://")); - *path = '/'; - http->request.uri = strdup(path); - } - } - else - { - DEBUG("Invalid URI format. %s", uri); - return RESTD_HTTP_ERROR; - } - - // Set request path. Only path part from URI. - http->request.path = strdup(http->request.uri); - tmp = strstr(http->request.path, "?"); - if (tmp) - { - *tmp = '\0'; - http->request.query = strdup(tmp + 1); - } - else - { - http->request.query = strdup(""); - } - qurl_decode(http->request.path); - - // check path - if (isValidPathname(http->request.path) == false) - { - DEBUG("Invalid URI format : %s", http->request.uri); - return RESTD_HTTP_ERROR; - } - correctPathname(http->request.path); - - DEBUG("Method=%s, URI=%s, VER=%s", http->request.method, http->request.uri, http->request.httpver); - - return RESTD_HTTP_REQ_REQUESTLINE_DONE; -} - -/*--------------------------------------------------------------------------*/ - -static int parse_headers(restd_http_t *http, struct evbuffer *in) -{ - char *line; - while ((line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF))) - { - if (IS_EMPTY_STR(line)) - { - const char *clen = http->request.headers->getstr( - http->request.headers, "Content-Length", false); - http->request.contentlength = (clen) ? atol(clen) : -1; - free(line); - return RESTD_HTTP_REQ_HEADER_DONE; - } - // Parse - char *name, *value; - char *tmp = strstr(line, ":"); - if (tmp) - { - *tmp = '\0'; - name = qstrtrim(line); - value = qstrtrim(tmp + 1); - } - else - { - name = qstrtrim(line); - value = ""; - } - // Add - http->request.headers->putstr(http->request.headers, name, value); - - free(line); - } - - return http->request.status; -} - -/*--------------------------------------------------------------------------*/ - -static int parse_body(restd_http_t *http, struct evbuffer *in) -{ - // Handle static data case. - if (http->request.contentlength == 0) - { - return RESTD_HTTP_REQ_DONE; - } - else if (http->request.contentlength > 0) - { - if (http->request.contentlength > http->request.bodyin) - { - size_t maxread = http->request.contentlength - http->request.bodyin; - if (maxread > 0 && evbuffer_get_length(in) > 0) - { - http->request.bodyin += http_add_inbuf(in, http, maxread); - } - } - if (http->request.contentlength == http->request.bodyin) - { - return RESTD_HTTP_REQ_DONE; - } - } - else - { - // Check if Transfer-Encoding is chunked. - const char *tranenc = http->request.headers->getstr( - http->request.headers, "Transfer-Encoding", false); - if (tranenc != NULL && !strcmp(tranenc, "chunked")) - { - // TODO: handle chunked encoding - for (;;) - { - ssize_t chunksize = parse_chunked_body(http, in); - if (chunksize > 0) - { - continue; - } - else if (chunksize == 0) - { - return RESTD_HTTP_REQ_DONE; - } - else if (chunksize == -1) - { - return http->request.status; - } - else - { - return RESTD_HTTP_ERROR; - } - } - } - else - { - return RESTD_HTTP_REQ_DONE; - } - } - - return http->request.status; -} - -/** - * Parse chunked body and append it to inbuf. - * - * @return number of bytes in a chunk. so 0 for the ending chunk. -1 for not enough data, -2 format error. - */ -static ssize_t parse_chunked_body(restd_http_t *http, struct evbuffer *in) -{ - // Peek chunk size. - size_t crlf_len = 0; - char *line = evbuffer_peekln(in, &crlf_len, EVBUFFER_EOL_CRLF); - if (line == NULL) - return -1; // not enough data. - size_t linelen = strlen(line); - - // Parse chunk size - int chunksize = -1; - sscanf(line, "%x", &chunksize); - free(line); - if (chunksize < 0) - return -2; // format error - - // Check if we've received whole data of this chunk. - size_t datalen = linelen + crlf_len + chunksize + crlf_len; - size_t inbuflen = evbuffer_get_length(in); - if (inbuflen < datalen) - { - return -1; // not enough data. - } - - // Copy chunk body - evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF); - http_add_inbuf(in, http, chunksize); - evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF); - - return chunksize; -} - -/** - * validate file path - */ -static bool isValidPathname(const char *path) -{ - if (path == NULL) - return false; - - int len = strlen(path); - if (len == 0 || len >= PATH_MAX) - return false; - else if (path[0] != '/') - return false; - else if (strpbrk(path, "\\:*?\"<>|") != NULL) - return false; - - // check folder name length - int n; - char *t; - for (n = 0, t = (char *)path; *t != '\0'; t++) - { - if (*t == '/') - { - n = 0; - continue; - } - if (n >= FILENAME_MAX) - { - DEBUG("Filename too long."); - return false; - } - n++; - } - - return true; -} - -/** - * Correct pathname. - * - * @note - * remove : heading & tailing white spaces, double slashes, tailing slash - */ -static void correctPathname(char *path) -{ - // Take care of head & tail white spaces. - qstrtrim(path); - - // Take care of double slashes. - while (strstr(path, "//") != NULL) - qstrreplace("sr", path, "//", "/"); - - // Take care of tailing slash. - int len = strlen(path); - if (len <= 1) - return; - if (path[len - 1] == '/') - path[len - 1] = '\0'; -} - -/*--------------------------------------------------------------------------*/ - -static char *evbuffer_peekln(struct evbuffer *buffer, size_t *n_rerestd_out, - enum evbuffer_eol_style eol_style) -{ - // Check if first line has arrived. - struct evbuffer_ptr ptr = evbuffer_search_eol(buffer, NULL, n_rerestd_out, - eol_style); - if (ptr.pos == -1) - return NULL; - - char *line = (char *)malloc(ptr.pos + 1); - if (line == NULL) - return NULL; - - // Linearizes buffer - if (ptr.pos > 0) - { - char *bufferptr = (char *)evbuffer_pullup(buffer, ptr.pos); - ASSERT(bufferptr != NULL); - strncpy(line, bufferptr, ptr.pos); - } - line[ptr.pos] = '\0'; - - return line; -} - -/*--------------------------------------------------------------------------*/ - -static ssize_t evbuffer_drainln(struct evbuffer *buffer, size_t *n_rerestd_out, - enum evbuffer_eol_style eol_style) -{ - char *line = evbuffer_readln(buffer, n_rerestd_out, eol_style); - if (line == NULL) - return -1; - - size_t linelen = strlen(line); - free(line); - return linelen; -} - -#endif // _DOXYGEN_SKIP diff --git a/lib/src/restd_rest_handler.c b/lib/src/restd_rest_handler.c deleted file mode 100644 index 8cb606f..0000000 --- a/lib/src/restd_rest_handler.c +++ /dev/null @@ -1,284 +0,0 @@ -/*! - * 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 -#include -#include - -#include - -#include "restd_server.h" -#include "restd_http_handler.h" -#include "restd_rest_handler.h" - -#include "macro.h" - -/*--------------------------------------------------------------------------*/ - -struct mimetype_s -{ - const char *extn; - const char *mime; -}; - -/*--------------------------------------------------------------------------*/ - -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}}; - -/*--------------------------------------------------------------------------*/ - -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; -} - -/*! ---------------------------------------------------------------------------- - * @fn restd_rest_handler - * - * @brief Main function to manage rest request. - */ -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; -} diff --git a/lib/src/restd_server.c b/lib/src/restd_server.c deleted file mode 100644 index 4340ea5..0000000 --- a/lib/src/restd_server.c +++ /dev/null @@ -1,837 +0,0 @@ -/*! - * restd_server.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 -#include -#include -#include - -#ifdef __linux__ -#include -#else -#include -#endif - -#include "macro.h" -#include "restd_server.h" - -/* - * Local variables. - */ -static bool initialized = false; - -/* - * Global variables. - */ -int _restd_log_level = RESTD_LOG_WARN; - - -/* - * Local functions. - */ -static int notify_loopexit(restd_server_t *server); -static void notify_cb(struct bufferevent *buffer, void *userdata); -static void *server_loop(void *instance); -static void close_server(restd_server_t *server); -static void libevent_log_cb(int severity, const char *msg); -static int set_undefined_options(restd_server_t *server); -static void listener_cb(struct evconnlistener *listener, - evutil_socket_t evsocket, struct sockaddr *sockaddr, - int socklen, void *userdata); -static restd_conn_t *conn_new(restd_server_t *server, struct bufferevent *buffer); -static void conn_reset(restd_conn_t *conn); -static void conn_free(restd_conn_t *conn); -static void conn_read_cb(struct bufferevent *buffer, void *userdata); -static void conn_write_cb(struct bufferevent *buffer, void *userdata); -static void conn_event_cb(struct bufferevent *buffer, short what, void *userdata); -static void conn_cb(restd_conn_t *conn, int event); -static int call_hooks(short event, restd_conn_t *conn); -static void *set_userdata(restd_conn_t *conn, int index, const void *userdata, restd_userdata_free_cb free_cb); -static void *get_userdata(restd_conn_t *conn, int index); - -/** - * 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 ----------------------------------*/ - -/** - * Set debug output level. - * - * @param debug_level debug output level. 0 to disable. - * - * @return previous debug level. - * - * @note - * debug_level: - * REST_LOG_DISABLE - * REST_LOG_ERROR - * REST_LOG_WARN (default) - * REST_LOG_INFO - * REST_LOG_DEBUG - * REST_LOG_DEBUG2 - */ -enum restd_log_e restd_log_level(enum restd_log_e log_level) -{ - int prev = _restd_log_level; - _restd_log_level = log_level; - return prev; -} - -/** - * Create a server object. - */ -restd_server_t *restd_server_new(void) -{ - if (initialized) - { - initialized = true; - } - - restd_server_t *server = NEW_OBJECT(restd_server_t); - if (server == NULL) - { - return NULL; - } - - // Initialize instance. - server->options = qhashtbl(0, 0); - server->stats = qhashtbl(100, QHASHTBL_THREADSAFE); - server->hooks = qlist(0); - server->call_hooks = call_hooks; - if (server->options == NULL || server->stats == NULL || server->hooks == NULL) - { - restd_server_free(server); - return NULL; - } - - DEBUG("Created a server object."); - return server; -} - -/** - * Release server object and all the resources. - */ -void restd_server_free(restd_server_t *server) -{ - if (server == NULL) - return; - - int thread = restd_server_get_option_int(server, "server.thread"); - if (thread && server->thread) - { - notify_loopexit(server); - sleep(1); - close_server(server); - } - - if (server->evbase) - { - event_base_free(server->evbase); - } - - if (server->options) - { - server->options->free(server->options); - } - if (server->stats) - { - server->stats->free(server->stats); - } - if (server->hooks) - { - qlist_t *tbl = server->hooks; - restd_hook_t *hook; - while ((hook = tbl->popfirst(tbl, NULL))) - { - if (hook->method) - free(hook->method); - if (hook->path) - free(hook->path); - free(hook); - } - server->hooks->free(server->hooks); - } - free(server); - DEBUG("Server terminated."); -} - -/** - * Start server. - * - * @return 0 if successful, otherwise -1. - */ -int restd_server_start(restd_server_t *server) -{ - DEBUG("Starting a server."); - - // 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; - } - } - - // Create a eventfd for notification channel. -#ifdef __linux__ - int notifyfd = eventfd(0, 0); -#else - int notifyfd = kqueue(); -#endif - server->notify_buffer = bufferevent_socket_new(server->evbase, notifyfd, BEV_OPT_CLOSE_ON_FREE); - bufferevent_setcb(server->notify_buffer, NULL, notify_cb, NULL, server); - - if (!server->listener) - { - server->listener = evconnlistener_new_bind( - server->evbase, listener_cb, (void *)server, - LEV_OPT_THREADSAFE | LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, - restd_server_get_option_int(server, "server.backlog"), - sockaddr, sockaddr_len); - if (!server->listener) - { - ERROR("Failed to bind on %s:%d", addr, port); - return -1; - } - } - - // Listen - INFO("Listening on %s:%d", addr, port); - - int exitstatus = 0; - if (restd_server_get_option_int(server, "server.thread")) - { - DEBUG("Launching server as a thread.") - 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; -} - -/*--------------------------------------------------------------------------*/ - -int restd_server_attach_event_loop(restd_server_t *server, struct event_base *ev_base) -{ - if (server == NULL) - return -1; - - server->evbase = ev_base; - return 0; -} - -/** - * Set server option. - * - * @see AD_SERVER_OPTIONS - */ -void restd_server_set_option(restd_server_t *server, const char *key, const char *value) -{ - server->options->putstr(server->options, key, value); -} - -/** - * Retrieve server option. - */ -char *restd_server_get_option(restd_server_t *server, const char *key) -{ - return server->options->getstr(server->options, key, false); -} - -/** - * Retrieve server option in integer format. - */ -int restd_server_get_option_int(restd_server_t *server, const char *key) -{ - char *value = restd_server_get_option(server, key); - return (value) ? atoi(value) : 0; -} - -/** - * 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 - * server get out of the loop without waiting for an event. - */ -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 void *server_loop(void *instance) -{ - restd_server_t *server = (restd_server_t *)instance; - - int *retval = NEW_OBJECT(int); - DEBUG("Loop start"); - event_base_loop(server->evbase, 0); - DEBUG("Loop finished"); - *retval = (event_base_got_break(server->evbase)) ? -1 : 0; - - return retval; -} -/*--------------------------------------------------------------------------*/ - -static void close_server(restd_server_t *server) -{ - DEBUG("Closing server."); - - if (server->notify_buffer) - { - bufferevent_free(server->notify_buffer); - server->notify_buffer = NULL; - } - - if (server->listener) - { - evconnlistener_free(server->listener); - server->listener = NULL; - } - - if (server->thread) - { - void *retval = NULL; - DEBUG("Waiting server's last loop to finish."); - pthread_join(*(server->thread), &retval); - free(retval); - free(server->thread); - server->thread = NULL; - } - 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; - } - } -} - -/*--------------------------------------------------------------------------*/ - -// 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; -} - -/*--------------------------------------------------------------------------*/ - -static void listener_cb(struct evconnlistener *listener, evutil_socket_t socket, - struct sockaddr *sockaddr, int socklen, void *userdata) -{ - DEBUG("New connection."); - restd_server_t *server = (restd_server_t *)userdata; - - // Create a new buffer. - struct bufferevent *buffer = NULL; - - buffer = bufferevent_socket_new(server->evbase, socket, BEV_OPT_CLOSE_ON_FREE); - - if (buffer == NULL) - goto error; - - // Set read timeout. - int timeout = restd_server_get_option_int(server, "server.timeout"); - if (timeout > 0) - { - struct timeval tm; - bzero((void *)&tm, sizeof(struct timeval)); - tm.tv_sec = timeout; - bufferevent_set_timeouts(buffer, &tm, NULL); - } - - // Create a connection. - void *conn = conn_new(server, buffer); - if (!conn) - goto error; - - return; - -error: - if (buffer) - bufferevent_free(buffer); - ERROR("Failed to create a connection handler."); - event_base_loopbreak(server->evbase); - server->errcode = ENOMEM; -} - -/*--------------------------------------------------------------------------*/ - -static restd_conn_t *conn_new(restd_server_t *server, struct bufferevent *buffer) -{ - if (server == NULL || buffer == NULL) - { - return NULL; - } - - // Create a new connection container. - restd_conn_t *conn = NEW_OBJECT(restd_conn_t); - if (conn == NULL) - return NULL; - - // Initialize with default values. - conn->server = server; - conn->buffer = buffer; - conn->in = bufferevent_get_input(buffer); - conn->out = bufferevent_get_output(buffer); - conn_reset(conn); - - // Bind callback - bufferevent_setcb(buffer, conn_read_cb, conn_write_cb, conn_event_cb, (void *)conn); - bufferevent_setwatermark(buffer, EV_WRITE, 0, 0); - bufferevent_enable(buffer, EV_WRITE); - bufferevent_enable(buffer, EV_READ); - - // Run callbacks with AD_EVENT_INIT event. - conn->status = conn->server->call_hooks(RESTD_EVENT_INIT | RESTD_EVENT_WRITE, conn); - - // - conn->id = -1; - - return conn; -} - -/*--------------------------------------------------------------------------*/ - -static void conn_reset(restd_conn_t *conn) -{ - conn->status = RESTD_OK; - - for (int i = 0; i < RESTD_NUM_USERDATA; i++) - { - if (conn->userdata[i]) - { - if (conn->userdata_free_cb[i] != NULL) - { - conn->userdata_free_cb[i](conn, conn->userdata[i]); - } - else - { - WARN("Found unreleased userdata."); - } - conn->userdata[i] = NULL; - } - } - - if (conn->method) - { - free(conn->method); - conn->method = NULL; - } -} - -/*--------------------------------------------------------------------------*/ - -static void conn_free(restd_conn_t *conn) -{ - if (conn) - { - if (conn->status != RESTD_CLOSE) - { - conn->server->call_hooks(RESTD_EVENT_CLOSE | RESTD_EVENT_SHUTDOWN, conn); - } - conn_reset(conn); - if (conn->buffer) - { - - bufferevent_free(conn->buffer); - } - free(conn); - } -} - -/*--------------------------------------------------------------------------*/ - -#define DRAIN_EVBUFFER(b) evbuffer_drain(b, evbuffer_get_length(b)) -static void conn_read_cb(struct bufferevent *buffer, void *userdata) -{ - DEBUG("read_cb"); - restd_conn_t *conn = userdata; - conn_cb(conn, RESTD_EVENT_READ); -} - -/*--------------------------------------------------------------------------*/ - -static void conn_write_cb(struct bufferevent *buffer, void *userdata) -{ - DEBUG("write_cb"); - restd_conn_t *conn = userdata; - conn_cb(conn, RESTD_EVENT_WRITE); -} - -/*--------------------------------------------------------------------------*/ - -static void conn_event_cb(struct bufferevent *buffer, short what, void *userdata) -{ - DEBUG("event_cb 0x%x", what); - restd_conn_t *conn = userdata; - - if (what & BEV_EVENT_EOF || what & BEV_EVENT_ERROR || what & BEV_EVENT_TIMEOUT) - { - conn->status = RESTD_CLOSE; - conn_cb(conn, RESTD_EVENT_CLOSE | ((what & BEV_EVENT_TIMEOUT) ? RESTD_EVENT_TIMEOUT : 0)); - } -} - -/*--------------------------------------------------------------------------*/ - -static void conn_cb(restd_conn_t *conn, int event) -{ - DEBUG("conn_cb: status:0x%x, event:0x%x", conn->status, event) - if (conn->status == RESTD_OK || conn->status == RESTD_TAKEOVER) - { - int status = conn->server->call_hooks(event, conn); - // Update status only when it's higher then before. - if (!(conn->status == RESTD_CLOSE || (conn->status == RESTD_DONE && conn->status >= status))) - { - conn->status = status; - } - } - - if (conn->status == RESTD_DONE) - { - if (restd_server_get_option_int(conn->server, "server.request_pipelining")) - { - conn->server->call_hooks(RESTD_EVENT_CLOSE, conn); - conn_reset(conn); - conn->server->call_hooks(RESTD_EVENT_INIT, conn); - } - else - { - // Do nothing but drain input buffer. - if (event == RESTD_EVENT_READ) - { - DEBUG("Draining in-buffer. %d", conn->status); - DRAIN_EVBUFFER(conn->in); - } - } - return; - } - else if (conn->status == RESTD_CLOSE) - { - if (evbuffer_get_length(conn->out) <= 0) - { - int newevent = (event & RESTD_EVENT_CLOSE) ? event : RESTD_EVENT_CLOSE; - //conn->server->call_hooks(newevent, conn); - conn_free(conn); - DEBUG("Connection closed."); - return; - } - } -} - -/*--------------------------------------------------------------------------*/ - -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; -} - -/*--------------------------------------------------------------------------*/ - -void restd_server_register_call_hooks_handler(restd_server_t *server, restd_call_hook_cb cb) -{ - server->call_hooks = cb; -} - -/*--------------------------------------------------------------------------*/ - -/** - * Register user hook. - */ -void restd_server_register_hook(restd_server_t *server, restd_callback cb, void *userdata) -{ - restd_server_register_hook_on_method(server, NULL, cb, userdata); -} - -/*--------------------------------------------------------------------------*/ - -/** - * 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; - - server->hooks->addlast(server->hooks, (void *)&hook, sizeof(restd_hook_t)); -} - -/*--------------------------------------------------------------------------*/ - -static int call_hooks(short event, restd_conn_t *conn) -{ - DEBUG("call_hooks: event 0x%x", event); - 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) - { - if (hook->method && conn->method && strcmp(hook->method, conn->method)) - { - continue; - } - int status = hook->cb(event, conn, hook->userdata); - if (status != RESTD_OK) - { - return status; - } - } - } - return RESTD_OK; -} - -/*--------------------------------------------------------------------------*/ - -static void *set_userdata(restd_conn_t *conn, int index, const void *userdata, restd_userdata_free_cb free_cb) -{ - void *prev = conn->userdata; - conn->userdata[index] = (void *)userdata; - conn->userdata_free_cb[index] = free_cb; - return prev; -} - -/*--------------------------------------------------------------------------*/ - -static void *get_userdata(restd_conn_t *conn, int index) -{ - return conn->userdata[index]; -} - -/*--------------------------------------------------------------------------*/ - -/** - * Set extra userdata into the connection. - * - * @return previous userdata; - * - * @note - * Extra userdata is for default protocol handler such as ad_http_handler to - * provide higher abstraction. End users should always use only ad_conn_set_userdata() - * to avoid any conflict with default handlers. - */ -void *restd_conn_set_extra(restd_conn_t *conn, const void *extra, restd_userdata_free_cb free_cb) -{ - return set_userdata(conn, 1, extra, free_cb); -} - -/** - * Get extra userdata attached in this connection. - */ -void *restd_conn_get_extra(restd_conn_t *conn) -{ - return get_userdata(conn, 1); -} - -/** - * Set method name on this connection. - * - * Once the method name is set, hooks registered by ad_server_register_hook_on_method() - * will be called if method name matches with the registered name. - * - * @see ad_server_register_hook_on_method() - */ -void restd_conn_set_method(restd_conn_t *conn, char *method) -{ - char *prev = conn->method; - conn->method = (method != NULL) ? strdup(method) : NULL; - if (prev) - { - free(prev); - } -} diff --git a/lib/tests/http-server.c b/lib/tests/http-server.c deleted file mode 100644 index 9ac28e4..0000000 --- a/lib/tests/http-server.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - A trivial static http webserver using Libevent's evhttp. - This is not the best code in the world, and it does some fairly stupid stuff - that you would never want to do in a production webserver. Caveat hackor! - */ - -/* Compatibility for possible missing IPv6 declarations */ -#include "../util-internal.h" - -#include -#include -#include - -#include -#include - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#include -#ifndef S_ISDIR -#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) -#endif -#else /* !_WIN32 */ -#include -#include -#include -#include -#include -#endif /* _WIN32 */ -#include - -#ifdef EVENT__HAVE_SYS_UN_H -#include -#endif -#ifdef EVENT__HAVE_AFUNIX_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif /* _WIN32 */ - -#ifdef EVENT__HAVE_NETINET_IN_H -#include -# ifdef _XOPEN_SOURCE_EXTENDED -# include -# endif -#endif - -#ifdef _WIN32 -#ifndef stat -#define stat _stat -#endif -#ifndef fstat -#define fstat _fstat -#endif -#ifndef open -#define open _open -#endif -#ifndef close -#define close _close -#endif -#ifndef O_RDONLY -#define O_RDONLY _O_RDONLY -#endif -#endif /* _WIN32 */ - -char uri_root[512]; - -static const struct table_entry { - const char *extension; - const char *content_type; -} content_type_table[] = { - { "txt", "text/plain" }, - { "c", "text/plain" }, - { "h", "text/plain" }, - { "html", "text/html" }, - { "htm", "text/htm" }, - { "css", "text/css" }, - { "gif", "image/gif" }, - { "jpg", "image/jpeg" }, - { "jpeg", "image/jpeg" }, - { "png", "image/png" }, - { "pdf", "application/pdf" }, - { "ps", "application/postscript" }, - { NULL, NULL }, -}; - -struct options { - int port; - int iocp; - int verbose; - - int unlink; - const char *unixsock; - const char *docroot; -}; - -/* Try to guess a good content-type for 'path' */ -static const char * -guess_content_type(const char *path) -{ - const char *last_period, *extension; - const struct table_entry *ent; - last_period = strrchr(path, '.'); - if (!last_period || strchr(last_period, '/')) - goto not_found; /* no exension */ - extension = last_period + 1; - for (ent = &content_type_table[0]; ent->extension; ++ent) { - if (!evutil_ascii_strcasecmp(ent->extension, extension)) - return ent->content_type; - } - -not_found: - return "application/misc"; -} - -/* Callback used for the /dump URI, and for every non-GET request: - * dumps all information to stdout and gives back a trivial 200 ok */ -static void -dump_request_cb(struct evhttp_request *req, void *arg) -{ - const char *cmdtype; - struct evkeyvalq *headers; - struct evkeyval *header; - struct evbuffer *buf; - - 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)); - - headers = evhttp_request_get_input_headers(req); - for (header = headers->tqh_first; header; - header = header->next.tqe_next) { - printf(" %s: %s\n", header->key, header->value); - } - - buf = evhttp_request_get_input_buffer(req); - puts("Input data: <<<"); - while (evbuffer_get_length(buf)) { - int n; - char cbuf[128]; - n = evbuffer_remove(buf, cbuf, sizeof(cbuf)); - if (n > 0) - (void) fwrite(cbuf, 1, n, stdout); - } - puts(">>>"); - - evhttp_send_reply(req, 200, "OK", NULL); -} - -/* This callback gets invoked when we get any http request that doesn't match - * any other callback. Like any evhttp server callback, it has a simple job: - * it must eventually call evhttp_send_error() or evhttp_send_reply(). - */ -static void -send_document_cb(struct evhttp_request *req, void *arg) -{ - struct evbuffer *evb = NULL; - struct options *o = arg; - const char *uri = evhttp_request_get_uri(req); - struct evhttp_uri *decoded = NULL; - const char *path; - char *decoded_path; - char *whole_path = NULL; - size_t len; - int fd = -1; - struct stat st; - - if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) { - dump_request_cb(req, arg); - return; - } - - printf("Got a GET request for <%s>\n", uri); - - /* Decode the URI */ - decoded = evhttp_uri_parse(uri); - if (!decoded) { - printf("It's not a good URI. Sending BADREQUEST\n"); - evhttp_send_error(req, HTTP_BADREQUEST, 0); - return; - } - - /* Let's see what path the user asked for. */ - path = evhttp_uri_get_path(decoded); - if (!path) path = "/"; - - /* We need to decode it, to see what path the user really wanted. */ - decoded_path = evhttp_uridecode(path, 0, NULL); - if (decoded_path == NULL) - goto err; - /* Don't allow any ".."s in the path, to avoid exposing stuff outside - * of the docroot. This test is both overzealous and underzealous: - * it forbids aceptable paths like "/this/one..here", but it doesn't - * do anything to prevent symlink following." */ - if (strstr(decoded_path, "..")) - goto err; - - len = strlen(decoded_path)+strlen(o->docroot)+2; - if (!(whole_path = malloc(len))) { - perror("malloc"); - goto err; - } - evutil_snprintf(whole_path, len, "%s/%s", o->docroot, decoded_path); - - if (stat(whole_path, &st)<0) { - goto err; - } - - /* This holds the content we're sending. */ - evb = evbuffer_new(); - - if (S_ISDIR(st.st_mode)) { - /* If it's a directory, read the comments and make a little - * index page */ -#ifdef _WIN32 - HANDLE d; - WIN32_FIND_DATAA ent; - char *pattern; - size_t dirlen; -#else - DIR *d; - struct dirent *ent; -#endif - const char *trailing_slash = ""; - - if (!strlen(path) || path[strlen(path)-1] != '/') - trailing_slash = "/"; - -#ifdef _WIN32 - dirlen = strlen(whole_path); - pattern = malloc(dirlen+3); - memcpy(pattern, whole_path, dirlen); - pattern[dirlen] = '\\'; - pattern[dirlen+1] = '*'; - pattern[dirlen+2] = '\0'; - d = FindFirstFileA(pattern, &ent); - free(pattern); - if (d == INVALID_HANDLE_VALUE) - goto err; -#else - if (!(d = opendir(whole_path))) - goto err; -#endif - - evbuffer_add_printf(evb, - "\n" - "\n \n" - " \n" - " %s\n" - " \n" - " \n" - " \n" - "

%s

\n" - "
    \n", - decoded_path, /* XXX html-escape this. */ - path, /* XXX html-escape this? */ - trailing_slash, - decoded_path /* XXX html-escape this */); -#ifdef _WIN32 - do { - const char *name = ent.cFileName; -#else - while ((ent = readdir(d))) { - const char *name = ent->d_name; -#endif - evbuffer_add_printf(evb, - "
  • %s\n", - name, name);/* XXX escape this */ -#ifdef _WIN32 - } while (FindNextFileA(d, &ent)); -#else - } -#endif - evbuffer_add_printf(evb, "
\n"); -#ifdef _WIN32 - FindClose(d); -#else - closedir(d); -#endif - evhttp_add_header(evhttp_request_get_output_headers(req), - "Content-Type", "text/html"); - } else { - /* Otherwise it's a file; add it to the buffer to get - * sent via sendfile */ - const char *type = guess_content_type(decoded_path); - if ((fd = open(whole_path, O_RDONLY)) < 0) { - perror("open"); - goto err; - } - - if (fstat(fd, &st)<0) { - /* Make sure the length still matches, now that we - * opened the file :/ */ - perror("fstat"); - goto err; - } - evhttp_add_header(evhttp_request_get_output_headers(req), - "Content-Type", type); - evbuffer_add_file(evb, fd, 0, st.st_size); - } - - evhttp_send_reply(req, 200, "OK", evb); - goto done; -err: - evhttp_send_error(req, HTTP_NOTFOUND, NULL); - if (fd>=0) - close(fd); -done: - if (decoded) - evhttp_uri_free(decoded); - if (decoded_path) - free(decoded_path); - if (whole_path) - free(whole_path); - if (evb) - evbuffer_free(evb); -} - -static void -print_usage(FILE *out, const char *prog, int exit_code) -{ - fprintf(out, - "Syntax: %s [ OPTS ] \n" - " -p - port\n" - " -U - bind to unix socket\n" - " -u - unlink unix socket before bind\n" - " -I - IOCP\n" - " -v - verbosity, enables libevent debug logging too\n", prog); - exit(exit_code); -} -static struct options -parse_opts(int argc, char **argv) -{ - struct options o; - int opt; - - memset(&o, 0, sizeof(o)); - - while ((opt = getopt(argc, argv, "hp:U:uIv")) != -1) { - switch (opt) { - case 'p': o.port = atoi(optarg); break; - case 'U': o.unixsock = optarg; break; - case 'u': o.unlink = 1; break; - case 'I': o.iocp = 1; break; - case 'v': ++o.verbose; break; - case 'h': print_usage(stdout, argv[0], 0); break; - default : fprintf(stderr, "Unknown option %c\n", opt); break; - } - } - - if (optind >= argc || (argc - optind) > 1) { - print_usage(stdout, argv[0], 1); - } - o.docroot = argv[optind]; - - return o; -} - -static void -do_term(int sig, short events, void *arg) -{ - struct event_base *base = arg; - event_base_loopbreak(base); - fprintf(stderr, "Got %i, Terminating\n", sig); -} - -static int -display_listen_sock(struct evhttp_bound_socket *handle) -{ - struct sockaddr_storage ss; - evutil_socket_t fd; - ev_socklen_t socklen = sizeof(ss); - char addrbuf[128]; - void *inaddr; - const char *addr; - int got_port = -1; - - fd = evhttp_bound_socket_get_fd(handle); - memset(&ss, 0, sizeof(ss)); - if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) { - perror("getsockname() failed"); - return 1; - } - - if (ss.ss_family == AF_INET) { - got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port); - inaddr = &((struct sockaddr_in*)&ss)->sin_addr; - } else if (ss.ss_family == AF_INET6) { - got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port); - inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr; - } -#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN - else if (ss.ss_family == AF_UNIX) { - printf("Listening on <%s>\n", ((struct sockaddr_un*)&ss)->sun_path); - return 0; - } -#endif - else { - fprintf(stderr, "Weird address family %d\n", - ss.ss_family); - return 1; - } - - addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, - sizeof(addrbuf)); - if (addr) { - printf("Listening on %s:%d\n", addr, got_port); - evutil_snprintf(uri_root, sizeof(uri_root), - "http://%s:%d",addr,got_port); - } else { - fprintf(stderr, "evutil_inet_ntop failed\n"); - return 1; - } - - return 0; -} - -int -main(int argc, char **argv) -{ - struct event_config *cfg = NULL; - struct event_base *base = NULL; - struct evhttp *http = NULL; - struct evhttp_bound_socket *handle = NULL; - struct evconnlistener *lev = NULL; - struct event *term = NULL; - struct options o = parse_opts(argc, argv); - int ret = 0; - -#ifdef _WIN32 - { - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 2); - WSAStartup(wVersionRequested, &wsaData); - } -#else - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - ret = 1; - goto err; - } -#endif - - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - /** Read env like in regress */ - if (o.verbose || getenv("EVENT_DEBUG_LOGGING_ALL")) - event_enable_debug_logging(EVENT_DBG_ALL); - - cfg = event_config_new(); -#ifdef _WIN32 - if (o.iocp) { -#ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED - evthread_use_windows_threads(); - event_config_set_num_cpus_hint(cfg, 8); -#endif - event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); - } -#endif - - base = event_base_new_with_config(cfg); - if (!base) { - fprintf(stderr, "Couldn't create an event_base: exiting\n"); - ret = 1; - } - event_config_free(cfg); - cfg = NULL; - - /* Create a new evhttp object to handle requests. */ - http = evhttp_new(base); - if (!http) { - fprintf(stderr, "couldn't create evhttp. Exiting.\n"); - ret = 1; - } - - /* The /dump URI will dump all requests to stdout and say 200 ok. */ - evhttp_set_cb(http, "/dump", dump_request_cb, NULL); - - /* We want to accept arbitrary requests, so we need to set a "generic" - * cb. We can also add callbacks for specific paths. */ - evhttp_set_gencb(http, send_document_cb, &o); - - if (o.unixsock) { -#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN - struct sockaddr_un addr; - - if (o.unlink && (unlink(o.unixsock) && errno != ENOENT)) { - perror(o.unixsock); - ret = 1; - goto err; - } - - addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, o.unixsock); - - lev = evconnlistener_new_bind(base, NULL, NULL, - LEV_OPT_CLOSE_ON_FREE, -1, - (struct sockaddr *)&addr, sizeof(addr)); - if (!lev) { - perror("Cannot create listener"); - ret = 1; - goto err; - } - - handle = evhttp_bind_listener(http, lev); - if (!handle) { - fprintf(stderr, "couldn't bind to %s. Exiting.\n", o.unixsock); - ret = 1; - goto err; - } -#else /* !EVENT__HAVE_STRUCT_SOCKADDR_UN */ - fprintf(stderr, "-U is not supported on this platform. Exiting.\n"); - ret = 1; - goto err; -#endif /* EVENT__HAVE_STRUCT_SOCKADDR_UN */ - } - else { - handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port); - if (!handle) { - fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port); - ret = 1; - goto err; - } - } - - if (display_listen_sock(handle)) { - ret = 1; - goto err; - } - - term = evsignal_new(base, SIGINT, do_term, base); - if (!term) - goto err; - if (event_add(term, NULL)) - goto err; - - event_base_dispatch(base); - -#ifdef _WIN32 - WSACleanup(); -#endif - -err: - if (cfg) - event_config_free(cfg); - if (http) - evhttp_free(http); - if (term) - event_free(term); - if (base) - event_base_free(base); - - return ret; -} diff --git a/lib/tests/launcher.sh b/lib/tests/launcher.sh deleted file mode 100755 index ff793f5..0000000 --- a/lib/tests/launcher.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -if [ $# = 0 ]; then - echo "This program is called by make. Please use \"make test\" command instead." - exit 1 -fi - -FAIL=0 -FAILDESC="" -for EXECUTABLE in $*; do - ./$EXECUTABLE - echo "" - if [ $? != 0 ]; then - FAIL=1 - FAILDESC="$FAILDESC $EXECUTABLE" - fi -done - -if [ $FAIL != 0 ]; then - echo "======================================================================" - echo "**** OOOOOPS!!! UNSUCESSFUL UNIT TEST FOUND. PLEASE FIX AND RERUN ****" - echo "======================================================================" - echo "Fails in =>$FAILDESC" - exit 1 -fi - -echo "======================================================================" -echo "**** Good job! All tests are successful ****" -echo "======================================================================" -echo "* ____ _ All tests have finished successfully. *" -echo "* / ___| ___ ___ __| | | | ___ | |__ | | *" -echo "* | | _ / _ \ / _ \ / _\` | _ | |/ _ \| '_ \ | | *" -echo "* | |_| | (_) | (_) | (_| | | |_| | (_) | |_) | |_| *" -echo "* \____|\___/ \___/ \__,_| \___/ \___/|_.__/ (_) *" -echo "======================================================================" -echo "Tested: $*" -exit 0 diff --git a/lib/tests/restd.c b/lib/tests/restd.c deleted file mode 100644 index 547d3b2..0000000 --- a/lib/tests/restd.c +++ /dev/null @@ -1,102 +0,0 @@ -/*! - * main.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: 08/11/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 - -/*--------------------------------------------------------------------------*/ - -int my_http_get_handler(short event, restd_conn_t *conn, void *userdata) -{ - if (event & RESTD_EVENT_READ) - { - if (restd_http_get_status(conn) == RESTD_HTTP_REQ_DONE) - { - restd_http_response(conn, 200, "application/json", "{\"status\": false}", 17); - return restd_http_is_keepalive_request(conn) ? RESTD_DONE : RESTD_CLOSE; - } - } - return RESTD_OK; -} - -/*--------------------------------------------------------------------------*/ - -int my_error_handler(short event, restd_conn_t *conn, void *userdata) -{ - int http_code = 500; - if (event & RESTD_ERROR_METHOD_NOT_ALLOWED) - { - http_code = 405; - } - else if (event & RESTD_ERROR_PATH_NOT_FOUND) - { - http_code = 404; - } - - restd_http_response(conn, http_code, "application/json", "{\"status\":\"error\"}", 18); - return RESTD_CLOSE; // Close connection. -} - -/*--------------------------------------------------------------------------*/ - -void usage(void) -{ - printf("Usage : domo-iot [web server path] \n"); - printf("web server path: \t root path of the Web server.\n"); -} - -/*--------------------------------------------------------------------------*/ - -int main(int argc, char **argv) -{ - restd_server_t *server; - - if (argc < 2) - { - usage(); - return -1; - } - - restd_log_level(RESTD_LOG_DEBUG); - server = restd_server_new(); - restd_server_set_option(server, "server.port", "8888"); - restd_server_set_option(server, "server.root_path", argv[1]); - - restd_server_register_request_handler(server, restd_rest_handler); - restd_server_register_error_handler(server, my_error_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); -} diff --git a/lib/tests/test_restd.c b/lib/tests/test_restd.c deleted file mode 100644 index 84730bf..0000000 --- a/lib/tests/test_restd.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "qunit.h" -#include "qlibc.h" - -QUNIT_START("Test title"); - -TEST("Test name1") { - ASSERT_EQUAL_STR("abc", "abc"); - ASSERT_EQUAL_INT(8, 8); -} - -TEST("Test name2") { - ASSERT_EQUAL_PT(NULL == NULL); -} - -QUNIT_END(); \ No newline at end of file diff --git a/src/rest/restd.h b/src/rest/restd.h index 14ce51f..c8171d8 100644 --- a/src/rest/restd.h +++ b/src/rest/restd.h @@ -78,17 +78,9 @@ 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 | -\*---------------------------------------------------------------------------*/ - - - /*---------------------------------------------------------------------------*\ | DATA STRUCTURES | \*---------------------------------------------------------------------------*/