Compare commits

...

16 Commits

70 changed files with 8601 additions and 4239 deletions

3
.gitignore vendored
View File

@ -7,7 +7,6 @@ demo/*
bin/* bin/*
obj/* obj/*
webstuff-* webstuff-*
Makefile
tmp tmp
tmp/* tmp/*
*.tar.gz *.tar.gz
@ -16,6 +15,8 @@ tmp/*
*.gz *.gz
*.bz2 *.bz2
pkg/* pkg/*
doc/html
vgcore.*
include/webstuff_bak include/webstuff_bak

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "cStuff"]
path = cStuff
url = https://github.com/lowenware/cStuff.git

View File

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}
)

90
Makefile Normal file
View File

@ -0,0 +1,90 @@
#
# Makefile
# Ilja Kartašov, 2019-03-02 17:32
#
.POSIX:
# Project directories
SRC_DIR ?= src
SDK_DIR ?= sdk
OUT_DIR ?= build
LIB_DIR ?= lib
DESTDIR ?=
# Project definition
include project.mk
# Examples submodule
include examples.mk
# CFLAGS
CFLAGS := \
$(PROJECT_INCLUDES) \
-std=c99 \
-pedantic \
-Wall \
-Wmissing-prototypes \
-Wstrict-prototypes \
-Wold-style-definition \
-O2 \
-s \
-fvisibility=hidden \
-DVERSION_MAJOR=$(PROJECT_VERSION_MAJOR) \
-DVERSION_MINOR=$(PROJECT_VERSION_MINOR) \
-DVERSION_TWEAK=$(PROJECT_VERSION_TWEAK) \
-DVERSION_LABEL=$(PROJECT_VERSION_LABEL) \
$(CPPFLAGS) \
$(CFLAGS) \
$(PROJECT_CFLAGS) \
LDFLAGS := \
$(PROJECT_LIBRARIES) \
$(LDFLAGS) \
$(PROJECT_LDFLAGS) \
SOURCE_LIST := $(wildcard $(PROJECT_SOURCES))
OBJECT_FILES := $(addprefix $(OUT_DIR)/o_, ${SOURCE_LIST:.c=.o})
library: dirs $(OBJECT_FILES)
$(info linking target: $@)
@$(CC) -shared -o $(OUT_DIR)/lib$(PROJECT_NAME).so $(OBJECT_FILES) $(LDFLAGS)
$(info done: $@)
build/o_%.o: %.c
$(info compiling file: $<)
@$(CC) $(CFLAGS) -fpic -c $< -o $@
dirs:
$(info preparing: build folders)
@mkdir -p $(OUT_DIR)/o_$(SRC_DIR)
@mkdir -p $(OUT_DIR)/o_$(SDK_DIR)
clean:
$(info cleaning: build files)
@rm -Rf $(OUT_DIR)
@rm -Rf ./vgcore.*
doc:
doxygen doc/api-reference.conf
all: library examples doc
default: library
.PHONY: all dirs clean doc install
install: library
$(info installing files)
@mkdir -p $(DESTDIR)$(PREFIX)/$(LIB_DIR)
@mkdir -p $(DESTDIR)$(PREFIX)/include
@cp $(OUT_DIR)/lib$(PROJECT_NAME).so $(DESTDIR)$(PREFIX)/$(LIB_DIR)
@cp -R include/aisl $(DESTDIR)$(PREFIX)/include
# vim:ft=make
#

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

1
cStuff

@ -1 +0,0 @@
Subproject commit a423fa7a6dfbd637f3c0b248123f682456fccad7

View File

@ -1,7 +0,0 @@
if(DEFINED CMAKE_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ")
add_definitions(
-DDEBUG
)
endif()

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}"
)
# -----------------------------------------------------------------------------

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}"
)

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}
)

2482
doc/api-reference.conf Normal file

File diff suppressed because it is too large Load Diff

21
doc/footer.html Normal file
View File

@ -0,0 +1,21 @@
<!-- HTML footer for doxygen 1.8.14-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
</a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

56
doc/header.html Normal file
View File

@ -0,0 +1,56 @@
<!-- HTML header for doxygen 1.8.14-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

1596
doc/stylesheet.css Normal file

File diff suppressed because it is too large Load Diff

31
examples.mk Normal file
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
#

99
examples/hello-world.c Normal file
View File

@ -0,0 +1,99 @@
/******************************************************************************
*
* 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>
#define DEFAULT_HTTP_PORT 8080 /**< Default HTTP server port */
static void
hello_world(const struct aisl_evt *evt, void *p_ctx)
{
if (evt->code != AISL_EVENT_STREAM_REQUEST)
return;
AislStatus status;
AislStream s = evt->source;
const char html[] =
"<html>"
"<head>"
"<title>Hello World</title>"
"</head>"
"<body>"
"<h1>Hello World</h1>"
"<p>Powered by AISL</p>"
"</body>"
"</html>";
status = aisl_response(s, AISL_HTTP_OK, sizeof (html)-1);
if (status == AISL_SUCCESS)
{
if (aisl_write(s, html, sizeof (html)-1) != -1)
{
aisl_flush(s);
}
else
aisl_reject(s);
}
(void) p_ctx;
}
int
main(int argc, char ** argv)
{
AislInstance aisl; /**< AISL instance pointer */
AislStatus status; /**< AISL status code */
struct aisl_cfg cfg = AISL_CFG_DEFAULT;
struct aisl_cfg_srv srv = {
.host = "0.0.0.0",
.port = DEFAULT_HTTP_PORT,
.secure = false
};
cfg.srv = &srv;
cfg.srv_cnt = 1;
cfg.callback = hello_world;
/* Initialize instance */
if ( (aisl = aisl_new(&cfg)) != NULL )
{
/* launch application loop */
fprintf(stdout, "Entering main loop" );
for(;;)
{
status = aisl_run_cycle(aisl);
if ( status != AISL_SUCCESS )
aisl_sleep(aisl, 500);
}
aisl_free(aisl);
}
else
fprintf(stderr, "Failed to initialize AISL");
return 0;
}

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 * @see https://lowenware.com/aisl/
* This file is not designed to be used as a documentation, but for looking at */
* the precise values of constants and definitions.
* Please, for documentation refer to web page https://lowenware.com/aisling/ or
* file READEME.md from library source package.
*
* LICENSE and DISCLAIMER
*
* -------------------------------------------------------------------------- */
#ifndef _AISL_H_ #ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
#define _AISL_H_ #define AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
/* system includes ---------------------------------------------------------- */ /* AISL configuration structure */
#include <aisl/config.h>
#include <stdint.h> /* AISL types and stringifiers */
#include <stdbool.h> #include <aisl/types.h>
#include <stdarg.h>
#include <arpa/inet.h>
/* aisl includes ------------------------------------------------------------ */ /* AISL instancing, initialization and processing */
#include <aisl/instance.h>
#include <aisl/status.h> /* Embedded HTTP(s) servers */
#include <aisl/event.h> #include <aisl/server.h>
/* HTTP(s) clients */
#include <aisl/client.h>
/* HTTP(s) streaming */
#include <aisl/stream.h> #include <aisl/stream.h>
#include <aisl/handle.h>
#include <aisl/http.h>
#endif /* !AISL_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

69
include/aisl/client.h Normal file
View File

@ -0,0 +1,69 @@
/******************************************************************************
*
* 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);
void
aisl_client_get_address(AislClient client, struct sockaddr_in *address);
#endif /* !AISL_CLIENT_H */

66
include/aisl/config.h Normal file
View File

@ -0,0 +1,66 @@
/******************************************************************************
*
* 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_DEFAULT { \
.callback = NULL \
, .p_ctx = NULL \
, .srv = NULL \
, .ssl = NULL \
, .srv_cnt = 0 \
, .ssl_cnt = 0 \
, .client_spool_size = 32 \
, .initial_buffer_size = 16536 \
, .client_accept_limit = 1024 \
, .client_silence_timeout = 30 \
} \
struct aisl_cfg_srv {
const char * host;
uint16_t port;
bool secure;
};
struct aisl_cfg_ssl {
const char * host;
const char * key_file;
const char * crt_file;
};
struct aisl_cfg
{
/* event handlers */
AislCallback callback;
void *p_ctx;
struct aisl_cfg_srv *srv;
struct aisl_cfg_ssl *ssl;
int srv_cnt;
int ssl_cnt;
int client_spool_size;
int initial_buffer_size;
int client_accept_limit;
int client_silence_timeout;
};
#endif /* !AISL_CONFIG_H */

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

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

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
include/aisl/instance.h Normal file
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
include/aisl/server.h Normal file
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 */

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

View File

@ -1,172 +1,106 @@
#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 */ * @file aisl/stream.h
/* HTTP requests */ * @author Ilja Kartašov <ik@lowenware.com>
#include <aisl/http.h> * @brief Declarations of #AislStream functions
#include <aisl/status.h> *
* @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);
/* -------------------------------------------------------------------------- */
bool bool
aisl_is_secure(aisl_stream_t s); aisl_is_secure(AislStream stream);
AislClient
aisl_get_client(AislStream stream);
AislServer
aisl_get_server(AislStream stream);
AislHttpVersion
aisl_get_http_version(AislStream stream);
AislInstance
aisl_stream_get_instance(AislStream s);
/* -------------------------------------------------------------------------- */
void * void *
aisl_get_context(aisl_stream_t s); aisl_get_context(AislStream stream);
/* -------------------------------------------------------------------------- */
void void
aisl_set_context(aisl_stream_t s, void * u_ptr); aisl_set_context(AislStream stream, void *context);
/* -------------------------------------------------------------------------- */
aisl_client_t AislStatus
aisl_get_client(aisl_stream_t s); 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);
/* -------------------------------------------------------------------------- */
void void
aisl_reject( aisl_stream_t s); aisl_reject(AislStream stream);
/* -------------------------------------------------------------------------- */
#endif AislStatus
aisl_response(AislStream stream,
AislHttpResponse status_code,
uint64_t content_length);
int
aisl_header(AislStream stream, const char *key, const char *value );
int
aisl_header_printf(AislStream stream,
const char *key,
const char *format,
... );
int
aisl_header_vprintf(AislStream stream,
const char *key,
const char *format,
va_list args );
int
aisl_printf(AislStream stream, const char *format, ...);
int
aisl_vprintf(AislStream stream, const char *format, va_list args);
int
aisl_write(AislStream stream, const char *data, int d_len);
int
aisl_puts(const char *str_data, AislStream stream);
void
aisl_set_output_event(AislStream stream, bool value);
bool
aisl_get_output_event(AislStream stream);
#endif /* !AISL_STREAM_H */

208
include/aisl/types.h Normal file
View File

@ -0,0 +1,208 @@
/******************************************************************************
*
* 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>
#ifndef AISL_WITHOUT_SSL
#include <openssl/ssl.h>
#endif
#define AISL_AUTO_LENGTH (~0)
/** type casts */
#define AISL_CALLBACK(x) ((AislCallback) x)
/** AISL Instance */
typedef struct aisl_instance * AislInstance;
/** HTTP(s) Server */
typedef struct aisl_server * AislServer;
/** HTTP(s) Client */
typedef struct aisl_client * AislClient;
/** 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;
#ifndef WITHOUT_STRINGIFIERS
const char *
aisl_status_to_string(AislStatus status);
#endif
/** Generic HTTP Enumerations */
typedef enum {
AISL_HTTP_0_9 = 0x0009
, AISL_HTTP_1_0 = 0x0100
, AISL_HTTP_1_1 = 0x0101
, AISL_HTTP_2_0 = 0x0200
} AislHttpVersion;
const char *
aisl_http_version_to_string(AislHttpVersion version);
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;
const char *
aisl_http_method_to_string( AislHttpMethod method );
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;
const char *
aisl_http_response_to_string( AislHttpResponse code );
/** Codes of AISL events */
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;
struct aisl_evt {
void *source;
AislEvent code;
AislStatus status;
};
/* void type event callback */
typedef void
(* AislCallback) (const struct aisl_evt *evt, void *ctx);
struct aisl_evt_open {
struct aisl_evt evt;
const char *path;
const char *query;
AislHttpMethod http_method;
};
struct aisl_evt_header {
struct aisl_evt evt;
const char *key;
const char *value;
};
struct aisl_evt_input {
struct aisl_evt evt;
const char *data;
int32_t size;
};
#ifndef WITHOUT_STRINGIFIERS
const char *
aisl_event_to_string(AislEvent evt);
#endif
#endif /* !AISL_TYPES_H */

19
libaisl.pc.example Normal file
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}

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));
}
/* -------------------------------------------------------------------------- */

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;
}
/* -------------------------------------------------------------------------- */

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

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;
}
/* -------------------------------------------------------------------------- */

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

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";
}

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

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);
}

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

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 "";
}
/* -------------------------------------------------------------------------- */

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;
}

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

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);
}
}
/* -------------------------------------------------------------------------- */

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

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 "";
}
/* -------------------------------------------------------------------------- */

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 );
}
/* -------------------------------------------------------------------------- */

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

63
project.mk Normal file
View File

@ -0,0 +1,63 @@
#
# 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 = 0
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_WITHOUT_SSL - exclude HTTPS support
# AISL_WITHOUT_STRINGIFIERS - exclude several *_to_string functions not
# flags
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
#PROJECT_CFLAGS += -DDEBUG
#PROJECT_CFLAGS += -DAISL_WITHOUT_SSL
#PROJECT_CFLAGS += -DAISL_WITHOUT_STRINGIFIERS
# PROJECT_LDFLAGS = -L
# vim:ft=make
#

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
# -----------------------------------------------------------------------------

539
src/) Normal file
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));
}

175
src/buffer.c Normal file
View File

@ -0,0 +1,175 @@
/******************************************************************************
*
* 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 "buffer.h"
static int32_t
buffer_set_size(struct buffer *buffer, int32_t new_size)
{
if (new_size != buffer->size) {
char *data;
if (new_size) {
int32_t s = new_size / 1024;
if ( new_size % 1024 ) {
new_size = (s+1) * 1024;
}
} 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;
if ( (result = buffer_set_size(buffer, 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)
{
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);
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)
{
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
src/buffer.h Normal file
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
src/client.c Normal file
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>
#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:
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;
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;
}
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 ( 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 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");
#ifndef AISL_WITHOUT_SSL
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);
#ifndef AISL_WITHOUT_SSL
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)
{
#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));
}

102
src/client.h Normal file
View File

@ -0,0 +1,102 @@
/******************************************************************************
*
* 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>
#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. */
#ifndef AISL_WITHOUT_SSL
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
src/debug.h Normal file
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
#ifdef DEBUG
#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 */

397
src/http.c Normal file
View File

@ -0,0 +1,397 @@
/******************************************************************************
*
* 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)
return HTTP_PARSER_ERROR;
break;
case '/':
if (!path && data > host) {
path = data;
if (!uri)
uri = path;
} else if (version && data-version != 4) {
return HTTP_PARSER_ERROR;
}
break;
case '?':
if (!query)
query = data+1;
else if (version)
return HTTP_PARSER_ERROR;
break;
case '\n':
newline = data;
break;
case '\r':
if (!version)
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)
return HTTP_PARSER_ERROR;
}
data++;
}
/* STEP 2. Verifly splitting was completed */
/* Was request sent? */
if (!newline)
return HTTP_PARSER_HUNGRY;
/* Check mandatory parts presence */
if (!method_end || !path || !uri_end || !version)
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)
return HTTP_PARSER_ERROR;
if ((http_version = http_version_from_string(version))==0)
return HTTP_PARSER_ERROR;
if (query) {
*(query-1)=0;
} else {
query = uri_end;
}
if (host) {
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8))
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;
while(!newline && size-- ) {
switch(*data) {
case ' ':
if (val && !val_end)
val_end = data;
break;
case ':':
if (!colon) {
if (colon == key)
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;
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;
return (aisl_stream_set_end_of_headers(stream) == 0) ?
HTTP_PARSER_SUCCESS : HTTP_PARSER_READY;
}
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;
switch (aisl_stream_set_body(stream, data, size)) {
case 0:
return HTTP_PARSER_READY;
case -1:
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
src/http.h Normal file
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
src/instance.c Normal file
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>
#ifndef AISL_WITHOUT_SSL
#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"
#ifndef AISL_WITHOUT_SSL
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;
}
#ifndef AISL_WITHOUT_SSL
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);
#ifndef AISL_WITHOUT_SSL
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);
}
#ifndef AISL_WITHOUT_SSL
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)
{
#ifdef AISL_WITHOUT_STRINGIFIERS
DPRINTF("! %d", evt->code);
#else
DPRINTF("! %s", aisl_event_to_string(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
src/instance.h Normal file
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
#ifndef AISL_WITHOUT_SSL
#include <openssl/ssl.h>
#endif
#include <aisl/instance.h>
#include "ssl.h"
#include "list.h"
struct aisl_instance {
AislServer *srv;
#ifndef AISL_WITHOUT_SSL
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;
};
#ifndef AISL_WITHOUT_SSL
/**
* @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
src/list.c Normal file
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
src/list.h Normal file
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
src/server.c Normal file
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));
}
#ifndef AISL_WITHOUT_SSL
__attribute__ ((visibility ("default") ))
bool
aisl_server_get_ssl(AislServer server)
{
return server->ssl;
}
#endif

83
src/server.h Normal file
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
src/ssl.c Normal file
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"
#ifndef AISL_WITHOUT_SSL
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
src/ssl.h Normal file
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
src/str-utils.c Normal file
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
src/str-utils.h Normal file
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 */

605
src/stream.c Normal file
View File

@ -0,0 +1,605 @@
#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_stream_get_instance(stream), (void*) stream,
AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
}
buffer_release(&stream->buffer);
stream->u_ptr = NULL;
stream->content_length = 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_stream_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_stream_get_instance(stream),
(struct aisl_evt *) &on_header);
}
int
aisl_stream_set_end_of_headers(AislStream stream)
{
int result;
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
stream->state = AISL_STREAM_STATE_WAIT_BODY;
result = (stream->content_length == 0);
} 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") ))
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_stream_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
src/stream.h Normal file
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
src/types.c Normal file
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>
#ifndef WITHOUT_STRINGIFIERS
__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

View File

@ -1 +0,0 @@
0.3.4-alpha