Update to version 1.0.5
This commit is contained in:
parent
2d67fdd5fb
commit
f7a9f255df
|
@ -1,3 +0,0 @@
|
|||
[submodule "cStuff"]
|
||||
path = cStuff
|
||||
url = https://github.com/lowenware/cStuff.git
|
|
@ -0,0 +1,8 @@
|
|||
# Release 1.0.0
|
||||
|
||||
By Ilja Kartašov <ik@lowenware.com>, 2019-06-02
|
||||
|
||||
1. Library release with new and final API and documented code.
|
||||
2. Previous version is being called `aisl-legacy` and will be maintained in
|
||||
security aspects in branch called `legacy` until all applications using it will
|
||||
be updated.
|
131
CMakeLists.txt
131
CMakeLists.txt
|
@ -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}
|
||||
)
|
||||
|
||||
|
48
README.md
48
README.md
|
@ -1,36 +1,32 @@
|
|||
# AISL
|
||||
Asynchronous Internet Server Library
|
||||
|
||||
## Installation on CentOS 7 / RedHat 7
|
||||
Asynchronous Internet Server Library provides innovative way of web development.
|
||||
AISL based applications have built-in web server giving full control of client
|
||||
serving. All you need to know is a request string? - Start prepare the response
|
||||
without waiting for headers and body! Don't need some headers? - Don't save them
|
||||
in memory! Unwanted content-body? - simply ignore it!
|
||||
|
||||
## Documentation
|
||||
|
||||
[Hello World](https://lowenware.com/aisl/handbook.html#getting-started) example
|
||||
and full [API reference](https://lowenware.com/aisl/handbook.html#api-reference)
|
||||
can be found in an oficial [AISL HandBook](https://lowenware.com/aisl/handbook.html).
|
||||
|
||||
## Installation
|
||||
|
||||
1. Add repository
|
||||
```
|
||||
sudo curl -o /etc/yum.repos.d/lowenware.repo https://lowenware.com/rpm/redhat-7/lowenware.repo
|
||||
$ make PREFIX=/usr/local
|
||||
$ sudo make PREFIX=/usr/local install
|
||||
$ sudo cp libaisl.pc.example /usr/lib/pkgconfig/libaisl.pc
|
||||
```
|
||||
|
||||
2. Import GPG key
|
||||
ArchLinux users can install from [AUR](https://aur.archlinux.org/packages/aisl-git/) :
|
||||
|
||||
```
|
||||
sudo rpm --import https://lowenware.com/rpm/RPM-GPG-KEY-Lowenware
|
||||
$ yaourt -S aisl-git
|
||||
```
|
||||
|
||||
3. Install
|
||||
```
|
||||
sudo yum install aisl aisl-devel
|
||||
```
|
||||
## License
|
||||
|
||||
## Installation from sources on any distro
|
||||
|
||||
1. Configuration
|
||||
```
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
```
|
||||
|
||||
2. Compilation
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
3. Installation
|
||||
```
|
||||
sudo make install
|
||||
```
|
||||
AISL is free for both commercial and non-commercial use, being distributed under
|
||||
terms of [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/).
|
||||
|
|
36
aisl.proj
36
aisl.proj
|
@ -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
cStuff
|
@ -1 +0,0 @@
|
|||
Subproject commit a423fa7a6dfbd637f3c0b248123f682456fccad7
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
if(DEFINED CMAKE_DEBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror ")
|
||||
add_definitions(
|
||||
-DDEBUG
|
||||
)
|
||||
endif()
|
90
cmake.paths
90
cmake.paths
|
@ -1,90 +0,0 @@
|
|||
# Paths -----------------------------------------------------------------------
|
||||
|
||||
# Constants -------------------------------------------------------------------
|
||||
|
||||
set(INCLUDE_DIR "include")
|
||||
set(BUILD_DIR "build")
|
||||
|
||||
# CMAKE installation paths ----------------------------------------------------
|
||||
|
||||
|
||||
# -DCMAKE_INSTALL_PREFIX:PATH=/usr
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr")
|
||||
endif()
|
||||
|
||||
|
||||
# -DINCLUDE_INSTALL_DIR:PATH=/usr/include
|
||||
|
||||
if(NOT DEFINED INCLUDE_INSTALL_DIR)
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include")
|
||||
endif()
|
||||
|
||||
|
||||
# -DSHARE_INSTALL_PREFIX:PATH=/usr/share
|
||||
|
||||
if(NOT DEFINED SHARE_INSTALL_PREFIX)
|
||||
set(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share")
|
||||
endif()
|
||||
|
||||
|
||||
# -DSYSCONF_INSTALL_DIR:PATH=/etc
|
||||
|
||||
if(NOT DEFINED SYSCONF_INSTALL_DIR)
|
||||
set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc")
|
||||
endif()
|
||||
|
||||
|
||||
# -DCMAKE_INSTALL_LIBDIR:PATH=lib64
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR "lib${SYSTEM_LIB_SUFFIX}")
|
||||
endif()
|
||||
|
||||
# -DLIB_INSTALL_DIR:PATH=/usr/lib64
|
||||
if(NOT DEFINED LIB_INSTALL_DIR)
|
||||
set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set(PATH_BIN "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
set(PATH_INC "${INCLUDE_INSTALL_DIR}")
|
||||
set(PATH_CFG "${SYSCONF_INSTALL_DIR}")
|
||||
set(PATH_RUN "/var/run")
|
||||
set(PATH_LIB "${LIB_INSTALL_DIR}")
|
||||
set(PATH_LOG "/var/log")
|
||||
set(PATH_RES "${SHARE_INSTALL_PREFIX}")
|
||||
set(PATH_LNG "${SHARE_INSTALL_PREFIX}/locale")
|
||||
|
||||
|
||||
|
||||
MESSAGE( STATUS "Paths:")
|
||||
MESSAGE( STATUS " Prefix: ${CMAKE_INSTALL_PREFIX}" )
|
||||
MESSAGE( STATUS " Binaries: ${PATH_BIN}" )
|
||||
MESSAGE( STATUS " Configuration: ${PATH_CFG}" )
|
||||
MESSAGE( STATUS " Libraries: ${PATH_LIB}" )
|
||||
MESSAGE( STATUS " Includes: ${PATH_INC}" )
|
||||
MESSAGE( STATUS " Run: ${PATH_RUN}" )
|
||||
MESSAGE( STATUS " Log Files: ${PATH_LOG}" )
|
||||
MESSAGE( STATUS " Resources: ${PATH_RES}" )
|
||||
MESSAGE( STATUS " Locale Files: ${PATH_LNG}" )
|
||||
MESSAGE( STATUS "")
|
||||
|
||||
# Compiler's Definitions ------------------------------------------------------
|
||||
|
||||
add_definitions(
|
||||
-DPREFIX="${CMAKE_INSTALL_PREFIX}"
|
||||
-DPATH_BIN="${PATH_BIN}"
|
||||
-DPATH_CFG="${PATH_CFG}"
|
||||
-DPATH_INC="${PATH_INC}"
|
||||
-DPATH_LIB="${PATH_LIB}"
|
||||
-DPATH_RUN="${PATH_RUN}"
|
||||
-DPATH_LOG="${PATH_LOG}"
|
||||
-DPATH_LNG="${PATH_LNG}"
|
||||
-DPATH_RES="${PATH_RES}"
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
18
cmake.system
18
cmake.system
|
@ -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}"
|
||||
)
|
|
@ -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}
|
||||
)
|
||||
|
|
@ -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
|
||||
#
|
|
@ -0,0 +1,104 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file hello-world.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL usage example: Hello World
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Include library meta header */
|
||||
#include <aisl/aisl.h>
|
||||
|
||||
|
||||
static void
|
||||
hello_world(const struct aisl_evt *evt, void *p_ctx);
|
||||
|
||||
|
||||
static const struct aisl_cfg_srv m_srv[] = {{
|
||||
.host = "0.0.0.0",
|
||||
.port = 8080,
|
||||
.secure = false
|
||||
}};
|
||||
|
||||
|
||||
static const struct aisl_cfg m_cfg = {
|
||||
AISL_CFG_DEFAULTS
|
||||
, .srv = m_srv
|
||||
, .srv_cnt = sizeof (m_srv) / sizeof (m_srv[0])
|
||||
, .ssl = NULL
|
||||
, .ssl_cnt = 0
|
||||
, .callback = hello_world
|
||||
, .p_ctx = NULL
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
hello_world(const struct aisl_evt *evt, void *p_ctx)
|
||||
{
|
||||
AislStream s;
|
||||
|
||||
const char html[] =
|
||||
"<html>"
|
||||
"<head>"
|
||||
"<title>Hello World</title>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<h1>Hello World</h1>"
|
||||
"<p>Powered by AISL</p>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
fprintf(stdout, "Event: %s\n", aisl_event_to_string(evt->code) );
|
||||
|
||||
if (evt->code != AISL_EVENT_STREAM_REQUEST)
|
||||
return;
|
||||
|
||||
s = evt->source;
|
||||
|
||||
if (aisl_response(s, AISL_HTTP_OK, sizeof (html)-1) == AISL_SUCCESS) {
|
||||
if (aisl_write(s, html, sizeof (html)-1) != -1) {
|
||||
aisl_flush(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aisl_reject(s);
|
||||
(void) p_ctx;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
AislInstance aisl; /**< AISL instance pointer */
|
||||
AislStatus status; /**< AISL status code */
|
||||
|
||||
/* Initialize instance */
|
||||
if ( (aisl = aisl_new(&m_cfg)) != NULL ) {
|
||||
/* launch application loop */
|
||||
fprintf(stdout, "Entering main loop\n" );
|
||||
for(;;) {
|
||||
status = aisl_run_cycle(aisl);
|
||||
|
||||
if ( status != AISL_SUCCESS )
|
||||
aisl_sleep(aisl, 500);
|
||||
}
|
||||
|
||||
aisl_free(aisl);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to initialize AISL\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,107 +1,37 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* aisl.h - header file for AISL library, part of AISLing Technology
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017 by Löwenware Ltd. (https://lowenware.com/)
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
* Authors and maintainers:
|
||||
* Ilja Kartaschoff <ik@lowenware.com>
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file aisl/aisl.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Meta header file of AISL
|
||||
*
|
||||
* DOCUMENTATION
|
||||
* This file is not designed to be used as a documentation, but for looking at
|
||||
* the precise values of constants and definitions.
|
||||
* Please, for documentation refer to web page https://lowenware.com/aisling/ or
|
||||
* file READEME.md from library source package.
|
||||
*
|
||||
* LICENSE and DISCLAIMER
|
||||
*
|
||||
* -------------------------------------------------------------------------- */
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef _AISL_H_
|
||||
#define _AISL_H_
|
||||
#ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
|
||||
#define AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
|
||||
|
||||
/* system includes ---------------------------------------------------------- */
|
||||
/* AISL configuration structure */
|
||||
#include <aisl/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <arpa/inet.h>
|
||||
/* AISL types and stringifiers */
|
||||
#include <aisl/types.h>
|
||||
|
||||
/* aisl includes ------------------------------------------------------------ */
|
||||
/* AISL instancing, initialization and processing */
|
||||
#include <aisl/instance.h>
|
||||
|
||||
#include <aisl/status.h>
|
||||
#include <aisl/event.h>
|
||||
/* Embedded HTTP(s) servers */
|
||||
#include <aisl/server.h>
|
||||
|
||||
/* HTTP(s) clients */
|
||||
#include <aisl/client.h>
|
||||
|
||||
/* HTTP(s) streaming */
|
||||
#include <aisl/stream.h>
|
||||
#include <aisl/handle.h>
|
||||
#include <aisl/http.h>
|
||||
|
||||
|
||||
|
||||
/* Control calls ------------------------------------------------------------ */
|
||||
|
||||
/* DEPRECATED, use aisl_handle_new instead
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_init();
|
||||
|
||||
/* DEPRECATED, use aisl_handle_free instead
|
||||
* */
|
||||
void
|
||||
aisl_release();
|
||||
|
||||
/* Tell library what socket should be opened. Could be called multiple times.
|
||||
* This function only save passed data. In fact, sockets are being opened only
|
||||
* inside aisl_run loop.
|
||||
* @address : host or IP to listen
|
||||
* @port : port to listen
|
||||
* */
|
||||
|
||||
aisl_status_t
|
||||
aisl_select(const char *address, int port);
|
||||
|
||||
/* Start main loop
|
||||
* @result : exit code
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_run( int * flags );
|
||||
|
||||
/* Event calls -------------------------------------------------------------- */
|
||||
|
||||
/* Add callback to be executed after timeout. If callback function will return
|
||||
* true, callback will be kept in main loop and raised again, otherwise it will
|
||||
* be removed
|
||||
* @cb : callback function: bool callback (void * u_data)
|
||||
* @usec : delay in milliseconds
|
||||
* @data : user-defined data to be passed to callback
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_delay(aisl_callback_t cb, uint32_t msec, void *u_data);
|
||||
|
||||
/* Add event listener
|
||||
* @source : pointer to event source
|
||||
* @e_id : event identifier
|
||||
* @cb : callback to be executed
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_listen(void *source, aisl_event_t e_id, aisl_callback_t cb);
|
||||
|
||||
/* Raise event
|
||||
* @source : pointer to event source data
|
||||
* @e_id : event identifier
|
||||
* @... : custom event data
|
||||
* @result : true if event was handled, false otherwise
|
||||
* */
|
||||
bool
|
||||
aisl_raise(void *source, aisl_event_t e_id, ... );
|
||||
|
||||
/* input stream functions --------------------------------------------------- */
|
||||
|
||||
|
||||
const char *
|
||||
aisl_header_get(aisl_stream_t stream, const char *key);
|
||||
|
||||
const char *
|
||||
aisl_header_get_by_index(aisl_stream_t stream, const char **key, uint32_t i);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
||||
#endif /* !AISL_H */
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file aisl/client.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of #AislCLient functions
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
|
||||
#define AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
|
||||
|
||||
#include <aisl/types.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets #AislServer instance associated with client.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return an associated #AislServer pointer.
|
||||
*/
|
||||
AislServer
|
||||
aisl_client_get_server(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets security connection status.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return true if SSL is enabled and false if disabled.
|
||||
*/
|
||||
bool
|
||||
aisl_client_is_secure(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets client's connection state.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return true if client is online and false if is offline.
|
||||
*/
|
||||
bool
|
||||
aisl_client_is_online(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Forcefully closes client's connection.
|
||||
* @param client an #AislClient instance pointer.
|
||||
*/
|
||||
void
|
||||
aisl_client_disconnect(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets HTTP protocol version.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return HTTP protocol version
|
||||
*/
|
||||
AislHttpVersion
|
||||
aisl_client_get_http_version(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copies #AislClient network address to provided sockaddr_in structure.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @param address a pointer to a sockaddr_in structure
|
||||
*/
|
||||
void
|
||||
aisl_client_get_address(AislClient client, struct sockaddr_in *address);
|
||||
|
||||
#endif /* !AISL_CLIENT_H */
|
|
@ -0,0 +1,64 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file aisl/config.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of AISL configuration structures
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
|
||||
#define AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
|
||||
|
||||
#include <aisl/types.h>
|
||||
|
||||
#define AISL_CFG_DEFAULTS \
|
||||
.client_spool_size = 32 \
|
||||
, .initial_buffer_size = 16536 \
|
||||
, .client_accept_limit = 1024 \
|
||||
, .client_silence_timeout = 30 \
|
||||
|
||||
|
||||
/** @brief Server configuration structure
|
||||
*/
|
||||
struct aisl_cfg_srv {
|
||||
const char *host; /**< server IP to listen */
|
||||
uint16_t port; /**< server port to listen */
|
||||
bool secure; /**< shall server use TLS */
|
||||
};
|
||||
|
||||
|
||||
/** @brief SSL configuration structure
|
||||
*/
|
||||
struct aisl_cfg_ssl {
|
||||
const char *host; /**< secure server hostname */
|
||||
const char *key_file; /**< path to SSL key file */
|
||||
const char *crt_file; /**< path to SSL certificate file */
|
||||
};
|
||||
|
||||
|
||||
/** @brief AISL initial configuration structure
|
||||
*/
|
||||
struct aisl_cfg {
|
||||
AislCallback callback; /**< A pointer to #AislCallback event handler */
|
||||
void *p_ctx; /**< User defined context for #AislCallback */
|
||||
|
||||
const struct aisl_cfg_srv *srv; /**< A pointer to array of #aisl_cfg_srv */
|
||||
const struct aisl_cfg_ssl *ssl; /**< A pointer to array of #aisl_cfg_ssl */
|
||||
|
||||
int srv_cnt; /**< Size of #aisl_cfg_srv array */
|
||||
int ssl_cnt; /**< Size of #aisl_cfg_ssl array */
|
||||
|
||||
int client_spool_size; /**< Initial size of client spool */
|
||||
int initial_buffer_size; /**< Initial size of communication buffer */
|
||||
int client_accept_limit; /**< Maximal number of concurent clients */
|
||||
int client_silence_timeout; /**< Client silence timeout */
|
||||
};
|
||||
|
||||
#endif /* !AISL_CONFIG_H */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
|
@ -1,172 +1,199 @@
|
|||
#ifndef _AISL_STREAM_H_
|
||||
#define _AISL_STREAM_H_
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
/* Library statuses */
|
||||
/* HTTP requests */
|
||||
#include <aisl/http.h>
|
||||
#include <aisl/status.h>
|
||||
/**
|
||||
* @file aisl/stream.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of #AislStream functions
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
#ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
|
||||
#define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <aisl/types.h>
|
||||
|
||||
typedef struct sockaddr_in * aisl_server_t;
|
||||
typedef struct sockaddr_in * aisl_client_t;
|
||||
|
||||
struct aisl_stream
|
||||
{
|
||||
/* DO NOT USE PROPERTIES DIRECTLY IN NEW CODE */
|
||||
|
||||
struct sockaddr_in *client;
|
||||
|
||||
const char *host;
|
||||
const char *path;
|
||||
const char *query;
|
||||
const char *scheme;
|
||||
|
||||
void *u_ptr; /* pointer to bind custom data to stream */
|
||||
|
||||
aisl_http_method_t request_method;
|
||||
};
|
||||
|
||||
/* pointer to stream descriptor */
|
||||
typedef struct aisl_stream * aisl_stream_t;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* start response to client
|
||||
* this function should be called before any header or content function
|
||||
* necessary protocol headers as well as Content-Type and Content-Length
|
||||
* will be set automaticaly
|
||||
* @stream : stream instance
|
||||
* @status : HTTP response status code (default 200)
|
||||
* @content_type : string with content type (default text/html), NULL -> no
|
||||
* @content_length : length of content, 0 = no content
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_response(aisl_stream_t stream, aisl_http_response_t status_code,
|
||||
const char *content_type,
|
||||
uint32_t content_length);
|
||||
|
||||
/* send all buffered data to client
|
||||
* ALL responses should always be finished with calling of this method
|
||||
* @stream : stream instance
|
||||
* */
|
||||
aisl_status_t
|
||||
aisl_flush(aisl_stream_t stream);
|
||||
|
||||
/* header functions --------------------------------------------------------- */
|
||||
|
||||
/* add custom header to stream
|
||||
* this function should be called before content functions
|
||||
* @stream : stream instance
|
||||
* @key : header key string
|
||||
* @value : header value string
|
||||
* */
|
||||
int
|
||||
aisl_header(aisl_stream_t stream, const char *key, const char *value);
|
||||
|
||||
/* add custom header to stream
|
||||
* this function should be called before content functions
|
||||
* @stream : stream instance
|
||||
* @key : header key string
|
||||
* @f_value : value format string, same as for printf function
|
||||
* @... : arguments according to f_value string
|
||||
* */
|
||||
int
|
||||
aisl_header_printf(aisl_stream_t stream, const char *key,
|
||||
const char *f_value, ...);
|
||||
|
||||
/* add custom header to stream
|
||||
* this function should be called before content functions
|
||||
* @stream : stream instance
|
||||
* @key : header key string
|
||||
* @f_value : value format string, same as for printf function
|
||||
* @args : arguments macro according to f_value string
|
||||
* */
|
||||
int
|
||||
aisl_header_vprintf(aisl_stream_t stream, const char *key,
|
||||
const char *format,
|
||||
va_list args);
|
||||
|
||||
|
||||
/* data response functions -------------------------------------------------- */
|
||||
|
||||
/* response formated data to client
|
||||
* @stream : stream instance
|
||||
* @format : format string, same as for printf
|
||||
* @... : arguments according to format string
|
||||
* @result : number of responed bytes
|
||||
* */
|
||||
int
|
||||
aisl_printf(aisl_stream_t stream, const char *format, ...);
|
||||
|
||||
|
||||
/* response formated data to client
|
||||
* @stream : stream instance
|
||||
* @format : format string, same as for printf
|
||||
* @args : arguments macro according to format string
|
||||
* @result : number of responed bytes
|
||||
* */
|
||||
int
|
||||
aisl_vprintf(aisl_stream_t stream, const char *format, va_list args);
|
||||
|
||||
/* response characters to client
|
||||
* @stream : stream instance
|
||||
* @data : characters to be sent
|
||||
* @d_len : number of characters to send
|
||||
* @result : number of responed bytes
|
||||
* */
|
||||
int
|
||||
aisl_write(aisl_stream_t s, const char *data, int d_len);
|
||||
|
||||
/* response string to client
|
||||
* @string : string to be sent
|
||||
* @stream : stream instance
|
||||
* @result : number of responed bytes
|
||||
* */
|
||||
int
|
||||
aisl_puts(const char *string, aisl_stream_t stream);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
aisl_cancel(aisl_stream_t s);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define aisl_stream_get_instance aisl_get_instance
|
||||
|
||||
/** @brief Gets a value of #AislStream security flag
|
||||
* @param stream an #AislStream instance
|
||||
* @return is true if stream is encrypted and false otherwise
|
||||
*/
|
||||
bool
|
||||
aisl_is_secure(aisl_stream_t s);
|
||||
aisl_is_secure(AislStream stream);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** @brief Gets an #AislClient instance associated with the stream
|
||||
* @param stream an #AislStream instance
|
||||
* @return an #AislClient instance pointer
|
||||
*/
|
||||
AislClient
|
||||
aisl_get_client(AislStream stream);
|
||||
|
||||
|
||||
/** @brief Gets an #AislServer instance associated with the stream
|
||||
* @param stream an #AislStream instance
|
||||
* @return an #AislServer instance pointer
|
||||
*/
|
||||
AislServer
|
||||
aisl_get_server(AislStream stream);
|
||||
|
||||
|
||||
/** @brief Gets an #AislHttpVersion of the stream
|
||||
* @param stream an #AislStream instance
|
||||
* @return an #AislHttpVersion value
|
||||
*/
|
||||
AislHttpVersion
|
||||
aisl_get_http_version(AislStream stream);
|
||||
|
||||
|
||||
/** @brief Gets an #AislInstance pointer associated with the stream
|
||||
* @param stream an #AislStream instance
|
||||
* @return an #AislInstance pointer
|
||||
*/
|
||||
AislInstance
|
||||
aisl_get_instance(AislStream s);
|
||||
|
||||
|
||||
/** @brief Gets the stream context previously set with #aisl_set_context
|
||||
* @param stream an #AislStream instance
|
||||
* @return a pointer to the stream context
|
||||
*/
|
||||
void *
|
||||
aisl_get_context(aisl_stream_t s);
|
||||
aisl_get_context(AislStream stream);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** @brief Associate a context pointer with the stream until its lifetime.
|
||||
* Previously allocated data should be free'd on #AISL_EVENT_STREAM_CLOSE if
|
||||
* not needed anymore.
|
||||
* @param stream an #AislStream instance
|
||||
* @param context a pointer to any user-defined data
|
||||
*/
|
||||
void
|
||||
aisl_set_context(aisl_stream_t s, void * u_ptr);
|
||||
aisl_set_context(AislStream stream, void *context);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
aisl_client_t
|
||||
aisl_get_client(aisl_stream_t s);
|
||||
/** @brief A call to start stream data transmission to a client
|
||||
* @param stream an #AislStream instance
|
||||
* @return a #AislStatus displaying if stream is ready to be proceed by teh engine
|
||||
*/
|
||||
AislStatus
|
||||
aisl_flush(AislStream stream);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
aisl_server_t
|
||||
aisl_get_server(aisl_stream_t s);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
aisl_http_version_t
|
||||
aisl_get_http_version(aisl_stream_t s);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** @brief A call to reject the stream. In HTTP 1.X it also closes client's connection
|
||||
* @param stream an #AislStream instance
|
||||
*/
|
||||
void
|
||||
aisl_reject( aisl_stream_t s);
|
||||
aisl_reject(AislStream stream);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
||||
/** @brief A call to begin the HTTP response
|
||||
* @param stream an #AislStream instance
|
||||
* @param status_code of the HTTP response
|
||||
* @param content_length in bytes or #AISL_AUTO_LENGTH if length is not knonw yet
|
||||
* @return #AislStatus code
|
||||
*/
|
||||
AislStatus
|
||||
aisl_response(AislStream stream,
|
||||
AislHttpResponse status_code,
|
||||
uint64_t content_length);
|
||||
|
||||
|
||||
/** @brief Adds HTTP header to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param key of HTTP header
|
||||
* @param value of HTTP header
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_header(AislStream stream, const char *key, const char *value);
|
||||
|
||||
|
||||
/** @brief Adds printf-like formatted HTTP header to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param key of HTTP header
|
||||
* @param format of HTTP header value
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_header_printf(AislStream stream,
|
||||
const char *key,
|
||||
const char *format,
|
||||
... );
|
||||
|
||||
|
||||
/** @brief Adds vprintf-like formatted HTTP header to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param key of HTTP header
|
||||
* @param format of HTTP header value
|
||||
* @param args list for HTTP header value construction
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_header_vprintf(AislStream stream,
|
||||
const char *key,
|
||||
const char *format,
|
||||
va_list args );
|
||||
|
||||
|
||||
/** @brief Adds printf-like formatted HTTP response to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param format of the HTTP response
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_printf(AislStream stream, const char *format, ...);
|
||||
|
||||
|
||||
/** @brief Adds vprintf-like formatted HTTP response to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param format of the HTTP response
|
||||
* @param args list for HTTP response construction
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_vprintf(AislStream stream, const char *format, va_list args);
|
||||
|
||||
|
||||
/** @brief Adds data of the HTTP response to the stream buffer
|
||||
* @param stream an #AislStream instance
|
||||
* @param data a pointer to HTTP response data array
|
||||
* @param d_len size of the HTTP response data array
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_write(AislStream stream, const char *data, int d_len);
|
||||
|
||||
|
||||
/** @brief Adds a null-terminated string to the stream buffer
|
||||
* @param str_data the HTTP response string
|
||||
* @param stream an #AislStream instance
|
||||
* @return a length of data added to the stream buffer
|
||||
*/
|
||||
int
|
||||
aisl_puts(const char *str_data, AislStream stream);
|
||||
|
||||
|
||||
/** @brief Switches triggering of #AISL_EVENT_STREAM_OUTPUT
|
||||
* @param stream an #AislStream instance
|
||||
* @param value a true to enable or false to disable (default) triggering
|
||||
*/
|
||||
void
|
||||
aisl_set_output_event(AislStream stream, bool value);
|
||||
|
||||
|
||||
/** @brief Gets state of the #AISL_EVENT_STREAM_OUTPUT triggering
|
||||
* @param stream an #AislStream instance
|
||||
* @return true if triggering is enabled and flase otherwise
|
||||
*/
|
||||
bool
|
||||
aisl_get_output_event(AislStream stream);
|
||||
|
||||
#endif /* !AISL_STREAM_H */
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file aisl/types.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of AISL types
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
|
||||
#define AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AISL_AUTO_LENGTH (~0)
|
||||
|
||||
/** type casts */
|
||||
#define AISL_CALLBACK(x) ((AislCallback) x)
|
||||
|
||||
|
||||
/** @brief AISL Instance */
|
||||
typedef struct aisl_instance * AislInstance;
|
||||
|
||||
/** @brief HTTP(s) Server */
|
||||
typedef struct aisl_server * AislServer;
|
||||
|
||||
/** @brief HTTP(s) Client */
|
||||
typedef struct aisl_client * AislClient;
|
||||
|
||||
/** @brief Server<->Client Stream */
|
||||
typedef struct aisl_stream * AislStream;
|
||||
|
||||
|
||||
/** status return codes */
|
||||
typedef enum {
|
||||
AISL_INPUT_ERROR = -4
|
||||
, AISL_EXTCALL_ERROR = -3
|
||||
, AISL_SYSCALL_ERROR = -2
|
||||
, AISL_MALLOC_ERROR = -1
|
||||
|
||||
, AISL_SUCCESS = 0
|
||||
, AISL_IDLE = 1
|
||||
} AislStatus;
|
||||
|
||||
|
||||
/** @brief Converts #AislStatus code to a null terminated string
|
||||
* @param status an #AislStatus code
|
||||
* @return pointer to the string representing #AislStatus
|
||||
*/
|
||||
const char *
|
||||
aisl_status_to_string(AislStatus status);
|
||||
|
||||
|
||||
/** @brief HTTP version enumeration */
|
||||
typedef enum {
|
||||
AISL_HTTP_0_9 = 0x0009
|
||||
, AISL_HTTP_1_0 = 0x0100
|
||||
, AISL_HTTP_1_1 = 0x0101
|
||||
, AISL_HTTP_2_0 = 0x0200
|
||||
} AislHttpVersion;
|
||||
|
||||
|
||||
/** @brief Converts #AislHttpVersion code to a null terminated string
|
||||
* @param version an #AislHttpVersion code
|
||||
* @return pointer to the string representing #AislHttpVersion
|
||||
*/
|
||||
const char *
|
||||
aisl_http_version_to_string(AislHttpVersion version);
|
||||
|
||||
|
||||
/** HTTP method enumeration */
|
||||
typedef enum {
|
||||
AISL_HTTP_METHOD_UNKNOWN
|
||||
, AISL_HTTP_GET
|
||||
, AISL_HTTP_PUT
|
||||
, AISL_HTTP_POST
|
||||
, AISL_HTTP_HEAD
|
||||
, AISL_HTTP_TRACE
|
||||
, AISL_HTTP_DELETE
|
||||
, AISL_HTTP_OPTIONS
|
||||
, AISL_HTTP_CONNECT
|
||||
, AISL_HTTP_PRI
|
||||
} AislHttpMethod;
|
||||
|
||||
|
||||
/** @brief Converts #AislHttpMethod code to a null terminated string
|
||||
* @param method an #AislHttpMethod code
|
||||
* @return pointer to the string representing #AislHttpMethod
|
||||
*/
|
||||
const char *
|
||||
aisl_http_method_to_string(AislHttpMethod method);
|
||||
|
||||
|
||||
/** @brief HTTP response status enumeration */
|
||||
typedef enum {
|
||||
AISL_HTTP_CONTINUE = 100
|
||||
, AISL_HTTP_SWITCHING_PROTOCOLS
|
||||
|
||||
, AISL_HTTP_OK = 200
|
||||
, AISL_HTTP_CREATED
|
||||
, AISL_HTTP_ACCEPTED
|
||||
, AISL_HTTP_NON_AUTHORITATIVE_INFORMATION
|
||||
, AISL_HTTP_NO_CONTENT
|
||||
, AISL_HTTP_RESET_CONTENT
|
||||
, AISL_HTTP_PARTIAL_CONTENT
|
||||
|
||||
, AISL_HTTP_MULTIPLE_CHOICES = 300
|
||||
, AISL_HTTP_MOVED_PERMANENTLY
|
||||
, AISL_HTTP_FOUND
|
||||
, AISL_HTTP_SEE_OTHER
|
||||
, AISL_HTTP_NOT_MODIFIED
|
||||
, AISL_HTTP_USE_PROXY
|
||||
, AISL_HTTP_UNUSED
|
||||
, AISL_HTTP_TEMPORARY_REDIRECT
|
||||
|
||||
, AISL_HTTP_BAD_REQUEST = 400
|
||||
, AISL_HTTP_UNAUTHORIZED
|
||||
, AISL_HTTP_PAYMENT_REQUIRED
|
||||
, AISL_HTTP_FORBIDDEN
|
||||
, AISL_HTTP_NOT_FOUND
|
||||
, AISL_HTTP_METHOD_NOT_ALLOWED
|
||||
, AISL_HTTP_NOT_ACCEPTABLE
|
||||
, AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED
|
||||
, AISL_HTTP_REQUEST_TIMEOUT
|
||||
, AISL_HTTP_CONFLICT
|
||||
, AISL_HTTP_GONE
|
||||
, AISL_HTTP_LENGTH_REQUIRED
|
||||
, AISL_HTTP_PRECONDITION_FAILED
|
||||
, AISL_HTTP_REQUEST_ENTITY_TOO_LARGE
|
||||
, AISL_HTTP_REQUEST_URI_TOO_LONG
|
||||
, AISL_HTTP_UNSUPPORTED_MEDIA_TYPE
|
||||
, AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
|
||||
, AISL_HTTP_EXPECTATION_FAILED
|
||||
|
||||
, AISL_HTTP_INTERNAL_SERVER_ERROR = 500
|
||||
, AISL_HTTP_NOT_IMPLEMENTED
|
||||
, AISL_HTTP_BAD_GATEWAY
|
||||
, AISL_HTTP_SERVICE_UNAVAILABLE
|
||||
, AISL_HTTP_GATEWAY_TIMEOUT
|
||||
, AISL_HTTP_VERSION_NOT_SUPPORTED
|
||||
} AislHttpResponse;
|
||||
|
||||
|
||||
/** @brief Converts #AislHttpResponse code to a null terminated string
|
||||
* @param code an #AislHttpResponse code
|
||||
* @return pointer to the string representing #AislHttpResponse
|
||||
*/
|
||||
const char *
|
||||
aisl_http_response_to_string(AislHttpResponse code);
|
||||
|
||||
|
||||
/** @brief AISL events enumeration */
|
||||
typedef enum {
|
||||
AISL_EVENT_SERVER_READY = 100
|
||||
, AISL_EVENT_SERVER_ERROR = 190
|
||||
|
||||
, AISL_EVENT_CLIENT_CONNECT = 200
|
||||
, AISL_EVENT_CLIENT_DISCONNECT = 210
|
||||
|
||||
, AISL_EVENT_STREAM_OPEN = 300
|
||||
, AISL_EVENT_STREAM_HEADER = 310
|
||||
, AISL_EVENT_STREAM_INPUT = 320
|
||||
, AISL_EVENT_STREAM_REQUEST = 330
|
||||
, AISL_EVENT_STREAM_OUTPUT = 340
|
||||
, AISL_EVENT_STREAM_CLOSE = 350
|
||||
, AISL_EVENT_STREAM_ERROR = 390
|
||||
} AislEvent;
|
||||
|
||||
|
||||
/** @brief generic AISL event structure */
|
||||
struct aisl_evt {
|
||||
void *source; /**< Pointer to an event source: #AislServer, #AislClient, #AislStream */
|
||||
AislEvent code; /**< Event code */
|
||||
AislStatus status; /**< Event status */
|
||||
};
|
||||
|
||||
|
||||
/** @brief event handler callback definition
|
||||
* @param evt a pointer to an #aisl_evt structure
|
||||
* @param ctx a pointer to a context defined by user (see #aisl_cfg)
|
||||
*/
|
||||
typedef void
|
||||
(* AislCallback) (const struct aisl_evt *evt, void *ctx);
|
||||
|
||||
|
||||
/** @brief AISL event structue passed on stream opening */
|
||||
struct aisl_evt_open {
|
||||
struct aisl_evt evt; /**< generic #aisl_evt structure */
|
||||
const char *path; /**< HTTP request path */
|
||||
const char *query; /**< HTTP request query (GET params) */
|
||||
AislHttpMethod http_method; /**< HTTP request method */
|
||||
};
|
||||
|
||||
|
||||
/** @brief AISL event structue passed on HTTP header reception */
|
||||
struct aisl_evt_header {
|
||||
struct aisl_evt evt; /**< generic #aisl_evt structure */
|
||||
const char *key; /**< low case HTTP header name */
|
||||
const char *value; /**< HTTP header string */
|
||||
};
|
||||
|
||||
|
||||
/** @brief AISL event structue passed on HTTP request data part received */
|
||||
struct aisl_evt_input {
|
||||
struct aisl_evt evt; /**< generic #aisl_evt structure */
|
||||
const char *data; /**< a pointer to received data array */
|
||||
int32_t size; /**< data array size */
|
||||
};
|
||||
|
||||
|
||||
/** @brief Converts #AislEvent code to a null terminated string
|
||||
* @param evt an #AislEvent code
|
||||
* @return pointer to the string representing #AislEvent
|
||||
*/
|
||||
const char *
|
||||
aisl_event_to_string(AislEvent evt);
|
||||
|
||||
#endif /* !AISL_TYPES_H */
|
|
@ -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));
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file buffer.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Buffer module source file
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
||||
static int32_t
|
||||
buffer_set_size(struct buffer *buffer, int32_t new_size)
|
||||
{
|
||||
if (!buffer->size || new_size != buffer->size) {
|
||||
char *data;
|
||||
|
||||
if (new_size) {
|
||||
if (new_size % 4096) {
|
||||
new_size = (new_size / 4096 + 1) * 4096;
|
||||
}
|
||||
} else {
|
||||
new_size = 16*1024;
|
||||
}
|
||||
|
||||
if ((data = realloc(buffer->data, new_size)) != NULL) {
|
||||
buffer->data = data;
|
||||
buffer->size = new_size;
|
||||
} else {
|
||||
new_size = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return new_size;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
buffer_init(struct buffer *buffer, int32_t size)
|
||||
{
|
||||
if ((size = buffer_set_size(buffer, size)) != -1)
|
||||
buffer->used = 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffer_release(struct buffer *buffer)
|
||||
{
|
||||
if (buffer->data) {
|
||||
free(buffer->data);
|
||||
buffer->data = NULL;
|
||||
}
|
||||
|
||||
buffer->used = 0;
|
||||
buffer->size = 0;
|
||||
}
|
||||
|
||||
|
||||
static int32_t
|
||||
buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size)
|
||||
{
|
||||
int32_t to_move = buffer->used - offset;
|
||||
|
||||
if (to_move < 0) {
|
||||
return -1;
|
||||
} else if (to_move) {
|
||||
memmove(&buffer->data[offset+size], &buffer->data[offset], to_move);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int32_t
|
||||
buffer_insert(struct buffer *buffer, int32_t offset, const char *data,
|
||||
int32_t size)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
|
||||
|
||||
if ( (result = buffer_set_size(buffer, buffer->size + size)) != -1) {
|
||||
if ((result = buffer_move_offset(buffer, offset, size)) != -1) {
|
||||
memcpy(&buffer->data[offset], data, size);
|
||||
buffer->used += result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
buffer_append_printf(struct buffer *buffer, const char *format, ...)
|
||||
{
|
||||
int32_t result;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
result = buffer_append_vprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t
|
||||
buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args)
|
||||
{
|
||||
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
|
||||
int32_t space, result;
|
||||
va_list cp_args;
|
||||
|
||||
va_copy(cp_args, args);
|
||||
space = buffer->size - buffer->used;
|
||||
result = vsnprintf(&buffer->data[buffer->used], space, format, args);
|
||||
|
||||
if (result < space) { /* enough space */
|
||||
buffer->used += result;
|
||||
} else {
|
||||
result = buffer_set_size(buffer, buffer->size + result - space + 1);
|
||||
if (result != -1)
|
||||
result = buffer_append_vprintf(buffer, format, cp_args);
|
||||
}
|
||||
va_end(cp_args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t
|
||||
buffer_append(struct buffer *buffer, const char *data, int32_t size)
|
||||
{
|
||||
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
|
||||
int32_t used, space;
|
||||
|
||||
used = buffer->used,
|
||||
space = buffer->size - used;
|
||||
|
||||
if (size > space) { /* enough space */
|
||||
if (buffer_set_size(buffer, buffer->size + size - space) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&buffer->data[used], data, size);
|
||||
buffer->used += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
buffer_shift(struct buffer *buffer, int32_t offset)
|
||||
{
|
||||
int32_t used = buffer->used - offset;
|
||||
|
||||
if (offset > 0) {
|
||||
if (offset < used) {
|
||||
memmove(buffer->data, &buffer->data[offset], used);
|
||||
} else {
|
||||
used = 0;
|
||||
}
|
||||
buffer->used = used;
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -0,0 +1,446 @@
|
|||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#include <aisl/aisl.h>
|
||||
#include "debug.h"
|
||||
#include "stream.h"
|
||||
#include "http.h"
|
||||
#include "server.h"
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
|
||||
#define FLAG_KEEPALIVE (1<<0)
|
||||
#define FLAG_HANDSHAKE (1<<1)
|
||||
#define FLAG_CAN_READ (1<<2)
|
||||
#define FLAG_CAN_WRITE (1<<3)
|
||||
|
||||
#define BUFFER_SIZE (16*1024)
|
||||
|
||||
|
||||
static void
|
||||
aisl_client_close(AislClient client, AislStatus status)
|
||||
{
|
||||
if (client->fd != -1) {
|
||||
aisl_raise(client->server->instance, (void *)client,
|
||||
AISL_EVENT_CLIENT_DISCONNECT, status );
|
||||
|
||||
close(client->fd);
|
||||
shutdown(client->fd, SHUT_RDWR);
|
||||
client->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
aisl_client_parse(AislClient client, char *data, int32_t size)
|
||||
{
|
||||
AislStatus result = AISL_SUCCESS;
|
||||
AislStream s = client->stream;
|
||||
ParserStatus p = HTTP_PARSER_SUCCESS;
|
||||
|
||||
int32_t data_size;
|
||||
|
||||
switch (client->http_version) {
|
||||
case AISL_HTTP_0_9:
|
||||
case AISL_HTTP_1_0:
|
||||
case AISL_HTTP_1_1:
|
||||
while (p == HTTP_PARSER_SUCCESS) {
|
||||
data_size = size;
|
||||
|
||||
switch (aisl_stream_get_state(s)) {
|
||||
case AISL_STREAM_STATE_IDLE:
|
||||
p = http_10_parse_request(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
case AISL_STREAM_STATE_WAIT_HEADER:
|
||||
p = http_10_parse_header(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
case AISL_STREAM_STATE_WAIT_BODY:
|
||||
p = http_10_parse_body(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
default: /* has input data, but request was already parsed */
|
||||
DPRINTF("misleading request length");
|
||||
p = HTTP_PARSER_ERROR;
|
||||
continue;
|
||||
}
|
||||
// size now has number of parsed bytes
|
||||
DPRINTF("%d bytes parsed, %d bytes left", (data_size - size), size);
|
||||
data += (data_size - size);
|
||||
}
|
||||
break;
|
||||
|
||||
case AISL_HTTP_2_0:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(p) {
|
||||
case HTTP_PARSER_READY:
|
||||
client->flags &= ~FLAG_CAN_READ;
|
||||
client->flags |= FLAG_CAN_WRITE;
|
||||
client->stream->content_length = AISL_AUTO_LENGTH;
|
||||
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST,
|
||||
result);
|
||||
break;
|
||||
|
||||
case HTTP_PARSER_ERROR:
|
||||
/* reply Bad Request here */
|
||||
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
|
||||
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR,
|
||||
result);
|
||||
|
||||
aisl_client_close(client, result);
|
||||
return result;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
|
||||
* parsed. If it is not NULL, then stream expects additional data -> same like
|
||||
* in mono stream HTTP 1.0
|
||||
*/
|
||||
static AislStatus
|
||||
aisl_client_input(AislClient client)
|
||||
{
|
||||
int l;
|
||||
char *data = &client->in.data[ client->in.used ];
|
||||
int32_t size = client->in.size - client->in.used;
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (client->ssl) {
|
||||
DPRINTF("SSL_read");
|
||||
if (!(client->flags & FLAG_HANDSHAKE)) {
|
||||
if ( (l = SSL_accept(client->ssl)) != 1 ) {
|
||||
l = SSL_get_error(client->ssl, l);
|
||||
|
||||
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
|
||||
return AISL_IDLE;
|
||||
|
||||
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
|
||||
|
||||
aisl_client_close(client, AISL_EXTCALL_ERROR);
|
||||
return AISL_EXTCALL_ERROR;
|
||||
}
|
||||
client->flags &= ~FLAG_HANDSHAKE;
|
||||
}
|
||||
l = SSL_read(client->ssl, data, size);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
l = recv( client->fd, data, size, 0);
|
||||
}
|
||||
|
||||
if (l > 0) {
|
||||
DPRINTF("%d bytes received from client", l);
|
||||
|
||||
data = client->in.data;
|
||||
size = client->in.used + l;
|
||||
client->in.used = size;
|
||||
return aisl_client_parse(client, data, size);
|
||||
} else if (l<0) {
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (client->ssl) {
|
||||
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
|
||||
return AISL_IDLE;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
||||
if(errno == EWOULDBLOCK)
|
||||
return AISL_IDLE;
|
||||
DPRINTF("client - %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* both: client disconnect + on read error */
|
||||
/* todo: raise client error here */
|
||||
aisl_client_close(client, AISL_SYSCALL_ERROR);
|
||||
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
aisl_client_output(AislClient client)
|
||||
{
|
||||
int l;
|
||||
char *data;
|
||||
|
||||
AislStream s = client->stream;
|
||||
|
||||
/* while stream is not flushed, we should raise event */
|
||||
if(aisl_get_output_event(s)) {
|
||||
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
|
||||
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
|
||||
* buffer->size will be used to carry amount of stored bytes
|
||||
* */
|
||||
l = aisl_stream_get_buffer_space(s);
|
||||
|
||||
/*
|
||||
if (bsz < OUTPUT_BUFFER_SIZE)
|
||||
{
|
||||
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
|
||||
return false;
|
||||
|
||||
s->buffer->size = bsz;
|
||||
bsz = OUTPUT_BUFFER_SIZE;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!(l < aisl_stream_get_buffer_size(s) / 2)) {
|
||||
aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT,
|
||||
AISL_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
data = aisl_stream_get_data(s, &l);
|
||||
|
||||
if ( !l )
|
||||
return AISL_IDLE;
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
l = (client->ssl) ? SSL_write(client->ssl, data, l) :
|
||||
send(client->fd, data, l, 0);
|
||||
#else
|
||||
l = send(client->fd, data, l, 0);
|
||||
#endif
|
||||
|
||||
if (l > 0) {
|
||||
aisl_stream_shift(s, l);
|
||||
if (aisl_stream_is_done(s)) {
|
||||
/* data has been sent */
|
||||
if (client->flags & FLAG_KEEPALIVE) {
|
||||
aisl_stream_free(s);
|
||||
|
||||
client->stream = aisl_stream_new(client, client->next_id++);
|
||||
if (client->stream != NULL )
|
||||
return AISL_SUCCESS;
|
||||
|
||||
/* in case of malloc error it will not be an error as long as the
|
||||
* request was handled and we just close the connection.
|
||||
*/
|
||||
}
|
||||
|
||||
aisl_client_close(client, AISL_SUCCESS);
|
||||
}
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
/* l < 0 */
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (client->ssl) {
|
||||
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE)
|
||||
return AISL_IDLE;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (errno == EWOULDBLOCK)
|
||||
return AISL_IDLE;
|
||||
}
|
||||
aisl_client_close(client, AISL_SYSCALL_ERROR);
|
||||
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
AislClient
|
||||
aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr)
|
||||
{
|
||||
AislClient client;
|
||||
AislStream stream;
|
||||
|
||||
if ((client = calloc(1, sizeof (struct aisl_client))) != NULL) {
|
||||
DPRINTF("client alocated");
|
||||
memcpy(&client->address, addr, sizeof (struct sockaddr_in));
|
||||
client->server = server;
|
||||
client->fd = fd;
|
||||
client->next_id = 2;
|
||||
client->http_version = AISL_HTTP_1_0;
|
||||
client->timestamp = time(NULL);
|
||||
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
|
||||
|
||||
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) {
|
||||
DPRINTF("client buffer alocated");
|
||||
memcpy(&client->out, &client->in, sizeof (struct buffer));
|
||||
|
||||
stream = aisl_stream_new(client, 0);
|
||||
|
||||
if (stream != NULL) {
|
||||
client->stream = stream;
|
||||
DPRINTF("client stream alocated");
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (server->ssl) {
|
||||
SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
|
||||
|
||||
if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) {
|
||||
SSL_set_fd(client->ssl, fd);
|
||||
return client;
|
||||
}
|
||||
} else {
|
||||
return client;
|
||||
}
|
||||
#else
|
||||
return client;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
aisl_client_free(client);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_client_free(AislClient client)
|
||||
{
|
||||
aisl_client_close(client, AISL_SUCCESS);
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (client->ssl)
|
||||
SSL_free(client->ssl);
|
||||
#endif
|
||||
|
||||
if (client->in.data)
|
||||
free(client->in.data);
|
||||
|
||||
if (client->stream)
|
||||
aisl_stream_free(client->stream);
|
||||
|
||||
free(client);
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
aisl_client_touch(AislClient client, int32_t timeout)
|
||||
{
|
||||
AislStatus result, status;
|
||||
|
||||
result = AISL_IDLE;
|
||||
status = AISL_IDLE;
|
||||
|
||||
/* input */
|
||||
if (client->flags & FLAG_CAN_READ) {
|
||||
if ( (result = aisl_client_input(client)) < 0 )
|
||||
return result;
|
||||
}
|
||||
|
||||
/* output */
|
||||
if (client->flags & FLAG_CAN_WRITE) {
|
||||
if ( (status = aisl_client_output(client)) < 0 )
|
||||
return status;
|
||||
}
|
||||
|
||||
if (result == AISL_IDLE)
|
||||
result = status;
|
||||
|
||||
if (result != AISL_SUCCESS) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
if (!(now - client->timestamp < timeout)) {
|
||||
aisl_client_close(client, result);
|
||||
}
|
||||
} else {
|
||||
client->timestamp = time(NULL);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_client_get_socket(AislClient client)
|
||||
{
|
||||
return client->fd;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
aisl_client_get_keepalive(AislClient client)
|
||||
{
|
||||
return (client->flags & FLAG_KEEPALIVE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_client_set_keepalive(AislClient client, bool value)
|
||||
{
|
||||
if (value)
|
||||
client->flags |= FLAG_KEEPALIVE;
|
||||
else
|
||||
client->flags &= ~FLAG_KEEPALIVE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* API Level ---------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislServer
|
||||
aisl_client_get_server(AislClient client)
|
||||
{
|
||||
return client->server;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_client_is_secure(AislClient client)
|
||||
{
|
||||
#if AISL_WITH_SSL == 1
|
||||
return (client->ssl == NULL) ? false : true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_client_is_online(AislClient client)
|
||||
{
|
||||
return (client->fd == -1) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_client_disconnect(AislClient client)
|
||||
{
|
||||
aisl_client_close(client, AISL_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislHttpVersion
|
||||
aisl_client_get_http_version(AislClient client)
|
||||
{
|
||||
return client->http_version;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_client_get_address(AislClient client, struct sockaddr_in *address)
|
||||
{
|
||||
memcpy(address, &client->address, sizeof (struct sockaddr_in));
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file client.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of aisl_client structure and functions
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
#ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
|
||||
#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include <aisl/client.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define AISL_CLIENT(x) ((AislClient) x)
|
||||
|
||||
|
||||
struct aisl_client {
|
||||
struct sockaddr_in address; /**< Client's address structure. */
|
||||
struct buffer in; /**< Client's input buffer. */
|
||||
struct buffer out; /**< Client's output buffer. */
|
||||
AislServer server; /**< Server instance. */
|
||||
AislStream stream; /**< Pending client's stream. */
|
||||
#if AISL_WITH_SSL == 1
|
||||
SSL *ssl; /**< SSL pointer for HTTPS. */
|
||||
#endif
|
||||
time_t timestamp; /**< Last communication timestamp. */
|
||||
int next_id; /**< Stream id generator (even). */
|
||||
int flags; /**< Client's flag bitmask. */
|
||||
int fd; /**< Client's socket descriptor. */
|
||||
AislHttpVersion http_version; /**< Client's http_version version. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constructor for #AislClient instance.
|
||||
* @param server an #AislServer instance pointer.
|
||||
* @param fd a client socket descriptor.
|
||||
* @param addr a pointer to client's address structure.
|
||||
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
|
||||
*/
|
||||
AislClient
|
||||
aisl_client_new(AislServer server,
|
||||
int fd,
|
||||
struct sockaddr_in *addr );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destructor for #AislClient instance.
|
||||
* @param client an #AislClient instance pointer.
|
||||
*/
|
||||
void
|
||||
aisl_client_free(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does all HTTP client routines.
|
||||
* Reads and parses requests, writes responses.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @param timeout an allowed client silence time in seconds.
|
||||
* @return #AislStatus code.
|
||||
*/
|
||||
AislStatus
|
||||
aisl_client_touch(AislClient client, int32_t timeout);
|
||||
|
||||
|
||||
/**
|
||||
* @Brief Checks if client is about to keep connection alive.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return true if keepalive mode is on, otherwise false.
|
||||
*/
|
||||
bool
|
||||
aisl_client_get_keepalive(AislClient client);
|
||||
|
||||
|
||||
/**
|
||||
* @Brief Sets if connection with client must be kept alive.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @param value a true to enable keepalive mode, false to disable.
|
||||
*/
|
||||
void
|
||||
aisl_client_set_keepalive(AislClient client, bool value);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets socket descriptor associated with #AislClient instance.
|
||||
* @param client an #AislClient instance pointer.
|
||||
* @return a client socket descriptor.
|
||||
*/
|
||||
int
|
||||
aisl_client_get_socket(AislClient client);
|
||||
|
||||
|
||||
#endif /* !AISL_CLIENT_H */
|
|
@ -0,0 +1,37 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file debug.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL debug module
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||
#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||
|
||||
#if AISL_WITH_DEBUG == 1
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define DPRINTF(...) do { \
|
||||
fprintf(stderr, "* AISL: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} while(0) \
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define DPRINTF(...)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* !AISL_DEBUG_H */
|
|
@ -0,0 +1,428 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file http.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief HTTP module source file
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "client.h"
|
||||
#include "stream.h"
|
||||
#include "debug.h"
|
||||
#include "http.h"
|
||||
|
||||
|
||||
static AislHttpMethod
|
||||
http_method_from_string(const char *method, int32_t length)
|
||||
{
|
||||
int i;
|
||||
AislHttpMethod methods[3] = {0, 0, 0};
|
||||
|
||||
switch(length) {
|
||||
case 3:
|
||||
methods[0] = AISL_HTTP_GET;
|
||||
methods[1] = AISL_HTTP_PUT;
|
||||
methods[2] = AISL_HTTP_PRI;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
methods[0] = AISL_HTTP_POST;
|
||||
methods[1] = AISL_HTTP_HEAD;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
methods[0] = AISL_HTTP_TRACE;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
methods[0] = AISL_HTTP_DELETE;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
methods[0] = AISL_HTTP_OPTIONS;
|
||||
methods[1] = AISL_HTTP_CONNECT;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i=0; i<sizeof (methods)/sizeof (AislHttpMethod); i++) {
|
||||
if (!(methods[i]))
|
||||
break;
|
||||
|
||||
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
|
||||
return methods[i];
|
||||
}
|
||||
|
||||
return AISL_HTTP_METHOD_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static AislHttpVersion
|
||||
http_version_from_string(const char *version_string)
|
||||
{
|
||||
if (strncmp(version_string, "HTTP/", 5)==0) {
|
||||
if (strncmp(&version_string[5], "0.9", 3)==0) return AISL_HTTP_0_9;
|
||||
if (strncmp(&version_string[5], "1.0", 3)==0) return AISL_HTTP_1_0;
|
||||
if (strncmp(&version_string[5], "1.1", 3)==0) return AISL_HTTP_1_1;
|
||||
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Library Level */
|
||||
|
||||
ParserStatus
|
||||
http_10_parse_request(char *data, int32_t *p_size, AislStream stream)
|
||||
{
|
||||
/* STEP 1. Split data according to HTTP request format
|
||||
*
|
||||
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
|
||||
* ^ ^ ^ ^ ^ ^ ^ ^
|
||||
* | | | | | | | |
|
||||
* | | | | | | | +--- newline
|
||||
* | | | | | | +------------- version
|
||||
* | | | | | +------------------------- query
|
||||
* | | | | +------------------------------------- path
|
||||
* | | | +--------------------------------------- port
|
||||
* | | +----------------------------------------------------- host
|
||||
* | +------------------------------------------------------------ uri
|
||||
* +---------------------------------------------------------------- method
|
||||
*/
|
||||
char *uri = NULL,
|
||||
*uri_end = NULL,
|
||||
*host = NULL,
|
||||
*port = NULL,
|
||||
*path = NULL,
|
||||
*query = NULL,
|
||||
*version = NULL,
|
||||
*newline = NULL,
|
||||
*method = data,
|
||||
*method_end = NULL;
|
||||
|
||||
AislHttpMethod http_method;
|
||||
AislHttpVersion http_version;
|
||||
|
||||
int32_t size = *p_size;
|
||||
|
||||
while(!newline && size--) {
|
||||
switch(*data)
|
||||
{
|
||||
case ' ':
|
||||
if (!method_end)
|
||||
method_end = data;
|
||||
else if (path && !uri_end)
|
||||
uri_end = data;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (uri && !host) {
|
||||
host = data+3;
|
||||
} else if (host && !port) {
|
||||
port = data+1;
|
||||
} else if (version) {
|
||||
DPRINTF("bad character in HTTP version (%c)", *data);
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (!path && data > host) {
|
||||
path = data;
|
||||
if (!uri)
|
||||
uri = path;
|
||||
} else if (version && data-version != 4) {
|
||||
DPRINTF("wrong HTTP version length");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (!query) {
|
||||
query = data+1;
|
||||
} else if (version) {
|
||||
DPRINTF("bad character in HTTP version (%c)", *data);
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
newline = data;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
if (!version) {
|
||||
DPRINTF("unexpected end of HTTP request");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!uri && method_end) {
|
||||
uri = data;
|
||||
} else if (!version && uri_end) {
|
||||
version = data;
|
||||
} else if (version && data-version > 7) {
|
||||
DPRINTF("bad HTTP version length");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
/* STEP 2. Verify splitting was completed */
|
||||
|
||||
/* Was request sent? */
|
||||
if (!newline)
|
||||
return HTTP_PARSER_HUNGRY;
|
||||
|
||||
/* Check mandatory parts presence */
|
||||
if (!method_end || !path || !uri_end || !version) {
|
||||
DPRINTF("parser error: method=%d, path=%d, uri_end=%d, version=%d",
|
||||
(method_end ? 1 : 0), (path ? 1 : 0), (uri_end ? 1 : 0),
|
||||
(version ? 1: 0));
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
*method_end = 0;
|
||||
*newline = 0;
|
||||
*uri_end = 0;
|
||||
|
||||
http_method = http_method_from_string(method, method_end - method);
|
||||
if (http_method == AISL_HTTP_METHOD_UNKNOWN) {
|
||||
DPRINTF("invalid HTTP method");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
if ((http_version = http_version_from_string(version))==0) {
|
||||
DPRINTF("invalid HTTP version");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
*(query - 1) = 0;
|
||||
} else {
|
||||
query = uri_end;
|
||||
}
|
||||
|
||||
if (host) {
|
||||
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) {
|
||||
DPRINTF("invalid HTTP uri");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
if (port)
|
||||
*(port - 1) = 0;
|
||||
}
|
||||
|
||||
stream->client->http_version = http_version;
|
||||
aisl_stream_set_request(stream, http_method, path, query);
|
||||
|
||||
if (host)
|
||||
aisl_stream_set_header(stream, "host", host);
|
||||
/* how many characters has been read */
|
||||
*(p_size) = size;
|
||||
return HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ParserStatus
|
||||
http_10_parse_header(char *data, int32_t *p_size, AislStream stream)
|
||||
{
|
||||
int32_t size = *p_size;
|
||||
char *key = data,
|
||||
*colon = NULL,
|
||||
*val = NULL,
|
||||
*val_end = NULL,
|
||||
*newline = NULL;
|
||||
|
||||
|
||||
DPRINTF("parse header: %p, %d, %02X %02X", data, *p_size, *data & 0xFF, *(data+1) & 0xFF);
|
||||
|
||||
while(!newline && size-- ) {
|
||||
switch(*data) {
|
||||
case ' ':
|
||||
if (val && !val_end)
|
||||
val_end = data;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (!colon) {
|
||||
if (colon == key) {
|
||||
DPRINTF("parser error: nameless HTTP header");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
colon = data;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
newline = data;
|
||||
|
||||
case '\r':
|
||||
if (!val_end && val)
|
||||
val_end = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!colon) {
|
||||
*data = tolower(*data);
|
||||
} else if (!val) {
|
||||
if (colon)
|
||||
val = data;
|
||||
}
|
||||
|
||||
if (val_end)
|
||||
val_end = NULL;
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
if (!newline)
|
||||
return HTTP_PARSER_HUNGRY;
|
||||
|
||||
/* DPRINTF("(%p == %p); *key == 0x%02x", newline, key, *key & 0xFF); */
|
||||
|
||||
if (colon && val && val_end) {
|
||||
*colon = 0;
|
||||
*val_end = 0;
|
||||
aisl_stream_set_header(stream, key, val);
|
||||
*p_size = size;
|
||||
return HTTP_PARSER_SUCCESS;
|
||||
} else if (newline == key || (newline == key+1 && *key == '\r')) {
|
||||
*p_size = size;
|
||||
DPRINTF("end of headers received");
|
||||
return (aisl_stream_set_end_of_headers(stream) == 0) ?
|
||||
HTTP_PARSER_READY : HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
DPRINTF("parser error: invalid HTTP header");
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ParserStatus
|
||||
http_10_parse_body(char *data, int32_t *p_size, AislStream stream)
|
||||
{
|
||||
int32_t size = *p_size;
|
||||
|
||||
if (!size)
|
||||
return HTTP_PARSER_HUNGRY;
|
||||
|
||||
*p_size = 0;
|
||||
|
||||
switch (aisl_stream_set_body(stream, data, size)) {
|
||||
case 0:
|
||||
return HTTP_PARSER_READY;
|
||||
case -1:
|
||||
DPRINTF("parser error: invalid HTTP body length");
|
||||
return HTTP_PARSER_ERROR;
|
||||
default:
|
||||
return HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* API Level */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_version_to_string(AislHttpVersion version)
|
||||
{
|
||||
switch (version) {
|
||||
case AISL_HTTP_0_9:
|
||||
return "HTTP/0.9";
|
||||
case AISL_HTTP_1_0:
|
||||
return "HTTP/1.0";
|
||||
case AISL_HTTP_1_1:
|
||||
return "HTTP/1.1";
|
||||
case AISL_HTTP_2_0:
|
||||
return "HTTP/2.0";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_response_to_string(AislHttpResponse code)
|
||||
{
|
||||
switch (code) {
|
||||
/* most common for faster behavior */
|
||||
case AISL_HTTP_OK: return "OK";
|
||||
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
|
||||
/* informational */
|
||||
case AISL_HTTP_CONTINUE: return "Continue";
|
||||
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
|
||||
/* Successful */
|
||||
case AISL_HTTP_CREATED: return "Created";
|
||||
case AISL_HTTP_ACCEPTED: return "Accepted";
|
||||
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
|
||||
case AISL_HTTP_NO_CONTENT: return "No Content";
|
||||
case AISL_HTTP_RESET_CONTENT: return "Reset Content";
|
||||
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
|
||||
/* redirection */
|
||||
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
|
||||
case AISL_HTTP_FOUND: return "Found";
|
||||
case AISL_HTTP_SEE_OTHER: return "See other";
|
||||
case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
|
||||
case AISL_HTTP_USE_PROXY: return "Use Proxy";
|
||||
case AISL_HTTP_UNUSED: return "(unused)";
|
||||
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
|
||||
/* client error */
|
||||
case AISL_HTTP_BAD_REQUEST: return "Bad Request";
|
||||
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
|
||||
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
|
||||
case AISL_HTTP_FORBIDDEN: return "Forbidden";
|
||||
case AISL_HTTP_NOT_FOUND: return "Not Found";
|
||||
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
|
||||
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
|
||||
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
|
||||
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
|
||||
case AISL_HTTP_CONFLICT: return "Conflict";
|
||||
case AISL_HTTP_GONE: return "Gone";
|
||||
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
|
||||
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
|
||||
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
|
||||
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
|
||||
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
|
||||
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
|
||||
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
|
||||
/* server error */
|
||||
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
|
||||
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
|
||||
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
|
||||
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
|
||||
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
|
||||
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_method_to_string( AislHttpMethod method )
|
||||
{
|
||||
switch(method) {
|
||||
case AISL_HTTP_GET: return "GET";
|
||||
case AISL_HTTP_PUT: return "PUT";
|
||||
case AISL_HTTP_POST: return "POST";
|
||||
case AISL_HTTP_HEAD: return "HEAD";
|
||||
case AISL_HTTP_TRACE: return "TRACE";
|
||||
case AISL_HTTP_DELETE: return "DELETE";
|
||||
case AISL_HTTP_OPTIONS: return "OPTIONS";
|
||||
case AISL_HTTP_CONNECT: return "CONNECT";
|
||||
case AISL_HTTP_PRI: return "PRI";
|
||||
case AISL_HTTP_METHOD_UNKNOWN: break;
|
||||
}
|
||||
return "";
|
||||
}
|
|
@ -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 */
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* src/instance.c
|
||||
*
|
||||
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
|
||||
*
|
||||
* Project homepage: https://lowenware.com/aisl/
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "str-utils.h"
|
||||
#include "buffer.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "stream.h"
|
||||
#include "instance.h"
|
||||
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
|
||||
static uint32_t m_instances = 0;
|
||||
|
||||
static struct aisl_ssl *
|
||||
aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl)
|
||||
{
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
struct aisl_ssl **list, *ssl;
|
||||
|
||||
list = instance->ssl;
|
||||
|
||||
/* lookup for existing contexts */
|
||||
while ((ssl = *list)) {
|
||||
if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 &&
|
||||
ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0
|
||||
) {
|
||||
ssl_ctx = ssl->ctx;
|
||||
break;
|
||||
}
|
||||
list++;
|
||||
}
|
||||
|
||||
ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file,
|
||||
ssl_ctx);
|
||||
|
||||
if (ssl) {
|
||||
if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) {
|
||||
aisl_ssl_free(ssl);
|
||||
ssl = NULL;
|
||||
}
|
||||
}
|
||||
return ssl;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Initialization functions */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislInstance
|
||||
aisl_new(const struct aisl_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
AislInstance instance;
|
||||
|
||||
/* allocate root structure */
|
||||
if (!(instance = calloc(1, sizeof (struct aisl_instance))))
|
||||
goto finally;
|
||||
|
||||
/* allocate servers */
|
||||
if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer))))
|
||||
goto release;
|
||||
|
||||
for (i = 0; i < cfg->srv_cnt; i++) {
|
||||
DPRINTF("new srv %d", i);
|
||||
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
|
||||
goto release;
|
||||
}
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if ((m_instances++) == 0) {
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
}
|
||||
|
||||
if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl))))
|
||||
goto release;
|
||||
|
||||
for (i=0; i<cfg->ssl_cnt; i++) {
|
||||
DPRINTF("new ssl %d", i);
|
||||
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
|
||||
goto release;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (list_init(&instance->client_spool, cfg->client_spool_size) == -1)
|
||||
goto release;
|
||||
|
||||
instance->accept_limit = cfg->client_accept_limit;
|
||||
instance->silence_timeout = cfg->client_silence_timeout;
|
||||
instance->callback = cfg->callback;
|
||||
instance->p_ctx = cfg->p_ctx;
|
||||
|
||||
goto finally;
|
||||
|
||||
release:
|
||||
aisl_free(instance);
|
||||
instance = NULL;
|
||||
|
||||
finally:
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_free(AislInstance instance)
|
||||
{
|
||||
if (instance->srv) {
|
||||
AislServer * srv = instance->srv;
|
||||
|
||||
while (*srv) {
|
||||
aisl_server_free(*(srv++));
|
||||
}
|
||||
|
||||
free(instance->srv);
|
||||
}
|
||||
|
||||
list_release(&instance->client_spool, (list_destructor_t)aisl_client_free);
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
if (instance->ssl) {
|
||||
struct aisl_ssl **ssl = instance->ssl;
|
||||
|
||||
while (*ssl) {
|
||||
aisl_ssl_free(*(ssl++));
|
||||
}
|
||||
free(instance->ssl);
|
||||
}
|
||||
|
||||
if ((--m_instances) == 0) {
|
||||
EVP_cleanup();
|
||||
}
|
||||
#endif
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
|
||||
SSL_CTX *
|
||||
aisl_get_ssl_ctx(AislInstance instance, const char * host)
|
||||
{
|
||||
struct aisl_ssl **list, *ssl;
|
||||
|
||||
list = instance->ssl;
|
||||
|
||||
if (host) {
|
||||
while ((ssl = *list)) {
|
||||
if (str_cmpi(ssl->host, host) == 0) {
|
||||
return ssl->ctx;
|
||||
}
|
||||
list++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt)
|
||||
{
|
||||
#if AISL_WITH_STRINGIFIERS == 1
|
||||
DPRINTF("! %s", aisl_event_to_string(evt->code));
|
||||
#else
|
||||
DPRINTF("! %d", evt->code);
|
||||
#endif
|
||||
|
||||
if (instance->callback)
|
||||
instance->callback(evt, instance->p_ctx);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_raise(AislInstance instance,
|
||||
void *source,
|
||||
AislEvent code,
|
||||
AislStatus status)
|
||||
{
|
||||
struct aisl_evt evt;
|
||||
|
||||
evt.source = source;
|
||||
evt.code = code;
|
||||
evt.status = status;
|
||||
|
||||
aisl_raise_evt(instance, &evt);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislStatus
|
||||
aisl_run_cycle(AislInstance instance)
|
||||
{
|
||||
AislStatus result = AISL_IDLE;
|
||||
AislServer *list, srv;
|
||||
AislClient cli;
|
||||
int32_t i;
|
||||
|
||||
list = instance->srv;
|
||||
|
||||
while ((srv = *list)) {
|
||||
cli = NULL;
|
||||
|
||||
if (aisl_server_touch(srv, &cli) != AISL_IDLE)
|
||||
result = AISL_SUCCESS;
|
||||
|
||||
if (cli) {
|
||||
DPRINTF("Accepted %p", (void*)cli);
|
||||
if (list_append(&instance->client_spool, cli) == -1)
|
||||
aisl_client_free(cli);
|
||||
}
|
||||
list++;
|
||||
}
|
||||
|
||||
for (i=0; i < instance->client_spool.count; i++) {
|
||||
cli = LIST_INDEX(instance->client_spool, i);
|
||||
|
||||
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
|
||||
result = AISL_SUCCESS;
|
||||
|
||||
if (!aisl_client_is_online(cli)) {
|
||||
aisl_client_free( cli );
|
||||
list_remove_index(&instance->client_spool, i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislStatus
|
||||
aisl_sleep(AislInstance instance, uint32_t usec)
|
||||
{
|
||||
AislServer *list, srv;
|
||||
int sd, maxfd = 0;
|
||||
size_t i;
|
||||
struct timeval timeout = {0,usec};
|
||||
|
||||
memset(&timeout, 0, sizeof (struct timeval));
|
||||
timeout.tv_usec = usec;
|
||||
|
||||
fd_set fs;
|
||||
FD_ZERO (&fs);
|
||||
|
||||
list = instance->srv;
|
||||
|
||||
while ((srv = *list)) {
|
||||
sd = aisl_server_get_socket(srv);
|
||||
|
||||
if (sd != -1) {
|
||||
FD_SET(sd, &fs);
|
||||
if (sd > maxfd) maxfd = sd;
|
||||
}
|
||||
list++;
|
||||
}
|
||||
|
||||
for (i=0; i<instance->client_spool.count; i++) {
|
||||
AislClient c = LIST_INDEX(instance->client_spool, i);
|
||||
sd = aisl_client_get_socket(c);
|
||||
if (sd != -1) {
|
||||
FD_SET(sd, &fs);
|
||||
if (sd > maxfd) maxfd = sd;
|
||||
}
|
||||
}
|
||||
|
||||
switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) {
|
||||
case -1:
|
||||
return AISL_SYSCALL_ERROR;
|
||||
|
||||
case 0:
|
||||
return AISL_IDLE;
|
||||
|
||||
default:
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file src/instance.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Declarations of aisl_instance structure and functions
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
|
||||
#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include <aisl/instance.h>
|
||||
#include "ssl.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
struct aisl_instance {
|
||||
AislServer *srv;
|
||||
#if AISL_WITH_SSL == 1
|
||||
struct aisl_ssl * *ssl;
|
||||
#endif
|
||||
struct list client_spool;
|
||||
AislCallback callback;
|
||||
void *p_ctx;
|
||||
|
||||
uint32_t accept_limit;
|
||||
uint32_t silence_timeout;
|
||||
uint32_t buffer_size;
|
||||
};
|
||||
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
/**
|
||||
* @brief Gets SSL context for appropriate server name.
|
||||
* @param instance a pointer to #AislInstance instance.
|
||||
* @param server_name a null-terminated string with server name or NULL.
|
||||
* @return a pointer to SSL context
|
||||
*/
|
||||
SSL_CTX *
|
||||
aisl_get_ssl_ctx(AislInstance instance, const char *server_name);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Raises event from source.
|
||||
* @param instance a pointer to #AislInstance instance.
|
||||
* @param evt a pointer to event structure.
|
||||
*/
|
||||
void
|
||||
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt);
|
||||
|
||||
|
||||
void
|
||||
aisl_raise(AislInstance instance,
|
||||
void *source,
|
||||
AislEvent code,
|
||||
AislStatus status);
|
||||
|
||||
#endif /* !AISL_INSTANCE_H */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -0,0 +1,169 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "str-utils.h"
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates TCP server socket, binds to address and starts to listen.
|
||||
* @param server a pointer to #AislServer instance.
|
||||
* @return #AislStatus code.
|
||||
*/
|
||||
static AislStatus
|
||||
aisl_server_open(AislServer server)
|
||||
{
|
||||
int fd, s_opt = 1;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
|
||||
if (fd != -1) {
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt,
|
||||
sizeof (int));
|
||||
|
||||
#ifdef __APPLE__
|
||||
ioctl(fd, FIONBIO, (char *)&s_opt);
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
s_opt = sizeof (struct sockaddr_in);
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &server->address, s_opt) == 0) {
|
||||
if (listen(fd, SOMAXCONN) == 0) {
|
||||
server->fd = fd;
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Tries to accept a new client.
|
||||
* @param server a pointer to #AislServer instance.
|
||||
* @param p_client a pointer to store #AislClient instance pointer.
|
||||
* @return #AislStatus code.
|
||||
*/
|
||||
static AislStatus
|
||||
aisl_server_accept(AislServer server, AislClient *p_client )
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof (struct sockaddr_in);
|
||||
|
||||
fd = accept(server->fd, (struct sockaddr *)&addr, &len);
|
||||
|
||||
if (fd != -1) {
|
||||
int flags;
|
||||
|
||||
DPRINTF("accepted fd=%d", fd);
|
||||
|
||||
if ((flags = fcntl(fd, F_GETFL, 0)) != -1) {
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) == 0) {
|
||||
return (!(*p_client = aisl_client_new(server, fd, &addr))) ?
|
||||
AISL_MALLOC_ERROR : AISL_SUCCESS;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
return AISL_IDLE;
|
||||
}
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
/* Library Level ------------------------------------------------------------ */
|
||||
|
||||
AislStatus
|
||||
aisl_server_touch(AislServer server, AislClient *p_client)
|
||||
{
|
||||
AislStatus result;
|
||||
|
||||
if (server->fd == -1) {
|
||||
if ((result = aisl_server_open(server)) != AISL_IDLE) {
|
||||
aisl_raise(server->instance, server, ((result == AISL_SUCCESS) ?
|
||||
AISL_EVENT_SERVER_READY : AISL_EVENT_SERVER_ERROR), result);
|
||||
}
|
||||
} else {
|
||||
result = aisl_server_accept(server, p_client);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_server_get_socket(AislServer server)
|
||||
{
|
||||
return server->fd;
|
||||
}
|
||||
|
||||
|
||||
AislServer
|
||||
aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance)
|
||||
{
|
||||
AislServer server;
|
||||
|
||||
if ((server = calloc(1, sizeof (struct aisl_server))) != NULL) {
|
||||
server->instance = instance;
|
||||
server->fd = -1;
|
||||
server->address.sin_family = AF_INET;
|
||||
server->address.sin_addr.s_addr = inet_addr(cfg_srv->host);
|
||||
server->address.sin_port = htons(cfg_srv->port);
|
||||
server->ssl = cfg_srv->secure;
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_server_free(AislServer server)
|
||||
{
|
||||
if (server) {
|
||||
if ( server->fd != -1) {
|
||||
close(server->fd);
|
||||
server->fd=-1;
|
||||
}
|
||||
free(server);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* API Level ---------------------------------------------------------------- */
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_server_get_address(AislServer server, struct sockaddr_in *address)
|
||||
{
|
||||
memcpy(address, &server->address, sizeof (struct sockaddr_in));
|
||||
}
|
||||
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_server_get_ssl(AislServer server)
|
||||
{
|
||||
return server->ssl;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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 */
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* ssl.c
|
||||
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include "str-utils.h"
|
||||
#include "instance.h"
|
||||
#include "ssl.h"
|
||||
|
||||
|
||||
#if AISL_WITH_SSL == 1
|
||||
|
||||
|
||||
static int
|
||||
aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance )
|
||||
{
|
||||
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name);
|
||||
|
||||
if (ctx)
|
||||
SSL_set_SSL_CTX(ssl, ctx);
|
||||
|
||||
(void)ptr;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
SSL_CTX *
|
||||
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance)
|
||||
{
|
||||
SSL_CTX * ctx;
|
||||
|
||||
if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) {
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx );
|
||||
SSL_CTX_set_tlsext_servername_arg( ctx, p_instance );
|
||||
|
||||
if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0))
|
||||
goto except;
|
||||
|
||||
if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0))
|
||||
goto except;
|
||||
|
||||
ssl->ctx = ctx;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
except:
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct aisl_ssl *
|
||||
aisl_ssl_new(const char *key_file,
|
||||
const char *crt_file,
|
||||
const char *host,
|
||||
SSL_CTX *ctx)
|
||||
{
|
||||
struct aisl_ssl *ssl;
|
||||
|
||||
if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) {
|
||||
if ((ssl->host = str_copy( host ? host : "*" )) != NULL) {
|
||||
if (ctx) {
|
||||
ssl->ctx = ctx;
|
||||
return ssl;
|
||||
} else {
|
||||
if ((ssl->key_file = str_copy(key_file)) != NULL) {
|
||||
if ((ssl->crt_file = str_copy(crt_file)) != NULL) {
|
||||
return ssl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
aisl_ssl_free(ssl);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_ssl_free( struct aisl_ssl *ssl )
|
||||
{
|
||||
if (ssl->host)
|
||||
free(ssl->host);
|
||||
|
||||
if (ssl->key_file) {
|
||||
free(ssl->key_file);
|
||||
SSL_CTX_free(ssl->ctx);
|
||||
}
|
||||
|
||||
if (ssl->crt_file)
|
||||
free(ssl->crt_file);
|
||||
|
||||
free(ssl);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -0,0 +1,616 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "str-utils.h"
|
||||
#include "debug.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define FLAG_FLUSHED (1<<0)
|
||||
#define FLAG_OUTPUT_CHUNKED (1<<1)
|
||||
#define FLAG_SERVER_HEADER_SENT (1<<2)
|
||||
#define FLAG_CONTENT_TYPE_HEADER_SENT (1<<3)
|
||||
#define FLAG_CONTENT_LENGTH_HEADER_SENT (1<<4)
|
||||
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
|
||||
|
||||
|
||||
/* Library level */
|
||||
|
||||
static void
|
||||
aisl_stream_reset(AislStream stream, bool initial)
|
||||
{
|
||||
if (!initial) {
|
||||
aisl_raise(aisl_get_instance(stream), (void*) stream,
|
||||
AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
|
||||
}
|
||||
|
||||
buffer_release(&stream->buffer);
|
||||
|
||||
stream->u_ptr = NULL;
|
||||
stream->content_length = 0; //AISL_AUTO_LENGTH;
|
||||
stream->head_offset = 0;
|
||||
stream->flags = 0;
|
||||
stream->state = AISL_STREAM_STATE_IDLE;
|
||||
stream->http_response = AISL_HTTP_OK;
|
||||
}
|
||||
|
||||
|
||||
AislStream
|
||||
aisl_stream_new(AislClient client, int id)
|
||||
{
|
||||
AislStream stream = calloc(1, sizeof (struct aisl_stream));
|
||||
|
||||
if (stream) {
|
||||
stream->id = id;
|
||||
stream->client = client;
|
||||
aisl_stream_reset(stream, true);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_free(AislStream stream)
|
||||
{
|
||||
aisl_stream_reset(stream, false);
|
||||
free(stream);
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
aisl_stream_get_buffer_space(AislStream stream)
|
||||
{
|
||||
return stream->buffer.size - stream->buffer.used;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
aisl_stream_get_buffer_size(AislStream stream)
|
||||
{
|
||||
return stream->buffer.size;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
aisl_stream_get_data(AislStream stream, int32_t *p_length)
|
||||
{
|
||||
*p_length = stream->buffer.used;
|
||||
|
||||
return stream->buffer.data;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_shift(AislStream stream, int32_t offset)
|
||||
{
|
||||
buffer_shift(&stream->buffer, offset);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
aisl_stream_is_done(AislStream stream)
|
||||
{
|
||||
return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE);
|
||||
}
|
||||
|
||||
|
||||
AislStreamState
|
||||
aisl_stream_get_state(AislStream stream)
|
||||
{
|
||||
return stream->state;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_set_request(AislStream stream,
|
||||
AislHttpMethod http_method,
|
||||
const char *path,
|
||||
const char *query)
|
||||
{
|
||||
struct aisl_evt_open on_open;
|
||||
|
||||
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
|
||||
|
||||
DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method),
|
||||
path, query);
|
||||
|
||||
on_open.evt.code = AISL_EVENT_STREAM_OPEN;
|
||||
on_open.evt.source = (void *) stream;
|
||||
on_open.evt.status = AISL_SUCCESS;
|
||||
on_open.http_method = http_method;
|
||||
on_open.path = path;
|
||||
on_open.query = query;
|
||||
|
||||
aisl_raise_evt(aisl_get_instance(stream), (struct aisl_evt *)&on_open);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_set_header(AislStream stream, const char *key, const char *value)
|
||||
{
|
||||
struct aisl_evt_header on_header;
|
||||
|
||||
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
|
||||
return;
|
||||
|
||||
if (strcmp(key, "content-length") == 0) {
|
||||
stream->content_length = strtoll(value, NULL, 10);
|
||||
} else if (strcmp(key, "connection") == 0) {
|
||||
aisl_client_set_keepalive(stream->client,
|
||||
(str_cmpi(value, "close")==0) ? false : true);
|
||||
}
|
||||
|
||||
DPRINTF("%s: %s", key, value);
|
||||
|
||||
on_header.evt.code = AISL_EVENT_STREAM_HEADER;
|
||||
on_header.evt.source = (void *) stream;
|
||||
on_header.evt.status = AISL_SUCCESS;
|
||||
on_header.key = key;
|
||||
on_header.value = value;
|
||||
|
||||
aisl_raise_evt(aisl_get_instance(stream),
|
||||
(struct aisl_evt *) &on_header);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_end_of_headers(AislStream stream)
|
||||
{
|
||||
int result;
|
||||
|
||||
DPRINTF("stream->content_length == %"PRIu64"", stream->content_length);
|
||||
|
||||
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
|
||||
result = (stream->content_length != 0);
|
||||
stream->state = (result) ? AISL_STREAM_STATE_WAIT_BODY :
|
||||
AISL_STREAM_STATE_READY;
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_body(AislStream stream, const char *data, int32_t size)
|
||||
{
|
||||
int result;
|
||||
if (stream->state == AISL_STREAM_STATE_WAIT_BODY) {
|
||||
if (!(stream->content_length < size)) {
|
||||
struct aisl_evt_input on_input;
|
||||
|
||||
stream->content_length -= size;
|
||||
|
||||
if (stream->content_length == 0) {
|
||||
stream->state = AISL_STREAM_STATE_READY;
|
||||
result = 0;
|
||||
} else {
|
||||
result = 1;
|
||||
}
|
||||
|
||||
on_input.evt.code = AISL_EVENT_STREAM_INPUT;
|
||||
on_input.evt.source = (void *)stream;
|
||||
on_input.evt.status = AISL_SUCCESS;
|
||||
on_input.data = data;
|
||||
on_input.size = size;
|
||||
|
||||
aisl_raise_evt(stream->client->server->instance,
|
||||
(struct aisl_evt *) &on_input);
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* API Level */
|
||||
|
||||
/* Why it was here?
|
||||
static int
|
||||
aisl_stream_write(AislStream stream, const char * data, uint32_t d_len)
|
||||
{
|
||||
return buffer_add( &stream->buffer, data, d_len);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_cancel(AislStream stream)
|
||||
{
|
||||
aisl_client_close( stream->client );
|
||||
}
|
||||
*/
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void *
|
||||
aisl_get_context(AislStream s)
|
||||
{
|
||||
return s->u_ptr;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_set_context(AislStream s, void *u_ptr)
|
||||
{
|
||||
s->u_ptr = u_ptr;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_is_secure(AislStream stream)
|
||||
{
|
||||
return aisl_client_is_secure(stream->client);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislClient
|
||||
aisl_get_client(AislStream s)
|
||||
{
|
||||
return s->client;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislServer
|
||||
aisl_get_server(AislStream s)
|
||||
{
|
||||
return aisl_client_get_server(s->client);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislHttpVersion
|
||||
aisl_get_http_version(AislStream s)
|
||||
{
|
||||
return aisl_client_get_http_version(s->client);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_reject(AislStream s)
|
||||
{
|
||||
aisl_client_disconnect( s->client );
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
aisl_start_response(AislStream stream)
|
||||
{
|
||||
return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
aisl_stream_close_headers(AislStream stream)
|
||||
{
|
||||
int32_t l;
|
||||
|
||||
if (aisl_start_response(stream) == AISL_MALLOC_ERROR)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) {
|
||||
l = buffer_append(&stream->buffer, "Server: AISL\r\n", 14);
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->flags |= FLAG_SERVER_HEADER_SENT;
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
|
||||
l = buffer_append(&stream->buffer,
|
||||
"Content-Type: text/html; encoding=utf-8\r\n", 41);
|
||||
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||
if (stream->content_length != AISL_AUTO_LENGTH) {
|
||||
l = buffer_append_printf(&stream->buffer, "Content-Length: %"PRIu64"\r\n",
|
||||
stream->content_length);
|
||||
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
|
||||
l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n",
|
||||
(aisl_client_get_keepalive(stream->client) ? "keepalive" : "close"));
|
||||
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
|
||||
}
|
||||
|
||||
if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->body_offset = stream->buffer.used;
|
||||
stream->state = AISL_STREAM_STATE_SEND_BODY;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislStatus
|
||||
aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len)
|
||||
{
|
||||
int32_t l;
|
||||
|
||||
/* check if those headers were already sent */
|
||||
if (stream->state > AISL_STREAM_STATE_READY)
|
||||
return AISL_IDLE;
|
||||
|
||||
stream->http_response = rs_code;
|
||||
stream->content_length = c_len;
|
||||
|
||||
buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0);
|
||||
|
||||
l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n",
|
||||
aisl_http_version_to_string(stream->client->http_version), rs_code,
|
||||
aisl_http_response_to_string(rs_code));
|
||||
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
stream->state = AISL_STREAM_STATE_SEND_HEADER;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislStatus
|
||||
aisl_flush(AislStream s)
|
||||
{
|
||||
if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||
char hdr[ 40 ];
|
||||
uint64_t c_len;
|
||||
int32_t l;
|
||||
|
||||
if (s->body_offset) {
|
||||
c_len = s->buffer.used - s->body_offset;
|
||||
l = snprintf(hdr, sizeof (hdr), "Content-Length: %"PRIu64"\r\n", c_len);
|
||||
l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l);
|
||||
if (l == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
} else {
|
||||
aisl_stream_close_headers(s);
|
||||
}
|
||||
s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||
}
|
||||
|
||||
s->state = AISL_STREAM_STATE_DONE;
|
||||
s->flags |= FLAG_FLUSHED;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int32_t
|
||||
aisl_stream_verify_header(AislStream stream, const char *key, const char *value)
|
||||
{
|
||||
if (stream->state < AISL_STREAM_STATE_SEND_HEADER) {
|
||||
if (aisl_start_response(stream) != AISL_SUCCESS)
|
||||
return -1;
|
||||
} else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
|
||||
if (str_cmpi(key, "connection")==0) {
|
||||
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
|
||||
if (value) {
|
||||
aisl_client_set_keepalive(stream->client,
|
||||
(str_cmpi(value, "keepalive") == 0));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
|
||||
if (str_cmpi(key, "content-type") == 0) {
|
||||
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||
if (str_cmpi(key, "content-length") == 0) {
|
||||
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||
if (str_cmpi(key, "content-length")==0) {
|
||||
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int32_t
|
||||
aisl_header(AislStream stream, const char *key, const char *value)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
if ( (result = aisl_stream_verify_header( stream, key, value )) != 1)
|
||||
return result;
|
||||
|
||||
result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value);
|
||||
|
||||
return result;
|
||||
|
||||
/* For debug purposes
|
||||
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL )
|
||||
{
|
||||
ret = strlen(pch);
|
||||
if ( buffer_insert(
|
||||
&stream->buffer,
|
||||
stream->end_of_headers,
|
||||
pch,
|
||||
ret
|
||||
) == -1 )
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
stream->end_of_headers += ret;
|
||||
|
||||
free(pch);
|
||||
}
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int32_t
|
||||
aisl_header_printf(AislStream stream, const char *key, const char *format, ...)
|
||||
{
|
||||
int32_t result;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
result = aisl_header_vprintf( stream, key, format, args );
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int32_t
|
||||
aisl_header_vprintf(AislStream stream,
|
||||
const char *key,
|
||||
const char *format,
|
||||
va_list args)
|
||||
{
|
||||
int32_t result, l;
|
||||
|
||||
if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1)
|
||||
return result;
|
||||
|
||||
result = buffer_append_printf( &stream->buffer, "%s: ", key );
|
||||
|
||||
if (result != -1) {
|
||||
l = buffer_append_vprintf( &stream->buffer, format, args );
|
||||
|
||||
if (l != -1) {
|
||||
result += l;
|
||||
if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) {
|
||||
result += l;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_printf(AislStream stream, const char *format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, format);
|
||||
result = aisl_vprintf(stream, format, arg);
|
||||
va_end(arg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int32_t
|
||||
aisl_vprintf(AislStream stream, const char *format, va_list args)
|
||||
{
|
||||
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||
return -1;
|
||||
}
|
||||
return buffer_append_vprintf(&stream->buffer, format, args);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int32_t
|
||||
aisl_write(AislStream stream, const char *data, int32_t d_len)
|
||||
{
|
||||
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d_len == -1)
|
||||
d_len = strlen(data);
|
||||
|
||||
return buffer_append(&stream->buffer, data, d_len);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_puts(const char *str, AislStream stream)
|
||||
{
|
||||
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||
return -1;
|
||||
}
|
||||
return aisl_write( stream, str, -1);
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
AislInstance
|
||||
aisl_get_instance(AislStream stream)
|
||||
{
|
||||
return stream->client->server->instance;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_set_output_event(AislStream stream, bool value)
|
||||
{
|
||||
if (value)
|
||||
stream->flags |= FLAG_OUTPUT_CHUNKED;
|
||||
else if (stream->flags & FLAG_OUTPUT_CHUNKED)
|
||||
stream->flags &= ~FLAG_OUTPUT_CHUNKED;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_get_output_event(AislStream stream)
|
||||
{
|
||||
return (stream->flags & FLAG_OUTPUT_CHUNKED);
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -0,0 +1,58 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file types.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Types stringifiers module
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#include <aisl/types.h>
|
||||
|
||||
|
||||
#if AISL_WITH_STRINGIFIERS == 1
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_status_to_string(AislStatus status)
|
||||
{
|
||||
switch(status) {
|
||||
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
|
||||
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
|
||||
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
|
||||
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
|
||||
case AISL_SUCCESS: return "AISL_SUCCESS";
|
||||
case AISL_IDLE: return "AISL_IDLE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_event_to_string(AislEvent evt_code)
|
||||
{
|
||||
switch(evt_code) {
|
||||
case AISL_EVENT_SERVER_READY: return "SERVER READY";
|
||||
case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR";
|
||||
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT";
|
||||
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT";
|
||||
case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN";
|
||||
case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER";
|
||||
case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT";
|
||||
case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST";
|
||||
case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT";
|
||||
case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE";
|
||||
case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
###############################################################################
|
||||
#
|
||||
# Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
# Please, refer LICENSE file for legal information
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
prefix=/usr/local
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: libaisl
|
||||
URL: https://lowenware.com/aisl/
|
||||
Description: Asynchronous Internet Server Library
|
||||
Version: 1.0.0
|
||||
Libs: -L${libdir} -laisl
|
||||
Libs.private: -lcrypto -lssl
|
||||
Cflags: -I${includedir}
|
367
library/aisl.c
367
library/aisl.c
|
@ -1,367 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <aisl/aisl.h>
|
||||
#include <cStuff/list.h>
|
||||
#include <cStuff/str-utils.h>
|
||||
|
||||
#include "handle.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "globals.h"
|
||||
#include "stream.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
||||
/* Globals ------------------------------------------------------------------ */
|
||||
|
||||
#define AISL_TERMINATED 1
|
||||
|
||||
aisl_handle_t gHandle = NULL;
|
||||
|
||||
|
||||
/* DEPRECATED --------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_init( )
|
||||
{
|
||||
gHandle = aisl_handle_new( AISL_MIN_CLIENTS, AISL_BUFFER_SIZE );
|
||||
|
||||
return (gHandle != NULL) ? AISL_SUCCESS : AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
/* DEPRECATED --------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_release()
|
||||
{
|
||||
aisl_handle_free(gHandle);
|
||||
}
|
||||
|
||||
/* DEPRECATED --------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_select(const char * address, int port)
|
||||
{
|
||||
return aisl_bind(gHandle, address, port, 0);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_run( int * flags )
|
||||
{
|
||||
aisl_status_t exit_code = AISL_SUCCESS;
|
||||
struct timeval timeout = {0,0};
|
||||
|
||||
while( !(*flags & AISL_TERMINATED) )
|
||||
{
|
||||
if ( (exit_code = aisl_run_cycle( gHandle )) == AISL_IDLE )
|
||||
{
|
||||
timeout.tv_usec = 300;
|
||||
timeout.tv_sec = 0;
|
||||
|
||||
select(0, NULL, NULL, NULL, &timeout);
|
||||
}
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
/* Event calls -------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_delay( aisl_callback_t cb, uint32_t usec, void * u_data )
|
||||
{
|
||||
return aisl_set_delay(gHandle, cb, usec, u_data);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_listen( void * source, aisl_event_t e_id, aisl_callback_t cb )
|
||||
{
|
||||
return aisl_set_callback( gHandle, source, e_id, cb );
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_raise( void *source, aisl_event_t e_id, ... )
|
||||
{
|
||||
bool result;
|
||||
va_list vl;
|
||||
va_start(vl, e_id);
|
||||
result = aisl_raise_event_vl( gHandle, source, e_id, vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_header_get(aisl_stream_t stream, const char *key)
|
||||
{
|
||||
int i;
|
||||
if (STREAM(stream)->headers)
|
||||
{
|
||||
for (i=0; i< STREAM(stream)->headers->count; i++)
|
||||
{
|
||||
if (str_cmpi(((pair_t)list_index(STREAM(stream)->headers,i))->key,key)==0)
|
||||
return ((pair_t) list_index(STREAM(stream)->headers,i))->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* response functions ------------------------------------------------------- */
|
||||
|
||||
static char *
|
||||
get_response_begin(stream_t stream)
|
||||
{
|
||||
char * r;
|
||||
client_t cli = CLIENT(ASTREAM(stream)->client);
|
||||
|
||||
r = str_printf(
|
||||
"%s %d %s\r\n"
|
||||
"Server: AISL\r\n"
|
||||
"Connection: %s\r\n\r\n",
|
||||
aisl_http_version_to_string(cli->protocol),
|
||||
stream->response,
|
||||
aisl_http_response_to_string(stream->response),
|
||||
((cli->flags & CLIENT_FLAG_KEEPALIVE) ? "keep-alive" : "close")
|
||||
);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_response(aisl_stream_t stream, aisl_http_response_t status_code,
|
||||
const char *content_type,
|
||||
uint32_t content_length)
|
||||
{
|
||||
char * pch;
|
||||
int l;
|
||||
|
||||
|
||||
/* check if those headers were already sent */
|
||||
if (STREAM(stream)->state > STREAM_REQUEST_READY) return AISL_IDLE;
|
||||
|
||||
STREAM(stream)->response = status_code;
|
||||
STREAM(stream)->c_type = content_type;
|
||||
STREAM(stream)->c_length = content_length;
|
||||
|
||||
if ( !(pch = get_response_begin(STREAM(stream))) )
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
l = strlen(pch);
|
||||
STREAM(stream)->c_offset = l-2;
|
||||
|
||||
buffer_clear(STREAM(stream)->buffer, content_length);
|
||||
|
||||
l = buffer_add( STREAM(stream)->buffer, pch, l );
|
||||
free(pch);
|
||||
|
||||
if (l == BUFFER_EOB) return AISL_MALLOC_ERROR;
|
||||
|
||||
if (content_length)
|
||||
{
|
||||
if (!aisl_header_printf( stream, "Content-Length", "%u", content_length ))
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
if (content_type)
|
||||
{
|
||||
if (!aisl_header( stream, "Content-Type", content_type ))
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
STREAM(stream)->state = STREAM_RESPONSE_HEADER;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_flush(aisl_stream_t stream)
|
||||
{
|
||||
stream_t s = STREAM(stream);
|
||||
if ( ! s->c_length )
|
||||
{
|
||||
s->c_length = s->buffer->size - s->c_offset - 2;
|
||||
if (!aisl_header_printf(stream, "Content-Length", "%u", s->c_length))
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
fprintf(stdout, "(%lu bytes)------->\n", STREAM(stream)->buffer->size);
|
||||
fwrite(STREAM(stream)->buffer->data, 1, STREAM(stream)->buffer->size, stdout);
|
||||
fprintf(stdout, "<------\n");
|
||||
*/
|
||||
s->state = STREAM_RESPONSE_READY;
|
||||
|
||||
s->flags |= STREAM_FLAG_OUTPUT_READY;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
/* header functions --------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_header(aisl_stream_t stream, const char *key, const char *value)
|
||||
{
|
||||
int ret;
|
||||
char * pch;
|
||||
|
||||
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL )
|
||||
{
|
||||
ret = strlen(pch);
|
||||
if ( buffer_insert(
|
||||
STREAM(stream)->buffer,
|
||||
STREAM(stream)->c_offset,
|
||||
pch,
|
||||
ret
|
||||
) == BUFFER_EOB )
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
STREAM(stream)->c_offset += ret;
|
||||
|
||||
free(pch);
|
||||
}
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_header_printf(aisl_stream_t stream, const char *key,
|
||||
const char *f_value, ...)
|
||||
{
|
||||
int ret;
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, f_value);
|
||||
|
||||
ret = aisl_header_vprintf(stream, key, f_value, arg);
|
||||
|
||||
va_end(arg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_header_vprintf(aisl_stream_t stream, const char *key,
|
||||
const char *format,
|
||||
va_list args)
|
||||
{
|
||||
int ret;
|
||||
char * value;
|
||||
|
||||
if ( (value = str_vprintf(format, args)) != NULL )
|
||||
{
|
||||
ret = aisl_header( stream, key, value );
|
||||
free(value);
|
||||
}
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_printf(aisl_stream_t stream, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
int result = aisl_vprintf(stream, format, arg);
|
||||
|
||||
va_end(arg);
|
||||
|
||||
/* No need to update length there, because vprintf do that
|
||||
*
|
||||
* if (STREAM(stream)->c_length_unknown)
|
||||
STREAM(stream)->c_length += result;
|
||||
*/
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_vprintf(aisl_stream_t stream, const char *format, va_list args)
|
||||
{
|
||||
int result;
|
||||
char * r;
|
||||
|
||||
if ( (r = str_vprintf(format, args)) != NULL)
|
||||
{
|
||||
result = strlen(r);
|
||||
if (buffer_add(STREAM(stream)->buffer, r, result) == BUFFER_EOB)
|
||||
result = -1;
|
||||
|
||||
free(r);
|
||||
}
|
||||
else
|
||||
result = -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_write(aisl_stream_t stream, const char *data, int d_len)
|
||||
{
|
||||
if (d_len < 0)
|
||||
d_len = strlen(data);
|
||||
|
||||
if (buffer_add(STREAM(stream)->buffer, data, d_len) == BUFFER_EOB)
|
||||
d_len = -1;
|
||||
|
||||
return d_len;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_puts(const char *str, aisl_stream_t stream)
|
||||
{
|
||||
return aisl_write( stream, str, strlen(str));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
129
library/buffer.c
129
library/buffer.c
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef _AISL_BUFFER_H_
|
||||
#define _AISL_BUFFER_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct buffer
|
||||
{
|
||||
char * data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct buffer * buffer_t;
|
||||
|
||||
#define BUFFER_EOB ( ~0 )
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
buffer_t
|
||||
buffer_new( size_t initial_size );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
buffer_free( buffer_t self );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
size_t
|
||||
buffer_insert( buffer_t self, size_t offset, const char * data, size_t size );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
size_t
|
||||
buffer_add( buffer_t self, const char * data, size_t size );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
size_t
|
||||
buffer_clear( buffer_t self, size_t to_alloc );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
size_t
|
||||
buffer_shift( buffer_t self, size_t size );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
460
library/client.c
460
library/client.c
|
@ -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;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#ifndef _AISL_GLOBALS_H_
|
||||
#define _AISL_GLOBALS_H_
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
|
||||
#include <aisl/handle.h>
|
||||
|
||||
/* MACOS FIX AND OTHER OS */
|
||||
#ifndef SOCK_NONBLOCK
|
||||
#define SOCK_NONBLOCK 0
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_SERVERS
|
||||
#define AISL_MIN_SERVERS 1
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_STREAMS
|
||||
#define AISL_MIN_STREAMS 1
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_CLIENTS
|
||||
#define AISL_MIN_CLIENTS 32
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_LISTENERS
|
||||
#define AISL_MIN_LISTENERS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_DELAYS
|
||||
#define AISL_MIN_DELAYS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_BUFFER_SIZE
|
||||
#define AISL_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_HEADERS
|
||||
#define AISL_MIN_HEADERS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MAX_CLIENT_SILENCE
|
||||
#define AISL_MAX_CLIENT_SILENCE 10
|
||||
#endif
|
||||
|
||||
|
||||
extern aisl_handle_t gHandle;
|
||||
|
||||
#endif
|
802
library/handle.c
802
library/handle.c
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#ifndef _AISL_HANDLE_H__
|
||||
#define _AISL_HANDLE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <aisl/handle.h>
|
||||
|
||||
#include <cStuff/list.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define AISL_HANDLE_HAS_STREAM_LISTENERS (1<<8)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct aisl_handle
|
||||
{
|
||||
list_t servers;
|
||||
list_t clients;
|
||||
list_t delays; /* deprecated */
|
||||
list_t listeners;
|
||||
list_t crypters;
|
||||
buffer_t buffer;
|
||||
char * lastError;
|
||||
int iterator;
|
||||
int stage;
|
||||
int flags;
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
aisl_status_t
|
||||
aisl_set_delay( aisl_handle_t self,
|
||||
aisl_callback_t cb,
|
||||
uint32_t usec,
|
||||
void * u_data );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool
|
||||
aisl_raise_event_vl( aisl_handle_t self,
|
||||
void * source,
|
||||
aisl_event_t e_id,
|
||||
va_list vl );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
SSL_CTX *
|
||||
aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
aisl_remove_listeners_for( aisl_handle_t self, void * source );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
112
library/http.c
112
library/http.c
|
@ -1,112 +0,0 @@
|
|||
#include <aisl/http.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_version_to_string(aisl_http_version_t version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case AISL_HTTP_1_0: return "HTTP/1.0";
|
||||
case AISL_HTTP_1_1: return "HTTP/1.1";
|
||||
case AISL_HTTP_2_0: return "HTTP/2.0";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_response_to_string(aisl_http_response_t code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
/* most common for faster behavior */
|
||||
case AISL_HTTP_OK: return "OK";
|
||||
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
|
||||
|
||||
/* informational */
|
||||
case AISL_HTTP_CONTINUE: return "Continue";
|
||||
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
|
||||
/* Successful */
|
||||
case AISL_HTTP_CREATED: return "Created";
|
||||
case AISL_HTTP_ACCEPTED: return "Accepted";
|
||||
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
|
||||
case AISL_HTTP_NO_CONTENT: return "No Content";
|
||||
case AISL_HTTP_RESET_CONTENT: return "Reset Content";
|
||||
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
|
||||
/* redirection */
|
||||
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
|
||||
case AISL_HTTP_FOUND: return "Found";
|
||||
case AISL_HTTP_SEE_OTHER: return "See other";
|
||||
case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
|
||||
case AISL_HTTP_USE_PROXY: return "Use Proxy";
|
||||
case AISL_HTTP_UNUSED: return "(unused)";
|
||||
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
|
||||
/* client error */
|
||||
case AISL_HTTP_BAD_REQUEST: return "Bad Request";
|
||||
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
|
||||
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
|
||||
case AISL_HTTP_FORBIDDEN: return "Forbidden";
|
||||
case AISL_HTTP_NOT_FOUND: return "Not Found";
|
||||
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
|
||||
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
|
||||
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
|
||||
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
|
||||
case AISL_HTTP_CONFLICT: return "Conflict";
|
||||
case AISL_HTTP_GONE: return "Gone";
|
||||
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
|
||||
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
|
||||
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
|
||||
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
|
||||
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
|
||||
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
|
||||
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
|
||||
/* server error */
|
||||
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
|
||||
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
|
||||
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
|
||||
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
|
||||
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
|
||||
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
|
||||
|
||||
}
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_secure_to_string( int is_secure )
|
||||
{
|
||||
return (is_secure ? "HTTPS" : "HTTP");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_method_to_string( aisl_http_method_t method )
|
||||
{
|
||||
switch(method)
|
||||
{
|
||||
case AISL_HTTP_GET: return "GET";
|
||||
case AISL_HTTP_PUT: return "PUT";
|
||||
case AISL_HTTP_POST: return "POST";
|
||||
case AISL_HTTP_HEAD: return "HEAD";
|
||||
case AISL_HTTP_TRACE: return "TRACE";
|
||||
case AISL_HTTP_DELETE: return "DELETE";
|
||||
case AISL_HTTP_OPTIONS: return "OPTIONS";
|
||||
case AISL_HTTP_CONNECT: return "CONNECT";
|
||||
|
||||
case AISL_HTTP_PRI: return "PRI";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
490
library/parser.c
490
library/parser.c
|
@ -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;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef _AISL_PARSER_H_
|
||||
#define _AISL_PARSER_H_
|
||||
|
||||
#include <aisl/aisl.h>
|
||||
#include "client.h"
|
||||
|
||||
|
||||
/* parser status ------------------------------------------------------------ */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PARSER_PENDING, /* process pending */
|
||||
PARSER_FINISHED, /* successful finish */
|
||||
PARSER_HUNGRY, /* not enough data in buffer */
|
||||
PARSER_FAILED /* error happened */
|
||||
|
||||
} parser_status_t;
|
||||
|
||||
|
||||
/* parse HTTP Request ------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_method(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP Request URI --------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_path(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP Version ------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_protocol(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP header key ---------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_key(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP header value -------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_value(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP data ---------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_content(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
188
library/server.c
188
library/server.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
@ -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
|
|
@ -1,21 +0,0 @@
|
|||
#include <aisl/status.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_status_to_string(aisl_status_t status)
|
||||
{
|
||||
switch( status )
|
||||
{
|
||||
case AISL_SUCCESS: return "success";
|
||||
case AISL_IDLE: return "idle";
|
||||
case AISL_MALLOC_ERROR: return "malloc error";
|
||||
case AISL_SYSCALL_ERROR: return "system call error";
|
||||
case AISL_EXTCALL_ERROR: return "external call error";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
196
library/stream.c
196
library/stream.c
|
@ -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 );
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
|
@ -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
|
|
@ -0,0 +1,65 @@
|
|||
#
|
||||
# config.mk
|
||||
# Löwenware Makefile Config, 2019-03-02 17:35
|
||||
#
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
PROJECT_NAME = aisl
|
||||
|
||||
# Version
|
||||
|
||||
PROJECT_VERSION_MAJOR = 1
|
||||
PROJECT_VERSION_MINOR = 0
|
||||
PROJECT_VERSION_TWEAK = 5
|
||||
PROJECT_VERSION_LABEL = 0
|
||||
|
||||
#SRC_DIR = src
|
||||
#SDK_DIR = sdk
|
||||
#OUT_DIR = ./build
|
||||
|
||||
|
||||
# Source files
|
||||
|
||||
PROJECT_SOURCES := \
|
||||
$(SRC_DIR)/instance.c \
|
||||
$(SRC_DIR)/server.c \
|
||||
$(SRC_DIR)/client.c \
|
||||
$(SRC_DIR)/stream.c \
|
||||
$(SRC_DIR)/http.c \
|
||||
$(SRC_DIR)/ssl.c \
|
||||
$(SRC_DIR)/list.c \
|
||||
$(SRC_DIR)/str-utils.c \
|
||||
$(SRC_DIR)/buffer.c \
|
||||
$(SRC_DIR)/types.c \
|
||||
|
||||
|
||||
# includes
|
||||
PROJECT_INCLUDES = -I./ \
|
||||
-I./include \
|
||||
`$(PKG_CONFIG) --cflags openssl` \
|
||||
|
||||
# libraries
|
||||
PROJECT_LIBRARIES = \
|
||||
`$(PKG_CONFIG) --libs openssl` \
|
||||
|
||||
|
||||
# compilation macro options:
|
||||
|
||||
AISL_WITH_DEBUG ?= 0 # disable debug output
|
||||
AISL_WITH_SSL ?= 1 # enable SSL support
|
||||
AISL_WITH_STRINGIFIERS ?= 1 # enable *_to_string functions
|
||||
|
||||
|
||||
# flags
|
||||
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
|
||||
PROJECT_CFLAGS += -DAISL_WITH_DEBUG=$(AISL_WITH_DEBUG)
|
||||
PROJECT_CFLAGS += -DAISL_WITH_SSL=$(AISL_WITH_SSL)
|
||||
PROJECT_CFLAGS += -DAISL_WITH_STRINGIFIERS=$(AISL_WITH_STRINGIFIERS)
|
||||
|
||||
# PROJECT_LDFLAGS = -L
|
||||
|
||||
|
||||
# vim:ft=make
|
||||
#
|
97
project.sh
97
project.sh
|
@ -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
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
Loading…
Reference in New Issue