commit 4b137d7c55111290c0b689b51e9525c8fecbd55b Author: Ilja Kartašov Date: Sat Feb 23 21:06:34 2019 +0100 Initiated 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