Update to version 1.0.5

This commit is contained in:
2019-07-13 14:04:06 +02:00
parent 2d67fdd5fb
commit f7a9f255df
77 changed files with 4552 additions and 4264 deletions
-3
View File
@@ -1,3 +0,0 @@
[submodule "cStuff"]
path = cStuff
url = https://github.com/lowenware/cStuff.git
+8
View File
@@ -0,0 +1,8 @@
# Release 1.0.0
By Ilja Kartašov <ik@lowenware.com>, 2019-06-02
1. Library release with new and final API and documented code.
2. Previous version is being called `aisl-legacy` and will be maintained in
security aspects in branch called `legacy` until all applications using it will
be updated.
-131
View File
@@ -1,131 +0,0 @@
cmake_minimum_required (VERSION 2.8)
project ( AISL C)
set (PROJECT_TITLE "aisl")
set (LIBRARY_NAME ${PROJECT_TITLE})
# set (DEMO_NAME "demo")
# Defaults --------------------------------------------------------------------
include (cmake.compiler)
include (cmake.version)
include (cmake.system)
include (cmake.paths)
# Definitions -----------------------------------------------------------------
add_definitions(
-DPROJECT_TITLE="${PROJECT_TITLE}"
)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden")
if(DEFINED CMAKE_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ")
add_definitions( -DDEBUG=${CMAKE_DEBUG} )
endif()
# Options ---------------------------------------------------------------------
#if( DEFINED WITH_EVERYTHING )
# set(WITH_TEMPLIGHT 1)
# set(WITH_OPTIONS 1)
# set(WITH_CONFIG 1)
#endif()
# Sources ---------------------------------------------------------------------
include_directories( "." ${INCLUDE_DIR} )
add_definitions( -DLIST_WITH_APPEND
-DCSTUFF_LIST_WITH_REMOVE_INDEX
-DCSTUFF_LIST_WITH_REMOVE
-DCSTUFF_LIST_WITH_APPEND
-DCSTUFF_LIST_WITH_INSERT
-DCSTUFF_LIST_WITH_COPY
-DCSTUFF_STR_UTILS_WITH_COPY
-DCSTUFF_STR_UTILS_WITH_NCAT
-DCSTUFF_STR_UTILS_WITH_CMPI
-DCSTUFF_STR_UTILS_WITH_PRINTF
-DCSTUFF_STR_UTILS_WITH_NCOPY
-DSTR_UTILS_WITH_COPY
-DSTR_UTILS_WITH_NCOPY
-DSTR_UTILS_WITH_CAT
-DSTR_UTILS_WITH_PRINTF
-DSTR_UTILS_WITH_CMPI
)
set ( LIBRARY_SOURCES library/aisl.c
library/buffer.c
library/server.c
library/client.c
library/stream.c
library/parser.c
library/http.c
library/handle.c
library/status.c
library/event.c
cStuff/list.c
cStuff/str-utils.c)
set ( DEMO_SOURCES demo/main.c demo/events.c demo/urls.c )
set ( META_FILES README.md LICENSE.md AUTHORS.md)
#if( DEFINED WITH_TEMPLIGHT )
# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/templight.c)
# add_definitions( -DWITH_TEMPLIGHT )
#endif()
#if( DEFINED WITH_OPTIONS )
# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/options.c)
# add_definitions( -DWITH_OPTIONS )
#endif()
#if( DEFINED WITH_CONFIG )
# set(SOURCE_FILES ${SOURCE_FILES} ${SOURCES_DIR}/config.c)
# add_definitions( -DWITH_CONFIG )
#endif()
find_package(OpenSSL)
include_directories(
${OPENSSL_INCLUDE_DIRS}
)
link_directories(
${OPENSSL_LIBRARY_DIRS}
)
# Library ----------------------------------------------------------------------
add_library(${LIBRARY_NAME} SHARED ${LIBRARY_SOURCES})
# Demos ------------------------------------------------------------------------
#add_executable(${DEMO_NAME} ${DEMO_SOURCES})
#target_link_libraries(${DEMO_NAME} ${LIBRARY_NAME})
target_link_libraries(${LIBRARY_NAME} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY})
# Installation ----------------------------------------------------------------
install(
TARGETS ${LIBRARY_NAME}
DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
# ${LIB_INSTALL_DIR}
#install(
# FILES ${META_FILES}
# DESTINATION ${SHARE_INSTALL_PREFIX}/doc/packages/${LIBRARY_NAME}/
#)
install(
DIRECTORY ${INCLUDE_DIR}/${LIBRARY_NAME}
DESTINATION ${INCLUDE_INSTALL_DIR}
)
+22 -26
View File
@@ -1,36 +1,32 @@
# AISL
Asynchronous Internet Server Library
## Installation on CentOS 7 / RedHat 7
Asynchronous Internet Server Library provides innovative way of web development.
AISL based applications have built-in web server giving full control of client
serving. All you need to know is a request string? - Start prepare the response
without waiting for headers and body! Don't need some headers? - Don't save them
in memory! Unwanted content-body? - simply ignore it!
## Documentation
[Hello World](https://lowenware.com/aisl/handbook.html#getting-started) example
and full [API reference](https://lowenware.com/aisl/handbook.html#api-reference)
can be found in an oficial [AISL HandBook](https://lowenware.com/aisl/handbook.html).
## Installation
1. Add repository
```
sudo curl -o /etc/yum.repos.d/lowenware.repo https://lowenware.com/rpm/redhat-7/lowenware.repo
$ make PREFIX=/usr/local
$ sudo make PREFIX=/usr/local install
$ sudo cp libaisl.pc.example /usr/lib/pkgconfig/libaisl.pc
```
2. Import GPG key
ArchLinux users can install from [AUR](https://aur.archlinux.org/packages/aisl-git/) :
```
sudo rpm --import https://lowenware.com/rpm/RPM-GPG-KEY-Lowenware
$ yaourt -S aisl-git
```
3. Install
```
sudo yum install aisl aisl-devel
```
## License
## Installation from sources on any distro
1. Configuration
```
cmake -DCMAKE_INSTALL_PREFIX=/usr/local
```
2. Compilation
```
make
```
3. Installation
```
sudo make install
```
AISL is free for both commercial and non-commercial use, being distributed under
terms of [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/).
-36
View File
@@ -1,36 +0,0 @@
name = AISL
todo = aisl.todo
handbook = handbook.md
version:
major = 0
minor = 0
include =
definitions =
profile:
name = debug
flags = -Wall -Werror
definitions =
profile:
name = release
flags =
definitions =
library:
output = aisl
compiler = gcc
version:
major = $version.major
minor = $version.minor
tweak = 0
build = auto
sources = library/aisl.c,
library/buffer.c
Submodule cStuff deleted from a423fa7a6d
-7
View File
@@ -1,7 +0,0 @@
if(DEFINED CMAKE_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ")
add_definitions(
-DDEBUG
)
endif()
-90
View File
@@ -1,90 +0,0 @@
# Paths -----------------------------------------------------------------------
# Constants -------------------------------------------------------------------
set(INCLUDE_DIR "include")
set(BUILD_DIR "build")
# CMAKE installation paths ----------------------------------------------------
# -DCMAKE_INSTALL_PREFIX:PATH=/usr
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "/usr")
endif()
# -DINCLUDE_INSTALL_DIR:PATH=/usr/include
if(NOT DEFINED INCLUDE_INSTALL_DIR)
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include")
endif()
# -DSHARE_INSTALL_PREFIX:PATH=/usr/share
if(NOT DEFINED SHARE_INSTALL_PREFIX)
set(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share")
endif()
# -DSYSCONF_INSTALL_DIR:PATH=/etc
if(NOT DEFINED SYSCONF_INSTALL_DIR)
set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc")
endif()
# -DCMAKE_INSTALL_LIBDIR:PATH=lib64
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR "lib${SYSTEM_LIB_SUFFIX}")
endif()
# -DLIB_INSTALL_DIR:PATH=/usr/lib64
if(NOT DEFINED LIB_INSTALL_DIR)
set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
# -----------------------------------------------------------------------------
set(PATH_BIN "${CMAKE_INSTALL_PREFIX}/bin")
set(PATH_INC "${INCLUDE_INSTALL_DIR}")
set(PATH_CFG "${SYSCONF_INSTALL_DIR}")
set(PATH_RUN "/var/run")
set(PATH_LIB "${LIB_INSTALL_DIR}")
set(PATH_LOG "/var/log")
set(PATH_RES "${SHARE_INSTALL_PREFIX}")
set(PATH_LNG "${SHARE_INSTALL_PREFIX}/locale")
MESSAGE( STATUS "Paths:")
MESSAGE( STATUS " Prefix: ${CMAKE_INSTALL_PREFIX}" )
MESSAGE( STATUS " Binaries: ${PATH_BIN}" )
MESSAGE( STATUS " Configuration: ${PATH_CFG}" )
MESSAGE( STATUS " Libraries: ${PATH_LIB}" )
MESSAGE( STATUS " Includes: ${PATH_INC}" )
MESSAGE( STATUS " Run: ${PATH_RUN}" )
MESSAGE( STATUS " Log Files: ${PATH_LOG}" )
MESSAGE( STATUS " Resources: ${PATH_RES}" )
MESSAGE( STATUS " Locale Files: ${PATH_LNG}" )
MESSAGE( STATUS "")
# Compiler's Definitions ------------------------------------------------------
add_definitions(
-DPREFIX="${CMAKE_INSTALL_PREFIX}"
-DPATH_BIN="${PATH_BIN}"
-DPATH_CFG="${PATH_CFG}"
-DPATH_INC="${PATH_INC}"
-DPATH_LIB="${PATH_LIB}"
-DPATH_RUN="${PATH_RUN}"
-DPATH_LOG="${PATH_LOG}"
-DPATH_LNG="${PATH_LNG}"
-DPATH_RES="${PATH_RES}"
)
# -----------------------------------------------------------------------------
-18
View File
@@ -1,18 +0,0 @@
# Architecture ----------------------------------------------------------------
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(SYSTEM_BITNESS 64)
set(SYSTEM_ARCH "amd64")
set(SYSTEM_LIB_SUFFIX "64")
else()
set(SYSTEM_BITNESS 32)
set(SYSTEM_ARCH "x86")
set(SYSTEM_LIB_SUFFIX "")
endif()
add_definitions(
-DSYSTEM_NAME="${CMAKE_SYSTEM_NAME}"
-DSYSTEM_BITNESS=${SYSTEM_BITNESS}
-DSYSTEM_ARCH_${SYSTEM_ARCH}
-DSYSTEM_ARCH="${SYSTEM_ARCH}"
)
-61
View File
@@ -1,61 +0,0 @@
# -----------------------------------------------------------------------------
#
# CMake module for paths generation in DEBUG and RELEASE modes
#
# (c) Copyright Löwenware Ltd. (https://lowenware.com/)
#
# -----------------------------------------------------------------------------
## Constants
set (VERSION_FILE "version")
set (
VERSION_REGEX
"^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-(pre|alpha|beta|rc|release))?"
)
# Read file
file (READ ${VERSION_FILE} VERSION_STRING)
# Match file content
string(REGEX MATCH ${VERSION_REGEX} VERSION_STRING ${VERSION_STRING} )
# Set Version constants
set (VERSION_MAJOR ${CMAKE_MATCH_1})
set (VERSION_MINOR ${CMAKE_MATCH_2})
set (VERSION_TWEAK ${CMAKE_MATCH_3})
if (CMAKE_MATCH_5 STREQUAL "pre")
set(VERSION_CYCLE 1)
elseif (CMAKE_MATCH_5 STREQUAL "alpha")
set (VERSION_CYCLE 2)
elseif (CMAKE_MATCH_5 STREQUAL "beta")
set (VERSION_CYCLE 3)
elseif (CMAKE_MATCH_5 STREQUAL "rc")
set (VERSION_CYCLE 4)
else()
set (VERSION_CYCLE 0)
endif()
set (VERSION_LABEL ${CMAKE_MATCH_4})
# Add compiler macros
add_definitions(
-DVERSION_MAJOR=${VERSION_MAJOR}
-DVERSION_MINOR=${VERSION_MINOR}
-DVERSION_TWEAK=${VERSION_TWEAK}
-DVERSION_CYCLE=${VERSION_CYCLE}
-DVERSION_LABEL="${VERSION_LABEL}"
)
#Print output
MESSAGE(
STATUS "${PROJECT_TITLE} version: " ${VERSION_MAJOR} "."
${VERSION_MINOR} "."
${VERSION_TWEAK} "-"
${VERSION_CYCLE_TEXT} " "
${VERSION_LABEL}
)
+31
View File
@@ -0,0 +1,31 @@
#
# examples.mk
# Ilja Kartašov, 2019-03-17 17:40
#
EXAMPLES_DIR ?= examples
EXAMPLES_CFLAGS := \
$(PROJECT_INCLUDES) \
-std=c99 \
-pedantic \
-Wall \
-Wmissing-prototypes \
-Wstrict-prototypes \
-Wold-style-definition \
-O2 \
-s \
$(CFLAGS) \
EXAMPLES_LDFLAGS = -L./ -L./build -laisl -Wl,-rpath=./build
examples: library hello_world
hello_world:
$(info compiling: hello world)
@$(CC) $(EXAMPLES_CFLAGS) \
-o $(OUT_DIR)/hello-world $(EXAMPLES_DIR)/hello-world.c $(EXAMPLES_LDFLAGS)
# vim:ft=make
#
+104
View File
@@ -0,0 +1,104 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file hello-world.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief AISL usage example: Hello World
*
* @see https://lowenware.com/aisl/
*/
#include <stdio.h>
#include <stdlib.h>
/* Include library meta header */
#include <aisl/aisl.h>
static void
hello_world(const struct aisl_evt *evt, void *p_ctx);
static const struct aisl_cfg_srv m_srv[] = {{
.host = "0.0.0.0",
.port = 8080,
.secure = false
}};
static const struct aisl_cfg m_cfg = {
AISL_CFG_DEFAULTS
, .srv = m_srv
, .srv_cnt = sizeof (m_srv) / sizeof (m_srv[0])
, .ssl = NULL
, .ssl_cnt = 0
, .callback = hello_world
, .p_ctx = NULL
};
static void
hello_world(const struct aisl_evt *evt, void *p_ctx)
{
AislStream s;
const char html[] =
"<html>"
"<head>"
"<title>Hello World</title>"
"</head>"
"<body>"
"<h1>Hello World</h1>"
"<p>Powered by AISL</p>"
"</body>"
"</html>";
fprintf(stdout, "Event: %s\n", aisl_event_to_string(evt->code) );
if (evt->code != AISL_EVENT_STREAM_REQUEST)
return;
s = evt->source;
if (aisl_response(s, AISL_HTTP_OK, sizeof (html)-1) == AISL_SUCCESS) {
if (aisl_write(s, html, sizeof (html)-1) != -1) {
aisl_flush(s);
return;
}
}
aisl_reject(s);
(void) p_ctx;
}
int
main(int argc, char **argv)
{
AislInstance aisl; /**< AISL instance pointer */
AislStatus status; /**< AISL status code */
/* Initialize instance */
if ( (aisl = aisl_new(&m_cfg)) != NULL ) {
/* launch application loop */
fprintf(stdout, "Entering main loop\n" );
for(;;) {
status = aisl_run_cycle(aisl);
if ( status != AISL_SUCCESS )
aisl_sleep(aisl, 500);
}
aisl_free(aisl);
} else {
fprintf(stderr, "Failed to initialize AISL\n");
}
return 0;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+27 -97
View File
@@ -1,107 +1,37 @@
/* ----------------------------------------------------------------------------
* aisl.h - header file for AISL library, part of AISLing Technology
/******************************************************************************
*
* Copyright (c) 2017 by Löwenware Ltd. (https://lowenware.com/)
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
* Authors and maintainers:
* Ilja Kartaschoff <ik@lowenware.com>
******************************************************************************/
/**
* @file aisl/aisl.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Meta header file of AISL
*
* DOCUMENTATION
* This file is not designed to be used as a documentation, but for looking at
* the precise values of constants and definitions.
* Please, for documentation refer to web page https://lowenware.com/aisling/ or
* file READEME.md from library source package.
*
* LICENSE and DISCLAIMER
*
* -------------------------------------------------------------------------- */
* @see https://lowenware.com/aisl/
*/
#ifndef _AISL_H_
#define _AISL_H_
#ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
#define AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
/* system includes ---------------------------------------------------------- */
/* AISL configuration structure */
#include <aisl/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <arpa/inet.h>
/* AISL types and stringifiers */
#include <aisl/types.h>
/* aisl includes ------------------------------------------------------------ */
/* AISL instancing, initialization and processing */
#include <aisl/instance.h>
#include <aisl/status.h>
#include <aisl/event.h>
/* Embedded HTTP(s) servers */
#include <aisl/server.h>
/* HTTP(s) clients */
#include <aisl/client.h>
/* HTTP(s) streaming */
#include <aisl/stream.h>
#include <aisl/handle.h>
#include <aisl/http.h>
/* Control calls ------------------------------------------------------------ */
/* DEPRECATED, use aisl_handle_new instead
* */
aisl_status_t
aisl_init();
/* DEPRECATED, use aisl_handle_free instead
* */
void
aisl_release();
/* Tell library what socket should be opened. Could be called multiple times.
* This function only save passed data. In fact, sockets are being opened only
* inside aisl_run loop.
* @address : host or IP to listen
* @port : port to listen
* */
aisl_status_t
aisl_select(const char *address, int port);
/* Start main loop
* @result : exit code
* */
aisl_status_t
aisl_run( int * flags );
/* Event calls -------------------------------------------------------------- */
/* Add callback to be executed after timeout. If callback function will return
* true, callback will be kept in main loop and raised again, otherwise it will
* be removed
* @cb : callback function: bool callback (void * u_data)
* @usec : delay in milliseconds
* @data : user-defined data to be passed to callback
* */
aisl_status_t
aisl_delay(aisl_callback_t cb, uint32_t msec, void *u_data);
/* Add event listener
* @source : pointer to event source
* @e_id : event identifier
* @cb : callback to be executed
* */
aisl_status_t
aisl_listen(void *source, aisl_event_t e_id, aisl_callback_t cb);
/* Raise event
* @source : pointer to event source data
* @e_id : event identifier
* @... : custom event data
* @result : true if event was handled, false otherwise
* */
bool
aisl_raise(void *source, aisl_event_t e_id, ... );
/* input stream functions --------------------------------------------------- */
const char *
aisl_header_get(aisl_stream_t stream, const char *key);
const char *
aisl_header_get_by_index(aisl_stream_t stream, const char **key, uint32_t i);
/* -------------------------------------------------------------------------- */
#endif
#endif /* !AISL_H */
+74
View File
@@ -0,0 +1,74 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file aisl/client.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislCLient functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
#define AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
#include <aisl/types.h>
/**
* @brief Gets #AislServer instance associated with client.
* @param client an #AislClient instance pointer.
* @return an associated #AislServer pointer.
*/
AislServer
aisl_client_get_server(AislClient client);
/**
* @brief Gets security connection status.
* @param client an #AislClient instance pointer.
* @return true if SSL is enabled and false if disabled.
*/
bool
aisl_client_is_secure(AislClient client);
/**
* @brief Gets client's connection state.
* @param client an #AislClient instance pointer.
* @return true if client is online and false if is offline.
*/
bool
aisl_client_is_online(AislClient client);
/**
* @brief Forcefully closes client's connection.
* @param client an #AislClient instance pointer.
*/
void
aisl_client_disconnect(AislClient client);
/**
* @brief Gets HTTP protocol version.
* @param client an #AislClient instance pointer.
* @return HTTP protocol version
*/
AislHttpVersion
aisl_client_get_http_version(AislClient client);
/**
* @brief Copies #AislClient network address to provided sockaddr_in structure.
* @param client an #AislClient instance pointer.
* @param address a pointer to a sockaddr_in structure
*/
void
aisl_client_get_address(AislClient client, struct sockaddr_in *address);
#endif /* !AISL_CLIENT_H */
+64
View File
@@ -0,0 +1,64 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file aisl/config.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of AISL configuration structures
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
#define AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
#include <aisl/types.h>
#define AISL_CFG_DEFAULTS \
.client_spool_size = 32 \
, .initial_buffer_size = 16536 \
, .client_accept_limit = 1024 \
, .client_silence_timeout = 30 \
/** @brief Server configuration structure
*/
struct aisl_cfg_srv {
const char *host; /**< server IP to listen */
uint16_t port; /**< server port to listen */
bool secure; /**< shall server use TLS */
};
/** @brief SSL configuration structure
*/
struct aisl_cfg_ssl {
const char *host; /**< secure server hostname */
const char *key_file; /**< path to SSL key file */
const char *crt_file; /**< path to SSL certificate file */
};
/** @brief AISL initial configuration structure
*/
struct aisl_cfg {
AislCallback callback; /**< A pointer to #AislCallback event handler */
void *p_ctx; /**< User defined context for #AislCallback */
const struct aisl_cfg_srv *srv; /**< A pointer to array of #aisl_cfg_srv */
const struct aisl_cfg_ssl *ssl; /**< A pointer to array of #aisl_cfg_ssl */
int srv_cnt; /**< Size of #aisl_cfg_srv array */
int ssl_cnt; /**< Size of #aisl_cfg_ssl array */
int client_spool_size; /**< Initial size of client spool */
int initial_buffer_size; /**< Initial size of communication buffer */
int client_accept_limit; /**< Maximal number of concurent clients */
int client_silence_timeout; /**< Client silence timeout */
};
#endif /* !AISL_CONFIG_H */
-128
View File
@@ -1,128 +0,0 @@
#ifndef _AISL_EVENT_H_
#define _AISL_EVENT_H_
#include <stdbool.h>
#include <stdarg.h>
#include <arpa/inet.h>
#include <aisl/stream.h>
#include <aisl/status.h>
#include <aisl/http.h>
/* -------------------------------------------------------------------------- */
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
-76
View File
@@ -1,76 +0,0 @@
#ifndef _AISL_HANDLE_H_
#define _AISL_HANDLE_H_
#include <aisl/status.h>
#include <aisl/event.h>
/* -------------------------------------------------------------------------- */
#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
-104
View File
@@ -1,104 +0,0 @@
#ifndef _AISL_HTTP_H_
#define _AISL_HTTP_H_
/* -------------------------------------------------------------------------- */
typedef enum
{
AISL_HTTP_1_0,
AISL_HTTP_1_1,
AISL_HTTP_2_0
} aisl_http_version_t;
/* -------------------------------------------------------------------------- */
typedef enum {
AISL_HTTP_GET,
AISL_HTTP_PUT,
AISL_HTTP_POST,
AISL_HTTP_HEAD,
AISL_HTTP_TRACE,
AISL_HTTP_DELETE,
AISL_HTTP_OPTIONS,
AISL_HTTP_CONNECT,
AISL_HTTP_PRI
} aisl_http_method_t;
/* -------------------------------------------------------------------------- */
typedef enum
{
/* informational ------------------------------ */
AISL_HTTP_CONTINUE = 100,
AISL_HTTP_SWITCHING_PROTOCOLS,
/* Successful --------------------------------- */
AISL_HTTP_OK = 200,
AISL_HTTP_CREATED,
AISL_HTTP_ACCEPTED,
AISL_HTTP_NON_AUTHORITATIVE_INFORMATION,
AISL_HTTP_NO_CONTENT,
AISL_HTTP_RESET_CONTENT,
AISL_HTTP_PARTIAL_CONTENT,
/* redirection -------------------------------- */
AISL_HTTP_MULTIPLE_CHOICES = 300,
AISL_HTTP_MOVED_PERMANENTLY,
AISL_HTTP_FOUND,
AISL_HTTP_SEE_OTHER,
AISL_HTTP_NOT_MODIFIED,
AISL_HTTP_USE_PROXY,
AISL_HTTP_UNUSED,
AISL_HTTP_TEMPORARY_REDIRECT,
/* client error ------------------------------- */
AISL_HTTP_BAD_REQUEST = 400,
AISL_HTTP_UNAUTHORIZED,
AISL_HTTP_PAYMENT_REQUIRED,
AISL_HTTP_FORBIDDEN,
AISL_HTTP_NOT_FOUND,
AISL_HTTP_METHOD_NOT_ALLOWED,
AISL_HTTP_NOT_ACCEPTABLE,
AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED,
AISL_HTTP_REQUEST_TIMEOUT,
AISL_HTTP_CONFLICT,
AISL_HTTP_GONE,
AISL_HTTP_LENGTH_REQUIRED,
AISL_HTTP_PRECONDITION_FAILED,
AISL_HTTP_REQUEST_ENTITY_TOO_LARGE,
AISL_HTTP_REQUEST_URI_TOO_LONG,
AISL_HTTP_UNSUPPORTED_MEDIA_TYPE,
AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE,
AISL_HTTP_EXPECTATION_FAILED,
/* server error ------------------------------- */
AISL_HTTP_INTERNAL_SERVER_ERROR = 500,
AISL_HTTP_NOT_IMPLEMENTED,
AISL_HTTP_BAD_GATEWAY,
AISL_HTTP_SERVICE_UNAVAILABLE,
AISL_HTTP_GATEWAY_TIMEOUT,
AISL_HTTP_VERSION_NOT_SUPPORTED
} aisl_http_response_t;
/* -------------------------------------------------------------------------- */
const char *
aisl_http_version_to_string(aisl_http_version_t version);
/* -------------------------------------------------------------------------- */
const char *
aisl_http_response_to_string(aisl_http_response_t code);
/* -------------------------------------------------------------------------- */
const char *
aisl_http_secure_to_string( int is_secure );
/* -------------------------------------------------------------------------- */
const char *
aisl_http_method_to_string( aisl_http_method_t method );
/* -------------------------------------------------------------------------- */
#endif
+66
View File
@@ -0,0 +1,66 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file aisl/instance.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislInstance functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230
#define AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230
#include <stdint.h>
#include <stdarg.h>
#include <aisl/types.h>
#include <aisl/config.h>
/**
* @brief Allocates new AISL instance.
*
* @param cfg a pointer to #aisl_cfg_t structure.
* @return an #AislInstance instance pointer.
*/
AislInstance
aisl_new(const struct aisl_cfg *cfg);
/**
* @brief Frees previously allocated pointer of AISL instance.
* @param instance a pointer to #AislInstance instance.
*/
void
aisl_free(AislInstance instance);
/**
* @brief A core function doing all the library routines.
* Designed to be called inside application main loop
* @param instance a pointer to #AislInstance instance.
* @return #AislStatus code.
*/
AislStatus
aisl_run_cycle(AislInstance instance);
/**
* @brief Function to sleep CPU if nothing to do.
* Calls select on all the opened sockets inside.
* @param instance a pointer to #AislInstance instance.
* @param usec a number of miliseconds to wait for any data on sockets.
* @return #AislStatus code.
*/
AislStatus
aisl_sleep(AislInstance instance, uint32_t usec);
#endif /* !AISL_INSTANCE_H */
+48
View File
@@ -0,0 +1,48 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file aisl/server.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislServer functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#define AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#include <arpa/inet.h>
#include <aisl/types.h>
/**
* @brief Function to get appropriate AISL instance pointer from server pointer.
* @param server an #AislServer pointer.
* @return an #AislInstance instance pointer.
*/
AislInstance
aisl_server_get_instance(AislServer server);
/**
* @brief Copies server listen address information to sockaddr_in structure.
* @param server an #AislServer pointer.
* @param address a pointer to sockaddr_in structure.
*/
void
aisl_server_get_address(AislServer server, struct sockaddr_in *address);
/**
* @brief Function to get on and off status of SSL for the #AislServer.
* @param server an #AislServer pointer.
* @return a boolean value representing SSL enabled/disabled state.
*/
bool
aisl_server_get_ssl(AislServer server);
#endif /* !AISL_SERVER_H */
-24
View File
@@ -1,24 +0,0 @@
#ifndef _AISL_STATUS_H_
#define _AISL_STATUS_H_
/* -------------------------------------------------------------------------- */
typedef enum {
AISL_EXTCALL_ERROR = -3,
AISL_SYSCALL_ERROR = -2,
AISL_MALLOC_ERROR = -1,
AISL_SUCCESS = 0,
AISL_IDLE = 1
} aisl_status_t;
/* -------------------------------------------------------------------------- */
const char *
aisl_status_to_string(aisl_status_t status);
/* -------------------------------------------------------------------------- */
#endif
+181 -154
View File
@@ -1,172 +1,199 @@
#ifndef _AISL_STREAM_H_
#define _AISL_STREAM_H_
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
#include <arpa/inet.h>
/* Library statuses */
/* HTTP requests */
#include <aisl/http.h>
#include <aisl/status.h>
/**
* @file aisl/stream.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislStream functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
#define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
/* -------------------------------------------------------------------------- */
#include <stdint.h>
#include <stdarg.h>
#include <aisl/types.h>
typedef struct sockaddr_in * aisl_server_t;
typedef struct sockaddr_in * aisl_client_t;
struct aisl_stream
{
/* DO NOT USE PROPERTIES DIRECTLY IN NEW CODE */
struct sockaddr_in *client;
const char *host;
const char *path;
const char *query;
const char *scheme;
void *u_ptr; /* pointer to bind custom data to stream */
aisl_http_method_t request_method;
};
/* pointer to stream descriptor */
typedef struct aisl_stream * aisl_stream_t;
/* -------------------------------------------------------------------------- */
/* start response to client
* this function should be called before any header or content function
* necessary protocol headers as well as Content-Type and Content-Length
* will be set automaticaly
* @stream : stream instance
* @status : HTTP response status code (default 200)
* @content_type : string with content type (default text/html), NULL -> no
* @content_length : length of content, 0 = no content
* */
aisl_status_t
aisl_response(aisl_stream_t stream, aisl_http_response_t status_code,
const char *content_type,
uint32_t content_length);
/* send all buffered data to client
* ALL responses should always be finished with calling of this method
* @stream : stream instance
* */
aisl_status_t
aisl_flush(aisl_stream_t stream);
/* header functions --------------------------------------------------------- */
/* add custom header to stream
* this function should be called before content functions
* @stream : stream instance
* @key : header key string
* @value : header value string
* */
int
aisl_header(aisl_stream_t stream, const char *key, const char *value);
/* add custom header to stream
* this function should be called before content functions
* @stream : stream instance
* @key : header key string
* @f_value : value format string, same as for printf function
* @... : arguments according to f_value string
* */
int
aisl_header_printf(aisl_stream_t stream, const char *key,
const char *f_value, ...);
/* add custom header to stream
* this function should be called before content functions
* @stream : stream instance
* @key : header key string
* @f_value : value format string, same as for printf function
* @args : arguments macro according to f_value string
* */
int
aisl_header_vprintf(aisl_stream_t stream, const char *key,
const char *format,
va_list args);
/* data response functions -------------------------------------------------- */
/* response formated data to client
* @stream : stream instance
* @format : format string, same as for printf
* @... : arguments according to format string
* @result : number of responed bytes
* */
int
aisl_printf(aisl_stream_t stream, const char *format, ...);
/* response formated data to client
* @stream : stream instance
* @format : format string, same as for printf
* @args : arguments macro according to format string
* @result : number of responed bytes
* */
int
aisl_vprintf(aisl_stream_t stream, const char *format, va_list args);
/* response characters to client
* @stream : stream instance
* @data : characters to be sent
* @d_len : number of characters to send
* @result : number of responed bytes
* */
int
aisl_write(aisl_stream_t s, const char *data, int d_len);
/* response string to client
* @string : string to be sent
* @stream : stream instance
* @result : number of responed bytes
* */
int
aisl_puts(const char *string, aisl_stream_t stream);
/* -------------------------------------------------------------------------- */
void
aisl_cancel(aisl_stream_t s);
/* -------------------------------------------------------------------------- */
#define aisl_stream_get_instance aisl_get_instance
/** @brief Gets a value of #AislStream security flag
* @param stream an #AislStream instance
* @return is true if stream is encrypted and false otherwise
*/
bool
aisl_is_secure(aisl_stream_t s);
aisl_is_secure(AislStream stream);
/* -------------------------------------------------------------------------- */
/** @brief Gets an #AislClient instance associated with the stream
* @param stream an #AislStream instance
* @return an #AislClient instance pointer
*/
AislClient
aisl_get_client(AislStream stream);
/** @brief Gets an #AislServer instance associated with the stream
* @param stream an #AislStream instance
* @return an #AislServer instance pointer
*/
AislServer
aisl_get_server(AislStream stream);
/** @brief Gets an #AislHttpVersion of the stream
* @param stream an #AislStream instance
* @return an #AislHttpVersion value
*/
AislHttpVersion
aisl_get_http_version(AislStream stream);
/** @brief Gets an #AislInstance pointer associated with the stream
* @param stream an #AislStream instance
* @return an #AislInstance pointer
*/
AislInstance
aisl_get_instance(AislStream s);
/** @brief Gets the stream context previously set with #aisl_set_context
* @param stream an #AislStream instance
* @return a pointer to the stream context
*/
void *
aisl_get_context(aisl_stream_t s);
aisl_get_context(AislStream stream);
/* -------------------------------------------------------------------------- */
/** @brief Associate a context pointer with the stream until its lifetime.
* Previously allocated data should be free'd on #AISL_EVENT_STREAM_CLOSE if
* not needed anymore.
* @param stream an #AislStream instance
* @param context a pointer to any user-defined data
*/
void
aisl_set_context(aisl_stream_t s, void * u_ptr);
aisl_set_context(AislStream stream, void *context);
/* -------------------------------------------------------------------------- */
aisl_client_t
aisl_get_client(aisl_stream_t s);
/** @brief A call to start stream data transmission to a client
* @param stream an #AislStream instance
* @return a #AislStatus displaying if stream is ready to be proceed by teh engine
*/
AislStatus
aisl_flush(AislStream stream);
/* -------------------------------------------------------------------------- */
aisl_server_t
aisl_get_server(aisl_stream_t s);
/* -------------------------------------------------------------------------- */
aisl_http_version_t
aisl_get_http_version(aisl_stream_t s);
/* -------------------------------------------------------------------------- */
/** @brief A call to reject the stream. In HTTP 1.X it also closes client's connection
* @param stream an #AislStream instance
*/
void
aisl_reject( aisl_stream_t s);
aisl_reject(AislStream stream);
/* -------------------------------------------------------------------------- */
#endif
/** @brief A call to begin the HTTP response
* @param stream an #AislStream instance
* @param status_code of the HTTP response
* @param content_length in bytes or #AISL_AUTO_LENGTH if length is not knonw yet
* @return #AislStatus code
*/
AislStatus
aisl_response(AislStream stream,
AislHttpResponse status_code,
uint64_t content_length);
/** @brief Adds HTTP header to the stream buffer
* @param stream an #AislStream instance
* @param key of HTTP header
* @param value of HTTP header
* @return a length of data added to the stream buffer
*/
int
aisl_header(AislStream stream, const char *key, const char *value);
/** @brief Adds printf-like formatted HTTP header to the stream buffer
* @param stream an #AislStream instance
* @param key of HTTP header
* @param format of HTTP header value
* @return a length of data added to the stream buffer
*/
int
aisl_header_printf(AislStream stream,
const char *key,
const char *format,
... );
/** @brief Adds vprintf-like formatted HTTP header to the stream buffer
* @param stream an #AislStream instance
* @param key of HTTP header
* @param format of HTTP header value
* @param args list for HTTP header value construction
* @return a length of data added to the stream buffer
*/
int
aisl_header_vprintf(AislStream stream,
const char *key,
const char *format,
va_list args );
/** @brief Adds printf-like formatted HTTP response to the stream buffer
* @param stream an #AislStream instance
* @param format of the HTTP response
* @return a length of data added to the stream buffer
*/
int
aisl_printf(AislStream stream, const char *format, ...);
/** @brief Adds vprintf-like formatted HTTP response to the stream buffer
* @param stream an #AislStream instance
* @param format of the HTTP response
* @param args list for HTTP response construction
* @return a length of data added to the stream buffer
*/
int
aisl_vprintf(AislStream stream, const char *format, va_list args);
/** @brief Adds data of the HTTP response to the stream buffer
* @param stream an #AislStream instance
* @param data a pointer to HTTP response data array
* @param d_len size of the HTTP response data array
* @return a length of data added to the stream buffer
*/
int
aisl_write(AislStream stream, const char *data, int d_len);
/** @brief Adds a null-terminated string to the stream buffer
* @param str_data the HTTP response string
* @param stream an #AislStream instance
* @return a length of data added to the stream buffer
*/
int
aisl_puts(const char *str_data, AislStream stream);
/** @brief Switches triggering of #AISL_EVENT_STREAM_OUTPUT
* @param stream an #AislStream instance
* @param value a true to enable or false to disable (default) triggering
*/
void
aisl_set_output_event(AislStream stream, bool value);
/** @brief Gets state of the #AISL_EVENT_STREAM_OUTPUT triggering
* @param stream an #AislStream instance
* @return true if triggering is enabled and flase otherwise
*/
bool
aisl_get_output_event(AislStream stream);
#endif /* !AISL_STREAM_H */
+226
View File
@@ -0,0 +1,226 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file aisl/types.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of AISL types
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
#define AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#define AISL_AUTO_LENGTH (~0)
/** type casts */
#define AISL_CALLBACK(x) ((AislCallback) x)
/** @brief AISL Instance */
typedef struct aisl_instance * AislInstance;
/** @brief HTTP(s) Server */
typedef struct aisl_server * AislServer;
/** @brief HTTP(s) Client */
typedef struct aisl_client * AislClient;
/** @brief Server<->Client Stream */
typedef struct aisl_stream * AislStream;
/** status return codes */
typedef enum {
AISL_INPUT_ERROR = -4
, AISL_EXTCALL_ERROR = -3
, AISL_SYSCALL_ERROR = -2
, AISL_MALLOC_ERROR = -1
, AISL_SUCCESS = 0
, AISL_IDLE = 1
} AislStatus;
/** @brief Converts #AislStatus code to a null terminated string
* @param status an #AislStatus code
* @return pointer to the string representing #AislStatus
*/
const char *
aisl_status_to_string(AislStatus status);
/** @brief HTTP version enumeration */
typedef enum {
AISL_HTTP_0_9 = 0x0009
, AISL_HTTP_1_0 = 0x0100
, AISL_HTTP_1_1 = 0x0101
, AISL_HTTP_2_0 = 0x0200
} AislHttpVersion;
/** @brief Converts #AislHttpVersion code to a null terminated string
* @param version an #AislHttpVersion code
* @return pointer to the string representing #AislHttpVersion
*/
const char *
aisl_http_version_to_string(AislHttpVersion version);
/** HTTP method enumeration */
typedef enum {
AISL_HTTP_METHOD_UNKNOWN
, AISL_HTTP_GET
, AISL_HTTP_PUT
, AISL_HTTP_POST
, AISL_HTTP_HEAD
, AISL_HTTP_TRACE
, AISL_HTTP_DELETE
, AISL_HTTP_OPTIONS
, AISL_HTTP_CONNECT
, AISL_HTTP_PRI
} AislHttpMethod;
/** @brief Converts #AislHttpMethod code to a null terminated string
* @param method an #AislHttpMethod code
* @return pointer to the string representing #AislHttpMethod
*/
const char *
aisl_http_method_to_string(AislHttpMethod method);
/** @brief HTTP response status enumeration */
typedef enum {
AISL_HTTP_CONTINUE = 100
, AISL_HTTP_SWITCHING_PROTOCOLS
, AISL_HTTP_OK = 200
, AISL_HTTP_CREATED
, AISL_HTTP_ACCEPTED
, AISL_HTTP_NON_AUTHORITATIVE_INFORMATION
, AISL_HTTP_NO_CONTENT
, AISL_HTTP_RESET_CONTENT
, AISL_HTTP_PARTIAL_CONTENT
, AISL_HTTP_MULTIPLE_CHOICES = 300
, AISL_HTTP_MOVED_PERMANENTLY
, AISL_HTTP_FOUND
, AISL_HTTP_SEE_OTHER
, AISL_HTTP_NOT_MODIFIED
, AISL_HTTP_USE_PROXY
, AISL_HTTP_UNUSED
, AISL_HTTP_TEMPORARY_REDIRECT
, AISL_HTTP_BAD_REQUEST = 400
, AISL_HTTP_UNAUTHORIZED
, AISL_HTTP_PAYMENT_REQUIRED
, AISL_HTTP_FORBIDDEN
, AISL_HTTP_NOT_FOUND
, AISL_HTTP_METHOD_NOT_ALLOWED
, AISL_HTTP_NOT_ACCEPTABLE
, AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED
, AISL_HTTP_REQUEST_TIMEOUT
, AISL_HTTP_CONFLICT
, AISL_HTTP_GONE
, AISL_HTTP_LENGTH_REQUIRED
, AISL_HTTP_PRECONDITION_FAILED
, AISL_HTTP_REQUEST_ENTITY_TOO_LARGE
, AISL_HTTP_REQUEST_URI_TOO_LONG
, AISL_HTTP_UNSUPPORTED_MEDIA_TYPE
, AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
, AISL_HTTP_EXPECTATION_FAILED
, AISL_HTTP_INTERNAL_SERVER_ERROR = 500
, AISL_HTTP_NOT_IMPLEMENTED
, AISL_HTTP_BAD_GATEWAY
, AISL_HTTP_SERVICE_UNAVAILABLE
, AISL_HTTP_GATEWAY_TIMEOUT
, AISL_HTTP_VERSION_NOT_SUPPORTED
} AislHttpResponse;
/** @brief Converts #AislHttpResponse code to a null terminated string
* @param code an #AislHttpResponse code
* @return pointer to the string representing #AislHttpResponse
*/
const char *
aisl_http_response_to_string(AislHttpResponse code);
/** @brief AISL events enumeration */
typedef enum {
AISL_EVENT_SERVER_READY = 100
, AISL_EVENT_SERVER_ERROR = 190
, AISL_EVENT_CLIENT_CONNECT = 200
, AISL_EVENT_CLIENT_DISCONNECT = 210
, AISL_EVENT_STREAM_OPEN = 300
, AISL_EVENT_STREAM_HEADER = 310
, AISL_EVENT_STREAM_INPUT = 320
, AISL_EVENT_STREAM_REQUEST = 330
, AISL_EVENT_STREAM_OUTPUT = 340
, AISL_EVENT_STREAM_CLOSE = 350
, AISL_EVENT_STREAM_ERROR = 390
} AislEvent;
/** @brief generic AISL event structure */
struct aisl_evt {
void *source; /**< Pointer to an event source: #AislServer, #AislClient, #AislStream */
AislEvent code; /**< Event code */
AislStatus status; /**< Event status */
};
/** @brief event handler callback definition
* @param evt a pointer to an #aisl_evt structure
* @param ctx a pointer to a context defined by user (see #aisl_cfg)
*/
typedef void
(* AislCallback) (const struct aisl_evt *evt, void *ctx);
/** @brief AISL event structue passed on stream opening */
struct aisl_evt_open {
struct aisl_evt evt; /**< generic #aisl_evt structure */
const char *path; /**< HTTP request path */
const char *query; /**< HTTP request query (GET params) */
AislHttpMethod http_method; /**< HTTP request method */
};
/** @brief AISL event structue passed on HTTP header reception */
struct aisl_evt_header {
struct aisl_evt evt; /**< generic #aisl_evt structure */
const char *key; /**< low case HTTP header name */
const char *value; /**< HTTP header string */
};
/** @brief AISL event structue passed on HTTP request data part received */
struct aisl_evt_input {
struct aisl_evt evt; /**< generic #aisl_evt structure */
const char *data; /**< a pointer to received data array */
int32_t size; /**< data array size */
};
/** @brief Converts #AislEvent code to a null terminated string
* @param evt an #AislEvent code
* @return pointer to the string representing #AislEvent
*/
const char *
aisl_event_to_string(AislEvent evt);
#endif /* !AISL_TYPES_H */
+539
View File
@@ -0,0 +1,539 @@
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef AISL_WITHOUT_SSL
#include <openssl/err.h>
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
#define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
#define BUFFER_SIZE (16*1024)
static void
aisl_client_close(AislClient client, AislStatus status)
{
if (client->fd != -1)
{
aisl_raise(
client->server->instance
, (void *)client
, AISL_EVENT_CLIENT_DISCONNECT
, status
);
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
static AislStatus
aisl_client_parse(AislClient client, char * data, int32_t size)
{
AislStatus result = AISL_SUCCESS;
AislStream s = client->stream;
ParserStatus p = HTTP_PARSER_SUCCESS;
int32_t bytes_left = size;
switch (client->http_version)
{
case AISL_HTTP_0_9:
case AISL_HTTP_1_0:
case AISL_HTTP_1_1:
/* s = client->stream; */
while ( p == HTTP_PARSER_SUCCESS )
{
switch ( aisl_stream_get_state(s) )
{
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
p = HTTP_PARSER_ERROR;
continue;
}
// size now has number of parsed bytes
data += size;
bytes_left -= size;
size = bytes_left;
}
break;
case AISL_HTTP_2_0:
break;
}
switch(p)
{
case HTTP_PARSER_READY:
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_REQUEST
, result
);
break;
case HTTP_PARSER_ERROR:
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_ERROR
, result
);
aisl_client_close(client, result);
return result;
default:
break;
}
if (size)
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
return result;
}
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
* parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0
*/
static AislStatus
aisl_client_input(AislClient client)
{
int l;
char * data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
DPRINTF("SSL_read");
if ( !(client->flags & FLAG_HANDSHAKE) )
{
if ( (l = SSL_accept(client->ssl)) != 1 )
{
l = SSL_get_error(client->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client, AISL_EXTCALL_ERROR);
return AISL_EXTCALL_ERROR;
}
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size) ;
}
else
#endif
l = recv( client->fd, data, size, 0);
if (l > 0)
{
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
}
else if (l<0)
{
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
return AISL_IDLE;
}
else
#endif
{
if(errno == EWOULDBLOCK)
return AISL_IDLE;
DPRINTF("client - %s", strerror(errno));
}
}
/* both: client disconnect + on read error */
/* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
static AislStatus
aisl_client_output(AislClient client)
{
int l;
char * data;
AislStream s = client->stream;
/* while stream is not flushed, we should raise event */
if( aisl_get_output_event(s) )
{
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* buffer->size will be used to carry amount of stored bytes
* */
l = aisl_stream_get_buffer_space(s);
/*
if (bsz < OUTPUT_BUFFER_SIZE)
{
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false;
s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE;
}
*/
if ( !(l < aisl_stream_get_buffer_size(s) / 2) )
{
aisl_raise(
client->server->instance
, (void*)s
, AISL_EVENT_STREAM_OUTPUT
, AISL_SUCCESS
);
}
}
data = aisl_stream_get_data(s, &l);
if ( !l )
return AISL_IDLE;
#ifdef AISL_WITHOUT_SSL
l = send( client->fd, data, l, 0);
#else
l = (client->ssl) ?
SSL_write(client->ssl, data, l) :
send( client->fd, data, l, 0);
#endif
if (l > 0)
{
aisl_stream_shift(s, l);
/*
if (s->state == STREAM_RESPONSE_READY && / * flushed * /
s->buffer->size == 0) / * all sent * /
*/
if ( aisl_stream_is_done(s) )
{
/* buffer_clear(s->buffer, 0); */
/* data has been sent */
if (client->flags & FLAG_KEEPALIVE)
{
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++);
if (client->stream != NULL )
return AISL_SUCCESS;
/* in case of malloc error it will not be error as long as request was
* handled and we just close the connection.
*/
}
aisl_client_close(client, AISL_SUCCESS);
}
return AISL_SUCCESS;
}
/* l < 0 */
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
if ( SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE )
return AISL_IDLE;
}
else
#endif
{
if (errno == EWOULDBLOCK)
return AISL_IDLE;
}
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
AislClient
aisl_client_new( AislServer server,
int fd,
struct sockaddr_in * addr )
{
AislClient client;
AislStream stream;
if ( (client = calloc(1, sizeof (struct aisl_client))) != NULL )
{
DPRINTF("client alocated");
memcpy(&client->address, addr, sizeof (struct sockaddr_in));
client->server = server;
client->fd = fd;
client->next_id = 2;
client->http_version = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1)
{
DPRINTF("client buffer alocated");
memcpy(&client->out, &client->in, sizeof (struct buffer));
stream = aisl_stream_new(client, 0);
if (stream != NULL)
{
client->stream = stream;
DPRINTF("client stream alocated");
#ifdef AISL_WITHOUT_SSL
return client;
#else
SSL_CTX * ssl_ctx;
if ( !server->ssl )
return client;
ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
if ((client->ssl = SSL_new(ssl_ctx)) != NULL )
{
SSL_set_fd(client->ssl, fd);
return client;
}
#endif
}
}
aisl_client_free(client);
}
return NULL;
}
void
aisl_client_free(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
SSL_free(client->ssl);
#endif
if (client->in.data)
free(client->in.data);
/* out buffer is a shared part of input buffer, so no need to free it */
if (client->stream)
aisl_stream_free(client->stream);
free(client);
}
AislStatus
AislClientouch(AislClient client, int32_t timeout)
{
AislStatus result = AISL_IDLE,
status = AISL_IDLE;
/* input */
if (client->flags & FLAG_CAN_READ)
{
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */
if (client->flags & FLAG_CAN_WRITE)
{
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
/*
if ((client->http_version==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) &&
(client_input(client)) ) result = true;
*/
/* output */
/*
s = list_index(client->streams, client->ostream);
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) )
result = client_output(client);
*/
/* update timestamp */
if (result == AISL_IDLE)
result = status;
if (result == AISL_SUCCESS)
client->timestamp = time(NULL);
else
{
time_t now;
time(&now);
if ( !(now - client->timestamp < timeout) )
{
aisl_client_close(client, result);
}
}
return result;
}
int
aisl_client_get_socket(AislClient client)
{
return client->fd;
}
bool
aisl_client_get_keepalive(AislClient client)
{
return (client->flags & FLAG_KEEPALIVE);
}
void
aisl_client_set_keepalive(AislClient client, bool value)
{
if (value)
client->flags |= FLAG_KEEPALIVE;
else
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
AislServer
aisl_client_get_server(AislClient client)
{
return client->server;
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_secure(AislClient client)
{
#ifdef AISL_WITHOUT_SSL
return false;
#else
return (client->ssl == NULL) ? false : true;
#endif
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_online(AislClient client)
{
return (client->fd == -1) ? false : true;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_disconnect(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_client_get_http_version(AislClient client)
{
return client->http_version;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_get_address( AislClient client, struct sockaddr_in * address)
{
memcpy(address, &client->address, sizeof (struct sockaddr_in));
}
+176
View File
@@ -0,0 +1,176 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file buffer.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Buffer module source file
*
* @see https://lowenware.com/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "buffer.h"
static int32_t
buffer_set_size(struct buffer *buffer, int32_t new_size)
{
if (!buffer->size || new_size != buffer->size) {
char *data;
if (new_size) {
if (new_size % 4096) {
new_size = (new_size / 4096 + 1) * 4096;
}
} else {
new_size = 16*1024;
}
if ((data = realloc(buffer->data, new_size)) != NULL) {
buffer->data = data;
buffer->size = new_size;
} else {
new_size = -1;
}
}
return new_size;
}
int32_t
buffer_init(struct buffer *buffer, int32_t size)
{
if ((size = buffer_set_size(buffer, size)) != -1)
buffer->used = 0;
return size;
}
void
buffer_release(struct buffer *buffer)
{
if (buffer->data) {
free(buffer->data);
buffer->data = NULL;
}
buffer->used = 0;
buffer->size = 0;
}
static int32_t
buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size)
{
int32_t to_move = buffer->used - offset;
if (to_move < 0) {
return -1;
} else if (to_move) {
memmove(&buffer->data[offset+size], &buffer->data[offset], to_move);
}
return size;
}
int32_t
buffer_insert(struct buffer *buffer, int32_t offset, const char *data,
int32_t size)
{
int32_t result;
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
if ( (result = buffer_set_size(buffer, buffer->size + size)) != -1) {
if ((result = buffer_move_offset(buffer, offset, size)) != -1) {
memcpy(&buffer->data[offset], data, size);
buffer->used += result;
}
}
return result;
}
int32_t
buffer_append_printf(struct buffer *buffer, const char *format, ...)
{
int32_t result;
va_list args;
va_start(args, format);
result = buffer_append_vprintf(buffer, format, args);
va_end(args);
return result;
}
int32_t
buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args)
{
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
int32_t space, result;
va_list cp_args;
va_copy(cp_args, args);
space = buffer->size - buffer->used;
result = vsnprintf(&buffer->data[buffer->used], space, format, args);
if (result < space) { /* enough space */
buffer->used += result;
} else {
result = buffer_set_size(buffer, buffer->size + result - space + 1);
if (result != -1)
result = buffer_append_vprintf(buffer, format, cp_args);
}
va_end(cp_args);
return result;
}
int32_t
buffer_append(struct buffer *buffer, const char *data, int32_t size)
{
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
int32_t used, space;
used = buffer->used,
space = buffer->size - used;
if (size > space) { /* enough space */
if (buffer_set_size(buffer, buffer->size + size - space) == -1)
return -1;
}
memcpy(&buffer->data[used], data, size);
buffer->used += size;
return size;
}
int32_t
buffer_shift(struct buffer *buffer, int32_t offset)
{
int32_t used = buffer->used - offset;
if (offset > 0) {
if (offset < used) {
memmove(buffer->data, &buffer->data[offset], used);
} else {
used = 0;
}
buffer->used = used;
}
return used;
}
+59
View File
@@ -0,0 +1,59 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/buffer.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of struct buffer and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9
#define AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9
#include <stdint.h>
#include <stdarg.h>
struct buffer {
char *data;
int32_t size;
int32_t used;
};
int32_t
buffer_init(struct buffer *bs, int32_t size);
void
buffer_release(struct buffer *bs );
int32_t
buffer_insert(struct buffer *bs, int32_t offset, const char *data, int32_t size);
int32_t
buffer_append_printf(struct buffer *bs, const char *format, ...);
int32_t
buffer_append_vprintf(struct buffer *bs, const char *format, va_list args);
int32_t
buffer_append(struct buffer *bs, const char *data, int32_t size);
int32_t
buffer_shift(struct buffer *bs, int32_t size);
#endif /* !AISL_BUFFER_H */
+446
View File
@@ -0,0 +1,446 @@
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#if AISL_WITH_SSL == 1
#include <openssl/err.h>
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
#define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
#define BUFFER_SIZE (16*1024)
static void
aisl_client_close(AislClient client, AislStatus status)
{
if (client->fd != -1) {
aisl_raise(client->server->instance, (void *)client,
AISL_EVENT_CLIENT_DISCONNECT, status );
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
static AislStatus
aisl_client_parse(AislClient client, char *data, int32_t size)
{
AislStatus result = AISL_SUCCESS;
AislStream s = client->stream;
ParserStatus p = HTTP_PARSER_SUCCESS;
int32_t data_size;
switch (client->http_version) {
case AISL_HTTP_0_9:
case AISL_HTTP_1_0:
case AISL_HTTP_1_1:
while (p == HTTP_PARSER_SUCCESS) {
data_size = size;
switch (aisl_stream_get_state(s)) {
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
DPRINTF("misleading request length");
p = HTTP_PARSER_ERROR;
continue;
}
// size now has number of parsed bytes
DPRINTF("%d bytes parsed, %d bytes left", (data_size - size), size);
data += (data_size - size);
}
break;
case AISL_HTTP_2_0:
break;
}
switch(p) {
case HTTP_PARSER_READY:
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
client->stream->content_length = AISL_AUTO_LENGTH;
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST,
result);
break;
case HTTP_PARSER_ERROR:
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR,
result);
aisl_client_close(client, result);
return result;
default:
break;
}
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
return result;
}
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
* parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0
*/
static AislStatus
aisl_client_input(AislClient client)
{
int l;
char *data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
#if AISL_WITH_SSL == 1
if (client->ssl) {
DPRINTF("SSL_read");
if (!(client->flags & FLAG_HANDSHAKE)) {
if ( (l = SSL_accept(client->ssl)) != 1 ) {
l = SSL_get_error(client->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client, AISL_EXTCALL_ERROR);
return AISL_EXTCALL_ERROR;
}
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size);
} else
#endif
{
l = recv( client->fd, data, size, 0);
}
if (l > 0) {
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
} else if (l<0) {
#if AISL_WITH_SSL == 1
if (client->ssl) {
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
return AISL_IDLE;
} else
#endif
{
if(errno == EWOULDBLOCK)
return AISL_IDLE;
DPRINTF("client - %s", strerror(errno));
}
}
/* both: client disconnect + on read error */
/* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
static AislStatus
aisl_client_output(AislClient client)
{
int l;
char *data;
AislStream s = client->stream;
/* while stream is not flushed, we should raise event */
if(aisl_get_output_event(s)) {
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* buffer->size will be used to carry amount of stored bytes
* */
l = aisl_stream_get_buffer_space(s);
/*
if (bsz < OUTPUT_BUFFER_SIZE)
{
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false;
s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE;
}
*/
if (!(l < aisl_stream_get_buffer_size(s) / 2)) {
aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT,
AISL_SUCCESS);
}
}
data = aisl_stream_get_data(s, &l);
if ( !l )
return AISL_IDLE;
#if AISL_WITH_SSL == 1
l = (client->ssl) ? SSL_write(client->ssl, data, l) :
send(client->fd, data, l, 0);
#else
l = send(client->fd, data, l, 0);
#endif
if (l > 0) {
aisl_stream_shift(s, l);
if (aisl_stream_is_done(s)) {
/* data has been sent */
if (client->flags & FLAG_KEEPALIVE) {
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++);
if (client->stream != NULL )
return AISL_SUCCESS;
/* in case of malloc error it will not be an error as long as the
* request was handled and we just close the connection.
*/
}
aisl_client_close(client, AISL_SUCCESS);
}
return AISL_SUCCESS;
}
/* l < 0 */
#if AISL_WITH_SSL == 1
if (client->ssl) {
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
} else
#endif
{
if (errno == EWOULDBLOCK)
return AISL_IDLE;
}
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
AislClient
aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr)
{
AislClient client;
AislStream stream;
if ((client = calloc(1, sizeof (struct aisl_client))) != NULL) {
DPRINTF("client alocated");
memcpy(&client->address, addr, sizeof (struct sockaddr_in));
client->server = server;
client->fd = fd;
client->next_id = 2;
client->http_version = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) {
DPRINTF("client buffer alocated");
memcpy(&client->out, &client->in, sizeof (struct buffer));
stream = aisl_stream_new(client, 0);
if (stream != NULL) {
client->stream = stream;
DPRINTF("client stream alocated");
#if AISL_WITH_SSL == 1
if (server->ssl) {
SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) {
SSL_set_fd(client->ssl, fd);
return client;
}
} else {
return client;
}
#else
return client;
#endif
}
}
aisl_client_free(client);
}
return NULL;
}
void
aisl_client_free(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
#if AISL_WITH_SSL == 1
if (client->ssl)
SSL_free(client->ssl);
#endif
if (client->in.data)
free(client->in.data);
if (client->stream)
aisl_stream_free(client->stream);
free(client);
}
AislStatus
aisl_client_touch(AislClient client, int32_t timeout)
{
AislStatus result, status;
result = AISL_IDLE;
status = AISL_IDLE;
/* input */
if (client->flags & FLAG_CAN_READ) {
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */
if (client->flags & FLAG_CAN_WRITE) {
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
if (result == AISL_IDLE)
result = status;
if (result != AISL_SUCCESS) {
time_t now;
time(&now);
if (!(now - client->timestamp < timeout)) {
aisl_client_close(client, result);
}
} else {
client->timestamp = time(NULL);
}
return result;
}
int
aisl_client_get_socket(AislClient client)
{
return client->fd;
}
bool
aisl_client_get_keepalive(AislClient client)
{
return (client->flags & FLAG_KEEPALIVE);
}
void
aisl_client_set_keepalive(AislClient client, bool value)
{
if (value)
client->flags |= FLAG_KEEPALIVE;
else
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
AislServer
aisl_client_get_server(AislClient client)
{
return client->server;
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_secure(AislClient client)
{
#if AISL_WITH_SSL == 1
return (client->ssl == NULL) ? false : true;
#else
return false;
#endif
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_online(AislClient client)
{
return (client->fd == -1) ? false : true;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_disconnect(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_client_get_http_version(AislClient client)
{
return client->http_version;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_get_address(AislClient client, struct sockaddr_in *address)
{
memcpy(address, &client->address, sizeof (struct sockaddr_in));
}
+106
View File
@@ -0,0 +1,106 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file client.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of aisl_client structure and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
#include <arpa/inet.h>
#if AISL_WITH_SSL == 1
#include <openssl/ssl.h>
#endif
#include <aisl/client.h>
#include "buffer.h"
#define AISL_CLIENT(x) ((AislClient) x)
struct aisl_client {
struct sockaddr_in address; /**< Client's address structure. */
struct buffer in; /**< Client's input buffer. */
struct buffer out; /**< Client's output buffer. */
AislServer server; /**< Server instance. */
AislStream stream; /**< Pending client's stream. */
#if AISL_WITH_SSL == 1
SSL *ssl; /**< SSL pointer for HTTPS. */
#endif
time_t timestamp; /**< Last communication timestamp. */
int next_id; /**< Stream id generator (even). */
int flags; /**< Client's flag bitmask. */
int fd; /**< Client's socket descriptor. */
AislHttpVersion http_version; /**< Client's http_version version. */
};
/**
* @brief Constructor for #AislClient instance.
* @param server an #AislServer instance pointer.
* @param fd a client socket descriptor.
* @param addr a pointer to client's address structure.
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
*/
AislClient
aisl_client_new(AislServer server,
int fd,
struct sockaddr_in *addr );
/**
* @brief Destructor for #AislClient instance.
* @param client an #AislClient instance pointer.
*/
void
aisl_client_free(AislClient client);
/**
* @brief Does all HTTP client routines.
* Reads and parses requests, writes responses.
* @param client an #AislClient instance pointer.
* @param timeout an allowed client silence time in seconds.
* @return #AislStatus code.
*/
AislStatus
aisl_client_touch(AislClient client, int32_t timeout);
/**
* @Brief Checks if client is about to keep connection alive.
* @param client an #AislClient instance pointer.
* @return true if keepalive mode is on, otherwise false.
*/
bool
aisl_client_get_keepalive(AislClient client);
/**
* @Brief Sets if connection with client must be kept alive.
* @param client an #AislClient instance pointer.
* @param value a true to enable keepalive mode, false to disable.
*/
void
aisl_client_set_keepalive(AislClient client, bool value);
/**
* @brief Gets socket descriptor associated with #AislClient instance.
* @param client an #AislClient instance pointer.
* @return a client socket descriptor.
*/
int
aisl_client_get_socket(AislClient client);
#endif /* !AISL_CLIENT_H */
+37
View File
@@ -0,0 +1,37 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file debug.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief AISL debug module
*
* @see https://lowenware.com/
*/
#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
#if AISL_WITH_DEBUG == 1
#include <stdio.h>
#define DPRINTF(...) do { \
fprintf(stderr, "* AISL: "); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while(0) \
#else
#define DPRINTF(...)
#endif
#endif /* !AISL_DEBUG_H */
+428
View File
@@ -0,0 +1,428 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file http.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief HTTP module source file
*
* @see https://lowenware.com/aisl/
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
#include "stream.h"
#include "debug.h"
#include "http.h"
static AislHttpMethod
http_method_from_string(const char *method, int32_t length)
{
int i;
AislHttpMethod methods[3] = {0, 0, 0};
switch(length) {
case 3:
methods[0] = AISL_HTTP_GET;
methods[1] = AISL_HTTP_PUT;
methods[2] = AISL_HTTP_PRI;
break;
case 4:
methods[0] = AISL_HTTP_POST;
methods[1] = AISL_HTTP_HEAD;
break;
case 5:
methods[0] = AISL_HTTP_TRACE;
break;
case 6:
methods[0] = AISL_HTTP_DELETE;
break;
case 7:
methods[0] = AISL_HTTP_OPTIONS;
methods[1] = AISL_HTTP_CONNECT;
break;
}
for (i=0; i<sizeof (methods)/sizeof (AislHttpMethod); i++) {
if (!(methods[i]))
break;
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
return methods[i];
}
return AISL_HTTP_METHOD_UNKNOWN;
}
static AislHttpVersion
http_version_from_string(const char *version_string)
{
if (strncmp(version_string, "HTTP/", 5)==0) {
if (strncmp(&version_string[5], "0.9", 3)==0) return AISL_HTTP_0_9;
if (strncmp(&version_string[5], "1.0", 3)==0) return AISL_HTTP_1_0;
if (strncmp(&version_string[5], "1.1", 3)==0) return AISL_HTTP_1_1;
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
}
return 0;
}
/* Library Level */
ParserStatus
http_10_parse_request(char *data, int32_t *p_size, AislStream stream)
{
/* STEP 1. Split data according to HTTP request format
*
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
* ^ ^ ^ ^ ^ ^ ^ ^
* | | | | | | | |
* | | | | | | | +--- newline
* | | | | | | +------------- version
* | | | | | +------------------------- query
* | | | | +------------------------------------- path
* | | | +--------------------------------------- port
* | | +----------------------------------------------------- host
* | +------------------------------------------------------------ uri
* +---------------------------------------------------------------- method
*/
char *uri = NULL,
*uri_end = NULL,
*host = NULL,
*port = NULL,
*path = NULL,
*query = NULL,
*version = NULL,
*newline = NULL,
*method = data,
*method_end = NULL;
AislHttpMethod http_method;
AislHttpVersion http_version;
int32_t size = *p_size;
while(!newline && size--) {
switch(*data)
{
case ' ':
if (!method_end)
method_end = data;
else if (path && !uri_end)
uri_end = data;
break;
case ':':
if (uri && !host) {
host = data+3;
} else if (host && !port) {
port = data+1;
} else if (version) {
DPRINTF("bad character in HTTP version (%c)", *data);
return HTTP_PARSER_ERROR;
}
break;
case '/':
if (!path && data > host) {
path = data;
if (!uri)
uri = path;
} else if (version && data-version != 4) {
DPRINTF("wrong HTTP version length");
return HTTP_PARSER_ERROR;
}
break;
case '?':
if (!query) {
query = data+1;
} else if (version) {
DPRINTF("bad character in HTTP version (%c)", *data);
return HTTP_PARSER_ERROR;
}
break;
case '\n':
newline = data;
break;
case '\r':
if (!version) {
DPRINTF("unexpected end of HTTP request");
return HTTP_PARSER_ERROR;
}
break;
default:
if (!uri && method_end) {
uri = data;
} else if (!version && uri_end) {
version = data;
} else if (version && data-version > 7) {
DPRINTF("bad HTTP version length");
return HTTP_PARSER_ERROR;
}
}
data++;
}
/* STEP 2. Verify splitting was completed */
/* Was request sent? */
if (!newline)
return HTTP_PARSER_HUNGRY;
/* Check mandatory parts presence */
if (!method_end || !path || !uri_end || !version) {
DPRINTF("parser error: method=%d, path=%d, uri_end=%d, version=%d",
(method_end ? 1 : 0), (path ? 1 : 0), (uri_end ? 1 : 0),
(version ? 1: 0));
return HTTP_PARSER_ERROR;
}
*method_end = 0;
*newline = 0;
*uri_end = 0;
http_method = http_method_from_string(method, method_end - method);
if (http_method == AISL_HTTP_METHOD_UNKNOWN) {
DPRINTF("invalid HTTP method");
return HTTP_PARSER_ERROR;
}
if ((http_version = http_version_from_string(version))==0) {
DPRINTF("invalid HTTP version");
return HTTP_PARSER_ERROR;
}
if (query) {
*(query - 1) = 0;
} else {
query = uri_end;
}
if (host) {
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) {
DPRINTF("invalid HTTP uri");
return HTTP_PARSER_ERROR;
}
if (port)
*(port - 1) = 0;
}
stream->client->http_version = http_version;
aisl_stream_set_request(stream, http_method, path, query);
if (host)
aisl_stream_set_header(stream, "host", host);
/* how many characters has been read */
*(p_size) = size;
return HTTP_PARSER_SUCCESS;
}
ParserStatus
http_10_parse_header(char *data, int32_t *p_size, AislStream stream)
{
int32_t size = *p_size;
char *key = data,
*colon = NULL,
*val = NULL,
*val_end = NULL,
*newline = NULL;
DPRINTF("parse header: %p, %d, %02X %02X", data, *p_size, *data & 0xFF, *(data+1) & 0xFF);
while(!newline && size-- ) {
switch(*data) {
case ' ':
if (val && !val_end)
val_end = data;
break;
case ':':
if (!colon) {
if (colon == key) {
DPRINTF("parser error: nameless HTTP header");
return HTTP_PARSER_ERROR;
}
colon = data;
}
break;
case '\n':
newline = data;
case '\r':
if (!val_end && val)
val_end = data;
break;
default:
if (!colon) {
*data = tolower(*data);
} else if (!val) {
if (colon)
val = data;
}
if (val_end)
val_end = NULL;
}
data++;
}
if (!newline)
return HTTP_PARSER_HUNGRY;
/* DPRINTF("(%p == %p); *key == 0x%02x", newline, key, *key & 0xFF); */
if (colon && val && val_end) {
*colon = 0;
*val_end = 0;
aisl_stream_set_header(stream, key, val);
*p_size = size;
return HTTP_PARSER_SUCCESS;
} else if (newline == key || (newline == key+1 && *key == '\r')) {
*p_size = size;
DPRINTF("end of headers received");
return (aisl_stream_set_end_of_headers(stream) == 0) ?
HTTP_PARSER_READY : HTTP_PARSER_SUCCESS;
}
DPRINTF("parser error: invalid HTTP header");
return HTTP_PARSER_ERROR;
}
ParserStatus
http_10_parse_body(char *data, int32_t *p_size, AislStream stream)
{
int32_t size = *p_size;
if (!size)
return HTTP_PARSER_HUNGRY;
*p_size = 0;
switch (aisl_stream_set_body(stream, data, size)) {
case 0:
return HTTP_PARSER_READY;
case -1:
DPRINTF("parser error: invalid HTTP body length");
return HTTP_PARSER_ERROR;
default:
return HTTP_PARSER_SUCCESS;
}
}
/* API Level */
__attribute__ ((visibility ("default") ))
const char *
aisl_http_version_to_string(AislHttpVersion version)
{
switch (version) {
case AISL_HTTP_0_9:
return "HTTP/0.9";
case AISL_HTTP_1_0:
return "HTTP/1.0";
case AISL_HTTP_1_1:
return "HTTP/1.1";
case AISL_HTTP_2_0:
return "HTTP/2.0";
}
return "";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_http_response_to_string(AislHttpResponse code)
{
switch (code) {
/* most common for faster behavior */
case AISL_HTTP_OK: return "OK";
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
/* informational */
case AISL_HTTP_CONTINUE: return "Continue";
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
/* Successful */
case AISL_HTTP_CREATED: return "Created";
case AISL_HTTP_ACCEPTED: return "Accepted";
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
case AISL_HTTP_NO_CONTENT: return "No Content";
case AISL_HTTP_RESET_CONTENT: return "Reset Content";
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
/* redirection */
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
case AISL_HTTP_FOUND: return "Found";
case AISL_HTTP_SEE_OTHER: return "See other";
case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
case AISL_HTTP_USE_PROXY: return "Use Proxy";
case AISL_HTTP_UNUSED: return "(unused)";
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
/* client error */
case AISL_HTTP_BAD_REQUEST: return "Bad Request";
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
case AISL_HTTP_FORBIDDEN: return "Forbidden";
case AISL_HTTP_NOT_FOUND: return "Not Found";
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
case AISL_HTTP_CONFLICT: return "Conflict";
case AISL_HTTP_GONE: return "Gone";
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
/* server error */
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
}
return "";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_http_method_to_string( AislHttpMethod method )
{
switch(method) {
case AISL_HTTP_GET: return "GET";
case AISL_HTTP_PUT: return "PUT";
case AISL_HTTP_POST: return "POST";
case AISL_HTTP_HEAD: return "HEAD";
case AISL_HTTP_TRACE: return "TRACE";
case AISL_HTTP_DELETE: return "DELETE";
case AISL_HTTP_OPTIONS: return "OPTIONS";
case AISL_HTTP_CONNECT: return "CONNECT";
case AISL_HTTP_PRI: return "PRI";
case AISL_HTTP_METHOD_UNKNOWN: break;
}
return "";
}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
*
* Copyright (c) 2017-%YEAR% by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file http.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of HTTP parser module
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01
#define AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01
#include <aisl/types.h>
typedef enum {
HTTP_PARSER_SUCCESS
, HTTP_PARSER_READY
, HTTP_PARSER_HUNGRY
, HTTP_PARSER_ERROR
} ParserStatus;
ParserStatus
http_10_parse_request(char *data, int32_t *size, AislStream stream);
ParserStatus
http_10_parse_header(char *data, int32_t *size, AislStream stream);
ParserStatus
http_10_parse_body(char *data, int32_t *size, AislStream stream);
#endif /* !AISL_HTTP_H */
+301
View File
@@ -0,0 +1,301 @@
/*
* src/instance.c
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/select.h>
#include <sys/time.h>
#if AISL_WITH_SSL == 1
#include <openssl/err.h>
#endif
#include "debug.h"
#include "str-utils.h"
#include "buffer.h"
#include "client.h"
#include "server.h"
#include "stream.h"
#include "instance.h"
#if AISL_WITH_SSL == 1
static uint32_t m_instances = 0;
static struct aisl_ssl *
aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl)
{
SSL_CTX *ssl_ctx = NULL;
struct aisl_ssl **list, *ssl;
list = instance->ssl;
/* lookup for existing contexts */
while ((ssl = *list)) {
if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 &&
ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0
) {
ssl_ctx = ssl->ctx;
break;
}
list++;
}
ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file,
ssl_ctx);
if (ssl) {
if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) {
aisl_ssl_free(ssl);
ssl = NULL;
}
}
return ssl;
}
#endif
/* Initialization functions */
__attribute__ ((visibility ("default") ))
AislInstance
aisl_new(const struct aisl_cfg *cfg)
{
int i;
AislInstance instance;
/* allocate root structure */
if (!(instance = calloc(1, sizeof (struct aisl_instance))))
goto finally;
/* allocate servers */
if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer))))
goto release;
for (i = 0; i < cfg->srv_cnt; i++) {
DPRINTF("new srv %d", i);
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
goto release;
}
#if AISL_WITH_SSL == 1
if ((m_instances++) == 0) {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl))))
goto release;
for (i=0; i<cfg->ssl_cnt; i++) {
DPRINTF("new ssl %d", i);
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
goto release;
}
#endif
if (list_init(&instance->client_spool, cfg->client_spool_size) == -1)
goto release;
instance->accept_limit = cfg->client_accept_limit;
instance->silence_timeout = cfg->client_silence_timeout;
instance->callback = cfg->callback;
instance->p_ctx = cfg->p_ctx;
goto finally;
release:
aisl_free(instance);
instance = NULL;
finally:
return instance;
}
__attribute__ ((visibility ("default") ))
void
aisl_free(AislInstance instance)
{
if (instance->srv) {
AislServer * srv = instance->srv;
while (*srv) {
aisl_server_free(*(srv++));
}
free(instance->srv);
}
list_release(&instance->client_spool, (list_destructor_t)aisl_client_free);
#if AISL_WITH_SSL == 1
if (instance->ssl) {
struct aisl_ssl **ssl = instance->ssl;
while (*ssl) {
aisl_ssl_free(*(ssl++));
}
free(instance->ssl);
}
if ((--m_instances) == 0) {
EVP_cleanup();
}
#endif
free(instance);
}
#if AISL_WITH_SSL == 1
SSL_CTX *
aisl_get_ssl_ctx(AislInstance instance, const char * host)
{
struct aisl_ssl **list, *ssl;
list = instance->ssl;
if (host) {
while ((ssl = *list)) {
if (str_cmpi(ssl->host, host) == 0) {
return ssl->ctx;
}
list++;
}
}
return NULL;
}
#endif
void
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt)
{
#if AISL_WITH_STRINGIFIERS == 1
DPRINTF("! %s", aisl_event_to_string(evt->code));
#else
DPRINTF("! %d", evt->code);
#endif
if (instance->callback)
instance->callback(evt, instance->p_ctx);
}
void
aisl_raise(AislInstance instance,
void *source,
AislEvent code,
AislStatus status)
{
struct aisl_evt evt;
evt.source = source;
evt.code = code;
evt.status = status;
aisl_raise_evt(instance, &evt);
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_run_cycle(AislInstance instance)
{
AislStatus result = AISL_IDLE;
AislServer *list, srv;
AislClient cli;
int32_t i;
list = instance->srv;
while ((srv = *list)) {
cli = NULL;
if (aisl_server_touch(srv, &cli) != AISL_IDLE)
result = AISL_SUCCESS;
if (cli) {
DPRINTF("Accepted %p", (void*)cli);
if (list_append(&instance->client_spool, cli) == -1)
aisl_client_free(cli);
}
list++;
}
for (i=0; i < instance->client_spool.count; i++) {
cli = LIST_INDEX(instance->client_spool, i);
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
result = AISL_SUCCESS;
if (!aisl_client_is_online(cli)) {
aisl_client_free( cli );
list_remove_index(&instance->client_spool, i);
}
}
return result;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_sleep(AislInstance instance, uint32_t usec)
{
AislServer *list, srv;
int sd, maxfd = 0;
size_t i;
struct timeval timeout = {0,usec};
memset(&timeout, 0, sizeof (struct timeval));
timeout.tv_usec = usec;
fd_set fs;
FD_ZERO (&fs);
list = instance->srv;
while ((srv = *list)) {
sd = aisl_server_get_socket(srv);
if (sd != -1) {
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
list++;
}
for (i=0; i<instance->client_spool.count; i++) {
AislClient c = LIST_INDEX(instance->client_spool, i);
sd = aisl_client_get_socket(c);
if (sd != -1) {
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
}
switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) {
case -1:
return AISL_SYSCALL_ERROR;
case 0:
return AISL_IDLE;
default:
return AISL_SUCCESS;
}
}
+70
View File
@@ -0,0 +1,70 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/instance.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of aisl_instance structure and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#if AISL_WITH_SSL == 1
#include <openssl/ssl.h>
#endif
#include <aisl/instance.h>
#include "ssl.h"
#include "list.h"
struct aisl_instance {
AislServer *srv;
#if AISL_WITH_SSL == 1
struct aisl_ssl * *ssl;
#endif
struct list client_spool;
AislCallback callback;
void *p_ctx;
uint32_t accept_limit;
uint32_t silence_timeout;
uint32_t buffer_size;
};
#if AISL_WITH_SSL == 1
/**
* @brief Gets SSL context for appropriate server name.
* @param instance a pointer to #AislInstance instance.
* @param server_name a null-terminated string with server name or NULL.
* @return a pointer to SSL context
*/
SSL_CTX *
aisl_get_ssl_ctx(AislInstance instance, const char *server_name);
#endif
/**
* @brief Raises event from source.
* @param instance a pointer to #AislInstance instance.
* @param evt a pointer to event structure.
*/
void
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt);
void
aisl_raise(AislInstance instance,
void *source,
AislEvent code,
AislStatus status);
#endif /* !AISL_INSTANCE_H */
+97
View File
@@ -0,0 +1,97 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file list.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief List module source file
*
* @see https://lowenware.com/aisl/
*/
#include <stdlib.h>
#include "debug.h"
#include "list.h"
int32_t
list_init(struct list *list, int32_t size)
{
if ((list->data = calloc(size, sizeof (void*))) != NULL) {
list->size = size;
list->count = 0;
return 0;
}
return -1;
}
void
list_release(struct list *list, list_destructor_t destructor)
{
if (list->data) {
if (destructor) {
int32_t i;
for (i=0; i<list->count; i++) {
void *ptr;
if ((ptr = list->data[i]) != NULL)
destructor(list->data[i]);
}
}
free(list->data);
}
}
int32_t
list_append(struct list *list, void *entry)
{
int32_t pos = list->count;
DPRINTF("pos = %d", pos);
if (!(pos < list->size)) {
DPRINTF("extending, size = %d", list->size);
void **new_list;
int32_t new_size = pos + 1;
DPRINTF("extending, ptr = %p", (void*)list->data);
if ((new_list = realloc( list->data, new_size*sizeof (void*) )) == NULL)
return -1;
list->data = new_list;
list->size = new_size;
}
list->data[pos]=entry;
list->count++;
return pos;
}
void *
list_remove_index(struct list *list, int32_t index)
{
void *result;
if (index < list->count) {
int32_t i, c = --list->count;
result = list->data[index];
for (i = index; i<c; i++) {
list->data[i]=list->data[i+1];
}
}
else
result = NULL;
return result;
}
+51
View File
@@ -0,0 +1,51 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/list.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief List module header file
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1
#define AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1
#include <stdint.h>
#define LIST_INDEX(L, I) ( L.data[ I ] )
struct list {
void **data;
int32_t size;
int32_t count;
};
typedef void
(* list_destructor_t)(void *list_item);
int32_t
list_init(struct list *lst, int32_t size);
void
list_release(struct list *lst, list_destructor_t destructor);
int32_t
list_append(struct list *lst, void * entry);
void *
list_remove_index(struct list *lst, int32_t index);
#endif /* !AISL_LIST_H */
+169
View File
@@ -0,0 +1,169 @@
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <openssl/ssl.h>
#ifdef __APPLE__
#include <sys/ioctl.h>
#endif
#include "debug.h"
#include "str-utils.h"
#include "instance.h"
#include "client.h"
#include "server.h"
/**
* @brief Creates TCP server socket, binds to address and starts to listen.
* @param server a pointer to #AislServer instance.
* @return #AislStatus code.
*/
static AislStatus
aisl_server_open(AislServer server)
{
int fd, s_opt = 1;
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd != -1) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt,
sizeof (int));
#ifdef __APPLE__
ioctl(fd, FIONBIO, (char *)&s_opt);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif
s_opt = sizeof (struct sockaddr_in);
if (bind(fd, (struct sockaddr *) &server->address, s_opt) == 0) {
if (listen(fd, SOMAXCONN) == 0) {
server->fd = fd;
return AISL_SUCCESS;
}
}
close(fd);
}
return AISL_SYSCALL_ERROR;
}
/**
* @brief Tries to accept a new client.
* @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #AislClient instance pointer.
* @return #AislStatus code.
*/
static AislStatus
aisl_server_accept(AislServer server, AislClient *p_client )
{
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof (struct sockaddr_in);
fd = accept(server->fd, (struct sockaddr *)&addr, &len);
if (fd != -1) {
int flags;
DPRINTF("accepted fd=%d", fd);
if ((flags = fcntl(fd, F_GETFL, 0)) != -1) {
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == 0) {
return (!(*p_client = aisl_client_new(server, fd, &addr))) ?
AISL_MALLOC_ERROR : AISL_SUCCESS;
}
}
close(fd);
} else if (errno == EWOULDBLOCK) {
return AISL_IDLE;
}
return AISL_SYSCALL_ERROR;
}
/* Library Level ------------------------------------------------------------ */
AislStatus
aisl_server_touch(AislServer server, AislClient *p_client)
{
AislStatus result;
if (server->fd == -1) {
if ((result = aisl_server_open(server)) != AISL_IDLE) {
aisl_raise(server->instance, server, ((result == AISL_SUCCESS) ?
AISL_EVENT_SERVER_READY : AISL_EVENT_SERVER_ERROR), result);
}
} else {
result = aisl_server_accept(server, p_client);
}
return result;
}
int
aisl_server_get_socket(AislServer server)
{
return server->fd;
}
AislServer
aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance)
{
AislServer server;
if ((server = calloc(1, sizeof (struct aisl_server))) != NULL) {
server->instance = instance;
server->fd = -1;
server->address.sin_family = AF_INET;
server->address.sin_addr.s_addr = inet_addr(cfg_srv->host);
server->address.sin_port = htons(cfg_srv->port);
server->ssl = cfg_srv->secure;
}
return server;
}
void
aisl_server_free(AislServer server)
{
if (server) {
if ( server->fd != -1) {
close(server->fd);
server->fd=-1;
}
free(server);
}
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
void
aisl_server_get_address(AislServer server, struct sockaddr_in *address)
{
memcpy(address, &server->address, sizeof (struct sockaddr_in));
}
#if AISL_WITH_SSL == 1
__attribute__ ((visibility ("default") ))
bool
aisl_server_get_ssl(AislServer server)
{
return server->ssl;
}
#endif
+83
View File
@@ -0,0 +1,83 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*//*
* @file src/server.h
*
* Copyright (c) 2017-2019 by Löwenware Ltd.
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#include <aisl/config.h>
#include <aisl/server.h>
#define AISL_SERVER(x) ((AislServer) x)
/**
* @brief HTTP(s) server data structure represented by #AislServer pointer.
*/
struct aisl_server {
struct sockaddr_in address; /**< TCP server address to listen to. */
AislInstance instance; /**< Associated AISL instance pointer. */
int fd; /**< System socket descriptor. */
bool ssl; /**< SSL enabled/disabled flag. */
};
/**
* @brief Allocates and instance of #AislServer.
* @param cfg_srv a pointer to server configuration structure.
* @param instance a pointer to #AislInstance instance.
* @return a pointer to #AislServer instance.
*/
AislServer
aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance);
/**
* @brief Frees memory allocated for #AislServer instance.
* @param server a pointer to #AislServer instance.
*/
void
aisl_server_free(AislServer server);
/**
* @brief Does server routines.
* Tries to open server if it was not opened yet, otherwise tries to accept a
* new client connecting to the server.
* @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #AislClient instance pointer.
* @return #AislStatus code:
* - AISL_SUCCESS if client connected,
* - AISL_IDLE if there is no client to connect,
* - AISL_SYSCALL_ERROR if error occured.
*/
AislStatus
aisl_server_touch(AislServer server, AislClient *p_client);
/**
* @brief Gets a socket descriptor associated with HTTP client.
* @param server a pointer to #AislServer instance.
* @return a client socket descriptor.
*/
int
aisl_server_get_socket(AislServer server);
#endif /* !AISL_SERVER_H */
+102
View File
@@ -0,0 +1,102 @@
/*
* ssl.c
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Distributed under terms of the MIT license.
*/
#include <openssl/err.h>
#include "str-utils.h"
#include "instance.h"
#include "ssl.h"
#if AISL_WITH_SSL == 1
static int
aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance )
{
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name);
if (ctx)
SSL_set_SSL_CTX(ssl, ctx);
(void)ptr;
return SSL_TLSEXT_ERR_OK;
}
SSL_CTX *
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance)
{
SSL_CTX * ctx;
if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) {
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx );
SSL_CTX_set_tlsext_servername_arg( ctx, p_instance );
if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0))
goto except;
if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0))
goto except;
ssl->ctx = ctx;
return ctx;
}
except:
SSL_CTX_free(ctx);
return NULL;
}
struct aisl_ssl *
aisl_ssl_new(const char *key_file,
const char *crt_file,
const char *host,
SSL_CTX *ctx)
{
struct aisl_ssl *ssl;
if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) {
if ((ssl->host = str_copy( host ? host : "*" )) != NULL) {
if (ctx) {
ssl->ctx = ctx;
return ssl;
} else {
if ((ssl->key_file = str_copy(key_file)) != NULL) {
if ((ssl->crt_file = str_copy(crt_file)) != NULL) {
return ssl;
}
}
}
}
aisl_ssl_free(ssl);
}
return NULL;
}
void
aisl_ssl_free( struct aisl_ssl *ssl )
{
if (ssl->host)
free(ssl->host);
if (ssl->key_file) {
free(ssl->key_file);
SSL_CTX_free(ssl->ctx);
}
if (ssl->crt_file)
free(ssl->crt_file);
free(ssl);
}
#endif
+54
View File
@@ -0,0 +1,54 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*//*
* src/ssl.h
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E
#define AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E
#include <aisl/config.h>
#include <aisl/types.h>
#include <openssl/ssl.h>
struct aisl_ssl {
char *key_file;
char *crt_file;
char *host;
SSL_CTX *ctx;
};
struct aisl_ssl *
aisl_ssl_new( const char *key_file,
const char *crt_file,
const char *host,
SSL_CTX *ctx );
SSL_CTX *
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance);
void
aisl_ssl_free(struct aisl_ssl *ssl);
#endif /* !AISL_SSL_H */
+52
View File
@@ -0,0 +1,52 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file str-utils.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief String utilities source file
*
* @see https://lowenware.com/aisl/
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "str-utils.h"
char *
str_copy(const char *source)
{
int l = strlen(source);
char *result = malloc(l+1);
if (result)
strcpy(result, source);
return result;
}
int
str_cmpi(const char *s1, const char *s2)
{
char c1, c2, r = 0;
do {
c1 = tolower(*(s1++));
c2 = tolower(*(s2++));
if ((r = c1-c2) != 0)
break;
} while (c1 != 0);
return r;
}
+26
View File
@@ -0,0 +1,26 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file str-utils.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief String utilities header file
*
* @see https://lowenware.com/aisl/
*/
#ifndef STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E
#define STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E
char *
str_copy(const char *source);
int
str_cmpi(const char *anycase, const char *lowcase);
#endif /* !STR_UTILS_H */
+616
View File
@@ -0,0 +1,616 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include "instance.h"
#include "client.h"
#include "server.h"
#include "str-utils.h"
#include "debug.h"
#include "stream.h"
#define FLAG_FLUSHED (1<<0)
#define FLAG_OUTPUT_CHUNKED (1<<1)
#define FLAG_SERVER_HEADER_SENT (1<<2)
#define FLAG_CONTENT_TYPE_HEADER_SENT (1<<3)
#define FLAG_CONTENT_LENGTH_HEADER_SENT (1<<4)
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
/* Library level */
static void
aisl_stream_reset(AislStream stream, bool initial)
{
if (!initial) {
aisl_raise(aisl_get_instance(stream), (void*) stream,
AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
}
buffer_release(&stream->buffer);
stream->u_ptr = NULL;
stream->content_length = 0; //AISL_AUTO_LENGTH;
stream->head_offset = 0;
stream->flags = 0;
stream->state = AISL_STREAM_STATE_IDLE;
stream->http_response = AISL_HTTP_OK;
}
AislStream
aisl_stream_new(AislClient client, int id)
{
AislStream stream = calloc(1, sizeof (struct aisl_stream));
if (stream) {
stream->id = id;
stream->client = client;
aisl_stream_reset(stream, true);
}
return stream;
}
void
aisl_stream_free(AislStream stream)
{
aisl_stream_reset(stream, false);
free(stream);
}
int32_t
aisl_stream_get_buffer_space(AislStream stream)
{
return stream->buffer.size - stream->buffer.used;
}
int32_t
aisl_stream_get_buffer_size(AislStream stream)
{
return stream->buffer.size;
}
char *
aisl_stream_get_data(AislStream stream, int32_t *p_length)
{
*p_length = stream->buffer.used;
return stream->buffer.data;
}
void
aisl_stream_shift(AislStream stream, int32_t offset)
{
buffer_shift(&stream->buffer, offset);
}
bool
aisl_stream_is_done(AislStream stream)
{
return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE);
}
AislStreamState
aisl_stream_get_state(AislStream stream)
{
return stream->state;
}
void
aisl_stream_set_request(AislStream stream,
AislHttpMethod http_method,
const char *path,
const char *query)
{
struct aisl_evt_open on_open;
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method),
path, query);
on_open.evt.code = AISL_EVENT_STREAM_OPEN;
on_open.evt.source = (void *) stream;
on_open.evt.status = AISL_SUCCESS;
on_open.http_method = http_method;
on_open.path = path;
on_open.query = query;
aisl_raise_evt(aisl_get_instance(stream), (struct aisl_evt *)&on_open);
}
void
aisl_stream_set_header(AislStream stream, const char *key, const char *value)
{
struct aisl_evt_header on_header;
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
return;
if (strcmp(key, "content-length") == 0) {
stream->content_length = strtoll(value, NULL, 10);
} else if (strcmp(key, "connection") == 0) {
aisl_client_set_keepalive(stream->client,
(str_cmpi(value, "close")==0) ? false : true);
}
DPRINTF("%s: %s", key, value);
on_header.evt.code = AISL_EVENT_STREAM_HEADER;
on_header.evt.source = (void *) stream;
on_header.evt.status = AISL_SUCCESS;
on_header.key = key;
on_header.value = value;
aisl_raise_evt(aisl_get_instance(stream),
(struct aisl_evt *) &on_header);
}
int
aisl_stream_set_end_of_headers(AislStream stream)
{
int result;
DPRINTF("stream->content_length == %"PRIu64"", stream->content_length);
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
result = (stream->content_length != 0);
stream->state = (result) ? AISL_STREAM_STATE_WAIT_BODY :
AISL_STREAM_STATE_READY;
} else {
result = 2;
}
return result;
}
int
aisl_stream_set_body(AislStream stream, const char *data, int32_t size)
{
int result;
if (stream->state == AISL_STREAM_STATE_WAIT_BODY) {
if (!(stream->content_length < size)) {
struct aisl_evt_input on_input;
stream->content_length -= size;
if (stream->content_length == 0) {
stream->state = AISL_STREAM_STATE_READY;
result = 0;
} else {
result = 1;
}
on_input.evt.code = AISL_EVENT_STREAM_INPUT;
on_input.evt.source = (void *)stream;
on_input.evt.status = AISL_SUCCESS;
on_input.data = data;
on_input.size = size;
aisl_raise_evt(stream->client->server->instance,
(struct aisl_evt *) &on_input);
} else {
result = -1;
}
} else {
result = 2;
}
return result;
}
/* API Level */
/* Why it was here?
static int
aisl_stream_write(AislStream stream, const char * data, uint32_t d_len)
{
return buffer_add( &stream->buffer, data, d_len);
}
__attribute__ ((visibility ("default") ))
void
aisl_cancel(AislStream stream)
{
aisl_client_close( stream->client );
}
*/
__attribute__ ((visibility ("default") ))
void *
aisl_get_context(AislStream s)
{
return s->u_ptr;
}
__attribute__ ((visibility ("default") ))
void
aisl_set_context(AislStream s, void *u_ptr)
{
s->u_ptr = u_ptr;
}
__attribute__ ((visibility ("default") ))
bool
aisl_is_secure(AislStream stream)
{
return aisl_client_is_secure(stream->client);
}
__attribute__ ((visibility ("default") ))
AislClient
aisl_get_client(AislStream s)
{
return s->client;
}
__attribute__ ((visibility ("default") ))
AislServer
aisl_get_server(AislStream s)
{
return aisl_client_get_server(s->client);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_get_http_version(AislStream s)
{
return aisl_client_get_http_version(s->client);
}
__attribute__ ((visibility ("default") ))
void
aisl_reject(AislStream s)
{
aisl_client_disconnect( s->client );
}
static AislStatus
aisl_start_response(AislStream stream)
{
return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH);
}
static AislStatus
aisl_stream_close_headers(AislStream stream)
{
int32_t l;
if (aisl_start_response(stream) == AISL_MALLOC_ERROR)
return AISL_MALLOC_ERROR;
if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) {
l = buffer_append(&stream->buffer, "Server: AISL\r\n", 14);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_SERVER_HEADER_SENT;
}
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
l = buffer_append(&stream->buffer,
"Content-Type: text/html; encoding=utf-8\r\n", 41);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (stream->content_length != AISL_AUTO_LENGTH) {
l = buffer_append_printf(&stream->buffer, "Content-Length: %"PRIu64"\r\n",
stream->content_length);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
}
}
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n",
(aisl_client_get_keepalive(stream->client) ? "keepalive" : "close"));
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
}
if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1)
return AISL_MALLOC_ERROR;
stream->body_offset = stream->buffer.used;
stream->state = AISL_STREAM_STATE_SEND_BODY;
return AISL_SUCCESS;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len)
{
int32_t l;
/* check if those headers were already sent */
if (stream->state > AISL_STREAM_STATE_READY)
return AISL_IDLE;
stream->http_response = rs_code;
stream->content_length = c_len;
buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0);
l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n",
aisl_http_version_to_string(stream->client->http_version), rs_code,
aisl_http_response_to_string(rs_code));
if (l == -1)
return AISL_MALLOC_ERROR;
stream->state = AISL_STREAM_STATE_SEND_HEADER;
return AISL_SUCCESS;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_flush(AislStream s)
{
if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
char hdr[ 40 ];
uint64_t c_len;
int32_t l;
if (s->body_offset) {
c_len = s->buffer.used - s->body_offset;
l = snprintf(hdr, sizeof (hdr), "Content-Length: %"PRIu64"\r\n", c_len);
l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l);
if (l == -1)
return AISL_MALLOC_ERROR;
} else {
aisl_stream_close_headers(s);
}
s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
}
s->state = AISL_STREAM_STATE_DONE;
s->flags |= FLAG_FLUSHED;
return AISL_SUCCESS;
}
static int32_t
aisl_stream_verify_header(AislStream stream, const char *key, const char *value)
{
if (stream->state < AISL_STREAM_STATE_SEND_HEADER) {
if (aisl_start_response(stream) != AISL_SUCCESS)
return -1;
} else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) {
return 0;
}
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
if (str_cmpi(key, "connection")==0) {
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
if (value) {
aisl_client_set_keepalive(stream->client,
(str_cmpi(value, "keepalive") == 0));
}
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
if (str_cmpi(key, "content-type") == 0) {
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (str_cmpi(key, "content-length") == 0) {
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (str_cmpi(key, "content-length")==0) {
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
return 1;
}
}
return 1;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header(AislStream stream, const char *key, const char *value)
{
int32_t result;
if ( (result = aisl_stream_verify_header( stream, key, value )) != 1)
return result;
result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value);
return result;
/* For debug purposes
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL )
{
ret = strlen(pch);
if ( buffer_insert(
&stream->buffer,
stream->end_of_headers,
pch,
ret
) == -1 )
{
ret = -1;
}
else
stream->end_of_headers += ret;
free(pch);
}
else
ret = -1;
return ret;
*/
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header_printf(AislStream stream, const char *key, const char *format, ...)
{
int32_t result;
va_list args;
va_start(args, format);
result = aisl_header_vprintf( stream, key, format, args );
va_end(args);
return result;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header_vprintf(AislStream stream,
const char *key,
const char *format,
va_list args)
{
int32_t result, l;
if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1)
return result;
result = buffer_append_printf( &stream->buffer, "%s: ", key );
if (result != -1) {
l = buffer_append_vprintf( &stream->buffer, format, args );
if (l != -1) {
result += l;
if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) {
result += l;
return result;
}
}
}
return -1;
}
__attribute__ ((visibility ("default") ))
int
aisl_printf(AislStream stream, const char *format, ...)
{
int result;
va_list arg;
va_start(arg, format);
result = aisl_vprintf(stream, format, arg);
va_end(arg);
return result;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_vprintf(AislStream stream, const char *format, va_list args)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
return buffer_append_vprintf(&stream->buffer, format, args);
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_write(AislStream stream, const char *data, int32_t d_len)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
if (d_len == -1)
d_len = strlen(data);
return buffer_append(&stream->buffer, data, d_len);
}
__attribute__ ((visibility ("default") ))
int
aisl_puts(const char *str, AislStream stream)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
return aisl_write( stream, str, -1);
}
__attribute__ ((visibility ("default") ))
AislInstance
aisl_get_instance(AislStream stream)
{
return stream->client->server->instance;
}
__attribute__ ((visibility ("default") ))
void
aisl_set_output_event(AislStream stream, bool value)
{
if (value)
stream->flags |= FLAG_OUTPUT_CHUNKED;
else if (stream->flags & FLAG_OUTPUT_CHUNKED)
stream->flags &= ~FLAG_OUTPUT_CHUNKED;
}
__attribute__ ((visibility ("default") ))
bool
aisl_get_output_event(AislStream stream)
{
return (stream->flags & FLAG_OUTPUT_CHUNKED);
}
+106
View File
@@ -0,0 +1,106 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/#ifndef AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3
#define AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3
#include <aisl/types.h>
#include <aisl/stream.h>
#include "buffer.h"
#define AISL_STREAM(x) ((AislStream) x)
typedef enum
{
AISL_STREAM_STATE_IDLE
, AISL_STREAM_STATE_WAIT_HEADER
, AISL_STREAM_STATE_WAIT_BODY
, AISL_STREAM_STATE_READY
, AISL_STREAM_STATE_SEND_HEADER
, AISL_STREAM_STATE_SEND_BODY
, AISL_STREAM_STATE_DONE
} AislStreamState;
struct aisl_stream {
struct buffer buffer;
AislClient client;
void *u_ptr;
uint64_t content_length;
int32_t head_offset;
int32_t body_offset;
int id;
int flags;
AislHttpResponse http_response;
AislStreamState state;
};
AislStream
aisl_stream_new(AislClient client, int id);
void
aisl_stream_free(AislStream stream);
int32_t
aisl_stream_get_buffer_space(AislStream stream);
int32_t
aisl_stream_get_buffer_size(AislStream stream);
char *
aisl_stream_get_data(AislStream stream, int32_t *p_length);
void
aisl_stream_shift(AislStream stream, int32_t offset);
bool
aisl_stream_is_done(AislStream stream);
AislStreamState
aisl_stream_get_state(AislStream stream);
void
aisl_stream_set_request(AislStream stream,
AislHttpMethod http_method,
const char *path,
const char *query );
void
aisl_stream_set_header(AislStream stream, const char *key, const char *value);
int
aisl_stream_set_end_of_headers(AislStream stream);
int
aisl_stream_set_body(AislStream stream, const char *data, int32_t size);
#endif /* !AISL_STREAM_H */
+58
View File
@@ -0,0 +1,58 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file types.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Types stringifiers module
*
* @see https://lowenware.com/aisl/
*/
#include <aisl/types.h>
#if AISL_WITH_STRINGIFIERS == 1
__attribute__ ((visibility ("default") ))
const char *
aisl_status_to_string(AislStatus status)
{
switch(status) {
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
case AISL_SUCCESS: return "AISL_SUCCESS";
case AISL_IDLE: return "AISL_IDLE";
}
return "UNKNOWN";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_event_to_string(AislEvent evt_code)
{
switch(evt_code) {
case AISL_EVENT_SERVER_READY: return "SERVER READY";
case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR";
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT";
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT";
case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN";
case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER";
case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT";
case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST";
case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT";
case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE";
case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR";
}
return "UNKNOWN";
}
#endif
+19
View File
@@ -0,0 +1,19 @@
###############################################################################
#
# Copyright (c) 2017-2019 by Löwenware Ltd
# Please, refer LICENSE file for legal information
#
###############################################################################
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: libaisl
URL: https://lowenware.com/aisl/
Description: Asynchronous Internet Server Library
Version: 1.0.0
Libs: -L${libdir} -laisl
Libs.private: -lcrypto -lssl
Cflags: -I${includedir}
-367
View File
@@ -1,367 +0,0 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/select.h>
#include <sys/time.h>
#include <aisl/aisl.h>
#include <cStuff/list.h>
#include <cStuff/str-utils.h>
#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));
}
/* -------------------------------------------------------------------------- */
-129
View File
@@ -1,129 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#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;
}
/* -------------------------------------------------------------------------- */
-49
View File
@@ -1,49 +0,0 @@
#ifndef _AISL_BUFFER_H_
#define _AISL_BUFFER_H_
#include <sys/types.h>
/* -------------------------------------------------------------------------- */
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
-460
View File
@@ -1,460 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <aisl/aisl.h>
#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->state<STREAM_REQUEST_READY) &&
(client_input(self)) ) result = true;
/* output */
s = list_index(self->streams, 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;
}
/* -------------------------------------------------------------------------- */
-67
View File
@@ -1,67 +0,0 @@
#ifndef _AISL_CLIENT_H_
#define _AISL_CLIENT_H_
#include <time.h>
#include <arpa/inet.h>
#include <aisl/aisl.h>
#include <aisl/http.h>
#include <cStuff/list.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#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
-28
View File
@@ -1,28 +0,0 @@
#include <aisl/event.h>
__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";
}
-48
View File
@@ -1,48 +0,0 @@
#ifndef _AISL_GLOBALS_H_
#define _AISL_GLOBALS_H_
#pragma GCC diagnostic ignored "-Wuninitialized"
#include <aisl/handle.h>
/* 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
-802
View File
@@ -1,802 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <openssl/err.h>
#include <aisl/handle.h>
#include <cStuff/str-utils.h>
#include <cStuff/list.h>
#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; i<self->crypters->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; i<self->crypters->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; i<self->servers->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; i<self->clients->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);
}
-61
View File
@@ -1,61 +0,0 @@
#ifndef _AISL_HANDLE_H__
#define _AISL_HANDLE_H__
#include <stdbool.h>
#include <stdarg.h>
#include <aisl/handle.h>
#include <cStuff/list.h>
#include <openssl/ssl.h>
#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
-112
View File
@@ -1,112 +0,0 @@
#include <aisl/http.h>
/* -------------------------------------------------------------------------- */
__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 "";
}
/* -------------------------------------------------------------------------- */
-490
View File
@@ -1,490 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <cStuff/str-utils.h>
#include <aisl/http.h>
#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;
}
-52
View File
@@ -1,52 +0,0 @@
#ifndef _AISL_PARSER_H_
#define _AISL_PARSER_H_
#include <aisl/aisl.h>
#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
-188
View File
@@ -1,188 +0,0 @@
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <cStuff/str-utils.h>
#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);
}
}
/* -------------------------------------------------------------------------- */
-47
View File
@@ -1,47 +0,0 @@
#ifndef _AISL_SERVER_H_
#define _AISL_SERVER_H_
#include <aisl/status.h>
#include <aisl/handle.h>
#include <arpa/inet.h>
#ifdef __APPLE__
#include <fcntl.h>
#include <sys/ioctl.h>
#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
-21
View File
@@ -1,21 +0,0 @@
#include <aisl/status.h>
/* -------------------------------------------------------------------------- */
__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 "";
}
/* -------------------------------------------------------------------------- */
-196
View File
@@ -1,196 +0,0 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <aisl/status.h>
#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 );
}
/* -------------------------------------------------------------------------- */
-93
View File
@@ -1,93 +0,0 @@
#ifndef _AISL_STREAM_H__
#define _AISL_STREAM_H__
#include <stdbool.h>
#include <aisl/stream.h>
#include <cStuff/list.h>
#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
+65
View File
@@ -0,0 +1,65 @@
#
# config.mk
# Löwenware Makefile Config, 2019-03-02 17:35
#
PREFIX ?= /usr/local
PKG_CONFIG ?= pkg-config
PROJECT_NAME = aisl
# Version
PROJECT_VERSION_MAJOR = 1
PROJECT_VERSION_MINOR = 0
PROJECT_VERSION_TWEAK = 5
PROJECT_VERSION_LABEL = 0
#SRC_DIR = src
#SDK_DIR = sdk
#OUT_DIR = ./build
# Source files
PROJECT_SOURCES := \
$(SRC_DIR)/instance.c \
$(SRC_DIR)/server.c \
$(SRC_DIR)/client.c \
$(SRC_DIR)/stream.c \
$(SRC_DIR)/http.c \
$(SRC_DIR)/ssl.c \
$(SRC_DIR)/list.c \
$(SRC_DIR)/str-utils.c \
$(SRC_DIR)/buffer.c \
$(SRC_DIR)/types.c \
# includes
PROJECT_INCLUDES = -I./ \
-I./include \
`$(PKG_CONFIG) --cflags openssl` \
# libraries
PROJECT_LIBRARIES = \
`$(PKG_CONFIG) --libs openssl` \
# compilation macro options:
AISL_WITH_DEBUG ?= 0 # disable debug output
AISL_WITH_SSL ?= 1 # enable SSL support
AISL_WITH_STRINGIFIERS ?= 1 # enable *_to_string functions
# flags
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
PROJECT_CFLAGS += -DAISL_WITH_DEBUG=$(AISL_WITH_DEBUG)
PROJECT_CFLAGS += -DAISL_WITH_SSL=$(AISL_WITH_SSL)
PROJECT_CFLAGS += -DAISL_WITH_STRINGIFIERS=$(AISL_WITH_STRINGIFIERS)
# PROJECT_LDFLAGS = -L
# vim:ft=make
#
-97
View File
@@ -1,97 +0,0 @@
#!/bin/bash
# -----------------------------------------------------------------------------
#
# CMake Wrapper v.1.0 for Linux
# (c) Copyright Löwenware Ltd. (https://lowenware.com/)
#
# -----------------------------------------------------------------------------
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT="aisl"
PROJECT_VERSION=$(cat ${ABSOLUTE_PATH}/version | sed 's/\([0-9]\{1,5\}.[0-9]\{1,5\}.[0-9]\{1,5\}\).*/\1/')
PREFIX="/usr"
SYSCONF="/etc"
DIR_BUILD="build"
DIR_ROOT="root"
# -----------------------------------------------------------------------------
function project_clean {
echo "Cleaning..."
if [ -d ./$DIR_BUILD ]; then
rm -Rf ./$DIR_BUILD/*
else
mkdir ./$DIR_BUILD
fi
if [ -d ./$DIR_ROOT ]; then
rm -Rf ./$DIR_ROOT/*
else
mkdir ./$DIR_ROOT
fi
}
# -----------------------------------------------------------------------------
function project_compile {
CMAKE="cmake"
if [[ "$OSTYPE" == "darwin"* ]]; then
ov="1.0.2n"
ov_p="-DOPENSSL_INCLUDE_DIRS=/usr/local/Cellar/openssl/${ov}/include -DOPENSSL_CRYPTO_LIBRARY=/usr/local/Cellar/openssl/${ov}/lib/libcrypto.dylib -DOPENSSL_SSL_LIBRARY=/usr/local/Cellar/openssl/${ov}/lib/libssl.dylib -DOPENSSL_LIBRARY_DIRS=/usr/local/Cellar/openssl/${ov}/lib"
CMAKE="cmake ${ov_p}"
fi
echo ${CMAKE}
${CMAKE} -B./$DIR_BUILD -H./ -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_DEBUG=1
cd $DIR_BUILD/
make
make DESTDIR=../$DIR_ROOT install
cd ..
}
# -----------------------------------------------------------------------------
case $1 in
clean)
project_clean
;;
compile)
project_compile
;;
build)
project_clean
project_compile
;;
install)
cmake -DWITH_EVERYTHING=1 -B./$DIR_BUILD -H./ -DCMAKE_INSTALL_PREFIX=$PREFIX
cd $DIR_BUILD
sudo make install
cd ..
;;
deploy)
DEPLOY_PATH="${PROJECT}-${PROJECT_VERSION}"
mkdir ${2}${DEPLOY_PATH}
cp -R ${ABSOLUTE_PATH}/{include,library,LICENSE,AUTHORS,version,README.md,cmake*,CMakeLists.txt,cStuff} ${2}${DEPLOY_PATH}
rm ${2}${DEPLOY_PATH}/cStuff/.git
CUR_DIR=$(pwd)
cd $2
tar cfz ${DEPLOY_PATH}.tar.gz ${DEPLOY_PATH}
cd $CUR_DIR
rm -Rf ${2}${DEPLOY_PATH}
echo "_version ${PROJECT_VERSION}"
;;
*)
echo "Usage: ./project.sh (compile|build|clean|install)"
;;
esac
# -----------------------------------------------------------------------------
-1
View File
@@ -1 +0,0 @@
0.3.4-alpha