diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6b92f82..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "cStuff"] - path = cStuff - url = https://github.com/lowenware/cStuff.git diff --git a/CHANGELOG b/CHANGELOG index e69de29..dfab137 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -0,0 +1,8 @@ +# Release 1.0.0 + +By Ilja Kartašov , 2019-06-02 + +1. Library release with new and final API and documented code. +2. Previous version is being called `aisl-legacy` and will be maintained in +security aspects in branch called `legacy` until all applications using it will +be updated. diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 3d42de2..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,131 +0,0 @@ -cmake_minimum_required (VERSION 2.8) - -project ( AISL C) - -set (PROJECT_TITLE "aisl") -set (LIBRARY_NAME ${PROJECT_TITLE}) -# set (DEMO_NAME "demo") - -# Defaults -------------------------------------------------------------------- - -include (cmake.compiler) -include (cmake.version) -include (cmake.system) -include (cmake.paths) - -# Definitions ----------------------------------------------------------------- - -add_definitions( - -DPROJECT_TITLE="${PROJECT_TITLE}" -) - -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden") - -if(DEFINED CMAKE_DEBUG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ") - add_definitions( -DDEBUG=${CMAKE_DEBUG} ) -endif() - - -# Options --------------------------------------------------------------------- - -#if( DEFINED WITH_EVERYTHING ) -# set(WITH_TEMPLIGHT 1) -# set(WITH_OPTIONS 1) -# set(WITH_CONFIG 1) -#endif() - -# Sources --------------------------------------------------------------------- - -include_directories( "." ${INCLUDE_DIR} ) - -add_definitions( -DLIST_WITH_APPEND - -DCSTUFF_LIST_WITH_REMOVE_INDEX - -DCSTUFF_LIST_WITH_REMOVE - -DCSTUFF_LIST_WITH_APPEND - -DCSTUFF_LIST_WITH_INSERT - -DCSTUFF_LIST_WITH_COPY - -DCSTUFF_STR_UTILS_WITH_COPY - -DCSTUFF_STR_UTILS_WITH_NCAT - -DCSTUFF_STR_UTILS_WITH_CMPI - -DCSTUFF_STR_UTILS_WITH_PRINTF - -DCSTUFF_STR_UTILS_WITH_NCOPY - -DSTR_UTILS_WITH_COPY - -DSTR_UTILS_WITH_NCOPY - -DSTR_UTILS_WITH_CAT - -DSTR_UTILS_WITH_PRINTF - -DSTR_UTILS_WITH_CMPI -) - -set ( LIBRARY_SOURCES library/aisl.c - library/buffer.c - library/server.c - library/client.c - library/stream.c - library/parser.c - library/http.c - library/handle.c - library/status.c - library/event.c - cStuff/list.c - cStuff/str-utils.c) - -set ( DEMO_SOURCES demo/main.c demo/events.c demo/urls.c ) - -set ( META_FILES README.md LICENSE.md AUTHORS.md) - -#if( DEFINED WITH_TEMPLIGHT ) -# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/templight.c) -# add_definitions( -DWITH_TEMPLIGHT ) -#endif() - -#if( DEFINED WITH_OPTIONS ) -# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/options.c) -# add_definitions( -DWITH_OPTIONS ) -#endif() - -#if( DEFINED WITH_CONFIG ) -# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/config.c) -# add_definitions( -DWITH_CONFIG ) -#endif() - -find_package(OpenSSL) - -include_directories( - ${OPENSSL_INCLUDE_DIRS} -) - -link_directories( - ${OPENSSL_LIBRARY_DIRS} -) - -# Library ---------------------------------------------------------------------- - -add_library(${LIBRARY_NAME} SHARED ${LIBRARY_SOURCES}) - -# Demos ------------------------------------------------------------------------ - -#add_executable(${DEMO_NAME} ${DEMO_SOURCES}) - -#target_link_libraries(${DEMO_NAME} ${LIBRARY_NAME}) -target_link_libraries(${LIBRARY_NAME} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) - -# Installation ---------------------------------------------------------------- - -install( - TARGETS ${LIBRARY_NAME} - DESTINATION ${CMAKE_INSTALL_LIBDIR} -) -# ${LIB_INSTALL_DIR} - -#install( -# FILES ${META_FILES} -# DESTINATION ${SHARE_INSTALL_PREFIX}/doc/packages/${LIBRARY_NAME}/ -#) - -install( - DIRECTORY ${INCLUDE_DIR}/${LIBRARY_NAME} - DESTINATION ${INCLUDE_INSTALL_DIR} -) - - diff --git a/README.md b/README.md index ebcb75e..e2e5a99 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,32 @@ # AISL -Asynchronous Internet Server Library -## Installation on CentOS 7 / RedHat 7 +Asynchronous Internet Server Library provides innovative way of web development. +AISL based applications have built-in web server giving full control of client +serving. All you need to know is a request string? - Start prepare the response +without waiting for headers and body! Don't need some headers? - Don't save them +in memory! Unwanted content-body? - simply ignore it! + +## Documentation + +[Hello World](https://lowenware.com/aisl/handbook.html#getting-started) example +and full [API reference](https://lowenware.com/aisl/handbook.html#api-reference) +can be found in an oficial [AISL HandBook](https://lowenware.com/aisl/handbook.html). + +## Installation -1. Add repository ``` -sudo curl -o /etc/yum.repos.d/lowenware.repo https://lowenware.com/rpm/redhat-7/lowenware.repo +$ make PREFIX=/usr/local +$ sudo make PREFIX=/usr/local install +$ sudo cp libaisl.pc.example /usr/lib/pkgconfig/libaisl.pc ``` -2. Import GPG key +ArchLinux users can install from [AUR](https://aur.archlinux.org/packages/aisl-git/) : + ``` -sudo rpm --import https://lowenware.com/rpm/RPM-GPG-KEY-Lowenware +$ yaourt -S aisl-git ``` -3. Install -``` -sudo yum install aisl aisl-devel -``` +## License -## Installation from sources on any distro - -1. Configuration -``` -cmake -DCMAKE_INSTALL_PREFIX=/usr/local -``` - -2. Compilation -``` -make -``` - -3. Installation -``` -sudo make install -``` +AISL is free for both commercial and non-commercial use, being distributed under +terms of [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/). diff --git a/aisl.proj b/aisl.proj deleted file mode 100644 index 4853133..0000000 --- a/aisl.proj +++ /dev/null @@ -1,36 +0,0 @@ -name = AISL -todo = aisl.todo -handbook = handbook.md -version: - major = 0 - minor = 0 - -include = -definitions = - -profile: - name = debug - flags = -Wall -Werror - definitions = - -profile: - name = release - flags = - definitions = - - - -library: - output = aisl - compiler = gcc - - version: - major = $version.major - minor = $version.minor - tweak = 0 - build = auto - - sources = library/aisl.c, - library/buffer.c - - diff --git a/cStuff b/cStuff deleted file mode 160000 index a423fa7..0000000 --- a/cStuff +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a423fa7a6dfbd637f3c0b248123f682456fccad7 diff --git a/cmake.compiler b/cmake.compiler deleted file mode 100755 index 3680029..0000000 --- a/cmake.compiler +++ /dev/null @@ -1,7 +0,0 @@ - -if(DEFINED CMAKE_DEBUG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ") - add_definitions( - -DDEBUG - ) -endif() diff --git a/cmake.paths b/cmake.paths deleted file mode 100644 index b0b055d..0000000 --- a/cmake.paths +++ /dev/null @@ -1,90 +0,0 @@ -# Paths ----------------------------------------------------------------------- - -# Constants ------------------------------------------------------------------- - -set(INCLUDE_DIR "include") -set(BUILD_DIR "build") - -# CMAKE installation paths ---------------------------------------------------- - - -# -DCMAKE_INSTALL_PREFIX:PATH=/usr - -if(NOT DEFINED CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX "/usr") -endif() - - -# -DINCLUDE_INSTALL_DIR:PATH=/usr/include - -if(NOT DEFINED INCLUDE_INSTALL_DIR) - set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include") -endif() - - -# -DSHARE_INSTALL_PREFIX:PATH=/usr/share - -if(NOT DEFINED SHARE_INSTALL_PREFIX) - set(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share") -endif() - - -# -DSYSCONF_INSTALL_DIR:PATH=/etc - -if(NOT DEFINED SYSCONF_INSTALL_DIR) - set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc") -endif() - - -# -DCMAKE_INSTALL_LIBDIR:PATH=lib64 - -if(NOT DEFINED CMAKE_INSTALL_LIBDIR) - set(CMAKE_INSTALL_LIBDIR "lib${SYSTEM_LIB_SUFFIX}") -endif() - -# -DLIB_INSTALL_DIR:PATH=/usr/lib64 -if(NOT DEFINED LIB_INSTALL_DIR) - set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") -endif() - - -# ----------------------------------------------------------------------------- - -set(PATH_BIN "${CMAKE_INSTALL_PREFIX}/bin") -set(PATH_INC "${INCLUDE_INSTALL_DIR}") -set(PATH_CFG "${SYSCONF_INSTALL_DIR}") -set(PATH_RUN "/var/run") -set(PATH_LIB "${LIB_INSTALL_DIR}") -set(PATH_LOG "/var/log") -set(PATH_RES "${SHARE_INSTALL_PREFIX}") -set(PATH_LNG "${SHARE_INSTALL_PREFIX}/locale") - - - -MESSAGE( STATUS "Paths:") -MESSAGE( STATUS " Prefix: ${CMAKE_INSTALL_PREFIX}" ) -MESSAGE( STATUS " Binaries: ${PATH_BIN}" ) -MESSAGE( STATUS " Configuration: ${PATH_CFG}" ) -MESSAGE( STATUS " Libraries: ${PATH_LIB}" ) -MESSAGE( STATUS " Includes: ${PATH_INC}" ) -MESSAGE( STATUS " Run: ${PATH_RUN}" ) -MESSAGE( STATUS " Log Files: ${PATH_LOG}" ) -MESSAGE( STATUS " Resources: ${PATH_RES}" ) -MESSAGE( STATUS " Locale Files: ${PATH_LNG}" ) -MESSAGE( STATUS "") - -# Compiler's Definitions ------------------------------------------------------ - -add_definitions( - -DPREFIX="${CMAKE_INSTALL_PREFIX}" - -DPATH_BIN="${PATH_BIN}" - -DPATH_CFG="${PATH_CFG}" - -DPATH_INC="${PATH_INC}" - -DPATH_LIB="${PATH_LIB}" - -DPATH_RUN="${PATH_RUN}" - -DPATH_LOG="${PATH_LOG}" - -DPATH_LNG="${PATH_LNG}" - -DPATH_RES="${PATH_RES}" -) - -# ----------------------------------------------------------------------------- diff --git a/cmake.system b/cmake.system deleted file mode 100644 index aeb769f..0000000 --- a/cmake.system +++ /dev/null @@ -1,18 +0,0 @@ -# Architecture ---------------------------------------------------------------- - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(SYSTEM_BITNESS 64) - set(SYSTEM_ARCH "amd64") - set(SYSTEM_LIB_SUFFIX "64") -else() - set(SYSTEM_BITNESS 32) - set(SYSTEM_ARCH "x86") - set(SYSTEM_LIB_SUFFIX "") -endif() - -add_definitions( - -DSYSTEM_NAME="${CMAKE_SYSTEM_NAME}" - -DSYSTEM_BITNESS=${SYSTEM_BITNESS} - -DSYSTEM_ARCH_${SYSTEM_ARCH} - -DSYSTEM_ARCH="${SYSTEM_ARCH}" -) diff --git a/cmake.version b/cmake.version deleted file mode 100644 index 5a89fc5..0000000 --- a/cmake.version +++ /dev/null @@ -1,61 +0,0 @@ -# ----------------------------------------------------------------------------- -# -# CMake module for paths generation in DEBUG and RELEASE modes -# -# (c) Copyright Löwenware Ltd. (https://lowenware.com/) -# -# ----------------------------------------------------------------------------- - - -## Constants -set (VERSION_FILE "version") -set ( - VERSION_REGEX - "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-(pre|alpha|beta|rc|release))?" -) - -# Read file -file (READ ${VERSION_FILE} VERSION_STRING) - -# Match file content -string(REGEX MATCH ${VERSION_REGEX} VERSION_STRING ${VERSION_STRING} ) - -# Set Version constants -set (VERSION_MAJOR ${CMAKE_MATCH_1}) -set (VERSION_MINOR ${CMAKE_MATCH_2}) -set (VERSION_TWEAK ${CMAKE_MATCH_3}) - -if (CMAKE_MATCH_5 STREQUAL "pre") - set(VERSION_CYCLE 1) -elseif (CMAKE_MATCH_5 STREQUAL "alpha") - set (VERSION_CYCLE 2) -elseif (CMAKE_MATCH_5 STREQUAL "beta") - set (VERSION_CYCLE 3) -elseif (CMAKE_MATCH_5 STREQUAL "rc") - set (VERSION_CYCLE 4) -else() - set (VERSION_CYCLE 0) -endif() - -set (VERSION_LABEL ${CMAKE_MATCH_4}) - -# Add compiler macros - -add_definitions( - -DVERSION_MAJOR=${VERSION_MAJOR} - -DVERSION_MINOR=${VERSION_MINOR} - -DVERSION_TWEAK=${VERSION_TWEAK} - -DVERSION_CYCLE=${VERSION_CYCLE} - -DVERSION_LABEL="${VERSION_LABEL}" -) - -#Print output - -MESSAGE( - STATUS "${PROJECT_TITLE} version: " ${VERSION_MAJOR} "." - ${VERSION_MINOR} "." - ${VERSION_TWEAK} "-" - ${VERSION_CYCLE_TEXT} " " - ${VERSION_LABEL} -) - diff --git a/examples.mk b/examples.mk new file mode 100644 index 0000000..3126042 --- /dev/null +++ b/examples.mk @@ -0,0 +1,31 @@ +# +# examples.mk +# Ilja Kartašov, 2019-03-17 17:40 +# + +EXAMPLES_DIR ?= examples + +EXAMPLES_CFLAGS := \ + $(PROJECT_INCLUDES) \ + -std=c99 \ + -pedantic \ + -Wall \ + -Wmissing-prototypes \ + -Wstrict-prototypes \ + -Wold-style-definition \ + -O2 \ + -s \ + $(CFLAGS) \ + + +EXAMPLES_LDFLAGS = -L./ -L./build -laisl -Wl,-rpath=./build + +examples: library hello_world + +hello_world: + $(info compiling: hello world) + @$(CC) $(EXAMPLES_CFLAGS) \ + -o $(OUT_DIR)/hello-world $(EXAMPLES_DIR)/hello-world.c $(EXAMPLES_LDFLAGS) + +# vim:ft=make +# diff --git a/examples/hello-world.c b/examples/hello-world.c new file mode 100644 index 0000000..1859d48 --- /dev/null +++ b/examples/hello-world.c @@ -0,0 +1,104 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file hello-world.c + * @author Ilja Kartašov + * @brief AISL usage example: Hello World + * + * @see https://lowenware.com/aisl/ + */ + +#include +#include + +/* Include library meta header */ +#include + + +static void +hello_world(const struct aisl_evt *evt, void *p_ctx); + + +static const struct aisl_cfg_srv m_srv[] = {{ + .host = "0.0.0.0", + .port = 8080, + .secure = false +}}; + + +static const struct aisl_cfg m_cfg = { + AISL_CFG_DEFAULTS + , .srv = m_srv + , .srv_cnt = sizeof (m_srv) / sizeof (m_srv[0]) + , .ssl = NULL + , .ssl_cnt = 0 + , .callback = hello_world + , .p_ctx = NULL +}; + + +static void +hello_world(const struct aisl_evt *evt, void *p_ctx) +{ + AislStream s; + + const char html[] = + "" + "" + "Hello World" + "" + "" + "

Hello World

" + "

Powered by AISL

" + "" + ""; + + fprintf(stdout, "Event: %s\n", aisl_event_to_string(evt->code) ); + + if (evt->code != AISL_EVENT_STREAM_REQUEST) + return; + + s = evt->source; + + if (aisl_response(s, AISL_HTTP_OK, sizeof (html)-1) == AISL_SUCCESS) { + if (aisl_write(s, html, sizeof (html)-1) != -1) { + aisl_flush(s); + return; + } + } + + aisl_reject(s); + (void) p_ctx; +} + + +int +main(int argc, char **argv) +{ + AislInstance aisl; /**< AISL instance pointer */ + AislStatus status; /**< AISL status code */ + + /* Initialize instance */ + if ( (aisl = aisl_new(&m_cfg)) != NULL ) { + /* launch application loop */ + fprintf(stdout, "Entering main loop\n" ); + for(;;) { + status = aisl_run_cycle(aisl); + + if ( status != AISL_SUCCESS ) + aisl_sleep(aisl, 500); + } + + aisl_free(aisl); + } else { + fprintf(stderr, "Failed to initialize AISL\n"); + } + + return 0; +} + diff --git a/examples/vgcore.12255 b/examples/vgcore.12255 new file mode 100644 index 0000000..78a2516 Binary files /dev/null and b/examples/vgcore.12255 differ diff --git a/examples/vgcore.13889 b/examples/vgcore.13889 new file mode 100644 index 0000000..3df2aac Binary files /dev/null and b/examples/vgcore.13889 differ diff --git a/examples/vgcore.18015 b/examples/vgcore.18015 new file mode 100644 index 0000000..017f36a Binary files /dev/null and b/examples/vgcore.18015 differ diff --git a/examples/vgcore.18664 b/examples/vgcore.18664 new file mode 100644 index 0000000..a4764a3 Binary files /dev/null and b/examples/vgcore.18664 differ diff --git a/examples/vgcore.18862 b/examples/vgcore.18862 new file mode 100644 index 0000000..78fe661 Binary files /dev/null and b/examples/vgcore.18862 differ diff --git a/examples/vgcore.23466 b/examples/vgcore.23466 new file mode 100644 index 0000000..22dc1f0 Binary files /dev/null and b/examples/vgcore.23466 differ diff --git a/examples/vgcore.27845 b/examples/vgcore.27845 new file mode 100644 index 0000000..561b71f Binary files /dev/null and b/examples/vgcore.27845 differ diff --git a/examples/vgcore.28571 b/examples/vgcore.28571 new file mode 100644 index 0000000..3f69c66 Binary files /dev/null and b/examples/vgcore.28571 differ diff --git a/examples/vgcore.30230 b/examples/vgcore.30230 new file mode 100644 index 0000000..e26c314 Binary files /dev/null and b/examples/vgcore.30230 differ diff --git a/examples/vgcore.31633 b/examples/vgcore.31633 new file mode 100644 index 0000000..b606cc1 Binary files /dev/null and b/examples/vgcore.31633 differ diff --git a/examples/vgcore.7201 b/examples/vgcore.7201 new file mode 100644 index 0000000..4ccdbf5 Binary files /dev/null and b/examples/vgcore.7201 differ diff --git a/examples/vgcore.8596 b/examples/vgcore.8596 new file mode 100644 index 0000000..6648873 Binary files /dev/null and b/examples/vgcore.8596 differ diff --git a/include/aisl/aisl.h b/include/aisl/aisl.h index 6e05852..e649d55 100644 --- a/include/aisl/aisl.h +++ b/include/aisl/aisl.h @@ -1,107 +1,37 @@ -/* ---------------------------------------------------------------------------- - * aisl.h - header file for AISL library, part of AISLing Technology +/****************************************************************************** * - * Copyright (c) 2017 by Löwenware Ltd. (https://lowenware.com/) + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information * - * Authors and maintainers: - * Ilja Kartaschoff + ******************************************************************************/ + +/** + * @file aisl/aisl.h + * @author Ilja Kartašov + * @brief Meta header file of AISL * - * DOCUMENTATION - * This file is not designed to be used as a documentation, but for looking at - * the precise values of constants and definitions. - * Please, for documentation refer to web page https://lowenware.com/aisling/ or - * file READEME.md from library source package. - * - * LICENSE and DISCLAIMER - * - * -------------------------------------------------------------------------- */ + * @see https://lowenware.com/aisl/ + */ -#ifndef _AISL_H_ -#define _AISL_H_ +#ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279 +#define AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279 -/* system includes ---------------------------------------------------------- */ +/* AISL configuration structure */ +#include -#include -#include -#include -#include +/* AISL types and stringifiers */ +#include -/* aisl includes ------------------------------------------------------------ */ +/* AISL instancing, initialization and processing */ +#include -#include -#include +/* Embedded HTTP(s) servers */ +#include + +/* HTTP(s) clients */ +#include + +/* HTTP(s) streaming */ #include -#include -#include - - -/* Control calls ------------------------------------------------------------ */ - -/* DEPRECATED, use aisl_handle_new instead - * */ -aisl_status_t -aisl_init(); - -/* DEPRECATED, use aisl_handle_free instead - * */ -void -aisl_release(); - -/* Tell library what socket should be opened. Could be called multiple times. - * This function only save passed data. In fact, sockets are being opened only - * inside aisl_run loop. - * @address : host or IP to listen - * @port : port to listen - * */ - -aisl_status_t -aisl_select(const char *address, int port); - -/* Start main loop - * @result : exit code - * */ -aisl_status_t -aisl_run( int * flags ); - -/* Event calls -------------------------------------------------------------- */ - -/* Add callback to be executed after timeout. If callback function will return - * true, callback will be kept in main loop and raised again, otherwise it will - * be removed - * @cb : callback function: bool callback (void * u_data) - * @usec : delay in milliseconds - * @data : user-defined data to be passed to callback - * */ -aisl_status_t -aisl_delay(aisl_callback_t cb, uint32_t msec, void *u_data); - -/* Add event listener - * @source : pointer to event source - * @e_id : event identifier - * @cb : callback to be executed - * */ -aisl_status_t -aisl_listen(void *source, aisl_event_t e_id, aisl_callback_t cb); - -/* Raise event - * @source : pointer to event source data - * @e_id : event identifier - * @... : custom event data - * @result : true if event was handled, false otherwise - * */ -bool -aisl_raise(void *source, aisl_event_t e_id, ... ); - -/* input stream functions --------------------------------------------------- */ - - -const char * -aisl_header_get(aisl_stream_t stream, const char *key); - -const char * -aisl_header_get_by_index(aisl_stream_t stream, const char **key, uint32_t i); - -/* -------------------------------------------------------------------------- */ - -#endif +#endif /* !AISL_H */ diff --git a/include/aisl/client.h b/include/aisl/client.h new file mode 100644 index 0000000..0717e16 --- /dev/null +++ b/include/aisl/client.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file aisl/client.h + * @author Ilja Kartašov + * @brief Declarations of #AislCLient functions + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08 +#define AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08 + +#include + + +/** + * @brief Gets #AislServer instance associated with client. + * @param client an #AislClient instance pointer. + * @return an associated #AislServer pointer. + */ +AislServer +aisl_client_get_server(AislClient client); + + +/** + * @brief Gets security connection status. + * @param client an #AislClient instance pointer. + * @return true if SSL is enabled and false if disabled. + */ +bool +aisl_client_is_secure(AislClient client); + + +/** + * @brief Gets client's connection state. + * @param client an #AislClient instance pointer. + * @return true if client is online and false if is offline. + */ +bool +aisl_client_is_online(AislClient client); + + +/** + * @brief Forcefully closes client's connection. + * @param client an #AislClient instance pointer. + */ +void +aisl_client_disconnect(AislClient client); + + +/** + * @brief Gets HTTP protocol version. + * @param client an #AislClient instance pointer. + * @return HTTP protocol version + */ +AislHttpVersion +aisl_client_get_http_version(AislClient client); + + +/** + * @brief Copies #AislClient network address to provided sockaddr_in structure. + * @param client an #AislClient instance pointer. + * @param address a pointer to a sockaddr_in structure + */ +void +aisl_client_get_address(AislClient client, struct sockaddr_in *address); + +#endif /* !AISL_CLIENT_H */ diff --git a/include/aisl/config.h b/include/aisl/config.h new file mode 100644 index 0000000..ecbbef6 --- /dev/null +++ b/include/aisl/config.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file aisl/config.h + * @author Ilja Kartašov + * @brief Declarations of AISL configuration structures + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6 +#define AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6 + +#include + +#define AISL_CFG_DEFAULTS \ + .client_spool_size = 32 \ + , .initial_buffer_size = 16536 \ + , .client_accept_limit = 1024 \ + , .client_silence_timeout = 30 \ + + +/** @brief Server configuration structure + */ +struct aisl_cfg_srv { + const char *host; /**< server IP to listen */ + uint16_t port; /**< server port to listen */ + bool secure; /**< shall server use TLS */ +}; + + +/** @brief SSL configuration structure + */ +struct aisl_cfg_ssl { + const char *host; /**< secure server hostname */ + const char *key_file; /**< path to SSL key file */ + const char *crt_file; /**< path to SSL certificate file */ +}; + + +/** @brief AISL initial configuration structure + */ +struct aisl_cfg { + AislCallback callback; /**< A pointer to #AislCallback event handler */ + void *p_ctx; /**< User defined context for #AislCallback */ + + const struct aisl_cfg_srv *srv; /**< A pointer to array of #aisl_cfg_srv */ + const struct aisl_cfg_ssl *ssl; /**< A pointer to array of #aisl_cfg_ssl */ + + int srv_cnt; /**< Size of #aisl_cfg_srv array */ + int ssl_cnt; /**< Size of #aisl_cfg_ssl array */ + + int client_spool_size; /**< Initial size of client spool */ + int initial_buffer_size; /**< Initial size of communication buffer */ + int client_accept_limit; /**< Maximal number of concurent clients */ + int client_silence_timeout; /**< Client silence timeout */ +}; + +#endif /* !AISL_CONFIG_H */ diff --git a/include/aisl/event.h b/include/aisl/event.h deleted file mode 100644 index 476ddf8..0000000 --- a/include/aisl/event.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef _AISL_EVENT_H_ -#define _AISL_EVENT_H_ - -#include -#include -#include -#include -#include -#include - -/* -------------------------------------------------------------------------- */ - -typedef unsigned int aisl_event_t; -/* -------------------------------------------------------------------------- */ - -typedef enum -{ - AISL_SERVER_OPEN = 100, - AISL_SERVER_ERROR = 190, - - AISL_CLIENT_CONNECT = 200, - AISL_CLIENT_DISCONNECT = 210, - AISL_CLIENT_TIMEOUT = 220, - - AISL_STREAM_OPEN = 300, /* 5 - headers recieved */ - AISL_STREAM_HEADER = 310, /* 5 - headers recieved */ - AISL_STREAM_INPUT = 320, /* 6 - chunk of data transmission */ - AISL_STREAM_REQUEST = 330, /* 7 - data received, response required */ - AISL_STREAM_OUTPUT = 340,/* event for long-size responses optimal handling */ - AISL_STREAM_CLOSE = 350, - AISL_STREAM_ERROR = 390, /* 8 - bad request */ - - AISL_EVENTS_CUSTOM = 999 - -} aisl_event_id_t; - - -/* -------------------------------------------------------------------------- */ - -/* AISL_SERVER_OPEN event handler */ -typedef bool -(*aisl_server_open_t)( aisl_server_t server, - int flags ); - -/* AISL_SERVER_ERROR event handler */ -typedef bool -(*aisl_server_error_t)( aisl_server_t server, - int flags, - const char * details ); - -/* AISL_CLIENT_CONNECT event handler */ -typedef bool -(*aisl_client_connect_t)( aisl_server_t server, - aisl_client_t client ); - -/* AISL_CLIENT_DISCONNECT event handler */ -typedef bool -(*aisl_client_disconnect_t)( aisl_server_t server, - aisl_client_t client ); - -/* AISL_CLIENT_DISCONNECT event handler */ -typedef bool -(*aisl_client_timeout_t)( aisl_server_t server, - aisl_client_t client ); - -/* AISL_STREAM_OPEN event handler */ -typedef bool -(*aisl_stream_open_t)( aisl_stream_t s, - aisl_http_method_t method, - const char * path, - const char * query ); - -typedef bool -(*aisl_stream_header_t)( aisl_stream_t s, - const char * key, - const char * value ); - - -/* AISL_STREAM_INPUT event handler */ -typedef bool -(*aisl_stream_input_t)( aisl_stream_t s, - char * data, - int len ); - -/* AISL_STREAM_REQUEST event handler */ -typedef bool -(*aisl_stream_request_t)( aisl_stream_t s ); - -/* AISL_STREAM_OUTPUT event handler */ -typedef bool -(*aisl_stream_output_t)( aisl_stream_t s, - uint32_t buffer_space ); - -typedef bool -(*aisl_stream_close_t)( aisl_stream_t s ); - -/* AISL_STREAM_ERROR event handler */ -typedef bool -(*aisl_stream_error_t)( aisl_stream_t s, - const char * details ); - -/* CUSTOM event_handler */ -typedef bool -(*aisl_custom_event_t)( void * source, - va_list vl ); - -/* on delay timeout */ -typedef bool -(*aisl_delay_timeout_t)( void * u_data ); - -/* -------------------------------------------------------------------------- */ - -/* type for event callbacks to use in structures and function prototypes */ -typedef bool -(* aisl_callback_t) (void); - -/* cast callback as aisl_callback_t */ -#define AISL_CALLBACK(x) ((aisl_callback_t) x) - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_event_get_text( aisl_event_t e_id ); - - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/include/aisl/handle.h b/include/aisl/handle.h deleted file mode 100644 index 29d9f09..0000000 --- a/include/aisl/handle.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef _AISL_HANDLE_H_ -#define _AISL_HANDLE_H_ - -#include -#include - -/* -------------------------------------------------------------------------- */ - -#define AISL_FLAG_SSL (1<<0) - -/* -------------------------------------------------------------------------- */ - -typedef struct aisl_handle * aisl_handle_t; - - -/* -------------------------------------------------------------------------- */ - -aisl_handle_t -aisl_handle_new(size_t min_clients, size_t buffer_size); - -/* -------------------------------------------------------------------------- */ - -aisl_handle_t -aisl_handle_from_stream( aisl_stream_t s ); - -/* -------------------------------------------------------------------------- */ - -void -aisl_handle_free( aisl_handle_t self ); - -/* -------------------------------------------------------------------------- */ - -aisl_status_t -aisl_bind( aisl_handle_t self, const char * address, int port, int flags ); - -/* -------------------------------------------------------------------------- */ - -aisl_status_t -aisl_set_ssl( aisl_handle_t self, const char * server_name, - const char * key_file, - const char * crt_file ); - -/* -------------------------------------------------------------------------- */ - -aisl_status_t -aisl_set_callback( aisl_handle_t self, - void * source, - aisl_event_t e_id, - aisl_callback_t cb ); - -/* -------------------------------------------------------------------------- */ - -bool -aisl_raise_event( aisl_handle_t self, - void * source, - aisl_event_t e_id, - ... ); - -/* -------------------------------------------------------------------------- */ - -aisl_status_t -aisl_run_cycle( aisl_handle_t self ); - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_handle_get_error( aisl_handle_t self ); - -/* -------------------------------------------------------------------------- */ - -int -aisl_sleep( aisl_handle_t self, unsigned long usec ); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/include/aisl/http.h b/include/aisl/http.h deleted file mode 100644 index 432492f..0000000 --- a/include/aisl/http.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef _AISL_HTTP_H_ -#define _AISL_HTTP_H_ - -/* -------------------------------------------------------------------------- */ - -typedef enum -{ - AISL_HTTP_1_0, - AISL_HTTP_1_1, - AISL_HTTP_2_0 - -} aisl_http_version_t; - -/* -------------------------------------------------------------------------- */ - -typedef enum { - AISL_HTTP_GET, - AISL_HTTP_PUT, - AISL_HTTP_POST, - AISL_HTTP_HEAD, - AISL_HTTP_TRACE, - AISL_HTTP_DELETE, - AISL_HTTP_OPTIONS, - AISL_HTTP_CONNECT, - - AISL_HTTP_PRI - -} aisl_http_method_t; - -/* -------------------------------------------------------------------------- */ - -typedef enum -{ - /* informational ------------------------------ */ - AISL_HTTP_CONTINUE = 100, - AISL_HTTP_SWITCHING_PROTOCOLS, - /* Successful --------------------------------- */ - AISL_HTTP_OK = 200, - AISL_HTTP_CREATED, - AISL_HTTP_ACCEPTED, - AISL_HTTP_NON_AUTHORITATIVE_INFORMATION, - AISL_HTTP_NO_CONTENT, - AISL_HTTP_RESET_CONTENT, - AISL_HTTP_PARTIAL_CONTENT, - /* redirection -------------------------------- */ - AISL_HTTP_MULTIPLE_CHOICES = 300, - AISL_HTTP_MOVED_PERMANENTLY, - AISL_HTTP_FOUND, - AISL_HTTP_SEE_OTHER, - AISL_HTTP_NOT_MODIFIED, - AISL_HTTP_USE_PROXY, - AISL_HTTP_UNUSED, - AISL_HTTP_TEMPORARY_REDIRECT, - /* client error ------------------------------- */ - AISL_HTTP_BAD_REQUEST = 400, - AISL_HTTP_UNAUTHORIZED, - AISL_HTTP_PAYMENT_REQUIRED, - AISL_HTTP_FORBIDDEN, - AISL_HTTP_NOT_FOUND, - AISL_HTTP_METHOD_NOT_ALLOWED, - AISL_HTTP_NOT_ACCEPTABLE, - AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED, - AISL_HTTP_REQUEST_TIMEOUT, - AISL_HTTP_CONFLICT, - AISL_HTTP_GONE, - AISL_HTTP_LENGTH_REQUIRED, - AISL_HTTP_PRECONDITION_FAILED, - AISL_HTTP_REQUEST_ENTITY_TOO_LARGE, - AISL_HTTP_REQUEST_URI_TOO_LONG, - AISL_HTTP_UNSUPPORTED_MEDIA_TYPE, - AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE, - AISL_HTTP_EXPECTATION_FAILED, - /* server error ------------------------------- */ - AISL_HTTP_INTERNAL_SERVER_ERROR = 500, - AISL_HTTP_NOT_IMPLEMENTED, - AISL_HTTP_BAD_GATEWAY, - AISL_HTTP_SERVICE_UNAVAILABLE, - AISL_HTTP_GATEWAY_TIMEOUT, - AISL_HTTP_VERSION_NOT_SUPPORTED - -} aisl_http_response_t; -/* -------------------------------------------------------------------------- */ - -const char * -aisl_http_version_to_string(aisl_http_version_t version); - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_http_response_to_string(aisl_http_response_t code); - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_http_secure_to_string( int is_secure ); - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_http_method_to_string( aisl_http_method_t method ); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/include/aisl/instance.h b/include/aisl/instance.h new file mode 100644 index 0000000..3785d4a --- /dev/null +++ b/include/aisl/instance.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file aisl/instance.h + * @author Ilja Kartašov + * @brief Declarations of #AislInstance functions + * + * @see https://lowenware.com/aisl/ + + */ + +#ifndef AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230 +#define AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230 + +#include +#include +#include +#include + + + +/** + * @brief Allocates new AISL instance. + * + * @param cfg a pointer to #aisl_cfg_t structure. + * @return an #AislInstance instance pointer. + */ +AislInstance +aisl_new(const struct aisl_cfg *cfg); + + +/** + * @brief Frees previously allocated pointer of AISL instance. + * @param instance a pointer to #AislInstance instance. + */ +void +aisl_free(AislInstance instance); + + +/** + * @brief A core function doing all the library routines. + * Designed to be called inside application main loop + * @param instance a pointer to #AislInstance instance. + * @return #AislStatus code. + */ +AislStatus +aisl_run_cycle(AislInstance instance); + + +/** + * @brief Function to sleep CPU if nothing to do. + * Calls select on all the opened sockets inside. + * @param instance a pointer to #AislInstance instance. + * @param usec a number of miliseconds to wait for any data on sockets. + * @return #AislStatus code. + */ +AislStatus +aisl_sleep(AislInstance instance, uint32_t usec); + + +#endif /* !AISL_INSTANCE_H */ diff --git a/include/aisl/server.h b/include/aisl/server.h new file mode 100644 index 0000000..c0233d9 --- /dev/null +++ b/include/aisl/server.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file aisl/server.h + * @author Ilja Kartašov + * @brief Declarations of #AislServer functions + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768 +#define AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768 + +#include +#include + +/** + * @brief Function to get appropriate AISL instance pointer from server pointer. + * @param server an #AislServer pointer. + * @return an #AislInstance instance pointer. + */ +AislInstance +aisl_server_get_instance(AislServer server); + + +/** + * @brief Copies server listen address information to sockaddr_in structure. + * @param server an #AislServer pointer. + * @param address a pointer to sockaddr_in structure. + */ +void +aisl_server_get_address(AislServer server, struct sockaddr_in *address); + + +/** + * @brief Function to get on and off status of SSL for the #AislServer. + * @param server an #AislServer pointer. + * @return a boolean value representing SSL enabled/disabled state. + */ +bool +aisl_server_get_ssl(AislServer server); + +#endif /* !AISL_SERVER_H */ diff --git a/include/aisl/status.h b/include/aisl/status.h deleted file mode 100644 index bcc4428..0000000 --- a/include/aisl/status.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _AISL_STATUS_H_ -#define _AISL_STATUS_H_ - -/* -------------------------------------------------------------------------- */ - -typedef enum { - - AISL_EXTCALL_ERROR = -3, - AISL_SYSCALL_ERROR = -2, - AISL_MALLOC_ERROR = -1, - - AISL_SUCCESS = 0, - AISL_IDLE = 1 - -} aisl_status_t; - -/* -------------------------------------------------------------------------- */ - -const char * -aisl_status_to_string(aisl_status_t status); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/include/aisl/stream.h b/include/aisl/stream.h index 7bcaf3c..5525793 100644 --- a/include/aisl/stream.h +++ b/include/aisl/stream.h @@ -1,172 +1,199 @@ -#ifndef _AISL_STREAM_H_ -#define _AISL_STREAM_H_ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ -#include -/* Library statuses */ -/* HTTP requests */ -#include -#include +/** + * @file aisl/stream.h + * @author Ilja Kartašov + * @brief Declarations of #AislStream functions + * + * @see https://lowenware.com/aisl/ + */ +#ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC +#define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC -/* -------------------------------------------------------------------------- */ +#include +#include +#include -typedef struct sockaddr_in * aisl_server_t; -typedef struct sockaddr_in * aisl_client_t; - -struct aisl_stream -{ - /* DO NOT USE PROPERTIES DIRECTLY IN NEW CODE */ - - struct sockaddr_in *client; - - const char *host; - const char *path; - const char *query; - const char *scheme; - - void *u_ptr; /* pointer to bind custom data to stream */ - - aisl_http_method_t request_method; -}; - -/* pointer to stream descriptor */ -typedef struct aisl_stream * aisl_stream_t; - -/* -------------------------------------------------------------------------- */ - -/* start response to client - * this function should be called before any header or content function - * necessary protocol headers as well as Content-Type and Content-Length - * will be set automaticaly - * @stream : stream instance - * @status : HTTP response status code (default 200) - * @content_type : string with content type (default text/html), NULL -> no - * @content_length : length of content, 0 = no content - * */ -aisl_status_t -aisl_response(aisl_stream_t stream, aisl_http_response_t status_code, - const char *content_type, - uint32_t content_length); - -/* send all buffered data to client - * ALL responses should always be finished with calling of this method - * @stream : stream instance - * */ -aisl_status_t -aisl_flush(aisl_stream_t stream); - -/* header functions --------------------------------------------------------- */ - -/* add custom header to stream - * this function should be called before content functions - * @stream : stream instance - * @key : header key string - * @value : header value string - * */ -int -aisl_header(aisl_stream_t stream, const char *key, const char *value); - -/* add custom header to stream - * this function should be called before content functions - * @stream : stream instance - * @key : header key string - * @f_value : value format string, same as for printf function - * @... : arguments according to f_value string - * */ -int -aisl_header_printf(aisl_stream_t stream, const char *key, - const char *f_value, ...); - -/* add custom header to stream - * this function should be called before content functions - * @stream : stream instance - * @key : header key string - * @f_value : value format string, same as for printf function - * @args : arguments macro according to f_value string - * */ -int -aisl_header_vprintf(aisl_stream_t stream, const char *key, - const char *format, - va_list args); - - -/* data response functions -------------------------------------------------- */ - -/* response formated data to client - * @stream : stream instance - * @format : format string, same as for printf - * @... : arguments according to format string - * @result : number of responed bytes - * */ -int -aisl_printf(aisl_stream_t stream, const char *format, ...); - - -/* response formated data to client - * @stream : stream instance - * @format : format string, same as for printf - * @args : arguments macro according to format string - * @result : number of responed bytes - * */ -int -aisl_vprintf(aisl_stream_t stream, const char *format, va_list args); - -/* response characters to client - * @stream : stream instance - * @data : characters to be sent - * @d_len : number of characters to send - * @result : number of responed bytes - * */ -int -aisl_write(aisl_stream_t s, const char *data, int d_len); - -/* response string to client - * @string : string to be sent - * @stream : stream instance - * @result : number of responed bytes - * */ -int -aisl_puts(const char *string, aisl_stream_t stream); - -/* -------------------------------------------------------------------------- */ - -void -aisl_cancel(aisl_stream_t s); - -/* -------------------------------------------------------------------------- */ +#define aisl_stream_get_instance aisl_get_instance +/** @brief Gets a value of #AislStream security flag + * @param stream an #AislStream instance + * @return is true if stream is encrypted and false otherwise + */ bool -aisl_is_secure(aisl_stream_t s); +aisl_is_secure(AislStream stream); -/* -------------------------------------------------------------------------- */ +/** @brief Gets an #AislClient instance associated with the stream + * @param stream an #AislStream instance + * @return an #AislClient instance pointer + */ +AislClient +aisl_get_client(AislStream stream); + + +/** @brief Gets an #AislServer instance associated with the stream + * @param stream an #AislStream instance + * @return an #AislServer instance pointer + */ +AislServer +aisl_get_server(AislStream stream); + + +/** @brief Gets an #AislHttpVersion of the stream + * @param stream an #AislStream instance + * @return an #AislHttpVersion value + */ +AislHttpVersion +aisl_get_http_version(AislStream stream); + + +/** @brief Gets an #AislInstance pointer associated with the stream + * @param stream an #AislStream instance + * @return an #AislInstance pointer + */ +AislInstance +aisl_get_instance(AislStream s); + + +/** @brief Gets the stream context previously set with #aisl_set_context + * @param stream an #AislStream instance + * @return a pointer to the stream context + */ void * -aisl_get_context(aisl_stream_t s); +aisl_get_context(AislStream stream); -/* -------------------------------------------------------------------------- */ +/** @brief Associate a context pointer with the stream until its lifetime. + * Previously allocated data should be free'd on #AISL_EVENT_STREAM_CLOSE if + * not needed anymore. + * @param stream an #AislStream instance + * @param context a pointer to any user-defined data + */ void -aisl_set_context(aisl_stream_t s, void * u_ptr); +aisl_set_context(AislStream stream, void *context); -/* -------------------------------------------------------------------------- */ -aisl_client_t -aisl_get_client(aisl_stream_t s); +/** @brief A call to start stream data transmission to a client + * @param stream an #AislStream instance + * @return a #AislStatus displaying if stream is ready to be proceed by teh engine + */ +AislStatus +aisl_flush(AislStream stream); -/* -------------------------------------------------------------------------- */ - -aisl_server_t -aisl_get_server(aisl_stream_t s); - - /* -------------------------------------------------------------------------- */ - -aisl_http_version_t -aisl_get_http_version(aisl_stream_t s); - -/* -------------------------------------------------------------------------- */ +/** @brief A call to reject the stream. In HTTP 1.X it also closes client's connection + * @param stream an #AislStream instance + */ void -aisl_reject( aisl_stream_t s); +aisl_reject(AislStream stream); -/* -------------------------------------------------------------------------- */ -#endif +/** @brief A call to begin the HTTP response + * @param stream an #AislStream instance + * @param status_code of the HTTP response + * @param content_length in bytes or #AISL_AUTO_LENGTH if length is not knonw yet + * @return #AislStatus code + */ +AislStatus +aisl_response(AislStream stream, + AislHttpResponse status_code, + uint64_t content_length); + + +/** @brief Adds HTTP header to the stream buffer + * @param stream an #AislStream instance + * @param key of HTTP header + * @param value of HTTP header + * @return a length of data added to the stream buffer + */ +int +aisl_header(AislStream stream, const char *key, const char *value); + + +/** @brief Adds printf-like formatted HTTP header to the stream buffer + * @param stream an #AislStream instance + * @param key of HTTP header + * @param format of HTTP header value + * @return a length of data added to the stream buffer + */ +int +aisl_header_printf(AislStream stream, + const char *key, + const char *format, + ... ); + + +/** @brief Adds vprintf-like formatted HTTP header to the stream buffer + * @param stream an #AislStream instance + * @param key of HTTP header + * @param format of HTTP header value + * @param args list for HTTP header value construction + * @return a length of data added to the stream buffer + */ +int +aisl_header_vprintf(AislStream stream, + const char *key, + const char *format, + va_list args ); + + +/** @brief Adds printf-like formatted HTTP response to the stream buffer + * @param stream an #AislStream instance + * @param format of the HTTP response + * @return a length of data added to the stream buffer + */ +int +aisl_printf(AislStream stream, const char *format, ...); + + +/** @brief Adds vprintf-like formatted HTTP response to the stream buffer + * @param stream an #AislStream instance + * @param format of the HTTP response + * @param args list for HTTP response construction + * @return a length of data added to the stream buffer + */ +int +aisl_vprintf(AislStream stream, const char *format, va_list args); + + +/** @brief Adds data of the HTTP response to the stream buffer + * @param stream an #AislStream instance + * @param data a pointer to HTTP response data array + * @param d_len size of the HTTP response data array + * @return a length of data added to the stream buffer + */ +int +aisl_write(AislStream stream, const char *data, int d_len); + + +/** @brief Adds a null-terminated string to the stream buffer + * @param str_data the HTTP response string + * @param stream an #AislStream instance + * @return a length of data added to the stream buffer + */ +int +aisl_puts(const char *str_data, AislStream stream); + + +/** @brief Switches triggering of #AISL_EVENT_STREAM_OUTPUT + * @param stream an #AislStream instance + * @param value a true to enable or false to disable (default) triggering + */ +void +aisl_set_output_event(AislStream stream, bool value); + + +/** @brief Gets state of the #AISL_EVENT_STREAM_OUTPUT triggering + * @param stream an #AislStream instance + * @return true if triggering is enabled and flase otherwise + */ +bool +aisl_get_output_event(AislStream stream); + +#endif /* !AISL_STREAM_H */ diff --git a/include/aisl/types.h b/include/aisl/types.h new file mode 100644 index 0000000..7e92ed2 --- /dev/null +++ b/include/aisl/types.h @@ -0,0 +1,226 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file aisl/types.h + * @author Ilja Kartašov + * @brief Declarations of AISL types + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B +#define AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B + +#include +#include +#include + +#define AISL_AUTO_LENGTH (~0) + +/** type casts */ +#define AISL_CALLBACK(x) ((AislCallback) x) + + +/** @brief AISL Instance */ +typedef struct aisl_instance * AislInstance; + +/** @brief HTTP(s) Server */ +typedef struct aisl_server * AislServer; + +/** @brief HTTP(s) Client */ +typedef struct aisl_client * AislClient; + +/** @brief Server<->Client Stream */ +typedef struct aisl_stream * AislStream; + + +/** status return codes */ +typedef enum { + AISL_INPUT_ERROR = -4 + , AISL_EXTCALL_ERROR = -3 + , AISL_SYSCALL_ERROR = -2 + , AISL_MALLOC_ERROR = -1 + + , AISL_SUCCESS = 0 + , AISL_IDLE = 1 +} AislStatus; + + +/** @brief Converts #AislStatus code to a null terminated string + * @param status an #AislStatus code + * @return pointer to the string representing #AislStatus + */ +const char * +aisl_status_to_string(AislStatus status); + + +/** @brief HTTP version enumeration */ +typedef enum { + AISL_HTTP_0_9 = 0x0009 + , AISL_HTTP_1_0 = 0x0100 + , AISL_HTTP_1_1 = 0x0101 + , AISL_HTTP_2_0 = 0x0200 +} AislHttpVersion; + + +/** @brief Converts #AislHttpVersion code to a null terminated string + * @param version an #AislHttpVersion code + * @return pointer to the string representing #AislHttpVersion + */ +const char * +aisl_http_version_to_string(AislHttpVersion version); + + +/** HTTP method enumeration */ +typedef enum { + AISL_HTTP_METHOD_UNKNOWN + , AISL_HTTP_GET + , AISL_HTTP_PUT + , AISL_HTTP_POST + , AISL_HTTP_HEAD + , AISL_HTTP_TRACE + , AISL_HTTP_DELETE + , AISL_HTTP_OPTIONS + , AISL_HTTP_CONNECT + , AISL_HTTP_PRI +} AislHttpMethod; + + +/** @brief Converts #AislHttpMethod code to a null terminated string + * @param method an #AislHttpMethod code + * @return pointer to the string representing #AislHttpMethod + */ +const char * +aisl_http_method_to_string(AislHttpMethod method); + + +/** @brief HTTP response status enumeration */ +typedef enum { + AISL_HTTP_CONTINUE = 100 + , AISL_HTTP_SWITCHING_PROTOCOLS + + , AISL_HTTP_OK = 200 + , AISL_HTTP_CREATED + , AISL_HTTP_ACCEPTED + , AISL_HTTP_NON_AUTHORITATIVE_INFORMATION + , AISL_HTTP_NO_CONTENT + , AISL_HTTP_RESET_CONTENT + , AISL_HTTP_PARTIAL_CONTENT + + , AISL_HTTP_MULTIPLE_CHOICES = 300 + , AISL_HTTP_MOVED_PERMANENTLY + , AISL_HTTP_FOUND + , AISL_HTTP_SEE_OTHER + , AISL_HTTP_NOT_MODIFIED + , AISL_HTTP_USE_PROXY + , AISL_HTTP_UNUSED + , AISL_HTTP_TEMPORARY_REDIRECT + + , AISL_HTTP_BAD_REQUEST = 400 + , AISL_HTTP_UNAUTHORIZED + , AISL_HTTP_PAYMENT_REQUIRED + , AISL_HTTP_FORBIDDEN + , AISL_HTTP_NOT_FOUND + , AISL_HTTP_METHOD_NOT_ALLOWED + , AISL_HTTP_NOT_ACCEPTABLE + , AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED + , AISL_HTTP_REQUEST_TIMEOUT + , AISL_HTTP_CONFLICT + , AISL_HTTP_GONE + , AISL_HTTP_LENGTH_REQUIRED + , AISL_HTTP_PRECONDITION_FAILED + , AISL_HTTP_REQUEST_ENTITY_TOO_LARGE + , AISL_HTTP_REQUEST_URI_TOO_LONG + , AISL_HTTP_UNSUPPORTED_MEDIA_TYPE + , AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE + , AISL_HTTP_EXPECTATION_FAILED + + , AISL_HTTP_INTERNAL_SERVER_ERROR = 500 + , AISL_HTTP_NOT_IMPLEMENTED + , AISL_HTTP_BAD_GATEWAY + , AISL_HTTP_SERVICE_UNAVAILABLE + , AISL_HTTP_GATEWAY_TIMEOUT + , AISL_HTTP_VERSION_NOT_SUPPORTED +} AislHttpResponse; + + +/** @brief Converts #AislHttpResponse code to a null terminated string + * @param code an #AislHttpResponse code + * @return pointer to the string representing #AislHttpResponse + */ +const char * +aisl_http_response_to_string(AislHttpResponse code); + + +/** @brief AISL events enumeration */ +typedef enum { + AISL_EVENT_SERVER_READY = 100 + , AISL_EVENT_SERVER_ERROR = 190 + + , AISL_EVENT_CLIENT_CONNECT = 200 + , AISL_EVENT_CLIENT_DISCONNECT = 210 + + , AISL_EVENT_STREAM_OPEN = 300 + , AISL_EVENT_STREAM_HEADER = 310 + , AISL_EVENT_STREAM_INPUT = 320 + , AISL_EVENT_STREAM_REQUEST = 330 + , AISL_EVENT_STREAM_OUTPUT = 340 + , AISL_EVENT_STREAM_CLOSE = 350 + , AISL_EVENT_STREAM_ERROR = 390 +} AislEvent; + + +/** @brief generic AISL event structure */ +struct aisl_evt { + void *source; /**< Pointer to an event source: #AislServer, #AislClient, #AislStream */ + AislEvent code; /**< Event code */ + AislStatus status; /**< Event status */ +}; + + +/** @brief event handler callback definition + * @param evt a pointer to an #aisl_evt structure + * @param ctx a pointer to a context defined by user (see #aisl_cfg) + */ +typedef void +(* AislCallback) (const struct aisl_evt *evt, void *ctx); + + +/** @brief AISL event structue passed on stream opening */ +struct aisl_evt_open { + struct aisl_evt evt; /**< generic #aisl_evt structure */ + const char *path; /**< HTTP request path */ + const char *query; /**< HTTP request query (GET params) */ + AislHttpMethod http_method; /**< HTTP request method */ +}; + + +/** @brief AISL event structue passed on HTTP header reception */ +struct aisl_evt_header { + struct aisl_evt evt; /**< generic #aisl_evt structure */ + const char *key; /**< low case HTTP header name */ + const char *value; /**< HTTP header string */ +}; + + +/** @brief AISL event structue passed on HTTP request data part received */ +struct aisl_evt_input { + struct aisl_evt evt; /**< generic #aisl_evt structure */ + const char *data; /**< a pointer to received data array */ + int32_t size; /**< data array size */ +}; + + +/** @brief Converts #AislEvent code to a null terminated string + * @param evt an #AislEvent code + * @return pointer to the string representing #AislEvent + */ +const char * +aisl_event_to_string(AislEvent evt); + +#endif /* !AISL_TYPES_H */ diff --git a/lib/) b/lib/) new file mode 100644 index 0000000..2a5d491 --- /dev/null +++ b/lib/) @@ -0,0 +1,539 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef AISL_WITHOUT_SSL +#include +#endif + +#include +#include "debug.h" +#include "stream.h" +#include "http.h" +#include "server.h" +#include "instance.h" +#include "client.h" + +#define FLAG_KEEPALIVE (1<<0) +#define FLAG_HANDSHAKE (1<<1) +#define FLAG_CAN_READ (1<<2) +#define FLAG_CAN_WRITE (1<<3) + +#define BUFFER_SIZE (16*1024) + + +static void +aisl_client_close(AislClient client, AislStatus status) +{ + if (client->fd != -1) + { + aisl_raise( + client->server->instance + , (void *)client + , AISL_EVENT_CLIENT_DISCONNECT + , status + ); + + close(client->fd); + shutdown(client->fd, SHUT_RDWR); + client->fd = -1; + } +} + + +static AislStatus +aisl_client_parse(AislClient client, char * data, int32_t size) +{ + AislStatus result = AISL_SUCCESS; + AislStream s = client->stream; + ParserStatus p = HTTP_PARSER_SUCCESS; + + + int32_t bytes_left = size; + + switch (client->http_version) + { + case AISL_HTTP_0_9: + case AISL_HTTP_1_0: + case AISL_HTTP_1_1: + + /* s = client->stream; */ + + while ( p == HTTP_PARSER_SUCCESS ) + { + + switch ( aisl_stream_get_state(s) ) + { + case AISL_STREAM_STATE_IDLE: + p = http_10_parse_request(data, &size, client->stream); + break; + + case AISL_STREAM_STATE_WAIT_HEADER: + p = http_10_parse_header(data, &size, client->stream); + break; + + case AISL_STREAM_STATE_WAIT_BODY: + p = http_10_parse_body(data, &size, client->stream); + break; + + default: /* has input data, but request was already parsed */ + p = HTTP_PARSER_ERROR; + continue; + } + // size now has number of parsed bytes + data += size; + bytes_left -= size; + size = bytes_left; + } + + + + break; + + case AISL_HTTP_2_0: + + break; + } + + switch(p) + { + case HTTP_PARSER_READY: + client->flags &= ~FLAG_CAN_READ; + client->flags |= FLAG_CAN_WRITE; + + aisl_raise( + client->server->instance + , (void *) s + , AISL_EVENT_STREAM_REQUEST + , result + ); + break; + + case HTTP_PARSER_ERROR: + /* reply Bad Request here */ + client->stream->http_response = AISL_HTTP_BAD_REQUEST; + + aisl_raise( + client->server->instance + , (void *) s + , AISL_EVENT_STREAM_ERROR + , result + ); + + aisl_client_close(client, result); + + return result; + + default: + break; + } + + if (size) + buffer_shift(&client->in, client->in.used - size); /* reset buffer */ + + return result; +} + + +/* In HTTP 2.0 client->stream will be NULL if stream related data was completely + * parsed. If it is not NULL, then stream expects additional data -> same like + * in mono stream HTTP 1.0 + */ +static AislStatus +aisl_client_input(AislClient client) +{ + int l; + + char * data = &client->in.data[ client->in.used ]; + int32_t size = client->in.size - client->in.used; + + #ifndef AISL_WITHOUT_SSL + if (client->ssl) + { + DPRINTF("SSL_read"); + if ( !(client->flags & FLAG_HANDSHAKE) ) + { + if ( (l = SSL_accept(client->ssl)) != 1 ) + { + l = SSL_get_error(client->ssl, l); + + if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE) + return AISL_IDLE; + + DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) ); + + aisl_client_close(client, AISL_EXTCALL_ERROR); + return AISL_EXTCALL_ERROR; + } + + client->flags &= ~FLAG_HANDSHAKE; + } + + l = SSL_read(client->ssl, data, size) ; + } + else + #endif + l = recv( client->fd, data, size, 0); + + if (l > 0) + { + DPRINTF("%d bytes received from client", l); + + data = client->in.data; + size = client->in.used + l; + + client->in.used = size; + + return aisl_client_parse(client, data, size); + } + else if (l<0) + { + #ifndef AISL_WITHOUT_SSL + if (client->ssl) + { + if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ) + return AISL_IDLE; + } + else + #endif + { + if(errno == EWOULDBLOCK) + return AISL_IDLE; + + DPRINTF("client - %s", strerror(errno)); + } + } + + /* both: client disconnect + on read error */ + /* todo: raise client error here */ + aisl_client_close(client, AISL_SYSCALL_ERROR); + + return AISL_SYSCALL_ERROR; +} + + +static AislStatus +aisl_client_output(AislClient client) +{ + int l; + char * data; + + AislStream s = client->stream; + + /* while stream is not flushed, we should raise event */ + if( aisl_get_output_event(s) ) + { + /* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event ) + * stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but + * buffer->size will be used to carry amount of stored bytes + * */ + l = aisl_stream_get_buffer_space(s); + + /* + if (bsz < OUTPUT_BUFFER_SIZE) + { + if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0) + return false; + + s->buffer->size = bsz; + bsz = OUTPUT_BUFFER_SIZE; + } + */ + + if ( !(l < aisl_stream_get_buffer_size(s) / 2) ) + { + aisl_raise( + client->server->instance + , (void*)s + , AISL_EVENT_STREAM_OUTPUT + , AISL_SUCCESS + ); + } + } + + data = aisl_stream_get_data(s, &l); + + if ( !l ) + return AISL_IDLE; + + #ifdef AISL_WITHOUT_SSL + l = send( client->fd, data, l, 0); + #else + l = (client->ssl) ? + SSL_write(client->ssl, data, l) : + send( client->fd, data, l, 0); + #endif + + if (l > 0) + { + aisl_stream_shift(s, l); + + /* + if (s->state == STREAM_RESPONSE_READY && / * flushed * / + s->buffer->size == 0) / * all sent * / + */ + if ( aisl_stream_is_done(s) ) + { + /* buffer_clear(s->buffer, 0); */ + + /* data has been sent */ + + if (client->flags & FLAG_KEEPALIVE) + { + aisl_stream_free(s); + + client->stream = aisl_stream_new(client, client->next_id++); + if (client->stream != NULL ) + return AISL_SUCCESS; + + /* in case of malloc error it will not be error as long as request was + * handled and we just close the connection. + */ + } + + aisl_client_close(client, AISL_SUCCESS); + } + + return AISL_SUCCESS; + } + + /* l < 0 */ + #ifndef AISL_WITHOUT_SSL + if (client->ssl) + { + if ( SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE ) + return AISL_IDLE; + } + else + #endif + { + if (errno == EWOULDBLOCK) + return AISL_IDLE; + } + + aisl_client_close(client, AISL_SYSCALL_ERROR); + + return AISL_SYSCALL_ERROR; +} + + +AislClient +aisl_client_new( AislServer server, + int fd, + struct sockaddr_in * addr ) +{ + AislClient client; + AislStream stream; + + if ( (client = calloc(1, sizeof (struct aisl_client))) != NULL ) + { + DPRINTF("client alocated"); + memcpy(&client->address, addr, sizeof (struct sockaddr_in)); + client->server = server; + client->fd = fd; + client->next_id = 2; + client->http_version = AISL_HTTP_1_0; + client->timestamp = time(NULL); + client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ; + + if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) + { + DPRINTF("client buffer alocated"); + memcpy(&client->out, &client->in, sizeof (struct buffer)); + + stream = aisl_stream_new(client, 0); + + if (stream != NULL) + { + client->stream = stream; + + DPRINTF("client stream alocated"); + + #ifdef AISL_WITHOUT_SSL + + return client; + + #else + + SSL_CTX * ssl_ctx; + + if ( !server->ssl ) + return client; + + ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL); + + if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) + { + SSL_set_fd(client->ssl, fd); + return client; + } + + #endif + } + } + aisl_client_free(client); + + } + + return NULL; +} + + +void +aisl_client_free(AislClient client) +{ + aisl_client_close(client, AISL_SUCCESS); + + #ifndef AISL_WITHOUT_SSL + if (client->ssl) + SSL_free(client->ssl); + #endif + + if (client->in.data) + free(client->in.data); + + /* out buffer is a shared part of input buffer, so no need to free it */ + + if (client->stream) + aisl_stream_free(client->stream); + + free(client); +} + + +AislStatus +AislClientouch(AislClient client, int32_t timeout) +{ + AislStatus result = AISL_IDLE, + status = AISL_IDLE; + + /* input */ + if (client->flags & FLAG_CAN_READ) + { + if ( (result = aisl_client_input(client)) < 0 ) + return result; + } + + /* output */ + if (client->flags & FLAG_CAN_WRITE) + { + if ( (status = aisl_client_output(client)) < 0 ) + return status; + } + + + /* + if ((client->http_version==AISL_HTTP_2_0 || s->statestreams, client->ostream); + + if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) ) + result = client_output(client); + */ + /* update timestamp */ + + if (result == AISL_IDLE) + result = status; + + if (result == AISL_SUCCESS) + client->timestamp = time(NULL); + else + { + time_t now; + time(&now); + + if ( !(now - client->timestamp < timeout) ) + { + aisl_client_close(client, result); + } + } + + + return result; +} + + +int +aisl_client_get_socket(AislClient client) +{ + return client->fd; +} + + +bool +aisl_client_get_keepalive(AislClient client) +{ + return (client->flags & FLAG_KEEPALIVE); +} + + +void +aisl_client_set_keepalive(AislClient client, bool value) +{ + if (value) + client->flags |= FLAG_KEEPALIVE; + else + client->flags &= ~FLAG_KEEPALIVE; +} + + + +/* API Level ---------------------------------------------------------------- */ + +__attribute__ ((visibility ("default") )) +AislServer +aisl_client_get_server(AislClient client) +{ + return client->server; +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_client_is_secure(AislClient client) +{ + #ifdef AISL_WITHOUT_SSL + return false; + #else + return (client->ssl == NULL) ? false : true; + #endif +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_client_is_online(AislClient client) +{ + return (client->fd == -1) ? false : true; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_client_disconnect(AislClient client) +{ + aisl_client_close(client, AISL_SUCCESS); +} + + +__attribute__ ((visibility ("default") )) +AislHttpVersion +aisl_client_get_http_version(AislClient client) +{ + return client->http_version; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_client_get_address( AislClient client, struct sockaddr_in * address) +{ + memcpy(address, &client->address, sizeof (struct sockaddr_in)); +} diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 0000000..32f7d5f --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,176 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file buffer.c + * @author Ilja Kartašov + * @brief Buffer module source file + * + * @see https://lowenware.com/ + */ +#include +#include +#include +#include "debug.h" +#include "buffer.h" + + +static int32_t +buffer_set_size(struct buffer *buffer, int32_t new_size) +{ + if (!buffer->size || new_size != buffer->size) { + char *data; + + if (new_size) { + if (new_size % 4096) { + new_size = (new_size / 4096 + 1) * 4096; + } + } else { + new_size = 16*1024; + } + + if ((data = realloc(buffer->data, new_size)) != NULL) { + buffer->data = data; + buffer->size = new_size; + } else { + new_size = -1; + } + } + + return new_size; +} + + +int32_t +buffer_init(struct buffer *buffer, int32_t size) +{ + if ((size = buffer_set_size(buffer, size)) != -1) + buffer->used = 0; + + return size; +} + + +void +buffer_release(struct buffer *buffer) +{ + if (buffer->data) { + free(buffer->data); + buffer->data = NULL; + } + + buffer->used = 0; + buffer->size = 0; +} + + +static int32_t +buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size) +{ + int32_t to_move = buffer->used - offset; + + if (to_move < 0) { + return -1; + } else if (to_move) { + memmove(&buffer->data[offset+size], &buffer->data[offset], to_move); + } + + return size; +} + +int32_t +buffer_insert(struct buffer *buffer, int32_t offset, const char *data, + int32_t size) +{ + int32_t result; + + DPRINTF("Buffer: %d of %d", buffer->used, buffer->size); + + if ( (result = buffer_set_size(buffer, buffer->size + size)) != -1) { + if ((result = buffer_move_offset(buffer, offset, size)) != -1) { + memcpy(&buffer->data[offset], data, size); + buffer->used += result; + } + } + + return result; +} + + +int32_t +buffer_append_printf(struct buffer *buffer, const char *format, ...) +{ + int32_t result; + va_list args; + + va_start(args, format); + result = buffer_append_vprintf(buffer, format, args); + va_end(args); + + return result; +} + +int32_t +buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args) +{ + DPRINTF("Buffer: %d of %d", buffer->used, buffer->size); + int32_t space, result; + va_list cp_args; + + va_copy(cp_args, args); + space = buffer->size - buffer->used; + result = vsnprintf(&buffer->data[buffer->used], space, format, args); + + if (result < space) { /* enough space */ + buffer->used += result; + } else { + result = buffer_set_size(buffer, buffer->size + result - space + 1); + if (result != -1) + result = buffer_append_vprintf(buffer, format, cp_args); + } + va_end(cp_args); + + return result; +} + +int32_t +buffer_append(struct buffer *buffer, const char *data, int32_t size) +{ + DPRINTF("Buffer: %d of %d", buffer->used, buffer->size); + int32_t used, space; + + used = buffer->used, + space = buffer->size - used; + + if (size > space) { /* enough space */ + if (buffer_set_size(buffer, buffer->size + size - space) == -1) + return -1; + } + + memcpy(&buffer->data[used], data, size); + buffer->used += size; + + return size; +} + + +int32_t +buffer_shift(struct buffer *buffer, int32_t offset) +{ + int32_t used = buffer->used - offset; + + if (offset > 0) { + if (offset < used) { + memmove(buffer->data, &buffer->data[offset], used); + } else { + used = 0; + } + buffer->used = used; + } + return used; +} + diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 0000000..e86e34d --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file src/buffer.h + * @author Ilja Kartašov + * @brief Declarations of struct buffer and functions + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9 +#define AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9 + +#include +#include + + +struct buffer { + char *data; + int32_t size; + int32_t used; +}; + + +int32_t +buffer_init(struct buffer *bs, int32_t size); + + +void +buffer_release(struct buffer *bs ); + + +int32_t +buffer_insert(struct buffer *bs, int32_t offset, const char *data, int32_t size); + + +int32_t +buffer_append_printf(struct buffer *bs, const char *format, ...); + + +int32_t +buffer_append_vprintf(struct buffer *bs, const char *format, va_list args); + + +int32_t +buffer_append(struct buffer *bs, const char *data, int32_t size); + + +int32_t +buffer_shift(struct buffer *bs, int32_t size); + + + +#endif /* !AISL_BUFFER_H */ diff --git a/lib/client.c b/lib/client.c new file mode 100644 index 0000000..ffbdfde --- /dev/null +++ b/lib/client.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include + +#if AISL_WITH_SSL == 1 +#include +#endif + +#include +#include "debug.h" +#include "stream.h" +#include "http.h" +#include "server.h" +#include "instance.h" +#include "client.h" + +#define FLAG_KEEPALIVE (1<<0) +#define FLAG_HANDSHAKE (1<<1) +#define FLAG_CAN_READ (1<<2) +#define FLAG_CAN_WRITE (1<<3) + +#define BUFFER_SIZE (16*1024) + + +static void +aisl_client_close(AislClient client, AislStatus status) +{ + if (client->fd != -1) { + aisl_raise(client->server->instance, (void *)client, + AISL_EVENT_CLIENT_DISCONNECT, status ); + + close(client->fd); + shutdown(client->fd, SHUT_RDWR); + client->fd = -1; + } +} + + +static AislStatus +aisl_client_parse(AislClient client, char *data, int32_t size) +{ + AislStatus result = AISL_SUCCESS; + AislStream s = client->stream; + ParserStatus p = HTTP_PARSER_SUCCESS; + + int32_t data_size; + + switch (client->http_version) { + case AISL_HTTP_0_9: + case AISL_HTTP_1_0: + case AISL_HTTP_1_1: + while (p == HTTP_PARSER_SUCCESS) { + data_size = size; + + switch (aisl_stream_get_state(s)) { + case AISL_STREAM_STATE_IDLE: + p = http_10_parse_request(data, &size, client->stream); + break; + + case AISL_STREAM_STATE_WAIT_HEADER: + p = http_10_parse_header(data, &size, client->stream); + break; + + case AISL_STREAM_STATE_WAIT_BODY: + p = http_10_parse_body(data, &size, client->stream); + break; + + default: /* has input data, but request was already parsed */ + DPRINTF("misleading request length"); + p = HTTP_PARSER_ERROR; + continue; + } + // size now has number of parsed bytes + DPRINTF("%d bytes parsed, %d bytes left", (data_size - size), size); + data += (data_size - size); + } + break; + + case AISL_HTTP_2_0: + break; + } + + switch(p) { + case HTTP_PARSER_READY: + client->flags &= ~FLAG_CAN_READ; + client->flags |= FLAG_CAN_WRITE; + client->stream->content_length = AISL_AUTO_LENGTH; + aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST, + result); + break; + + case HTTP_PARSER_ERROR: + /* reply Bad Request here */ + client->stream->http_response = AISL_HTTP_BAD_REQUEST; + aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR, + result); + + aisl_client_close(client, result); + return result; + + default: + break; + } + + buffer_shift(&client->in, client->in.used - size); /* reset buffer */ + + return result; +} + + +/* In HTTP 2.0 client->stream will be NULL if stream related data was completely + * parsed. If it is not NULL, then stream expects additional data -> same like + * in mono stream HTTP 1.0 + */ +static AislStatus +aisl_client_input(AislClient client) +{ + int l; + char *data = &client->in.data[ client->in.used ]; + int32_t size = client->in.size - client->in.used; + + #if AISL_WITH_SSL == 1 + if (client->ssl) { + DPRINTF("SSL_read"); + if (!(client->flags & FLAG_HANDSHAKE)) { + if ( (l = SSL_accept(client->ssl)) != 1 ) { + l = SSL_get_error(client->ssl, l); + + if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE) + return AISL_IDLE; + + DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) ); + + aisl_client_close(client, AISL_EXTCALL_ERROR); + return AISL_EXTCALL_ERROR; + } + client->flags &= ~FLAG_HANDSHAKE; + } + l = SSL_read(client->ssl, data, size); + } else + #endif + { + l = recv( client->fd, data, size, 0); + } + + if (l > 0) { + DPRINTF("%d bytes received from client", l); + + data = client->in.data; + size = client->in.used + l; + client->in.used = size; + return aisl_client_parse(client, data, size); + } else if (l<0) { + + #if AISL_WITH_SSL == 1 + if (client->ssl) { + if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ) + return AISL_IDLE; + } else + #endif + { + + if(errno == EWOULDBLOCK) + return AISL_IDLE; + DPRINTF("client - %s", strerror(errno)); + } + } + + /* both: client disconnect + on read error */ + /* todo: raise client error here */ + aisl_client_close(client, AISL_SYSCALL_ERROR); + + return AISL_SYSCALL_ERROR; +} + + +static AislStatus +aisl_client_output(AislClient client) +{ + int l; + char *data; + + AislStream s = client->stream; + + /* while stream is not flushed, we should raise event */ + if(aisl_get_output_event(s)) { + /* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event ) + * stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but + * buffer->size will be used to carry amount of stored bytes + * */ + l = aisl_stream_get_buffer_space(s); + + /* + if (bsz < OUTPUT_BUFFER_SIZE) + { + if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0) + return false; + + s->buffer->size = bsz; + bsz = OUTPUT_BUFFER_SIZE; + } + */ + + if (!(l < aisl_stream_get_buffer_size(s) / 2)) { + aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT, + AISL_SUCCESS); + } + } + + data = aisl_stream_get_data(s, &l); + + if ( !l ) + return AISL_IDLE; + + #if AISL_WITH_SSL == 1 + l = (client->ssl) ? SSL_write(client->ssl, data, l) : + send(client->fd, data, l, 0); + #else + l = send(client->fd, data, l, 0); + #endif + + if (l > 0) { + aisl_stream_shift(s, l); + if (aisl_stream_is_done(s)) { + /* data has been sent */ + if (client->flags & FLAG_KEEPALIVE) { + aisl_stream_free(s); + + client->stream = aisl_stream_new(client, client->next_id++); + if (client->stream != NULL ) + return AISL_SUCCESS; + + /* in case of malloc error it will not be an error as long as the + * request was handled and we just close the connection. + */ + } + + aisl_client_close(client, AISL_SUCCESS); + } + + return AISL_SUCCESS; + } + + /* l < 0 */ + #if AISL_WITH_SSL == 1 + if (client->ssl) { + if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE) + return AISL_IDLE; + } else + #endif + { + if (errno == EWOULDBLOCK) + return AISL_IDLE; + } + aisl_client_close(client, AISL_SYSCALL_ERROR); + + return AISL_SYSCALL_ERROR; +} + + +AislClient +aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr) +{ + AislClient client; + AislStream stream; + + if ((client = calloc(1, sizeof (struct aisl_client))) != NULL) { + DPRINTF("client alocated"); + memcpy(&client->address, addr, sizeof (struct sockaddr_in)); + client->server = server; + client->fd = fd; + client->next_id = 2; + client->http_version = AISL_HTTP_1_0; + client->timestamp = time(NULL); + client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ; + + if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) { + DPRINTF("client buffer alocated"); + memcpy(&client->out, &client->in, sizeof (struct buffer)); + + stream = aisl_stream_new(client, 0); + + if (stream != NULL) { + client->stream = stream; + DPRINTF("client stream alocated"); + + #if AISL_WITH_SSL == 1 + if (server->ssl) { + SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL); + + if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) { + SSL_set_fd(client->ssl, fd); + return client; + } + } else { + return client; + } + #else + return client; + #endif + } + } + aisl_client_free(client); + } + return NULL; +} + + +void +aisl_client_free(AislClient client) +{ + aisl_client_close(client, AISL_SUCCESS); + + #if AISL_WITH_SSL == 1 + if (client->ssl) + SSL_free(client->ssl); + #endif + + if (client->in.data) + free(client->in.data); + + if (client->stream) + aisl_stream_free(client->stream); + + free(client); +} + + +AislStatus +aisl_client_touch(AislClient client, int32_t timeout) +{ + AislStatus result, status; + + result = AISL_IDLE; + status = AISL_IDLE; + + /* input */ + if (client->flags & FLAG_CAN_READ) { + if ( (result = aisl_client_input(client)) < 0 ) + return result; + } + + /* output */ + if (client->flags & FLAG_CAN_WRITE) { + if ( (status = aisl_client_output(client)) < 0 ) + return status; + } + + if (result == AISL_IDLE) + result = status; + + if (result != AISL_SUCCESS) { + time_t now; + time(&now); + + if (!(now - client->timestamp < timeout)) { + aisl_client_close(client, result); + } + } else { + client->timestamp = time(NULL); + } + return result; +} + + +int +aisl_client_get_socket(AislClient client) +{ + return client->fd; +} + + +bool +aisl_client_get_keepalive(AislClient client) +{ + return (client->flags & FLAG_KEEPALIVE); +} + + +void +aisl_client_set_keepalive(AislClient client, bool value) +{ + if (value) + client->flags |= FLAG_KEEPALIVE; + else + client->flags &= ~FLAG_KEEPALIVE; +} + + + +/* API Level ---------------------------------------------------------------- */ + +__attribute__ ((visibility ("default") )) +AislServer +aisl_client_get_server(AislClient client) +{ + return client->server; +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_client_is_secure(AislClient client) +{ + #if AISL_WITH_SSL == 1 + return (client->ssl == NULL) ? false : true; + #else + return false; + #endif +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_client_is_online(AislClient client) +{ + return (client->fd == -1) ? false : true; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_client_disconnect(AislClient client) +{ + aisl_client_close(client, AISL_SUCCESS); +} + + +__attribute__ ((visibility ("default") )) +AislHttpVersion +aisl_client_get_http_version(AislClient client) +{ + return client->http_version; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_client_get_address(AislClient client, struct sockaddr_in *address) +{ + memcpy(address, &client->address, sizeof (struct sockaddr_in)); +} diff --git a/lib/client.h b/lib/client.h new file mode 100644 index 0000000..462ff82 --- /dev/null +++ b/lib/client.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file client.h + * @author Ilja Kartašov + * @brief Declarations of aisl_client structure and functions + * + * @see https://lowenware.com/aisl/ + */ +#ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777 +#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777 + +#include + +#if AISL_WITH_SSL == 1 +#include +#endif + +#include +#include "buffer.h" + +#define AISL_CLIENT(x) ((AislClient) x) + + +struct aisl_client { + struct sockaddr_in address; /**< Client's address structure. */ + struct buffer in; /**< Client's input buffer. */ + struct buffer out; /**< Client's output buffer. */ + AislServer server; /**< Server instance. */ + AislStream stream; /**< Pending client's stream. */ + #if AISL_WITH_SSL == 1 + SSL *ssl; /**< SSL pointer for HTTPS. */ + #endif + time_t timestamp; /**< Last communication timestamp. */ + int next_id; /**< Stream id generator (even). */ + int flags; /**< Client's flag bitmask. */ + int fd; /**< Client's socket descriptor. */ + AislHttpVersion http_version; /**< Client's http_version version. */ +}; + + +/** + * @brief Constructor for #AislClient instance. + * @param server an #AislServer instance pointer. + * @param fd a client socket descriptor. + * @param addr a pointer to client's address structure. + * @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled + */ +AislClient +aisl_client_new(AislServer server, + int fd, + struct sockaddr_in *addr ); + + +/** + * @brief Destructor for #AislClient instance. + * @param client an #AislClient instance pointer. + */ +void +aisl_client_free(AislClient client); + + +/** + * @brief Does all HTTP client routines. + * Reads and parses requests, writes responses. + * @param client an #AislClient instance pointer. + * @param timeout an allowed client silence time in seconds. + * @return #AislStatus code. + */ +AislStatus +aisl_client_touch(AislClient client, int32_t timeout); + + +/** + * @Brief Checks if client is about to keep connection alive. + * @param client an #AislClient instance pointer. + * @return true if keepalive mode is on, otherwise false. + */ +bool +aisl_client_get_keepalive(AislClient client); + + +/** + * @Brief Sets if connection with client must be kept alive. + * @param client an #AislClient instance pointer. + * @param value a true to enable keepalive mode, false to disable. + */ +void +aisl_client_set_keepalive(AislClient client, bool value); + + +/** + * @brief Gets socket descriptor associated with #AislClient instance. + * @param client an #AislClient instance pointer. + * @return a client socket descriptor. + */ +int +aisl_client_get_socket(AislClient client); + + +#endif /* !AISL_CLIENT_H */ diff --git a/lib/debug.h b/lib/debug.h new file mode 100644 index 0000000..c1fc724 --- /dev/null +++ b/lib/debug.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file debug.h + * @author Ilja Kartašov + * @brief AISL debug module + * + * @see https://lowenware.com/ + */ + +#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B +#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B + +#if AISL_WITH_DEBUG == 1 + +#include + +#define DPRINTF(...) do { \ + fprintf(stderr, "* AISL: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} while(0) \ + + +#else + +#define DPRINTF(...) + +#endif + + +#endif /* !AISL_DEBUG_H */ diff --git a/lib/http.c b/lib/http.c new file mode 100644 index 0000000..ff38e03 --- /dev/null +++ b/lib/http.c @@ -0,0 +1,428 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file http.c + * @author Ilja Kartašov + * @brief HTTP module source file + * + * @see https://lowenware.com/aisl/ + */ +#include +#include +#include +#include "client.h" +#include "stream.h" +#include "debug.h" +#include "http.h" + + +static AislHttpMethod +http_method_from_string(const char *method, int32_t length) +{ + int i; + AislHttpMethod methods[3] = {0, 0, 0}; + + switch(length) { + case 3: + methods[0] = AISL_HTTP_GET; + methods[1] = AISL_HTTP_PUT; + methods[2] = AISL_HTTP_PRI; + break; + + case 4: + methods[0] = AISL_HTTP_POST; + methods[1] = AISL_HTTP_HEAD; + break; + + case 5: + methods[0] = AISL_HTTP_TRACE; + break; + + case 6: + methods[0] = AISL_HTTP_DELETE; + break; + + case 7: + methods[0] = AISL_HTTP_OPTIONS; + methods[1] = AISL_HTTP_CONNECT; + break; + } + + for (i=0; i host) { + path = data; + if (!uri) + uri = path; + } else if (version && data-version != 4) { + DPRINTF("wrong HTTP version length"); + return HTTP_PARSER_ERROR; + } + break; + + case '?': + if (!query) { + query = data+1; + } else if (version) { + DPRINTF("bad character in HTTP version (%c)", *data); + return HTTP_PARSER_ERROR; + } + break; + + case '\n': + newline = data; + break; + + case '\r': + if (!version) { + DPRINTF("unexpected end of HTTP request"); + return HTTP_PARSER_ERROR; + } + break; + + default: + if (!uri && method_end) { + uri = data; + } else if (!version && uri_end) { + version = data; + } else if (version && data-version > 7) { + DPRINTF("bad HTTP version length"); + return HTTP_PARSER_ERROR; + } + } + data++; + } + + /* STEP 2. Verify splitting was completed */ + + /* Was request sent? */ + if (!newline) + return HTTP_PARSER_HUNGRY; + + /* Check mandatory parts presence */ + if (!method_end || !path || !uri_end || !version) { + DPRINTF("parser error: method=%d, path=%d, uri_end=%d, version=%d", + (method_end ? 1 : 0), (path ? 1 : 0), (uri_end ? 1 : 0), + (version ? 1: 0)); + return HTTP_PARSER_ERROR; + } + + *method_end = 0; + *newline = 0; + *uri_end = 0; + + http_method = http_method_from_string(method, method_end - method); + if (http_method == AISL_HTTP_METHOD_UNKNOWN) { + DPRINTF("invalid HTTP method"); + return HTTP_PARSER_ERROR; + } + + if ((http_version = http_version_from_string(version))==0) { + DPRINTF("invalid HTTP version"); + return HTTP_PARSER_ERROR; + } + + if (query) { + *(query - 1) = 0; + } else { + query = uri_end; + } + + if (host) { + if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) { + DPRINTF("invalid HTTP uri"); + return HTTP_PARSER_ERROR; + } + + if (port) + *(port - 1) = 0; + } + + stream->client->http_version = http_version; + aisl_stream_set_request(stream, http_method, path, query); + + if (host) + aisl_stream_set_header(stream, "host", host); + /* how many characters has been read */ + *(p_size) = size; + return HTTP_PARSER_SUCCESS; +} + + +ParserStatus +http_10_parse_header(char *data, int32_t *p_size, AislStream stream) +{ + int32_t size = *p_size; + char *key = data, + *colon = NULL, + *val = NULL, + *val_end = NULL, + *newline = NULL; + + + DPRINTF("parse header: %p, %d, %02X %02X", data, *p_size, *data & 0xFF, *(data+1) & 0xFF); + + while(!newline && size-- ) { + switch(*data) { + case ' ': + if (val && !val_end) + val_end = data; + break; + + case ':': + if (!colon) { + if (colon == key) { + DPRINTF("parser error: nameless HTTP header"); + return HTTP_PARSER_ERROR; + } + + colon = data; + } + break; + + case '\n': + newline = data; + + case '\r': + if (!val_end && val) + val_end = data; + break; + + default: + if (!colon) { + *data = tolower(*data); + } else if (!val) { + if (colon) + val = data; + } + + if (val_end) + val_end = NULL; + } + data++; + } + + if (!newline) + return HTTP_PARSER_HUNGRY; + + /* DPRINTF("(%p == %p); *key == 0x%02x", newline, key, *key & 0xFF); */ + + if (colon && val && val_end) { + *colon = 0; + *val_end = 0; + aisl_stream_set_header(stream, key, val); + *p_size = size; + return HTTP_PARSER_SUCCESS; + } else if (newline == key || (newline == key+1 && *key == '\r')) { + *p_size = size; + DPRINTF("end of headers received"); + return (aisl_stream_set_end_of_headers(stream) == 0) ? + HTTP_PARSER_READY : HTTP_PARSER_SUCCESS; + } + DPRINTF("parser error: invalid HTTP header"); + return HTTP_PARSER_ERROR; +} + + +ParserStatus +http_10_parse_body(char *data, int32_t *p_size, AislStream stream) +{ + int32_t size = *p_size; + + if (!size) + return HTTP_PARSER_HUNGRY; + + *p_size = 0; + + switch (aisl_stream_set_body(stream, data, size)) { + case 0: + return HTTP_PARSER_READY; + case -1: + DPRINTF("parser error: invalid HTTP body length"); + return HTTP_PARSER_ERROR; + default: + return HTTP_PARSER_SUCCESS; + } +} + + +/* API Level */ + +__attribute__ ((visibility ("default") )) +const char * +aisl_http_version_to_string(AislHttpVersion version) +{ + switch (version) { + case AISL_HTTP_0_9: + return "HTTP/0.9"; + case AISL_HTTP_1_0: + return "HTTP/1.0"; + case AISL_HTTP_1_1: + return "HTTP/1.1"; + case AISL_HTTP_2_0: + return "HTTP/2.0"; + } + return ""; +} + + +__attribute__ ((visibility ("default") )) +const char * +aisl_http_response_to_string(AislHttpResponse code) +{ + switch (code) { + /* most common for faster behavior */ + case AISL_HTTP_OK: return "OK"; + case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; + /* informational */ + case AISL_HTTP_CONTINUE: return "Continue"; + case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols"; + /* Successful */ + case AISL_HTTP_CREATED: return "Created"; + case AISL_HTTP_ACCEPTED: return "Accepted"; + case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information"; + case AISL_HTTP_NO_CONTENT: return "No Content"; + case AISL_HTTP_RESET_CONTENT: return "Reset Content"; + case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content"; + /* redirection */ + case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices"; + case AISL_HTTP_FOUND: return "Found"; + case AISL_HTTP_SEE_OTHER: return "See other"; + case AISL_HTTP_NOT_MODIFIED: return "Not Modified"; + case AISL_HTTP_USE_PROXY: return "Use Proxy"; + case AISL_HTTP_UNUSED: return "(unused)"; + case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect"; + /* client error */ + case AISL_HTTP_BAD_REQUEST: return "Bad Request"; + case AISL_HTTP_UNAUTHORIZED: return "Unauthorized"; + case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required"; + case AISL_HTTP_FORBIDDEN: return "Forbidden"; + case AISL_HTTP_NOT_FOUND: return "Not Found"; + case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed"; + case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable"; + case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required"; + case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout"; + case AISL_HTTP_CONFLICT: return "Conflict"; + case AISL_HTTP_GONE: return "Gone"; + case AISL_HTTP_LENGTH_REQUIRED: return "Length Required"; + case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed"; + case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large"; + case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long"; + case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; + case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable"; + case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed"; + /* server error */ + case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; + case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented"; + case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway"; + case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable"; + case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout"; + case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported"; + } + return ""; +} + + +__attribute__ ((visibility ("default") )) +const char * +aisl_http_method_to_string( AislHttpMethod method ) +{ + switch(method) { + case AISL_HTTP_GET: return "GET"; + case AISL_HTTP_PUT: return "PUT"; + case AISL_HTTP_POST: return "POST"; + case AISL_HTTP_HEAD: return "HEAD"; + case AISL_HTTP_TRACE: return "TRACE"; + case AISL_HTTP_DELETE: return "DELETE"; + case AISL_HTTP_OPTIONS: return "OPTIONS"; + case AISL_HTTP_CONNECT: return "CONNECT"; + case AISL_HTTP_PRI: return "PRI"; + case AISL_HTTP_METHOD_UNKNOWN: break; + } + return ""; +} diff --git a/lib/http.h b/lib/http.h new file mode 100644 index 0000000..75da224 --- /dev/null +++ b/lib/http.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * Copyright (c) 2017-%YEAR% by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file http.h + * @author Ilja Kartašov + * @brief Declarations of HTTP parser module + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01 +#define AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01 + +#include + +typedef enum { + HTTP_PARSER_SUCCESS + , HTTP_PARSER_READY + , HTTP_PARSER_HUNGRY + , HTTP_PARSER_ERROR +} ParserStatus; + + +ParserStatus +http_10_parse_request(char *data, int32_t *size, AislStream stream); + + +ParserStatus +http_10_parse_header(char *data, int32_t *size, AislStream stream); + + +ParserStatus +http_10_parse_body(char *data, int32_t *size, AislStream stream); + + +#endif /* !AISL_HTTP_H */ diff --git a/lib/instance.c b/lib/instance.c new file mode 100644 index 0000000..974d07d --- /dev/null +++ b/lib/instance.c @@ -0,0 +1,301 @@ +/* + * src/instance.c + * + * Copyright (C) 2019 Ilja Kartašov + * + * Project homepage: https://lowenware.com/aisl/ + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#if AISL_WITH_SSL == 1 +#include +#endif + +#include "debug.h" +#include "str-utils.h" +#include "buffer.h" +#include "client.h" +#include "server.h" +#include "stream.h" +#include "instance.h" + + +#if AISL_WITH_SSL == 1 + +static uint32_t m_instances = 0; + +static struct aisl_ssl * +aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl) +{ + SSL_CTX *ssl_ctx = NULL; + struct aisl_ssl **list, *ssl; + + list = instance->ssl; + + /* lookup for existing contexts */ + while ((ssl = *list)) { + if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 && + ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0 + ) { + ssl_ctx = ssl->ctx; + break; + } + list++; + } + + ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file, + ssl_ctx); + + if (ssl) { + if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) { + aisl_ssl_free(ssl); + ssl = NULL; + } + } + return ssl; +} + +#endif + + +/* Initialization functions */ + +__attribute__ ((visibility ("default") )) +AislInstance +aisl_new(const struct aisl_cfg *cfg) +{ + int i; + AislInstance instance; + + /* allocate root structure */ + if (!(instance = calloc(1, sizeof (struct aisl_instance)))) + goto finally; + + /* allocate servers */ + if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer)))) + goto release; + + for (i = 0; i < cfg->srv_cnt; i++) { + DPRINTF("new srv %d", i); + if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance))) + goto release; + } + + #if AISL_WITH_SSL == 1 + if ((m_instances++) == 0) { + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + } + + if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl)))) + goto release; + + for (i=0; issl_cnt; i++) { + DPRINTF("new ssl %d", i); + if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i]))) + goto release; + } + #endif + + if (list_init(&instance->client_spool, cfg->client_spool_size) == -1) + goto release; + + instance->accept_limit = cfg->client_accept_limit; + instance->silence_timeout = cfg->client_silence_timeout; + instance->callback = cfg->callback; + instance->p_ctx = cfg->p_ctx; + + goto finally; + +release: + aisl_free(instance); + instance = NULL; + +finally: + return instance; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_free(AislInstance instance) +{ + if (instance->srv) { + AislServer * srv = instance->srv; + + while (*srv) { + aisl_server_free(*(srv++)); + } + + free(instance->srv); + } + + list_release(&instance->client_spool, (list_destructor_t)aisl_client_free); + + #if AISL_WITH_SSL == 1 + if (instance->ssl) { + struct aisl_ssl **ssl = instance->ssl; + + while (*ssl) { + aisl_ssl_free(*(ssl++)); + } + free(instance->ssl); + } + + if ((--m_instances) == 0) { + EVP_cleanup(); + } + #endif + + free(instance); +} + + +#if AISL_WITH_SSL == 1 + +SSL_CTX * +aisl_get_ssl_ctx(AislInstance instance, const char * host) +{ + struct aisl_ssl **list, *ssl; + + list = instance->ssl; + + if (host) { + while ((ssl = *list)) { + if (str_cmpi(ssl->host, host) == 0) { + return ssl->ctx; + } + list++; + } + } + return NULL; +} + +#endif + + +void +aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt) +{ + #if AISL_WITH_STRINGIFIERS == 1 + DPRINTF("! %s", aisl_event_to_string(evt->code)); + #else + DPRINTF("! %d", evt->code); + #endif + + if (instance->callback) + instance->callback(evt, instance->p_ctx); +} + + +void +aisl_raise(AislInstance instance, + void *source, + AislEvent code, + AislStatus status) +{ + struct aisl_evt evt; + + evt.source = source; + evt.code = code; + evt.status = status; + + aisl_raise_evt(instance, &evt); +} + + +__attribute__ ((visibility ("default") )) +AislStatus +aisl_run_cycle(AislInstance instance) +{ + AislStatus result = AISL_IDLE; + AislServer *list, srv; + AislClient cli; + int32_t i; + + list = instance->srv; + + while ((srv = *list)) { + cli = NULL; + + if (aisl_server_touch(srv, &cli) != AISL_IDLE) + result = AISL_SUCCESS; + + if (cli) { + DPRINTF("Accepted %p", (void*)cli); + if (list_append(&instance->client_spool, cli) == -1) + aisl_client_free(cli); + } + list++; + } + + for (i=0; i < instance->client_spool.count; i++) { + cli = LIST_INDEX(instance->client_spool, i); + + if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE) + result = AISL_SUCCESS; + + if (!aisl_client_is_online(cli)) { + aisl_client_free( cli ); + list_remove_index(&instance->client_spool, i); + } + } + return result; +} + + +__attribute__ ((visibility ("default") )) +AislStatus +aisl_sleep(AislInstance instance, uint32_t usec) +{ + AislServer *list, srv; + int sd, maxfd = 0; + size_t i; + struct timeval timeout = {0,usec}; + + memset(&timeout, 0, sizeof (struct timeval)); + timeout.tv_usec = usec; + + fd_set fs; + FD_ZERO (&fs); + + list = instance->srv; + + while ((srv = *list)) { + sd = aisl_server_get_socket(srv); + + if (sd != -1) { + FD_SET(sd, &fs); + if (sd > maxfd) maxfd = sd; + } + list++; + } + + for (i=0; iclient_spool.count; i++) { + AislClient c = LIST_INDEX(instance->client_spool, i); + sd = aisl_client_get_socket(c); + if (sd != -1) { + FD_SET(sd, &fs); + if (sd > maxfd) maxfd = sd; + } + } + + switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) { + case -1: + return AISL_SYSCALL_ERROR; + + case 0: + return AISL_IDLE; + + default: + return AISL_SUCCESS; + } +} diff --git a/lib/instance.h b/lib/instance.h new file mode 100644 index 0000000..34df430 --- /dev/null +++ b/lib/instance.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file src/instance.h + * @author Ilja Kartašov + * @brief Declarations of aisl_instance structure and functions + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484 +#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484 + +#if AISL_WITH_SSL == 1 +#include +#endif + +#include +#include "ssl.h" +#include "list.h" + + +struct aisl_instance { + AislServer *srv; + #if AISL_WITH_SSL == 1 + struct aisl_ssl * *ssl; + #endif + struct list client_spool; + AislCallback callback; + void *p_ctx; + + uint32_t accept_limit; + uint32_t silence_timeout; + uint32_t buffer_size; +}; + + +#if AISL_WITH_SSL == 1 +/** + * @brief Gets SSL context for appropriate server name. + * @param instance a pointer to #AislInstance instance. + * @param server_name a null-terminated string with server name or NULL. + * @return a pointer to SSL context + */ +SSL_CTX * +aisl_get_ssl_ctx(AislInstance instance, const char *server_name); +#endif + + +/** + * @brief Raises event from source. + * @param instance a pointer to #AislInstance instance. + * @param evt a pointer to event structure. + */ +void +aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt); + + +void +aisl_raise(AislInstance instance, + void *source, + AislEvent code, + AislStatus status); + +#endif /* !AISL_INSTANCE_H */ diff --git a/lib/list.c b/lib/list.c new file mode 100644 index 0000000..ad20426 --- /dev/null +++ b/lib/list.c @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file list.c + * @author Ilja Kartašov + * @brief List module source file + * + * @see https://lowenware.com/aisl/ + */ + +#include +#include "debug.h" +#include "list.h" + +int32_t +list_init(struct list *list, int32_t size) +{ + if ((list->data = calloc(size, sizeof (void*))) != NULL) { + list->size = size; + list->count = 0; + return 0; + } + return -1; +} + + +void +list_release(struct list *list, list_destructor_t destructor) +{ + if (list->data) { + if (destructor) { + int32_t i; + + for (i=0; icount; i++) { + void *ptr; + + if ((ptr = list->data[i]) != NULL) + destructor(list->data[i]); + } + } + free(list->data); + } +} + + +int32_t +list_append(struct list *list, void *entry) +{ + int32_t pos = list->count; + + DPRINTF("pos = %d", pos); + + if (!(pos < list->size)) { + DPRINTF("extending, size = %d", list->size); + void **new_list; + int32_t new_size = pos + 1; + + DPRINTF("extending, ptr = %p", (void*)list->data); + if ((new_list = realloc( list->data, new_size*sizeof (void*) )) == NULL) + return -1; + + list->data = new_list; + list->size = new_size; + } + + list->data[pos]=entry; + list->count++; + + return pos; +} + + +void * +list_remove_index(struct list *list, int32_t index) +{ + void *result; + + if (index < list->count) { + int32_t i, c = --list->count; + + result = list->data[index]; + + for (i = index; idata[i]=list->data[i+1]; + } + } + else + result = NULL; + + return result; +} + diff --git a/lib/list.h b/lib/list.h new file mode 100644 index 0000000..403295e --- /dev/null +++ b/lib/list.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file src/list.h + * @author Ilja Kartašov + * @brief List module header file + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1 +#define AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1 + +#include + +#define LIST_INDEX(L, I) ( L.data[ I ] ) + + +struct list { + void **data; + int32_t size; + int32_t count; +}; + + +typedef void +(* list_destructor_t)(void *list_item); + + +int32_t +list_init(struct list *lst, int32_t size); + + +void +list_release(struct list *lst, list_destructor_t destructor); + + +int32_t +list_append(struct list *lst, void * entry); + + +void * +list_remove_index(struct list *lst, int32_t index); + + +#endif /* !AISL_LIST_H */ diff --git a/lib/server.c b/lib/server.c new file mode 100644 index 0000000..64c1941 --- /dev/null +++ b/lib/server.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#include "debug.h" +#include "str-utils.h" +#include "instance.h" +#include "client.h" +#include "server.h" + + +/** + * @brief Creates TCP server socket, binds to address and starts to listen. + * @param server a pointer to #AislServer instance. + * @return #AislStatus code. + */ +static AislStatus +aisl_server_open(AislServer server) +{ + int fd, s_opt = 1; + + fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + + if (fd != -1) { + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, + sizeof (int)); + + #ifdef __APPLE__ + ioctl(fd, FIONBIO, (char *)&s_opt); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + #endif + + s_opt = sizeof (struct sockaddr_in); + + if (bind(fd, (struct sockaddr *) &server->address, s_opt) == 0) { + if (listen(fd, SOMAXCONN) == 0) { + server->fd = fd; + return AISL_SUCCESS; + } + } + close(fd); + } + + return AISL_SYSCALL_ERROR; +} + + +/** + * @brief Tries to accept a new client. + * @param server a pointer to #AislServer instance. + * @param p_client a pointer to store #AislClient instance pointer. + * @return #AislStatus code. + */ +static AislStatus +aisl_server_accept(AislServer server, AislClient *p_client ) +{ + int fd; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + fd = accept(server->fd, (struct sockaddr *)&addr, &len); + + if (fd != -1) { + int flags; + + DPRINTF("accepted fd=%d", fd); + + if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) == 0) { + return (!(*p_client = aisl_client_new(server, fd, &addr))) ? + AISL_MALLOC_ERROR : AISL_SUCCESS; + } + } + close(fd); + } else if (errno == EWOULDBLOCK) { + return AISL_IDLE; + } + return AISL_SYSCALL_ERROR; +} + +/* Library Level ------------------------------------------------------------ */ + +AislStatus +aisl_server_touch(AislServer server, AislClient *p_client) +{ + AislStatus result; + + if (server->fd == -1) { + if ((result = aisl_server_open(server)) != AISL_IDLE) { + aisl_raise(server->instance, server, ((result == AISL_SUCCESS) ? + AISL_EVENT_SERVER_READY : AISL_EVENT_SERVER_ERROR), result); + } + } else { + result = aisl_server_accept(server, p_client); + } + return result; +} + + +int +aisl_server_get_socket(AislServer server) +{ + return server->fd; +} + + +AislServer +aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance) +{ + AislServer server; + + if ((server = calloc(1, sizeof (struct aisl_server))) != NULL) { + server->instance = instance; + server->fd = -1; + server->address.sin_family = AF_INET; + server->address.sin_addr.s_addr = inet_addr(cfg_srv->host); + server->address.sin_port = htons(cfg_srv->port); + server->ssl = cfg_srv->secure; + } + return server; +} + + +void +aisl_server_free(AislServer server) +{ + if (server) { + if ( server->fd != -1) { + close(server->fd); + server->fd=-1; + } + free(server); + } +} + + +/* API Level ---------------------------------------------------------------- */ + + +__attribute__ ((visibility ("default") )) +void +aisl_server_get_address(AislServer server, struct sockaddr_in *address) +{ + memcpy(address, &server->address, sizeof (struct sockaddr_in)); +} + + +#if AISL_WITH_SSL == 1 + +__attribute__ ((visibility ("default") )) +bool +aisl_server_get_ssl(AislServer server) +{ + return server->ssl; +} + +#endif diff --git a/lib/server.h b/lib/server.h new file mode 100644 index 0000000..86d7945 --- /dev/null +++ b/lib/server.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file dummy.h + * @author Ilja Kartašov + * @brief + * + * @see https://lowenware.com/ + *//* + * @file src/server.h + * + * Copyright (c) 2017-2019 by Löwenware Ltd. + * + * Project homepage: https://lowenware.com/aisl/ + * + */ + +#ifndef AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2 +#define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2 + +#include +#include + +#define AISL_SERVER(x) ((AislServer) x) + +/** + * @brief HTTP(s) server data structure represented by #AislServer pointer. + */ +struct aisl_server { + struct sockaddr_in address; /**< TCP server address to listen to. */ + AislInstance instance; /**< Associated AISL instance pointer. */ + int fd; /**< System socket descriptor. */ + bool ssl; /**< SSL enabled/disabled flag. */ +}; + +/** + * @brief Allocates and instance of #AislServer. + * @param cfg_srv a pointer to server configuration structure. + * @param instance a pointer to #AislInstance instance. + * @return a pointer to #AislServer instance. + */ +AislServer +aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance); + + +/** + * @brief Frees memory allocated for #AislServer instance. + * @param server a pointer to #AislServer instance. + */ +void +aisl_server_free(AislServer server); + + +/** + * @brief Does server routines. + * Tries to open server if it was not opened yet, otherwise tries to accept a + * new client connecting to the server. + * @param server a pointer to #AislServer instance. + * @param p_client a pointer to store #AislClient instance pointer. + * @return #AislStatus code: + * - AISL_SUCCESS if client connected, + * - AISL_IDLE if there is no client to connect, + * - AISL_SYSCALL_ERROR if error occured. + */ +AislStatus +aisl_server_touch(AislServer server, AislClient *p_client); + + +/** + * @brief Gets a socket descriptor associated with HTTP client. + * @param server a pointer to #AislServer instance. + * @return a client socket descriptor. + */ +int +aisl_server_get_socket(AislServer server); + + +#endif /* !AISL_SERVER_H */ diff --git a/lib/ssl.c b/lib/ssl.c new file mode 100644 index 0000000..074d7d5 --- /dev/null +++ b/lib/ssl.c @@ -0,0 +1,102 @@ +/* + * ssl.c + * Copyright (C) 2019 Ilja Kartašov + * + * Distributed under terms of the MIT license. + */ + +#include +#include "str-utils.h" +#include "instance.h" +#include "ssl.h" + + +#if AISL_WITH_SSL == 1 + + +static int +aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance ) +{ + const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name); + + if (ctx) + SSL_set_SSL_CTX(ssl, ctx); + + (void)ptr; + + return SSL_TLSEXT_ERR_OK; +} + + +SSL_CTX * +aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance) +{ + SSL_CTX * ctx; + + if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) { + SSL_CTX_set_ecdh_auto(ctx, 1); + SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx ); + SSL_CTX_set_tlsext_servername_arg( ctx, p_instance ); + + if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0)) + goto except; + + if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0)) + goto except; + + ssl->ctx = ctx; + + return ctx; + } + +except: + SSL_CTX_free(ctx); + return NULL; +} + +struct aisl_ssl * +aisl_ssl_new(const char *key_file, + const char *crt_file, + const char *host, + SSL_CTX *ctx) +{ + struct aisl_ssl *ssl; + + if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) { + if ((ssl->host = str_copy( host ? host : "*" )) != NULL) { + if (ctx) { + ssl->ctx = ctx; + return ssl; + } else { + if ((ssl->key_file = str_copy(key_file)) != NULL) { + if ((ssl->crt_file = str_copy(crt_file)) != NULL) { + return ssl; + } + } + } + } + aisl_ssl_free(ssl); + } + return NULL; +} + + +void +aisl_ssl_free( struct aisl_ssl *ssl ) +{ + if (ssl->host) + free(ssl->host); + + if (ssl->key_file) { + free(ssl->key_file); + SSL_CTX_free(ssl->ctx); + } + + if (ssl->crt_file) + free(ssl->crt_file); + + free(ssl); +} + +#endif diff --git a/lib/ssl.h b/lib/ssl.h new file mode 100644 index 0000000..d381fff --- /dev/null +++ b/lib/ssl.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file dummy.h + * @author Ilja Kartašov + * @brief + * + * @see https://lowenware.com/ + *//* + * src/ssl.h + * + * Copyright (C) 2019 Ilja Kartašov + * + * Project homepage: https://lowenware.com/aisl/ + * + */ + +#ifndef AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E +#define AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E + +#include +#include +#include + + +struct aisl_ssl { + char *key_file; + char *crt_file; + char *host; + SSL_CTX *ctx; +}; + + +struct aisl_ssl * +aisl_ssl_new( const char *key_file, + const char *crt_file, + const char *host, + SSL_CTX *ctx ); + + +SSL_CTX * +aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance); + + +void +aisl_ssl_free(struct aisl_ssl *ssl); + + +#endif /* !AISL_SSL_H */ diff --git a/lib/str-utils.c b/lib/str-utils.c new file mode 100644 index 0000000..c602678 --- /dev/null +++ b/lib/str-utils.c @@ -0,0 +1,52 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file str-utils.c + * @author Ilja Kartašov + * @brief String utilities source file + * + * @see https://lowenware.com/aisl/ + */ + +#include +#include +#include +#include "str-utils.h" + + +char * +str_copy(const char *source) +{ + int l = strlen(source); + + char *result = malloc(l+1); + + if (result) + strcpy(result, source); + + return result; +} + + +int +str_cmpi(const char *s1, const char *s2) +{ + char c1, c2, r = 0; + + do { + c1 = tolower(*(s1++)); + c2 = tolower(*(s2++)); + + if ((r = c1-c2) != 0) + break; + + } while (c1 != 0); + + return r; +} + diff --git a/lib/str-utils.h b/lib/str-utils.h new file mode 100644 index 0000000..e160b22 --- /dev/null +++ b/lib/str-utils.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file str-utils.h + * @author Ilja Kartašov + * @brief String utilities header file + * + * @see https://lowenware.com/aisl/ + */ + +#ifndef STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E +#define STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E + + +char * +str_copy(const char *source); + +int +str_cmpi(const char *anycase, const char *lowcase); + +#endif /* !STR_UTILS_H */ diff --git a/lib/stream.c b/lib/stream.c new file mode 100644 index 0000000..dfac68a --- /dev/null +++ b/lib/stream.c @@ -0,0 +1,616 @@ +#include +#include +#include +#include + +#include "instance.h" +#include "client.h" +#include "server.h" +#include "str-utils.h" +#include "debug.h" +#include "stream.h" + +#define FLAG_FLUSHED (1<<0) +#define FLAG_OUTPUT_CHUNKED (1<<1) +#define FLAG_SERVER_HEADER_SENT (1<<2) +#define FLAG_CONTENT_TYPE_HEADER_SENT (1<<3) +#define FLAG_CONTENT_LENGTH_HEADER_SENT (1<<4) +#define FLAG_CONNECTION_HEADER_SENT (1<<5) + + +/* Library level */ + +static void +aisl_stream_reset(AislStream stream, bool initial) +{ + if (!initial) { + aisl_raise(aisl_get_instance(stream), (void*) stream, + AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS); + } + + buffer_release(&stream->buffer); + + stream->u_ptr = NULL; + stream->content_length = 0; //AISL_AUTO_LENGTH; + stream->head_offset = 0; + stream->flags = 0; + stream->state = AISL_STREAM_STATE_IDLE; + stream->http_response = AISL_HTTP_OK; +} + + +AislStream +aisl_stream_new(AislClient client, int id) +{ + AislStream stream = calloc(1, sizeof (struct aisl_stream)); + + if (stream) { + stream->id = id; + stream->client = client; + aisl_stream_reset(stream, true); + } + return stream; +} + + +void +aisl_stream_free(AislStream stream) +{ + aisl_stream_reset(stream, false); + free(stream); +} + + +int32_t +aisl_stream_get_buffer_space(AislStream stream) +{ + return stream->buffer.size - stream->buffer.used; +} + + +int32_t +aisl_stream_get_buffer_size(AislStream stream) +{ + return stream->buffer.size; +} + + +char * +aisl_stream_get_data(AislStream stream, int32_t *p_length) +{ + *p_length = stream->buffer.used; + + return stream->buffer.data; +} + + +void +aisl_stream_shift(AislStream stream, int32_t offset) +{ + buffer_shift(&stream->buffer, offset); +} + + +bool +aisl_stream_is_done(AislStream stream) +{ + return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE); +} + + +AislStreamState +aisl_stream_get_state(AislStream stream) +{ + return stream->state; +} + + +void +aisl_stream_set_request(AislStream stream, + AislHttpMethod http_method, + const char *path, + const char *query) +{ + struct aisl_evt_open on_open; + + stream->state = AISL_STREAM_STATE_WAIT_HEADER; + + DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method), + path, query); + + on_open.evt.code = AISL_EVENT_STREAM_OPEN; + on_open.evt.source = (void *) stream; + on_open.evt.status = AISL_SUCCESS; + on_open.http_method = http_method; + on_open.path = path; + on_open.query = query; + + aisl_raise_evt(aisl_get_instance(stream), (struct aisl_evt *)&on_open); +} + + +void +aisl_stream_set_header(AislStream stream, const char *key, const char *value) +{ + struct aisl_evt_header on_header; + + if (stream->state != AISL_STREAM_STATE_WAIT_HEADER) + return; + + if (strcmp(key, "content-length") == 0) { + stream->content_length = strtoll(value, NULL, 10); + } else if (strcmp(key, "connection") == 0) { + aisl_client_set_keepalive(stream->client, + (str_cmpi(value, "close")==0) ? false : true); + } + + DPRINTF("%s: %s", key, value); + + on_header.evt.code = AISL_EVENT_STREAM_HEADER; + on_header.evt.source = (void *) stream; + on_header.evt.status = AISL_SUCCESS; + on_header.key = key; + on_header.value = value; + + aisl_raise_evt(aisl_get_instance(stream), + (struct aisl_evt *) &on_header); +} + + +int +aisl_stream_set_end_of_headers(AislStream stream) +{ + int result; + + DPRINTF("stream->content_length == %"PRIu64"", stream->content_length); + + if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) { + result = (stream->content_length != 0); + stream->state = (result) ? AISL_STREAM_STATE_WAIT_BODY : + AISL_STREAM_STATE_READY; + } else { + result = 2; + } + + return result; +} + + +int +aisl_stream_set_body(AislStream stream, const char *data, int32_t size) +{ + int result; + if (stream->state == AISL_STREAM_STATE_WAIT_BODY) { + if (!(stream->content_length < size)) { + struct aisl_evt_input on_input; + + stream->content_length -= size; + + if (stream->content_length == 0) { + stream->state = AISL_STREAM_STATE_READY; + result = 0; + } else { + result = 1; + } + + on_input.evt.code = AISL_EVENT_STREAM_INPUT; + on_input.evt.source = (void *)stream; + on_input.evt.status = AISL_SUCCESS; + on_input.data = data; + on_input.size = size; + + aisl_raise_evt(stream->client->server->instance, + (struct aisl_evt *) &on_input); + } else { + result = -1; + } + } else { + result = 2; + } + return result; +} + +/* API Level */ + +/* Why it was here? +static int +aisl_stream_write(AislStream stream, const char * data, uint32_t d_len) +{ + return buffer_add( &stream->buffer, data, d_len); +} + + +__attribute__ ((visibility ("default") )) +void +aisl_cancel(AislStream stream) +{ + aisl_client_close( stream->client ); +} +*/ + +__attribute__ ((visibility ("default") )) +void * +aisl_get_context(AislStream s) +{ + return s->u_ptr; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_set_context(AislStream s, void *u_ptr) +{ + s->u_ptr = u_ptr; +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_is_secure(AislStream stream) +{ + return aisl_client_is_secure(stream->client); +} + + +__attribute__ ((visibility ("default") )) +AislClient +aisl_get_client(AislStream s) +{ + return s->client; +} + + +__attribute__ ((visibility ("default") )) +AislServer +aisl_get_server(AislStream s) +{ + return aisl_client_get_server(s->client); +} + + +__attribute__ ((visibility ("default") )) +AislHttpVersion +aisl_get_http_version(AislStream s) +{ + return aisl_client_get_http_version(s->client); +} + + +__attribute__ ((visibility ("default") )) +void +aisl_reject(AislStream s) +{ + aisl_client_disconnect( s->client ); +} + + +static AislStatus +aisl_start_response(AislStream stream) +{ + return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH); +} + + +static AislStatus +aisl_stream_close_headers(AislStream stream) +{ + int32_t l; + + if (aisl_start_response(stream) == AISL_MALLOC_ERROR) + return AISL_MALLOC_ERROR; + + if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) { + l = buffer_append(&stream->buffer, "Server: AISL\r\n", 14); + if (l == -1) + return AISL_MALLOC_ERROR; + + stream->flags |= FLAG_SERVER_HEADER_SENT; + } + + if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) { + l = buffer_append(&stream->buffer, + "Content-Type: text/html; encoding=utf-8\r\n", 41); + + if (l == -1) + return AISL_MALLOC_ERROR; + + stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT; + } + + if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) { + if (stream->content_length != AISL_AUTO_LENGTH) { + l = buffer_append_printf(&stream->buffer, "Content-Length: %"PRIu64"\r\n", + stream->content_length); + + if (l == -1) + return AISL_MALLOC_ERROR; + + stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; + } + } + + if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) { + l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n", + (aisl_client_get_keepalive(stream->client) ? "keepalive" : "close")); + + if (l == -1) + return AISL_MALLOC_ERROR; + + stream->flags |= FLAG_CONNECTION_HEADER_SENT; + } + + if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1) + return AISL_MALLOC_ERROR; + + stream->body_offset = stream->buffer.used; + stream->state = AISL_STREAM_STATE_SEND_BODY; + + return AISL_SUCCESS; +} + + +__attribute__ ((visibility ("default") )) +AislStatus +aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len) +{ + int32_t l; + + /* check if those headers were already sent */ + if (stream->state > AISL_STREAM_STATE_READY) + return AISL_IDLE; + + stream->http_response = rs_code; + stream->content_length = c_len; + + buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0); + + l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n", + aisl_http_version_to_string(stream->client->http_version), rs_code, + aisl_http_response_to_string(rs_code)); + + if (l == -1) + return AISL_MALLOC_ERROR; + + stream->state = AISL_STREAM_STATE_SEND_HEADER; + + return AISL_SUCCESS; +} + + +__attribute__ ((visibility ("default") )) +AislStatus +aisl_flush(AislStream s) +{ + if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) { + char hdr[ 40 ]; + uint64_t c_len; + int32_t l; + + if (s->body_offset) { + c_len = s->buffer.used - s->body_offset; + l = snprintf(hdr, sizeof (hdr), "Content-Length: %"PRIu64"\r\n", c_len); + l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l); + if (l == -1) + return AISL_MALLOC_ERROR; + } else { + aisl_stream_close_headers(s); + } + s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; + } + + s->state = AISL_STREAM_STATE_DONE; + s->flags |= FLAG_FLUSHED; + + return AISL_SUCCESS; +} + + +static int32_t +aisl_stream_verify_header(AislStream stream, const char *key, const char *value) +{ + if (stream->state < AISL_STREAM_STATE_SEND_HEADER) { + if (aisl_start_response(stream) != AISL_SUCCESS) + return -1; + } else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) { + return 0; + } + + if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) { + if (str_cmpi(key, "connection")==0) { + stream->flags |= FLAG_CONNECTION_HEADER_SENT; + if (value) { + aisl_client_set_keepalive(stream->client, + (str_cmpi(value, "keepalive") == 0)); + } + return 1; + } + } + + if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) { + if (str_cmpi(key, "content-type") == 0) { + stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT; + return 1; + } + } + + if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) { + if (str_cmpi(key, "content-length") == 0) { + stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; + return 1; + } + } + + if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) { + if (str_cmpi(key, "content-length")==0) { + stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; + return 1; + } + } + + return 1; +} + +__attribute__ ((visibility ("default") )) +int32_t +aisl_header(AislStream stream, const char *key, const char *value) +{ + int32_t result; + + if ( (result = aisl_stream_verify_header( stream, key, value )) != 1) + return result; + + result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value); + + return result; + + /* For debug purposes + if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL ) + { + ret = strlen(pch); + if ( buffer_insert( + &stream->buffer, + stream->end_of_headers, + pch, + ret + ) == -1 ) + { + ret = -1; + } + else + stream->end_of_headers += ret; + + free(pch); + } + else + ret = -1; + + return ret; + */ +} + + +__attribute__ ((visibility ("default") )) +int32_t +aisl_header_printf(AislStream stream, const char *key, const char *format, ...) +{ + int32_t result; + va_list args; + + va_start(args, format); + result = aisl_header_vprintf( stream, key, format, args ); + va_end(args); + + return result; +} + + +__attribute__ ((visibility ("default") )) +int32_t +aisl_header_vprintf(AislStream stream, + const char *key, + const char *format, + va_list args) +{ + int32_t result, l; + + if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1) + return result; + + result = buffer_append_printf( &stream->buffer, "%s: ", key ); + + if (result != -1) { + l = buffer_append_vprintf( &stream->buffer, format, args ); + + if (l != -1) { + result += l; + if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) { + result += l; + return result; + } + } + } + return -1; +} + + +__attribute__ ((visibility ("default") )) +int +aisl_printf(AislStream stream, const char *format, ...) +{ + int result; + va_list arg; + + va_start(arg, format); + result = aisl_vprintf(stream, format, arg); + va_end(arg); + + return result; +} + + +__attribute__ ((visibility ("default") )) +int32_t +aisl_vprintf(AislStream stream, const char *format, va_list args) +{ + if (stream->state < AISL_STREAM_STATE_SEND_BODY) { + if (aisl_stream_close_headers(stream) != AISL_SUCCESS) + return -1; + } + return buffer_append_vprintf(&stream->buffer, format, args); +} + + +__attribute__ ((visibility ("default") )) +int32_t +aisl_write(AislStream stream, const char *data, int32_t d_len) +{ + if (stream->state < AISL_STREAM_STATE_SEND_BODY) { + if (aisl_stream_close_headers(stream) != AISL_SUCCESS) + return -1; + } + + if (d_len == -1) + d_len = strlen(data); + + return buffer_append(&stream->buffer, data, d_len); +} + + +__attribute__ ((visibility ("default") )) +int +aisl_puts(const char *str, AislStream stream) +{ + if (stream->state < AISL_STREAM_STATE_SEND_BODY) { + if (aisl_stream_close_headers(stream) != AISL_SUCCESS) + return -1; + } + return aisl_write( stream, str, -1); +} + + +__attribute__ ((visibility ("default") )) +AislInstance +aisl_get_instance(AislStream stream) +{ + return stream->client->server->instance; +} + + +__attribute__ ((visibility ("default") )) +void +aisl_set_output_event(AislStream stream, bool value) +{ + if (value) + stream->flags |= FLAG_OUTPUT_CHUNKED; + else if (stream->flags & FLAG_OUTPUT_CHUNKED) + stream->flags &= ~FLAG_OUTPUT_CHUNKED; +} + + +__attribute__ ((visibility ("default") )) +bool +aisl_get_output_event(AislStream stream) +{ + return (stream->flags & FLAG_OUTPUT_CHUNKED); +} + diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 0000000..a787505 --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file dummy.h + * @author Ilja Kartašov + * @brief + * + * @see https://lowenware.com/ + */#ifndef AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3 +#define AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3 + +#include +#include +#include "buffer.h" + +#define AISL_STREAM(x) ((AislStream) x) + +typedef enum +{ + AISL_STREAM_STATE_IDLE + + , AISL_STREAM_STATE_WAIT_HEADER + , AISL_STREAM_STATE_WAIT_BODY + + , AISL_STREAM_STATE_READY + + , AISL_STREAM_STATE_SEND_HEADER + , AISL_STREAM_STATE_SEND_BODY + + , AISL_STREAM_STATE_DONE + +} AislStreamState; + + +struct aisl_stream { + struct buffer buffer; + AislClient client; + + void *u_ptr; + + uint64_t content_length; + int32_t head_offset; + int32_t body_offset; + int id; + int flags; + + AislHttpResponse http_response; + AislStreamState state; +}; + + +AislStream +aisl_stream_new(AislClient client, int id); + + +void +aisl_stream_free(AislStream stream); + + +int32_t +aisl_stream_get_buffer_space(AislStream stream); + + +int32_t +aisl_stream_get_buffer_size(AislStream stream); + + +char * +aisl_stream_get_data(AislStream stream, int32_t *p_length); + + +void +aisl_stream_shift(AislStream stream, int32_t offset); + + +bool +aisl_stream_is_done(AislStream stream); + + +AislStreamState +aisl_stream_get_state(AislStream stream); + + +void +aisl_stream_set_request(AislStream stream, + AislHttpMethod http_method, + const char *path, + const char *query ); + +void +aisl_stream_set_header(AislStream stream, const char *key, const char *value); + + +int +aisl_stream_set_end_of_headers(AislStream stream); + + +int +aisl_stream_set_body(AislStream stream, const char *data, int32_t size); + +#endif /* !AISL_STREAM_H */ diff --git a/lib/types.c b/lib/types.c new file mode 100644 index 0000000..a9f0b6f --- /dev/null +++ b/lib/types.c @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file types.c + * @author Ilja Kartašov + * @brief Types stringifiers module + * + * @see https://lowenware.com/aisl/ + */ + +#include + + +#if AISL_WITH_STRINGIFIERS == 1 + +__attribute__ ((visibility ("default") )) +const char * +aisl_status_to_string(AislStatus status) +{ + switch(status) { + case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR"; + case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR"; + case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR"; + case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR"; + case AISL_SUCCESS: return "AISL_SUCCESS"; + case AISL_IDLE: return "AISL_IDLE"; + } + return "UNKNOWN"; +} + + +__attribute__ ((visibility ("default") )) +const char * +aisl_event_to_string(AislEvent evt_code) +{ + switch(evt_code) { + case AISL_EVENT_SERVER_READY: return "SERVER READY"; + case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR"; + case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT"; + case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT"; + case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN"; + case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER"; + case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT"; + case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST"; + case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT"; + case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE"; + case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR"; + } + + return "UNKNOWN"; +} + +#endif diff --git a/libaisl.pc.example b/libaisl.pc.example new file mode 100644 index 0000000..f8110ce --- /dev/null +++ b/libaisl.pc.example @@ -0,0 +1,19 @@ +############################################################################### +# +# Copyright (c) 2017-2019 by Löwenware Ltd +# Please, refer LICENSE file for legal information +# +############################################################################### + +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libaisl +URL: https://lowenware.com/aisl/ +Description: Asynchronous Internet Server Library +Version: 1.0.0 +Libs: -L${libdir} -laisl +Libs.private: -lcrypto -lssl +Cflags: -I${includedir} diff --git a/library/aisl.c b/library/aisl.c deleted file mode 100644 index bae54a1..0000000 --- a/library/aisl.c +++ /dev/null @@ -1,367 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "handle.h" -#include "client.h" -#include "server.h" -#include "globals.h" -#include "stream.h" -#include "buffer.h" - - -/* Globals ------------------------------------------------------------------ */ - -#define AISL_TERMINATED 1 - -aisl_handle_t gHandle = NULL; - - -/* DEPRECATED --------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_init( ) -{ - gHandle = aisl_handle_new( AISL_MIN_CLIENTS, AISL_BUFFER_SIZE ); - - return (gHandle != NULL) ? AISL_SUCCESS : AISL_MALLOC_ERROR; -} - -/* DEPRECATED --------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_release() -{ - aisl_handle_free(gHandle); -} - -/* DEPRECATED --------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_select(const char * address, int port) -{ - return aisl_bind(gHandle, address, port, 0); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_run( int * flags ) -{ - aisl_status_t exit_code = AISL_SUCCESS; - struct timeval timeout = {0,0}; - - while( !(*flags & AISL_TERMINATED) ) - { - if ( (exit_code = aisl_run_cycle( gHandle )) == AISL_IDLE ) - { - timeout.tv_usec = 300; - timeout.tv_sec = 0; - - select(0, NULL, NULL, NULL, &timeout); - } - } - - return exit_code; -} - -/* Event calls -------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_delay( aisl_callback_t cb, uint32_t usec, void * u_data ) -{ - return aisl_set_delay(gHandle, cb, usec, u_data); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_listen( void * source, aisl_event_t e_id, aisl_callback_t cb ) -{ - return aisl_set_callback( gHandle, source, e_id, cb ); -} - -/* -------------------------------------------------------------------------- */ - - -__attribute__ ((visibility ("default") )) -bool -aisl_raise( void *source, aisl_event_t e_id, ... ) -{ - bool result; - va_list vl; - va_start(vl, e_id); - result = aisl_raise_event_vl( gHandle, source, e_id, vl); - va_end(vl); - return result; -} - -__attribute__ ((visibility ("default") )) -const char * -aisl_header_get(aisl_stream_t stream, const char *key) -{ - int i; - if (STREAM(stream)->headers) - { - for (i=0; i< STREAM(stream)->headers->count; i++) - { - if (str_cmpi(((pair_t)list_index(STREAM(stream)->headers,i))->key,key)==0) - return ((pair_t) list_index(STREAM(stream)->headers,i))->value; - } - } - - return NULL; -} - -/* response functions ------------------------------------------------------- */ - -static char * -get_response_begin(stream_t stream) -{ - char * r; - client_t cli = CLIENT(ASTREAM(stream)->client); - - r = str_printf( - "%s %d %s\r\n" - "Server: AISL\r\n" - "Connection: %s\r\n\r\n", - aisl_http_version_to_string(cli->protocol), - stream->response, - aisl_http_response_to_string(stream->response), - ((cli->flags & CLIENT_FLAG_KEEPALIVE) ? "keep-alive" : "close") - ); - - return r; -} - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_response(aisl_stream_t stream, aisl_http_response_t status_code, - const char *content_type, - uint32_t content_length) -{ - char * pch; - int l; - - - /* check if those headers were already sent */ - if (STREAM(stream)->state > STREAM_REQUEST_READY) return AISL_IDLE; - - STREAM(stream)->response = status_code; - STREAM(stream)->c_type = content_type; - STREAM(stream)->c_length = content_length; - - if ( !(pch = get_response_begin(STREAM(stream))) ) - return AISL_MALLOC_ERROR; - - l = strlen(pch); - STREAM(stream)->c_offset = l-2; - - buffer_clear(STREAM(stream)->buffer, content_length); - - l = buffer_add( STREAM(stream)->buffer, pch, l ); - free(pch); - - if (l == BUFFER_EOB) return AISL_MALLOC_ERROR; - - if (content_length) - { - if (!aisl_header_printf( stream, "Content-Length", "%u", content_length )) - return AISL_MALLOC_ERROR; - } - - if (content_type) - { - if (!aisl_header( stream, "Content-Type", content_type )) - return AISL_MALLOC_ERROR; - } - - STREAM(stream)->state = STREAM_RESPONSE_HEADER; - - return AISL_SUCCESS; -} - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_flush(aisl_stream_t stream) -{ - stream_t s = STREAM(stream); - if ( ! s->c_length ) - { - s->c_length = s->buffer->size - s->c_offset - 2; - if (!aisl_header_printf(stream, "Content-Length", "%u", s->c_length)) - return AISL_MALLOC_ERROR; - } - - /* - fprintf(stdout, "(%lu bytes)------->\n", STREAM(stream)->buffer->size); - fwrite(STREAM(stream)->buffer->data, 1, STREAM(stream)->buffer->size, stdout); - fprintf(stdout, "<------\n"); - */ - s->state = STREAM_RESPONSE_READY; - - s->flags |= STREAM_FLAG_OUTPUT_READY; - - return AISL_SUCCESS; -} - -/* header functions --------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_header(aisl_stream_t stream, const char *key, const char *value) -{ - int ret; - char * pch; - - if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL ) - { - ret = strlen(pch); - if ( buffer_insert( - STREAM(stream)->buffer, - STREAM(stream)->c_offset, - pch, - ret - ) == BUFFER_EOB ) - { - ret = -1; - } - else - STREAM(stream)->c_offset += ret; - - free(pch); - } - else - ret = -1; - - return ret; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_header_printf(aisl_stream_t stream, const char *key, - const char *f_value, ...) -{ - int ret; - - va_list arg; - va_start(arg, f_value); - - ret = aisl_header_vprintf(stream, key, f_value, arg); - - va_end(arg); - - return ret; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_header_vprintf(aisl_stream_t stream, const char *key, - const char *format, - va_list args) -{ - int ret; - char * value; - - if ( (value = str_vprintf(format, args)) != NULL ) - { - ret = aisl_header( stream, key, value ); - free(value); - } - else - ret = -1; - - return ret; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_printf(aisl_stream_t stream, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - - int result = aisl_vprintf(stream, format, arg); - - va_end(arg); - - /* No need to update length there, because vprintf do that - * - * if (STREAM(stream)->c_length_unknown) - STREAM(stream)->c_length += result; - */ - - - return result; - -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_vprintf(aisl_stream_t stream, const char *format, va_list args) -{ - int result; - char * r; - - if ( (r = str_vprintf(format, args)) != NULL) - { - result = strlen(r); - if (buffer_add(STREAM(stream)->buffer, r, result) == BUFFER_EOB) - result = -1; - - free(r); - } - else - result = -1; - - return result; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_write(aisl_stream_t stream, const char *data, int d_len) -{ - if (d_len < 0) - d_len = strlen(data); - - if (buffer_add(STREAM(stream)->buffer, data, d_len) == BUFFER_EOB) - d_len = -1; - - return d_len; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_puts(const char *str, aisl_stream_t stream) -{ - return aisl_write( stream, str, strlen(str)); -} - -/* -------------------------------------------------------------------------- */ - diff --git a/library/buffer.c b/library/buffer.c deleted file mode 100644 index a5f2076..0000000 --- a/library/buffer.c +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include -#include "buffer.h" - -/* -------------------------------------------------------------------------- */ - -buffer_t -buffer_new( size_t initial_size ) -{ - buffer_t self; - - if ( (self = calloc(1, sizeof(struct buffer))) != NULL) - { - if ( (self->size = initial_size) > 0 ) - { - if ( !(self->data = calloc( initial_size+1, sizeof(char) )) ) - { - free(self); - self = NULL; - } - } - } - - return self; -} - -/* -------------------------------------------------------------------------- */ - -void -buffer_free( buffer_t self ) -{ - if (self->data) - free(self->data); - - free(self); -} - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_add( buffer_t self, const char * data, size_t size ) -{ - size_t result = size+self->size; - - char * ptr; - - if ( (ptr = realloc(self->data, result+1)) != NULL ) - { - memcpy( &ptr[self->size], data, size ); - ptr[ result ] = 0; - self->data = ptr; - self->size = result; - } - else - result = BUFFER_EOB; - - return result; -} - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_clear( buffer_t self, size_t to_alloc ) -{ - char * data; - - self->size = 0; - - if (to_alloc) - { - if ( (data = realloc(self->data, to_alloc)) != NULL ) - { - self->data = data; - return to_alloc; - } - } - - free(self->data); - self->data = NULL; - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_shift( buffer_t self, size_t size ) -{ - size_t result; - - if (size && !(size > self->size)) - { - result = self->size - size; - memmove(self->data, &self->data[size], result); - self->size = result; - } - else - result = BUFFER_EOB; - - return result; -} - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_insert( buffer_t self, size_t offset, const char * data, size_t size ) -{ - size_t result = size + self->size; - - char * ptr; - - if ( (ptr = realloc(self->data, result+1)) != NULL ) - { - memmove( &ptr[offset+size], &ptr[offset], self->size - offset ); - memcpy( &ptr[offset], data, size ); - ptr[ result ] = 0; - self->data = ptr; - self->size = result; - } - else - result = BUFFER_EOB; - - return result; - -} - -/* -------------------------------------------------------------------------- */ diff --git a/library/buffer.h b/library/buffer.h deleted file mode 100644 index 72d81d6..0000000 --- a/library/buffer.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _AISL_BUFFER_H_ -#define _AISL_BUFFER_H_ - -#include - -/* -------------------------------------------------------------------------- */ - -struct buffer -{ - char * data; - size_t size; -}; - -typedef struct buffer * buffer_t; - -#define BUFFER_EOB ( ~0 ) - -/* -------------------------------------------------------------------------- */ - -buffer_t -buffer_new( size_t initial_size ); - -/* -------------------------------------------------------------------------- */ - -void -buffer_free( buffer_t self ); - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_insert( buffer_t self, size_t offset, const char * data, size_t size ); - -/* -------------------------------------------------------------------------- */ -size_t -buffer_add( buffer_t self, const char * data, size_t size ); - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_clear( buffer_t self, size_t to_alloc ); - -/* -------------------------------------------------------------------------- */ - -size_t -buffer_shift( buffer_t self, size_t size ); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/library/client.c b/library/client.c deleted file mode 100644 index 2f04654..0000000 --- a/library/client.c +++ /dev/null @@ -1,460 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include "client.h" -#include "stream.h" -#include "parser.h" -#include "globals.h" -#include "handle.h" - -#ifndef OUTPUT_BUFFER_SIZE -#define OUTPUT_BUFFER_SIZE 4096 -#endif - -/* -------------------------------------------------------------------------- */ - -void -client_close(client_t self) -{ - close(self->fd); - /* will provide double free - if (self->ssl) - SSL_free(self->ssl); - */ - shutdown(self->fd, SHUT_RDWR); - self->fd=-1; -} - -/* -------------------------------------------------------------------------- */ - -static bool -client_input(client_t self) -{ - int l; - parser_status_t p_status = PARSER_PENDING; - char *ptr; - buffer_t buffer = self->server->owner->buffer; - - stream_t s = list_index(self->streams, self->istream); - - - if (self->ssl) - { - if (self->flags & CLIENT_FLAG_HANDSHAKE) - { - if ( (l = SSL_accept(self->ssl)) != 1 ) - { - l = SSL_get_error(self->ssl, l); - - if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE) - return false; - - client_close(self); - fprintf(stderr, "SSL handshake fail: %s\n", ERR_error_string(l, NULL)); - return true; - } - - self->flags ^= CLIENT_FLAG_HANDSHAKE; - } - - l = SSL_read(self->ssl, buffer->data, buffer->size) ; - } - else - l = recv( self->fd, buffer->data, buffer->size, 0); - - if (l>0) - { - if( buffer_add(s->buffer, buffer->data, l) == BUFFER_EOB ) - { - client_close(self); - return true; - } - - ptr = s->buffer->data; - l = s->buffer->size; - - /* parse next data chunk */ - while ( p_status == PARSER_PENDING ) - { - switch(s->state) - { - case STREAM_REQUEST_METHOD: - p_status = parse_request_method(self, &ptr, &l); - break; - case STREAM_REQUEST_PATH: - p_status = parse_request_path(self, &ptr, &l); - break; - case STREAM_REQUEST_PROTOCOL: - p_status = parse_request_protocol(self, &ptr, &l); - break; - - case STREAM_REQUEST_HEADER_KEY: - p_status = parse_request_header_key(self, &ptr, &l); - break; - case STREAM_REQUEST_HEADER_VALUE: - p_status = parse_request_header_value(self, &ptr, &l); - break; - - case STREAM_REQUEST_CONTENT: - p_status = parse_request_content(self, &ptr, &l); - break; - - default: - p_status = PARSER_FINISHED; - /* this is error actually */ - - } - } - - if (p_status == PARSER_FAILED) - { - /* reply Bad Request here */ - client_close(self); - } - else if (l) - { - buffer_shift(s->buffer, s->buffer->size-l); /* reset buffer */ - } - /* - else - buffer_clear(s->buffer, 0);*/ - - return true; - } - else if (l<0) - { - if (self->ssl) - { - if (SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_READ) - return false; - } - else - { - if(errno == EWOULDBLOCK) - return false; - } - } - - /* both: client disconnect + on read error */ - /* todo: raise client error here */ - client_close(self); - - return true; -} - -static bool -client_output(client_t self) -{ - if (self->fd < 0) - { - fprintf(stderr, "[aisl] assertion !(client->fd<0) failed\n"); - return true; - } - - stream_t s; - int l; - - s = list_index(self->streams, self->ostream); - - /* - if (!s->c_length_unknown && s->buffer && s->buffer->len) - buffer_move(gBuffer, s->buffer); - */ - - /* while stream is not flushed, we should raise event */ - if(s->flags & STREAM_FLAG_OUTPUT_CHUNKED) - { - /* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event ) - * stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but - * buffer->size will be used to carry amount of stored bytes - * */ - size_t bsz = s->buffer->size; - - if (bsz < OUTPUT_BUFFER_SIZE) - { - if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0) - return false; - - s->buffer->size = bsz; - bsz = OUTPUT_BUFFER_SIZE; - } - - if ( (l = bsz - s->buffer->size) > OUTPUT_BUFFER_SIZE / 2 ) - aisl_raise_event( self->server->owner, s, AISL_STREAM_OUTPUT, l); - } - - if (s->buffer->size == 0) - return false; - - l = (self->ssl) ? - SSL_write(self->ssl, s->buffer->data, s->buffer->size) : - send( self->fd, s->buffer->data, s->buffer->size, 0); - - if (l > 0) - { - buffer_shift(s->buffer, l); - if (s->state == STREAM_RESPONSE_READY && /* flushed */ - s->buffer->size == 0) /* all sent */ - { - buffer_clear(s->buffer, 0); - - /* data has been sent */ - /* - if (self->protocol == AISL_HTTP_2_0) - { - - } - else*/ - if (self->flags & CLIENT_FLAG_KEEPALIVE) - { - list_remove(self->streams, s); - stream_free(s); - - s = stream_new((struct sockaddr_in *) self, self->next_id++, STREAM_REQUEST_METHOD ); - list_append(self->streams, s); - } - else - { - client_close(self); - } - } - return true; - } - - /* l < 0 */ - if (self->ssl) - { - if ( SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_WRITE ) - return false; - } - else - { - if (errno == EWOULDBLOCK) - return false; - } - - client_close(self); - - return true; -} - -bool -client_touch(client_t self) -{ - bool result = false; - stream_t s; - - /* input */ - s = list_index(self->streams, self->istream); - - if ((self->protocol==AISL_HTTP_2_0 || s->statestreams, self->ostream); - - if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) ) - result = client_output(self); - - /* update timestamp */ - if (result) - self->timestamp = time(NULL); - - return result; -} - - -/* constructor -------------------------------------------------------------- */ - -static client_t -client_new( int fd, struct sockaddr_in * addr ) -{ - client_t self; - stream_t stream; - - if ( !(self = calloc(1, sizeof(struct client))) ) - goto finally; - - memcpy(&self->address, addr, sizeof(struct sockaddr_in)); - - self->fd = fd; - self->next_id = 2; - /* - self->istream = 0; - self->ostream = 0; - * UTPUT - */ - self->protocol = AISL_HTTP_1_0; - self->timestamp = time(NULL); - self->flags = CLIENT_FLAG_KEEPALIVE | CLIENT_FLAG_HANDSHAKE; - - if ( !(self->streams = list_new(AISL_MIN_STREAMS)) ) - goto except; - - if ( !(stream = stream_new((struct sockaddr_in *)self, 0, STREAM_REQUEST_METHOD)) ) - goto e_stream; - - if (list_append(self->streams, stream) == -1) - goto e_append; - - goto finally; - -e_append: - stream_free(stream); - -e_stream: - list_free(self->streams, NULL); - -except: - free(self); - self=NULL; - -finally: - return self; -} - -aisl_status_t -client_accept(client_t * p_self, server_t server) -{ - aisl_status_t result; - const char * e_detail = NULL; - int fd; - struct sockaddr_in addr; - socklen_t len = sizeof(struct sockaddr_in); - SSL * ssl = NULL; - SSL_CTX * ssl_ctx; - - *p_self = NULL; - - if ( (fd = accept(server->fd, (struct sockaddr *) &addr, &len)) < 0 ) - { - if (errno != EWOULDBLOCK) - { - result = AISL_SYSCALL_ERROR; - e_detail = strerror(errno); - goto raise; - } - - result = AISL_IDLE; - goto finally; - } - - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - result = AISL_SYSCALL_ERROR; - e_detail = strerror(errno); - goto raise; - } - - flags |= O_NONBLOCK; - - if (fcntl(fd, F_SETFL, flags) != 0) - { - result = AISL_SYSCALL_ERROR; - e_detail = strerror(errno); - goto raise; - } - - if (server->flags & AISL_FLAG_SSL) - { - if ( !(ssl_ctx = aisl_get_ssl_ctx( server->owner, NULL )) ) - goto except; - - if ( !(ssl = SSL_new(ssl_ctx)) ) - { - e_detail = "SSL_new"; - result = AISL_EXTCALL_ERROR; - goto except; - } - - SSL_set_fd(ssl, fd); - - } - else - ssl = NULL; - - if ( !(*p_self = client_new(fd, &addr)) ) - { - result = AISL_MALLOC_ERROR; - e_detail = "client_t"; - goto raise; - } - - (*p_self)->server = server; - (*p_self)->ssl = ssl; - result = AISL_SUCCESS; - - goto finally; - -except: - close(fd); - if (ssl) - SSL_free(ssl); - -raise: - aisl_raise_event( - server->owner, - server, - AISL_SERVER_ERROR, - server->flags, - e_detail - ); - -finally: - return result; -} - -/* destructor --------------------------------------------------------------- */ - -void -client_free(client_t self) -{ - if (self->fd > -1) - close(self->fd); - - if (self->ssl) - SSL_free(self->ssl); - - list_free(self->streams, (list_destructor_t)stream_free); - - aisl_raise_event( - self->server->owner, - self->server, - AISL_CLIENT_DISCONNECT, - self - ); - - free(self); -} - -/* check if communication time with client is expired ----------------------- */ - -bool -client_is_timeout(client_t self) -{ - bool result = false; - stream_t s; - if (self->protocol == AISL_HTTP_2_0) - { - - } - else - { - s = list_index(self->streams, self->istream); - if ( (s->state < STREAM_REQUEST_READY) && /* still waiting for data */ - (time(NULL)-self->timestamp > AISL_MAX_CLIENT_SILENCE) ) result=true; - } - - if (result) - client_close(self); - - return result; -} - -/* -------------------------------------------------------------------------- */ diff --git a/library/client.h b/library/client.h deleted file mode 100644 index 44912ad..0000000 --- a/library/client.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _AISL_CLIENT_H_ -#define _AISL_CLIENT_H_ - -#include -#include - -#include -#include -#include - -#include -#include - -#include "server.h" - -#define CLIENT_FLAG_KEEPALIVE (1<<0) -#define CLIENT_FLAG_HANDSHAKE (1<<1) - - -struct client -{ - struct sockaddr_in address; - server_t server; - int fd; - - int next_id; /* server id generator (even, starts from 2) */ - int istream; /* input stream id */ - int ostream; /* output stream id */ - list_t streams; - SSL * ssl; - - time_t timestamp; - aisl_http_version_t protocol; - int flags; -}; - -typedef struct client * client_t; - -#define CLIENT(x) ((client_t)x) - -/* constructor -------------------------------------------------------------- */ - -aisl_status_t -client_accept(client_t * self, server_t server); - -/* destructor --------------------------------------------------------------- */ - -void -client_free(client_t self); - -/* all regular client routines. return true if something happened ----------- */ - -bool -client_touch(client_t self); - -/* check if communication time with client is expired ----------------------- */ - -bool -client_is_timeout(client_t self); - -/* -------------------------------------------------------------------------- */ - -void -client_close(client_t self); - -/* -------------------------------------------------------------------------- */ -#endif diff --git a/library/event.c b/library/event.c deleted file mode 100644 index 425852a..0000000 --- a/library/event.c +++ /dev/null @@ -1,28 +0,0 @@ -#include - -__attribute__ ((visibility ("default") )) -const char * -aisl_event_get_text( aisl_event_t e_id ) -{ - switch(e_id) - { - case AISL_SERVER_OPEN: return "AISL_SERVER_OPEN"; - case AISL_SERVER_ERROR: return "AISL_SERVER_ERROR"; - - case AISL_CLIENT_CONNECT: return "AISL_CLIENT_CONNECT"; - case AISL_CLIENT_DISCONNECT: return "AISL_CLIENT_DISCONNECT"; - case AISL_CLIENT_TIMEOUT: return "AISL_CLIENT_TIMEOUT"; - - case AISL_STREAM_OPEN: return "AISL_STREAM_OPEN"; - case AISL_STREAM_INPUT: return "AISL_STREAM_INPUT"; - case AISL_STREAM_REQUEST: return "AISL_STREAM_REQUEST"; - case AISL_STREAM_OUTPUT: return "AISL_STREAM_OUTPUT"; - case AISL_STREAM_CLOSE: return "AISL_STREAM_CLOSE"; - case AISL_STREAM_ERROR: return "AISL_STREAM_ERROR"; - default: - break; - } - return "AISL_CUSTOM_EVENT"; -} - - diff --git a/library/globals.h b/library/globals.h deleted file mode 100644 index f5abe2a..0000000 --- a/library/globals.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _AISL_GLOBALS_H_ -#define _AISL_GLOBALS_H_ - -#pragma GCC diagnostic ignored "-Wuninitialized" - -#include - -/* MACOS FIX AND OTHER OS */ -#ifndef SOCK_NONBLOCK -#define SOCK_NONBLOCK 0 -#endif - -#ifndef AISL_MIN_SERVERS -#define AISL_MIN_SERVERS 1 -#endif - -#ifndef AISL_MIN_STREAMS -#define AISL_MIN_STREAMS 1 -#endif - -#ifndef AISL_MIN_CLIENTS -#define AISL_MIN_CLIENTS 32 -#endif - -#ifndef AISL_MIN_LISTENERS -#define AISL_MIN_LISTENERS 8 -#endif - -#ifndef AISL_MIN_DELAYS -#define AISL_MIN_DELAYS 8 -#endif - -#ifndef AISL_BUFFER_SIZE -#define AISL_BUFFER_SIZE 4096 -#endif - -#ifndef AISL_MIN_HEADERS -#define AISL_MIN_HEADERS 8 -#endif - -#ifndef AISL_MAX_CLIENT_SILENCE -#define AISL_MAX_CLIENT_SILENCE 10 -#endif - - -extern aisl_handle_t gHandle; - -#endif diff --git a/library/handle.c b/library/handle.c deleted file mode 100644 index 4f4927f..0000000 --- a/library/handle.c +++ /dev/null @@ -1,802 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include "stream.h" -#include "server.h" -#include "client.h" -#include "globals.h" -#include "handle.h" - -/* -------------------------------------------------------------------------- */ - -struct listener -{ - void *source; - aisl_callback_t cb; - aisl_event_t e_id; -}; - -typedef struct listener * listener_t; - -/* -------------------------------------------------------------------------- */ - -struct delay -{ - struct timespec next; - uint32_t delay; - void *u_data; - aisl_callback_t cb; -}; - -typedef struct delay * delay_t; - -/* -------------------------------------------------------------------------- */ - -struct crypter -{ - char * keyFile; - char * crtFile; - char * srvName; - SSL_CTX * sslCtx; -}; - -typedef struct crypter * crypter_t; - - - -/* -------------------------------------------------------------------------- */ -static int gHandles = 0; - -/* -------------------------------------------------------------------------- */ - -static listener_t -listener_new( void * source, aisl_event_t e_id, aisl_callback_t cb) -{ - listener_t self = malloc(sizeof(struct listener)); - if (self) - { - self->source = source; - self->e_id = e_id; - self->cb = cb; - } - - return self; -} - -/* -------------------------------------------------------------------------- */ - -void -aisl_remove_listeners_for( aisl_handle_t self, void * source ) -{ - int i=self->listeners->count-1; - while ( !(i < 0) ) - { - listener_t listener = list_index(self->listeners, i); - if ( listener->source == source ) - { - free(listener); - list_remove_index(self->listeners, i); - } - - i--; - } -} - -/* -------------------------------------------------------------------------- */ - -static void -crypter_free( crypter_t self ) -{ - if (self->srvName) - free(self->srvName); - - if (self->keyFile) - { - free(self->keyFile); - SSL_CTX_free(self->sslCtx); - } - - if (self->crtFile) - free(self->crtFile); - - free(self); -} - -/* -------------------------------------------------------------------------- */ - -static crypter_t -crypter_new( const char * server_name, - const char * key_file, - const char * crt_file ) -{ - crypter_t self; - - if ( (self=calloc(1, sizeof(struct crypter))) != NULL ) - { - if (!(self->srvName = str_copy( server_name ? server_name : "*" ))) - goto release; - - if ( key_file && !(self->keyFile = str_copy(key_file))) - goto release; - - if ( crt_file && !(self->crtFile = str_copy(crt_file))) - goto release; - - } - - goto finally; - - -release: - crypter_free(self); - self = NULL; - -finally: - return self; -} - -/* -------------------------------------------------------------------------- */ - -static bool -delay_is_expired(delay_t self) -{ - if (!self->delay) return true; - - struct timespec tv; - clock_gettime(CLOCK_REALTIME, &tv); - - /* - printf("> %ld.%ld & %ld.%ld\n", self->next.tv_sec, self->next.tv_nsec, - tv.tv_sec, tv.tv_nsec); - */ - - if (tv.tv_sec > self->next.tv_sec) - return true; - else if (tv.tv_sec == self->next.tv_sec && tv.tv_nsec >= self->next.tv_nsec) - return true; - - return false; -} - -/* -------------------------------------------------------------------------- */ - -static void -delay_reset(delay_t self) -{ - clock_gettime(CLOCK_REALTIME, &self->next); - - self->next.tv_sec += self->delay / 1000; - self->next.tv_nsec += (self->delay % 1000) * 1000000; - - self->next.tv_sec += self->next.tv_nsec / 1000000000; - self->next.tv_nsec = self->next.tv_nsec % 1000000000; - -} - -/* -------------------------------------------------------------------------- */ - -static delay_t -delay_new(aisl_callback_t cb, uint32_t delay, void *u_data) -{ - delay_t self = malloc(sizeof(struct delay)); - - if (self) - { - self->cb = cb; - self->u_data = u_data; - self->delay = delay; - - delay_reset(self); - } - - return self; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_handle_t -aisl_handle_new( size_t min_clients, size_t buffer_size ) -{ - aisl_handle_t self; - - if ((gHandles++) == 0) - { - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - } - - if ( !(self = calloc(1, sizeof(struct aisl_handle))) ) - goto finally; - - if ( !(self->servers = list_new(1)) ) - goto release; - - if ( !(self->clients = list_new(min_clients)) ) - goto release; - - if ( !(self->listeners = list_new(16)) ) - goto release; - - if ( !(self->buffer = buffer_new(buffer_size)) ) - goto release; - - if ( !(self->crypters = list_new(0)) ) - goto release; - - if ( !(self->delays = list_new(0)) ) - goto release; - - goto finally; - -release: - aisl_handle_free(self); - self = NULL; - -finally: - return self; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_handle_t -aisl_handle_from_stream( aisl_stream_t s ) -{ - return ((client_t)s->client)->server->owner; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_handle_free( aisl_handle_t self ) -{ - if ((--gHandles) == 0) - { - EVP_cleanup(); - } - - if (self->clients) - list_free(self->clients, (list_destructor_t) client_free ); - - if (self->servers) - list_free(self->servers, (list_destructor_t) server_free ); - - if (self->listeners) - list_free(self->listeners, free); - - if (self->delays) - list_free(self->delays, free); - - if (self->buffer) - buffer_free(self->buffer); - - if (self->crypters) - list_free(self->crypters, (list_destructor_t) crypter_free ); - - if (self->lastError) - free(self->lastError); - - free(self); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_handle_set_error( aisl_handle_t self, const char * err_msg ) -{ - if (self->lastError) - free(self->lastError); - - self->lastError = str_copy(err_msg); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_bind( aisl_handle_t self, const char * address, int port, int flags ) -{ - server_t server; - - if ( !(server = server_new(address, port)) ) - goto finally; - - server->owner = self; - server->flags |= (flags & AISL_FLAG_SSL); - - if ( list_append(self->servers, server) == -1 ) - goto release; - - goto finally; - -release: - server_free(server); - server = NULL; - -finally: - return server ? AISL_SUCCESS : AISL_MALLOC_ERROR; -} - -/* -------------------------------------------------------------------------- */ - -static int -get_ssl_context( SSL * ssl, int * ptr, void * handle ) -{ - const char * server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - - SSL_CTX * ctx = aisl_get_ssl_ctx( (aisl_handle_t) handle, server_name ); - - if (ctx) - { - SSL_set_SSL_CTX(ssl, ctx); - } - - return SSL_TLSEXT_ERR_OK; -} - -/* -------------------------------------------------------------------------- */ - -static SSL_CTX * -create_ssl_context( aisl_handle_t self, - const char * key_file, - const char * crt_file ) -{ - const SSL_METHOD * method; - SSL_CTX * ctx; - - method = SSLv23_server_method(); - - if ( !(ctx = SSL_CTX_new(method)) ) - goto except; - - SSL_CTX_set_ecdh_auto(ctx, 1); - - SSL_CTX_set_tlsext_servername_callback( ctx, get_ssl_context ); - SSL_CTX_set_tlsext_servername_arg( ctx, (void *) self ); - - if (!(SSL_CTX_use_certificate_file(ctx, crt_file, SSL_FILETYPE_PEM) > 0)) - goto release; - - if (!(SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) > 0)) - goto release; - - goto finally; - -release: - SSL_CTX_free(ctx); - ctx = NULL; - -except: - aisl_handle_set_error( self, ERR_error_string(ERR_get_error(),NULL) ); - -finally: - return ctx; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_set_ssl( aisl_handle_t self, const char * server_name, - const char * key_file, - const char * crt_file ) -{ - SSL_CTX * ssl_ctx = NULL; - int i; - crypter_t crypter; - - /* lookup for existing contexts */ - for (i=0; icrypters->count; i++) - { - crypter = list_index(self->crypters, i); - if (crypter->keyFile && strcmp(crypter->keyFile, key_file)==0 && - crypter->crtFile && strcmp(crypter->crtFile, crt_file)==0 ) - { - ssl_ctx = crypter->sslCtx; - key_file = NULL; - crt_file = NULL; - break; - } - } - - if (! (crypter = crypter_new(server_name, key_file, crt_file)) ) - { - return AISL_MALLOC_ERROR; - } - - if (! ssl_ctx) - { - if (!(ssl_ctx = create_ssl_context(self, key_file, crt_file))) - { - crypter_free(crypter); - return AISL_EXTCALL_ERROR; - } - } - - crypter->sslCtx = ssl_ctx; - - if (list_append(self->crypters, crypter)==-1) - { - crypter_free(crypter); - return AISL_MALLOC_ERROR; - } - - return AISL_SUCCESS; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -SSL_CTX * -aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name ) -{ - int i; - crypter_t crypter; - - for (i=0; icrypters->count; i++) - { - crypter = list_index(self->crypters, i); - if (server_name) - { - if (strcmp(crypter->srvName, server_name)!=0) - continue; - } - - return crypter->sslCtx; - } - - return NULL; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_set_callback( aisl_handle_t self, - void * source, - aisl_event_t e_id, - aisl_callback_t cb ) -{ - listener_t listener; - - if (! (listener = listener_new(source, e_id, cb)) ) - return AISL_MALLOC_ERROR; - - if (list_append(self->listeners, listener) == -1) - { - free(listener); - return AISL_MALLOC_ERROR; - } - - if (e_id == AISL_STREAM_OUTPUT) /* subscribtion for chunked output */ - { - if (source) - { - ( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED; - } - } - else if (e_id == AISL_STREAM_OPEN) - { - self->flags |= AISL_HANDLE_HAS_STREAM_LISTENERS; - } - - return AISL_SUCCESS; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_set_delay( aisl_handle_t self, - aisl_callback_t cb, - uint32_t usec, - void * u_data ) -{ - delay_t delay = delay_new(cb, usec, u_data); - if (!delay) - return AISL_MALLOC_ERROR; - - if (list_append(self->delays, delay) == -1) - { - free(delay); - return AISL_MALLOC_ERROR; - } - - return AISL_SUCCESS; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -bool -aisl_raise_event( aisl_handle_t self, - void * source, - aisl_event_t e_id, - ... ) -{ - va_list vl; - bool result; - - va_start(vl, e_id); - result = aisl_raise_event_vl(self, source, e_id, vl); - va_end(vl); - - return result; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -bool -aisl_raise_event_vl( aisl_handle_t self, - void * source, - aisl_event_t e_id, - va_list vl ) -{ - int i, - i_val; - listener_t lst; - bool res = false; - - char * c_ptr, - * c_ptr2; - - for(i=self->listeners->count-1; i>=0; i--) - { - lst = list_index(self->listeners, i); - - /*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/ - if (lst->e_id == e_id && (source == lst->source || lst->source == NULL)) - { - /* - if (e_id == AISL_STREAM_HEADER) - fprintf(stderr,"FOUND HANDLER %d\n", i); - */ - - /*printf(" catch\n");*/ - switch(e_id) - { - /* server events */ - case AISL_SERVER_OPEN: - i_val = va_arg(vl, aisl_status_t); - res = ((aisl_server_open_t) lst->cb)( source, i_val ); - break; - case AISL_SERVER_ERROR: - i_val = va_arg(vl, aisl_status_t); - c_ptr = va_arg(vl, char *); - res = ((aisl_server_error_t) lst->cb)( source, i_val, c_ptr ); - break; - - /* client events */ - case AISL_CLIENT_CONNECT: - res = ((aisl_client_connect_t) lst->cb)(source, va_arg(vl, void *)); - break; - case AISL_CLIENT_DISCONNECT: - res = ((aisl_client_disconnect_t) lst->cb)(source, va_arg(vl, void*)); - aisl_remove_listeners_for( self, source ); - break; - case AISL_CLIENT_TIMEOUT: - res = ((aisl_client_timeout_t) lst->cb)(source, va_arg(vl, void *)); - break; - - /* request events */ - case AISL_STREAM_OPEN: - i_val = va_arg(vl, int); - c_ptr = va_arg(vl, char *); - c_ptr2 = va_arg(vl, char *); - res = ((aisl_stream_open_t) lst->cb)(source, i_val, c_ptr, c_ptr2); - break; - - case AISL_STREAM_HEADER: - c_ptr = va_arg(vl, char *); - c_ptr2 = va_arg(vl, char *); - res = ((aisl_stream_header_t) lst->cb)(source, c_ptr, c_ptr2); - break; - - - case AISL_STREAM_INPUT: - /*printf("AISL> raise AISL_STREAM_INPUT\n");*/ - c_ptr = va_arg(vl, char *); - i_val = va_arg(vl, int ); - res = ((aisl_stream_input_t) lst->cb)(source, c_ptr, i_val); - break; - case AISL_STREAM_REQUEST: - /*printf("AISL> raise AISL_STREAM_REQUEST\n");*/ - buffer_clear( STREAM(source)->buffer, 0); - res = ((aisl_stream_request_t) lst->cb)(source); - break; - - - case AISL_STREAM_ERROR: - res = ((aisl_stream_error_t) lst->cb)( source, va_arg(vl, char *)); - break; - - /* response events */ - case AISL_STREAM_OUTPUT: - res = ((aisl_stream_output_t)lst->cb)( - source, - va_arg(vl, uint32_t) - ); - break; - case AISL_STREAM_CLOSE: - res = ((aisl_stream_close_t)lst->cb)( source ); - aisl_remove_listeners_for( self, source ); - ((aisl_stream_t) source)->u_ptr=NULL; - break; - - default: - res = ((aisl_custom_event_t) lst->cb)(source, vl); - } - if (res) break; - } - } - - return res; -} - - - -/* -------------------------------------------------------------------------- */ -/* -aisl_status_t -aisl_run( int * flags ) -{ - aisl_status_t exit_code = AISL_SUCCESS; - struct timeval timeout; - - while( !(*flags & (1<<0)) ) - { - exit_code = aisl_run_cycle( gHandle ); - - if (exit_code == AISL_IDLE) - { - timeout.tv_usec = 300; - timeout.tv_sec = 0; - - select(0, NULL, NULL, NULL, &timeout); - } - } - - return exit_code; -} -*/ -/* -------------------------------------------------------------------------- */ - -#define STAGE_SERVER 0 -#define STAGE_CLIENT 1 -#define STAGE_DELAY 2 - -__attribute__ ((visibility ("default") )) -aisl_status_t -aisl_run_cycle( aisl_handle_t self ) -{ - int max = self->servers->count+self->clients->count+self->delays->count, - cnt = 0; - - - switch (self->stage) - { - case STAGE_SERVER: - while (self->iterator < self->servers->count ) - { - server_t srv = (server_t)list_index(self->servers, self->iterator++); - if ( server_touch(srv) != AISL_IDLE ) - return AISL_SUCCESS; - - if ( ! (++cnt < max) ) return AISL_IDLE; - } - if ( ! (self->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) ) - return AISL_IDLE; - - self->iterator = 0; - self->stage++; - - - case STAGE_CLIENT: - while (self->iterator < self->clients->count ) - { - int i = self->iterator++; - client_t cli = list_index(self->clients, i); - bool r = client_touch( cli ); - - if (client_is_timeout( cli ) ) - aisl_raise_event( self, cli->server, AISL_CLIENT_TIMEOUT, cli ); - - if ( cli->fd == -1 ) - { - client_free( cli ); - list_remove_index(self->clients, i); - } - - if (r) return AISL_SUCCESS; - - if ( ! (++cnt < max) ) return AISL_IDLE; - } - self->iterator = 0; - self->stage++; - - case STAGE_DELAY: - while (self->iterator < self->delays->count ) - { - int i = self->iterator++; - delay_t dly = list_index(self->delays, i); - - if( delay_is_expired(dly) ) - { - if ( ((aisl_delay_timeout_t) dly->cb)(dly->u_data)) - { - delay_reset(dly); - } - else - list_remove_index(self->delays, i); - - return AISL_SUCCESS; - } - - if ( ! (++cnt < max) ) return AISL_IDLE; - } - self->iterator = 0; - self->stage = 0; - } - - return AISL_IDLE; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_handle_get_error( aisl_handle_t self ) -{ - return self->lastError; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -int -aisl_sleep( aisl_handle_t self, unsigned long usec ) -{ - int maxfd=0; - size_t i; - struct timeval timeout = {0,usec}; - - fd_set fs; - FD_ZERO (&fs); - - for (i=0; iservers->count; i++) - { - server_t s = list_index(self->servers, i); - if (s->fd != -1) - { - FD_SET(s->fd, &fs); - if (s->fd > maxfd) maxfd = s->fd; - } - } - - - for (i=0; iclients->count; i++) - { - client_t c = list_index(self->clients, i); - if (c->fd != -1) - { - FD_SET(c->fd, &fs); - if (c->fd > maxfd) maxfd = c->fd; - } - } - - return select(maxfd+1, &fs, NULL, NULL, &timeout); - -} - - - - diff --git a/library/handle.h b/library/handle.h deleted file mode 100644 index 9b3048b..0000000 --- a/library/handle.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _AISL_HANDLE_H__ -#define _AISL_HANDLE_H__ - -#include -#include - -#include - -#include -#include -#include "buffer.h" - -#define AISL_HANDLE_HAS_STREAM_LISTENERS (1<<8) - -/* -------------------------------------------------------------------------- */ - -struct aisl_handle -{ - list_t servers; - list_t clients; - list_t delays; /* deprecated */ - list_t listeners; - list_t crypters; - buffer_t buffer; - char * lastError; - int iterator; - int stage; - int flags; -}; - - -/* -------------------------------------------------------------------------- */ - -aisl_status_t -aisl_set_delay( aisl_handle_t self, - aisl_callback_t cb, - uint32_t usec, - void * u_data ); - -/* -------------------------------------------------------------------------- */ - -bool -aisl_raise_event_vl( aisl_handle_t self, - void * source, - aisl_event_t e_id, - va_list vl ); - -/* -------------------------------------------------------------------------- */ - - -SSL_CTX * -aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name ); - -/* -------------------------------------------------------------------------- */ - -void -aisl_remove_listeners_for( aisl_handle_t self, void * source ); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/library/http.c b/library/http.c deleted file mode 100644 index 300ccaa..0000000 --- a/library/http.c +++ /dev/null @@ -1,112 +0,0 @@ -#include - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_http_version_to_string(aisl_http_version_t version) -{ - switch (version) - { - case AISL_HTTP_1_0: return "HTTP/1.0"; - case AISL_HTTP_1_1: return "HTTP/1.1"; - case AISL_HTTP_2_0: return "HTTP/2.0"; - } - return ""; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_http_response_to_string(aisl_http_response_t code) -{ - switch (code) - { - /* most common for faster behavior */ - case AISL_HTTP_OK: return "OK"; - case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; - - /* informational */ - case AISL_HTTP_CONTINUE: return "Continue"; - case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols"; - /* Successful */ - case AISL_HTTP_CREATED: return "Created"; - case AISL_HTTP_ACCEPTED: return "Accepted"; - case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information"; - case AISL_HTTP_NO_CONTENT: return "No Content"; - case AISL_HTTP_RESET_CONTENT: return "Reset Content"; - case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content"; - /* redirection */ - case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices"; - case AISL_HTTP_FOUND: return "Found"; - case AISL_HTTP_SEE_OTHER: return "See other"; - case AISL_HTTP_NOT_MODIFIED: return "Not Modified"; - case AISL_HTTP_USE_PROXY: return "Use Proxy"; - case AISL_HTTP_UNUSED: return "(unused)"; - case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect"; - /* client error */ - case AISL_HTTP_BAD_REQUEST: return "Bad Request"; - case AISL_HTTP_UNAUTHORIZED: return "Unauthorized"; - case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required"; - case AISL_HTTP_FORBIDDEN: return "Forbidden"; - case AISL_HTTP_NOT_FOUND: return "Not Found"; - case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed"; - case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable"; - case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required"; - case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout"; - case AISL_HTTP_CONFLICT: return "Conflict"; - case AISL_HTTP_GONE: return "Gone"; - case AISL_HTTP_LENGTH_REQUIRED: return "Length Required"; - case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed"; - case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large"; - case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long"; - case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; - case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable"; - case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed"; - /* server error */ - case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; - case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented"; - case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway"; - case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable"; - case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout"; - case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported"; - - } - return ""; - -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_http_secure_to_string( int is_secure ) -{ - return (is_secure ? "HTTPS" : "HTTP"); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_http_method_to_string( aisl_http_method_t method ) -{ - switch(method) - { - case AISL_HTTP_GET: return "GET"; - case AISL_HTTP_PUT: return "PUT"; - case AISL_HTTP_POST: return "POST"; - case AISL_HTTP_HEAD: return "HEAD"; - case AISL_HTTP_TRACE: return "TRACE"; - case AISL_HTTP_DELETE: return "DELETE"; - case AISL_HTTP_OPTIONS: return "OPTIONS"; - case AISL_HTTP_CONNECT: return "CONNECT"; - - case AISL_HTTP_PRI: return "PRI"; - } - - return ""; -} - -/* -------------------------------------------------------------------------- */ diff --git a/library/parser.c b/library/parser.c deleted file mode 100644 index 78b4372..0000000 --- a/library/parser.c +++ /dev/null @@ -1,490 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include "parser.h" -#include "globals.h" -#include "stream.h" - -/* length(multipart/form-data; boundary=) = 30 */ -#define B_OFFSET 30 - -/* common HTTP headers */ -static const char cCookie[] = "Cookie"; -static const char cContentType[] = "Content-Type"; -static const char cContentLength[] = "Content-Length"; -/* -static const char cConnection[] = "Connection"; -static const char cHost[] = "Host"; -static const char cUserAgent[] = "User-Agent"; -static const char cAccept[] = "Accept"; -static const char cAcceptLanguage[] = "Accept-Language"; -static const char cAcceptEncoding[] = "Accept-Encoding"; -*/ - -#define CLI_STREAM(x) ( ((stream_t)list_index(x->streams,x->istream)) ) -/* -static void -debug(const char * label, char * buffer, int len) -{ - printf("<<< %s [", label); - fwrite(buffer, 1, len, stdout); - printf("]\n"); -} -*/ - -static pair_t -pair_new(const char * key, int length) -{ - pair_t p = calloc(1, sizeof(struct pair)); - if (p) - { - p->key = str_ncopy(key, length); - if (!p->key) - { - free(p); - p = NULL; - } - } - - return p; -} - - -/* HTTP METHOD -------------------------------------------------------------- */ - -parser_status_t -parse_request_method(client_t cli, char ** b_ptr, int *b_len) -{ - char * cur = memchr(*b_ptr, ' ', *b_len); - - if (!cur) - { - if (*b_len < 8) - return PARSER_HUNGRY; - else - return PARSER_FAILED; - } - - int l = (int) (cur - *b_ptr); - - stream_t s = list_index(cli->streams, cli->istream); - - switch( l ) - { - case 3: - if (strncmp(*b_ptr, "GET", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_GET; - else if (strncmp(*b_ptr, "PRI", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_PRI; - else if (strncmp(*b_ptr, "PUT", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_PUT; - else - return PARSER_FAILED; - break; - case 4: - if (strncmp(*b_ptr, "POST", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_POST; - else if (strncmp(*b_ptr, "HEAD", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_HEAD; - else - return PARSER_FAILED; - break; - case 5: - if (strncmp(*b_ptr, "TRACE", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_TRACE; - else - return PARSER_FAILED; - break; - case 6: - if (strncmp(*b_ptr, "DELETE", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_DELETE; - else - return PARSER_FAILED; - break; - case 7: - if (strncmp(*b_ptr, "OPTIONS", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_OPTIONS; - else if (strncmp(*b_ptr, "CONNECT", l)==0) - ASTREAM(s)->request_method = AISL_HTTP_CONNECT; - else - return PARSER_FAILED; - break; - default: - return PARSER_FAILED; - } - - *b_ptr += ++l; - *b_len -= l; /* count method + space character */ - - s->state = STREAM_REQUEST_PATH; - - return PARSER_PENDING; -} - -/* HTTP REQUEST_URI and HOST ------------------------------------------------ */ -static void -str_to_lower( char * src ) -{ - while (*src) - { - *src = tolower(*src); - src++; - } -} - -parser_status_t -parse_request_path(client_t cli, char ** b_ptr, int *b_len) -{ - parser_status_t result = PARSER_PENDING; - stream_t s = list_index(cli->streams, cli->istream); - - int i; - char * host = NULL, - * path = NULL, - * query = NULL; - - - for ( i=0; i<*b_len; i++) - { - switch( (*b_ptr)[i] ) - { - case ':': - if (host) /* if host is set, we parse host and it could not contain : */ - { - result = PARSER_FAILED; - break; - } - else /* could be protocol separator */ - { - if (i==5) - { - if (*b_len > 7 && strncmp(*b_ptr, "http://", 7)==0 ) /* protocol defined */ - { - host = &(*b_ptr)[i+3]; - i+=2; - continue; - } - - result = PARSER_FAILED; /* something is wrong */ - break; - } - } - - continue; - case '?': - query = (*b_ptr) +i; - continue; - - case ' ': - if (! path) path = *b_ptr; - if (query) - { - ASTREAM(s)->path = str_ncopy(path, (uint32_t) (query-path)); - query++; - ASTREAM(s)->query = str_ncopy(query, (uint32_t)(&(*b_ptr)[i]-query)); - } - else - ASTREAM(s)->path = str_ncopy(path, (uint32_t) (&(*b_ptr)[i]-path)); - - *b_len -= ++i; - *b_ptr += i; - - s->state = STREAM_REQUEST_PROTOCOL; - - return PARSER_PENDING; - break; - case '/': - if (host) - { - /* debug(" > host", host, (int) (&(*b_ptr)[i] - host)); */ - pair_t p = malloc(sizeof(struct pair)); - if (p) - { - p->key = str_copy("host"); - p->value = str_ncopy(host, (uint32_t) (&(*b_ptr)[i] - host)); - } - s->headers = list_new(AISL_MIN_HEADERS); - - list_append(s->headers, p); - host = NULL; - - path = &(*b_ptr)[i]; - } - default: - continue; - } - break; - - } - - if (result == PARSER_PENDING) /* end space was not found */ - result = PARSER_HUNGRY; - - /* - if (result == PARSER_HUNGRY && *b_len == gBuffer->size) - result = PARSER_FAILED;*/ /* buffer is overloaded */ - - return result; -} - -/* HTTP VERSION ------------------------------------------------------------- */ - -parser_status_t -parse_request_protocol(client_t cli, char ** b_ptr, int *b_len) -{ - stream_t stream = CLI_STREAM(cli); - /* HTTP/X.X = 8 characters minimal */ - - if (*b_len < 8) return PARSER_HUNGRY; - - char * ptr = memchr(*b_ptr, '\n', *b_len); - - if (!ptr) return PARSER_HUNGRY; - - int l = (int) (ptr - *b_ptr); - - if (strncmp(*b_ptr, "HTTP/", 5)==0) - { - if (strncmp(&(*b_ptr)[5], "2.0", 3)==0) cli->protocol = AISL_HTTP_2_0; else - if (strncmp(&(*b_ptr)[5], "1.1", 3)==0) cli->protocol = AISL_HTTP_1_1; else - if (strncmp(&(*b_ptr)[5], "1.0", 3)==0) cli->protocol = AISL_HTTP_1_0; else - return PARSER_FAILED; - - if ( (l==10 && *b_ptr[8]=='\r') || (l==9) ) - { - /*r->version = str_ncopy(*b_ptr, 8); */ - - *b_ptr += ++l; - *b_len -=l; - - stream->state=STREAM_REQUEST_HEADER_KEY; - - - - aisl_raise_event( - cli->server->owner, - stream, - AISL_STREAM_OPEN, - ASTREAM(stream)->request_method, - ASTREAM(stream)->path, - ASTREAM(stream)->query - ); - - if (!stream->headers) - stream->headers = list_new(AISL_MIN_HEADERS); - else if (stream->headers->count == 1) - { - /* raise event for Host header */ - pair_t p = list_index(stream->headers, 0); - - aisl_raise_event( - cli->server->owner, - stream, - AISL_STREAM_HEADER, - p->key, p->value - ); - - } - - - return PARSER_PENDING; - } - } - - return PARSER_FAILED; -} - -/* HTTP HEADER KEY ---------------------------------------------------------- */ - -parser_status_t -parse_request_header_key(client_t cli, char ** b_ptr, int *b_len) -{ - stream_t stream = CLI_STREAM(cli); - int l; - - /* check end of headers */ - switch (*b_ptr[0]) - { - case '\r': - if (*b_len>1) - { - if( strncmp(*b_ptr, "\r\n", 2)==0 ) - { - l=2; - } - else - return PARSER_FAILED; - } - else - return PARSER_HUNGRY; - break; - - case '\n': - l=1; - break; - default: - l=0; - } - - if (l) - { - /* end of headers */ - *b_len -= l; - *b_ptr += l; - - - stream->state = STREAM_REQUEST_CONTENT; - - /* aisl_raise_event(cli->server->owner, CLI_STREAM(cli), AISL_STREAM_OPEN); - * */ - - return PARSER_PENDING; - } - - /* header key */ - - char * ptr = memchr(*b_ptr, ':', *b_len); - - if (!ptr) - { - /* - if (*b_len == gBuffer->size) - return PARSER_FAILED; - */ - return PARSER_HUNGRY; - } - - l = (int) (ptr-*b_ptr); - - - pair_t ppp = pair_new(*b_ptr, l); - - str_to_lower(ppp->key); - - if (ppp) - list_append(stream->headers, ppp); - - *b_len -= ++l; - *b_ptr += l; - - stream->state=STREAM_REQUEST_HEADER_VALUE; - - return PARSER_PENDING; - -} - -/* HTTP HEADER VALUE -------------------------------------------------------- */ - -parser_status_t -parse_request_header_value(client_t cli, char ** b_ptr, int *b_len) -{ - stream_t stream = CLI_STREAM(cli); - /* skip first space */ - - if (*b_len) - { - if ((*b_ptr)[0]==' ') - { - (*b_ptr)++; - (*b_len)--; - if (*b_len == 0) - return PARSER_HUNGRY; - } - } - else - return PARSER_HUNGRY; - - char * ptr = memchr(*b_ptr, '\n', *b_len); - int l; - - l = (ptr) ? (int) (ptr-*b_ptr) : *b_len; - - uint32_t index = stream->headers->count -1; - - pair_t p = list_index(stream->headers, index); - - p->value = str_ncat(p->value, *b_ptr, (l && (*b_ptr)[l-1]=='\r') ? l-1 : l); - - *b_len -= ++l; - *b_ptr += l; - - stream->state=STREAM_REQUEST_HEADER_KEY; - - /* todo: add limit for maximal header length */ - - if (ptr) - { - if (str_cmpi(p->key, cCookie )==0) - { - /* parse cookies */ - } - else if (str_cmpi(p->key, cContentType )==0) - { - /* CLI(r)->c_type_index = index; */ - } - else if (str_cmpi(p->key, cContentLength )==0) - { - stream->c_length = strtol(p->value, NULL, 0); - } - /* CLI(r)->c_length = strtol(p->value, NULL, 10); */ - - aisl_raise_event( - cli->server->owner, - stream, - AISL_STREAM_HEADER, - p->key, p->value - ); - - return PARSER_PENDING; - } - else - return PARSER_HUNGRY; - -} - - - -parser_status_t -parse_request_content(client_t cli, char ** b_ptr, int *b_len) -{ - stream_t stream = CLI_STREAM(cli); - /* - fprintf(stdout, "AISL [%d]> ", CLI_STREAM(cli)->c_length); - fwrite(*b_ptr, 1, *b_len, stdout); - fprintf(stdout, "\n<"); - */ - - if (stream->c_length) - { - int l = *b_len; - - aisl_raise_event( - cli->server->owner, - stream, - AISL_STREAM_INPUT, - *b_ptr, - l - ); - - *b_ptr += l; - *b_len = 0; - stream->c_length -= l; - } - else - goto request_ready; - - if ( stream->c_length == 0 ) - { - request_ready: - stream->state=STREAM_REQUEST_READY; - aisl_raise_event( cli->server->owner, stream, AISL_STREAM_REQUEST ); - return PARSER_FINISHED; - } - else - return PARSER_HUNGRY; -} diff --git a/library/parser.h b/library/parser.h deleted file mode 100644 index 835d0f3..0000000 --- a/library/parser.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _AISL_PARSER_H_ -#define _AISL_PARSER_H_ - -#include -#include "client.h" - - -/* parser status ------------------------------------------------------------ */ - -typedef enum -{ - PARSER_PENDING, /* process pending */ - PARSER_FINISHED, /* successful finish */ - PARSER_HUNGRY, /* not enough data in buffer */ - PARSER_FAILED /* error happened */ - -} parser_status_t; - - -/* parse HTTP Request ------------------------------------------------------- */ - -parser_status_t -parse_request_method(client_t cli, char ** b_ptr, int *b_len); - -/* parse HTTP Request URI --------------------------------------------------- */ - -parser_status_t -parse_request_path(client_t cli, char ** b_ptr, int *b_len); - -/* parse HTTP Version ------------------------------------------------------- */ - -parser_status_t -parse_request_protocol(client_t cli, char ** b_ptr, int *b_len); - -/* parse HTTP header key ---------------------------------------------------- */ - -parser_status_t -parse_request_header_key(client_t cli, char ** b_ptr, int *b_len); - -/* parse HTTP header value -------------------------------------------------- */ - -parser_status_t -parse_request_header_value(client_t cli, char ** b_ptr, int *b_len); - -/* parse HTTP data ---------------------------------------------------------- */ - -parser_status_t -parse_request_content(client_t cli, char ** b_ptr, int *b_len); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/library/server.c b/library/server.c deleted file mode 100644 index 3d16a8f..0000000 --- a/library/server.c +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - - -#include "server.h" -#include "handle.h" -#include "client.h" -#include "globals.h" - -/* -------------------------------------------------------------------------- */ - -static bool -_set_addres_from_host_and_port( struct sockaddr_in * sa, - const char * host, - int port ) -{ - int rs; - struct addrinfo * ai; - - memset(sa, 0, sizeof( struct sockaddr_in )); - sa->sin_family = AF_INET; - - rs = getaddrinfo(host, NULL, NULL, &ai); - - if(rs != 0) - { - return false; - } - - sa->sin_addr.s_addr=((struct sockaddr_in*)(ai->ai_addr))->sin_addr.s_addr; - sa->sin_port =htons(port); - - freeaddrinfo(ai); - - return true; -} - - -/* -------------------------------------------------------------------------- */ - -static void -server_close(server_t self) -{ - close(self->fd); - self->fd=-1; -} - -/* -------------------------------------------------------------------------- */ - - -static aisl_status_t -server_open(server_t self) -{ - - aisl_status_t result = AISL_SUCCESS; - - int s_opt = 1; - - if (!_set_addres_from_host_and_port(&self->address, self->host, self->port)) - return AISL_EXTCALL_ERROR; - - self->fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - - if (self->fd != -1) - { - setsockopt( - self->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int) - ); - -#ifdef __APPLE__ - - int on = 1; - - ioctl(self->fd, FIONBIO, (char *)&on); - fcntl(self->fd, F_SETFL, fcntl(self->fd, F_GETFL) | O_NONBLOCK); - -#endif - - if (bind( self->fd, - (struct sockaddr *) &self->address, - sizeof(struct sockaddr_in) )==0) - { - if (listen(self->fd, SOMAXCONN) == 0) - return result; - } - - server_close(self); - } - - result = AISL_SYSCALL_ERROR; - - return result; -} - - -/* -------------------------------------------------------------------------- */ - -bool -server_touch(server_t self) -{ - aisl_status_t result; - client_t cli; - - if (self->fd == -1) - { - result = server_open(self); - if (result == AISL_SUCCESS) - aisl_raise_event( - self->owner, - self, - AISL_SERVER_OPEN, - self->flags - ); - else - aisl_raise_event( - self->owner, - self, - AISL_SERVER_ERROR, - self->flags, - strerror(errno) - ); - - return result; - } - - result = client_accept(&cli, self); - - if (result == AISL_SUCCESS) - { - if (list_append(self->owner->clients, cli) == -1) - { - client_free(cli); - result = AISL_MALLOC_ERROR; - } - else - aisl_raise_event(self->owner, self, AISL_CLIENT_CONNECT, cli); - - } - - return result; -} - -/* -------------------------------------------------------------------------- */ - -server_t -server_new(const char * address, int port) -{ - server_t self; - - if ( (self = calloc(1, sizeof(struct server))) != NULL ) - { - self->fd = -1; - self->port = port; - if ( !(self->host = str_copy(address)) ) - { - free(self); - self = NULL; - } - } - - return self; -} - -/* -------------------------------------------------------------------------- */ - -void -server_free(server_t self) -{ - if (self) - { - if (self->fd > -1) - server_close(self); - - if (self->host) - free(self->host); - - free(self); - } -} - - -/* -------------------------------------------------------------------------- */ - diff --git a/library/server.h b/library/server.h deleted file mode 100644 index 4c48575..0000000 --- a/library/server.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _AISL_SERVER_H_ -#define _AISL_SERVER_H_ - -#include -#include -#include - -#ifdef __APPLE__ - -#include -#include - -#endif - -/* types -------------------------------------------------------------------- */ -struct server -{ - struct sockaddr_in address; - aisl_handle_t owner; - char * host; - int fd; - int port; - int flags; -}; - -typedef struct server * server_t; - -#define SERVER(x) ((server_t) x) - -/* -------------------------------------------------------------------------- */ - -server_t -server_new(const char * address, int port); - -/* -------------------------------------------------------------------------- */ - -void -server_free(server_t self); - -/* -------------------------------------------------------------------------- */ - -bool -server_touch(server_t self); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/library/status.c b/library/status.c deleted file mode 100644 index 4a363ed..0000000 --- a/library/status.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -const char * -aisl_status_to_string(aisl_status_t status) -{ - switch( status ) - { - case AISL_SUCCESS: return "success"; - case AISL_IDLE: return "idle"; - case AISL_MALLOC_ERROR: return "malloc error"; - case AISL_SYSCALL_ERROR: return "system call error"; - case AISL_EXTCALL_ERROR: return "external call error"; - } - - return ""; -} - -/* -------------------------------------------------------------------------- */ diff --git a/library/stream.c b/library/stream.c deleted file mode 100644 index 9414f5a..0000000 --- a/library/stream.c +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include - -#include "stream.h" -#include "globals.h" -#include "client.h" -#include "handle.h" - -static void -pair_free( pair_t self ) -{ - if (!self) return; - - if(self->key) free(self->key); - if(self->value) free(self->value); - free(self); -} -/* -------------------------------------------------------------------------- */ - -stream_t -stream_new(struct sockaddr_in *client, int id, stream_state_t state) -{ - stream_t self = malloc(sizeof(struct stream)); - - if (self) - { - /* public data */ - ASTREAM(self)->client = client; - ASTREAM(self)->host = NULL; - ASTREAM(self)->path = NULL; - ASTREAM(self)->query = NULL; - ASTREAM(self)->scheme = NULL; - ASTREAM(self)->u_ptr = NULL; - ASTREAM(self)->request_method = AISL_HTTP_GET; - - /* private data */ - self->headers = NULL; /* request headers */ - self->buffer = buffer_new(0); - - self->c_type = NULL; - - self->response = AISL_HTTP_OK; - self->state = STREAM_REQUEST_METHOD; - self->c_length = 0; - self->c_offset = 0; /* headers length */ - self->id = id; - self->c_length_unknown = true; - self->flags = 0; - } - return self; -} - -/* -stream_t -stream_reset(stream_t self) -{ - if (ASTREAM(self)->path) - { - free( (char*) ASTREAM(self)->path); - ASTREAM(self)->path = NULL; - } - - if (ASTREAM(self)->query) - { - free( (char*) ASTREAM(self)->query); - ASTREAM(self)->query = NULL; - } - - ASTREAM(self)->u_ptr = NULL; - ASTREAM(self)->request_method = AISL_HTTP_GET; - - if (self->headers) - { - list_free(self->headers, (list_destructor_t) pair_free); - self->headers = NULL; - } - - self->c_type = NULL; - self->response = AISL_HTTP_OK; - self->state = STREAM_REQUEST_METHOD; - self->c_length = 0; - self->c_offset = 0; / * headers length * / - self->c_length_unknown = true; - self->flags = 0; - - return self; -} -*/ -/* -------------------------------------------------------------------------- */ - -void -stream_free(stream_t self) -{ - if (self->buffer) buffer_free(self->buffer); - if (self->headers) list_free(self->headers, (list_destructor_t) pair_free); - - if (ASTREAM(self)->path) free( (char*) ASTREAM(self)->path); - if (ASTREAM(self)->query) free( (char*) ASTREAM(self)->query); - - aisl_handle_t hd = ((client_t) ASTREAM(self)->client)->server->owner; - aisl_raise_event(hd, self, AISL_STREAM_CLOSE); - ASTREAM(self)->u_ptr = NULL; - aisl_remove_listeners_for(hd, self); - free(self); -} - -/* -------------------------------------------------------------------------- */ - -int -stream_write(stream_t self, const char * data, uint32_t d_len) -{ - return buffer_add( self->buffer, data, d_len); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_cancel(aisl_stream_t s) -{ - client_close( (client_t) s->client ); -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -bool -aisl_is_secure(aisl_stream_t s) -{ - client_t cli = (client_t) s->client; - - return (cli->ssl) ? true : false; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void * -aisl_get_context(aisl_stream_t s) -{ - return s->u_ptr; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_set_context(aisl_stream_t s, void * u_ptr) -{ - s->u_ptr = u_ptr; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_client_t -aisl_get_client(aisl_stream_t s) -{ - return s->client; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_server_t -aisl_get_server(aisl_stream_t s) -{ - return (aisl_server_t) (((client_t) s->client)->server); -} - - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -aisl_http_version_t -aisl_get_http_version(aisl_stream_t s) -{ - client_t cli = (client_t) s->client; - - return cli->protocol; -} - -/* -------------------------------------------------------------------------- */ - -__attribute__ ((visibility ("default") )) -void -aisl_reject(aisl_stream_t s) -{ - client_t cli = (client_t) s->client; - - client_close( cli ); -} - -/* -------------------------------------------------------------------------- */ diff --git a/library/stream.h b/library/stream.h deleted file mode 100644 index d308a52..0000000 --- a/library/stream.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef _AISL_STREAM_H__ -#define _AISL_STREAM_H__ - -#include -#include -#include -#include "buffer.h" - -/* -------------------------------------------------------------------------- */ - -#define STREAM_FLAG_OUTPUT_READY (1<<0) -#define STREAM_FLAG_OUTPUT_CHUNKED (1<<1) - -/* -------------------------------------------------------------------------- */ - -struct pair -{ - char *key; - char *value; -}; - -typedef struct pair * pair_t; - -/* -------------------------------------------------------------------------- */ - -typedef enum { - - STREAM_REQUEST_METHOD, - STREAM_REQUEST_PATH, - STREAM_REQUEST_PROTOCOL, - - STREAM_REQUEST_HEADER_KEY, /* HTTP1 header key */ - STREAM_REQUEST_HEADER_VALUE, /* HTTP1 header value */ - - STREAM_REQUEST_CONTENT, /* HTTP1 data value */ - - /* states below show stream state - * and do not show what data was sent to client - * */ - STREAM_REQUEST_READY, - - STREAM_RESPONSE_HEADER, - STREAM_RESPONSE_CONTENT, - STREAM_RESPONSE_READY - -} stream_state_t; - - -/* real wrapper for aisl_stream_t */ -struct stream -{ - struct aisl_stream _public; - - /* private data */ - list_t headers; /* request headers */ - buffer_t buffer; - - const char *c_type; - - aisl_http_response_t response; - stream_state_t state; - uint32_t c_length; - uint32_t c_offset; - int id; - int flags; - - bool c_length_unknown; - -}; - -typedef struct stream * stream_t; - -#define STREAM(x) ((stream_t) x) -#define ASTREAM(x) ((aisl_stream_t) x) - -/* -------------------------------------------------------------------------- */ - -stream_t -stream_new(struct sockaddr_in *client, int id, stream_state_t state); - -/* -------------------------------------------------------------------------- */ - -void -stream_free(stream_t self); - -/* -------------------------------------------------------------------------- */ - -stream_t -stream_reset(stream_t self); - -/* -------------------------------------------------------------------------- */ - -#endif diff --git a/project.mk b/project.mk new file mode 100644 index 0000000..580e0df --- /dev/null +++ b/project.mk @@ -0,0 +1,65 @@ +# +# config.mk +# Löwenware Makefile Config, 2019-03-02 17:35 +# + +PREFIX ?= /usr/local +PKG_CONFIG ?= pkg-config + +PROJECT_NAME = aisl + +# Version + +PROJECT_VERSION_MAJOR = 1 +PROJECT_VERSION_MINOR = 0 +PROJECT_VERSION_TWEAK = 5 +PROJECT_VERSION_LABEL = 0 + +#SRC_DIR = src +#SDK_DIR = sdk +#OUT_DIR = ./build + + +# Source files + +PROJECT_SOURCES := \ + $(SRC_DIR)/instance.c \ + $(SRC_DIR)/server.c \ + $(SRC_DIR)/client.c \ + $(SRC_DIR)/stream.c \ + $(SRC_DIR)/http.c \ + $(SRC_DIR)/ssl.c \ + $(SRC_DIR)/list.c \ + $(SRC_DIR)/str-utils.c \ + $(SRC_DIR)/buffer.c \ + $(SRC_DIR)/types.c \ + + +# includes +PROJECT_INCLUDES = -I./ \ + -I./include \ + `$(PKG_CONFIG) --cflags openssl` \ + +# libraries +PROJECT_LIBRARIES = \ + `$(PKG_CONFIG) --libs openssl` \ + + +# compilation macro options: + +AISL_WITH_DEBUG ?= 0 # disable debug output +AISL_WITH_SSL ?= 1 # enable SSL support +AISL_WITH_STRINGIFIERS ?= 1 # enable *_to_string functions + + +# flags +PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L +PROJECT_CFLAGS += -DAISL_WITH_DEBUG=$(AISL_WITH_DEBUG) +PROJECT_CFLAGS += -DAISL_WITH_SSL=$(AISL_WITH_SSL) +PROJECT_CFLAGS += -DAISL_WITH_STRINGIFIERS=$(AISL_WITH_STRINGIFIERS) + +# PROJECT_LDFLAGS = -L + + +# vim:ft=make +# diff --git a/project.sh b/project.sh deleted file mode 100755 index 8a1f6b5..0000000 --- a/project.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -# ----------------------------------------------------------------------------- -# -# CMake Wrapper v.1.0 for Linux -# (c) Copyright Löwenware Ltd. (https://lowenware.com/) -# -# ----------------------------------------------------------------------------- - -ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -PROJECT="aisl" -PROJECT_VERSION=$(cat ${ABSOLUTE_PATH}/version | sed 's/\([0-9]\{1,5\}.[0-9]\{1,5\}.[0-9]\{1,5\}\).*/\1/') - -PREFIX="/usr" -SYSCONF="/etc" -DIR_BUILD="build" -DIR_ROOT="root" - -# ----------------------------------------------------------------------------- - -function project_clean { - echo "Cleaning..." - - if [ -d ./$DIR_BUILD ]; then - rm -Rf ./$DIR_BUILD/* - else - mkdir ./$DIR_BUILD - fi - - if [ -d ./$DIR_ROOT ]; then - rm -Rf ./$DIR_ROOT/* - else - mkdir ./$DIR_ROOT - fi - -} - -# ----------------------------------------------------------------------------- - -function project_compile { - - CMAKE="cmake" - - if [[ "$OSTYPE" == "darwin"* ]]; then - ov="1.0.2n" - ov_p="-DOPENSSL_INCLUDE_DIRS=/usr/local/Cellar/openssl/${ov}/include -DOPENSSL_CRYPTO_LIBRARY=/usr/local/Cellar/openssl/${ov}/lib/libcrypto.dylib -DOPENSSL_SSL_LIBRARY=/usr/local/Cellar/openssl/${ov}/lib/libssl.dylib -DOPENSSL_LIBRARY_DIRS=/usr/local/Cellar/openssl/${ov}/lib" - CMAKE="cmake ${ov_p}" - fi - - echo ${CMAKE} - - ${CMAKE} -B./$DIR_BUILD -H./ -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_DEBUG=1 - cd $DIR_BUILD/ - make - make DESTDIR=../$DIR_ROOT install - cd .. -} - -# ----------------------------------------------------------------------------- - -case $1 in - clean) - project_clean - ;; - compile) - project_compile - ;; - build) - project_clean - project_compile - ;; - install) - cmake -DWITH_EVERYTHING=1 -B./$DIR_BUILD -H./ -DCMAKE_INSTALL_PREFIX=$PREFIX - cd $DIR_BUILD - sudo make install - cd .. - ;; - deploy) - DEPLOY_PATH="${PROJECT}-${PROJECT_VERSION}" - mkdir ${2}${DEPLOY_PATH} - cp -R ${ABSOLUTE_PATH}/{include,library,LICENSE,AUTHORS,version,README.md,cmake*,CMakeLists.txt,cStuff} ${2}${DEPLOY_PATH} - rm ${2}${DEPLOY_PATH}/cStuff/.git - CUR_DIR=$(pwd) - cd $2 - tar cfz ${DEPLOY_PATH}.tar.gz ${DEPLOY_PATH} - cd $CUR_DIR - rm -Rf ${2}${DEPLOY_PATH} - echo "_version ${PROJECT_VERSION}" - ;; - *) - echo "Usage: ./project.sh (compile|build|clean|install)" - ;; -esac - - -# ----------------------------------------------------------------------------- diff --git a/version b/version deleted file mode 100644 index 9edeaea..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -0.3.4-alpha