From 4b137d7c55111290c0b689b51e9525c8fecbd55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilja=20Karta=C5=A1ov?= Date: Sat, 23 Feb 2019 21:06:34 +0100 Subject: [PATCH] Initiated --- .gitignore | 26 ++ .gitmodules | 3 + AUTHORS | 5 + CHANGELOG | 0 CMakeLists.txt | 131 +++++++ LICENSE | 26 ++ README.md | 55 +++ aisl.proj | 36 ++ cStuff | 1 + cmake.compiler | 7 + cmake.paths | 90 +++++ cmake.system | 18 + cmake.version | 61 ++++ include/aisl/aisl.h | 107 ++++++ include/aisl/event.h | 128 +++++++ include/aisl/handle.h | 76 ++++ include/aisl/http.h | 104 ++++++ include/aisl/status.h | 24 ++ include/aisl/stream.h | 172 +++++++++ library/aisl.c | 367 +++++++++++++++++++ library/buffer.c | 129 +++++++ library/buffer.h | 49 +++ library/client.c | 460 ++++++++++++++++++++++++ library/client.h | 67 ++++ library/event.c | 28 ++ library/globals.h | 48 +++ library/handle.c | 802 ++++++++++++++++++++++++++++++++++++++++++ library/handle.h | 61 ++++ library/http.c | 112 ++++++ library/parser.c | 490 ++++++++++++++++++++++++++ library/parser.h | 52 +++ library/server.c | 188 ++++++++++ library/server.h | 47 +++ library/status.c | 21 ++ library/stream.c | 196 +++++++++++ library/stream.h | 93 +++++ project.sh | 97 +++++ version | 1 + 38 files changed, 4378 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 AUTHORS create mode 100644 CHANGELOG create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 aisl.proj create mode 160000 cStuff create mode 100755 cmake.compiler create mode 100644 cmake.paths create mode 100644 cmake.system create mode 100644 cmake.version create mode 100644 include/aisl/aisl.h create mode 100644 include/aisl/event.h create mode 100644 include/aisl/handle.h create mode 100644 include/aisl/http.h create mode 100644 include/aisl/status.h create mode 100644 include/aisl/stream.h create mode 100644 library/aisl.c create mode 100644 library/buffer.c create mode 100644 library/buffer.h create mode 100644 library/client.c create mode 100644 library/client.h create mode 100644 library/event.c create mode 100644 library/globals.h create mode 100644 library/handle.c create mode 100644 library/handle.h create mode 100644 library/http.c create mode 100644 library/parser.c create mode 100644 library/parser.h create mode 100644 library/server.c create mode 100644 library/server.h create mode 100644 library/status.c create mode 100644 library/stream.c create mode 100644 library/stream.h create mode 100755 project.sh create mode 100644 version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d13b9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Binaries + +build/* +root/* +demo/* + +bin/* +obj/* +webstuff-* +Makefile +tmp +tmp/* +*.tar.gz +*.zip +*.rar +*.gz +*.bz2 +pkg/* + +include/webstuff_bak + +CMakeCache.txt +CMakeFiles/* +cmake_install.cmake +libaisl.dylib +demo/demo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6b92f82 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cStuff"] + path = cStuff + url = https://github.com/lowenware/cStuff.git diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..f396c51 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +(c) Copyright 2017 by Löwenware Ltd. + +Developers: + Elias Löwe + Stanislav Ivanov diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7347064 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,131 @@ +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 AUTHORS) + +#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/LICENSE b/LICENSE new file mode 100644 index 0000000..c91f459 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014-2017 Elias Löwe + * Copyright (C) 2014-2017 Löwenware Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4c9c17 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# AISL +Asynchronous Internet Server Library + +## Installation on CentOS 7 / RedHat 7 + +1. Add repository +``` +cd ~ +wget http://lowenware.com/rpm/redhat-7/lowenware.repo +sudo mv lowenware.repo /etc/yum.repos.d/ +``` + +2. Import GPG key +``` +sudo rpm --import http://lowenware.com/rpm/RPM-GPG-KEY-Lowenware +``` + +3. Install +``` +sudo yum install aisl aisl-devel +``` + +## Installation on OpenSUSE Tumbleweed + +1. Add repository +``` +sudo zypper ar http://lowenware.com/rpm/opensuse-tumbleweed/lowenware.repo +``` + +2. Import GPG key +``` +sudo rpm --import http://lowenware.com/rpm/RPM-GPG-KEY-Lowenware +``` + +3. Install +``` +sudo zypper install aisl aisl-devel +``` + +## Installation from sources on any distro + +1. Configuration +``` +cmake -DCMAKE_INSTALL_PREFIX=/usr/local +``` + +2. Compilation +``` +make +``` + +3. Installation +``` +sudo make install +``` diff --git a/aisl.proj b/aisl.proj new file mode 100644 index 0000000..4853133 --- /dev/null +++ b/aisl.proj @@ -0,0 +1,36 @@ +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 new file mode 160000 index 0000000..a423fa7 --- /dev/null +++ b/cStuff @@ -0,0 +1 @@ +Subproject commit a423fa7a6dfbd637f3c0b248123f682456fccad7 diff --git a/cmake.compiler b/cmake.compiler new file mode 100755 index 0000000..3680029 --- /dev/null +++ b/cmake.compiler @@ -0,0 +1,7 @@ + +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 new file mode 100644 index 0000000..b0b055d --- /dev/null +++ b/cmake.paths @@ -0,0 +1,90 @@ +# 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 new file mode 100644 index 0000000..aeb769f --- /dev/null +++ b/cmake.system @@ -0,0 +1,18 @@ +# 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 new file mode 100644 index 0000000..5a89fc5 --- /dev/null +++ b/cmake.version @@ -0,0 +1,61 @@ +# ----------------------------------------------------------------------------- +# +# 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/include/aisl/aisl.h b/include/aisl/aisl.h new file mode 100644 index 0000000..6e05852 --- /dev/null +++ b/include/aisl/aisl.h @@ -0,0 +1,107 @@ +/* ---------------------------------------------------------------------------- + * aisl.h - header file for AISL library, part of AISLing Technology + * + * Copyright (c) 2017 by Löwenware Ltd. (https://lowenware.com/) + * + * Authors and maintainers: + * Ilja Kartaschoff + * + * 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 + * + * -------------------------------------------------------------------------- */ + +#ifndef _AISL_H_ +#define _AISL_H_ + +/* system includes ---------------------------------------------------------- */ + +#include +#include +#include +#include + +/* aisl includes ------------------------------------------------------------ */ + +#include +#include +#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 diff --git a/include/aisl/event.h b/include/aisl/event.h new file mode 100644 index 0000000..476ddf8 --- /dev/null +++ b/include/aisl/event.h @@ -0,0 +1,128 @@ +#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 new file mode 100644 index 0000000..29d9f09 --- /dev/null +++ b/include/aisl/handle.h @@ -0,0 +1,76 @@ +#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 new file mode 100644 index 0000000..432492f --- /dev/null +++ b/include/aisl/http.h @@ -0,0 +1,104 @@ +#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/status.h b/include/aisl/status.h new file mode 100644 index 0000000..bcc4428 --- /dev/null +++ b/include/aisl/status.h @@ -0,0 +1,24 @@ +#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 new file mode 100644 index 0000000..7bcaf3c --- /dev/null +++ b/include/aisl/stream.h @@ -0,0 +1,172 @@ +#ifndef _AISL_STREAM_H_ +#define _AISL_STREAM_H_ + +#include +/* Library statuses */ +/* HTTP requests */ +#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); + +/* -------------------------------------------------------------------------- */ + +bool +aisl_is_secure(aisl_stream_t s); + +/* -------------------------------------------------------------------------- */ + +void * +aisl_get_context(aisl_stream_t s); + +/* -------------------------------------------------------------------------- */ + +void +aisl_set_context(aisl_stream_t s, void * u_ptr); + +/* -------------------------------------------------------------------------- */ + +aisl_client_t +aisl_get_client(aisl_stream_t s); + +/* -------------------------------------------------------------------------- */ + +aisl_server_t +aisl_get_server(aisl_stream_t s); + + /* -------------------------------------------------------------------------- */ + +aisl_http_version_t +aisl_get_http_version(aisl_stream_t s); + +/* -------------------------------------------------------------------------- */ + +void +aisl_reject( aisl_stream_t s); + +/* -------------------------------------------------------------------------- */ + +#endif diff --git a/library/aisl.c b/library/aisl.c new file mode 100644 index 0000000..bae54a1 --- /dev/null +++ b/library/aisl.c @@ -0,0 +1,367 @@ +#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 new file mode 100644 index 0000000..a5f2076 --- /dev/null +++ b/library/buffer.c @@ -0,0 +1,129 @@ +#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 new file mode 100644 index 0000000..72d81d6 --- /dev/null +++ b/library/buffer.h @@ -0,0 +1,49 @@ +#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 new file mode 100644 index 0000000..2f04654 --- /dev/null +++ b/library/client.c @@ -0,0 +1,460 @@ +#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 new file mode 100644 index 0000000..44912ad --- /dev/null +++ b/library/client.h @@ -0,0 +1,67 @@ +#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 new file mode 100644 index 0000000..425852a --- /dev/null +++ b/library/event.c @@ -0,0 +1,28 @@ +#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 new file mode 100644 index 0000000..f5abe2a --- /dev/null +++ b/library/globals.h @@ -0,0 +1,48 @@ +#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 new file mode 100644 index 0000000..4f4927f --- /dev/null +++ b/library/handle.c @@ -0,0 +1,802 @@ +#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 new file mode 100644 index 0000000..9b3048b --- /dev/null +++ b/library/handle.h @@ -0,0 +1,61 @@ +#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 new file mode 100644 index 0000000..300ccaa --- /dev/null +++ b/library/http.c @@ -0,0 +1,112 @@ +#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 new file mode 100644 index 0000000..78b4372 --- /dev/null +++ b/library/parser.c @@ -0,0 +1,490 @@ +#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 new file mode 100644 index 0000000..835d0f3 --- /dev/null +++ b/library/parser.h @@ -0,0 +1,52 @@ +#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 new file mode 100644 index 0000000..3d16a8f --- /dev/null +++ b/library/server.c @@ -0,0 +1,188 @@ +#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 new file mode 100644 index 0000000..4c48575 --- /dev/null +++ b/library/server.h @@ -0,0 +1,47 @@ +#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 new file mode 100644 index 0000000..4a363ed --- /dev/null +++ b/library/status.c @@ -0,0 +1,21 @@ +#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 new file mode 100644 index 0000000..9414f5a --- /dev/null +++ b/library/stream.c @@ -0,0 +1,196 @@ +#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 new file mode 100644 index 0000000..d308a52 --- /dev/null +++ b/library/stream.h @@ -0,0 +1,93 @@ +#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.sh b/project.sh new file mode 100755 index 0000000..8a1f6b5 --- /dev/null +++ b/project.sh @@ -0,0 +1,97 @@ +#!/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 new file mode 100644 index 0000000..9edeaea --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.3.4-alpha