Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
Ilja Kartašov | d8fa3f68e5 | |
Ilja Kartašov | 85f427ad91 | |
Ilja Kartašov | 2af6556748 | |
Ilja Kartašov | 12be39c747 | |
Ilja Kartašov | ee90cbc37c | |
Ilja Kartašov | 461f5f52f2 | |
Ilja Kartašov | bd4273050c | |
Ilja Kartašov | 345450b35b | |
Ilja Kartašov | d28f942cf7 | |
Ilja Kartašov | 8847f92528 | |
Ilja Kartašov | fc33a0e49e | |
Ilja Kartašov | a49e32dff8 | |
Ilja Kartašov | 0b05984f85 | |
Ilja Kartašov | 3efc86a341 | |
Ilja Kartašov | d87c7454b5 | |
Ilja Kartašov | 86b69c89b8 |
|
@ -7,7 +7,6 @@ demo/*
|
||||||
bin/*
|
bin/*
|
||||||
obj/*
|
obj/*
|
||||||
webstuff-*
|
webstuff-*
|
||||||
Makefile
|
|
||||||
tmp
|
tmp
|
||||||
tmp/*
|
tmp/*
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
@ -16,6 +15,8 @@ tmp/*
|
||||||
*.gz
|
*.gz
|
||||||
*.bz2
|
*.bz2
|
||||||
pkg/*
|
pkg/*
|
||||||
|
doc/html
|
||||||
|
vgcore.*
|
||||||
|
|
||||||
include/webstuff_bak
|
include/webstuff_bak
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "cStuff"]
|
|
||||||
path = cStuff
|
|
||||||
url = https://github.com/lowenware/cStuff.git
|
|
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}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#
|
||||||
|
# Makefile
|
||||||
|
# Ilja Kartašov, 2019-03-02 17:32
|
||||||
|
#
|
||||||
|
.POSIX:
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
SRC_DIR ?= src
|
||||||
|
SDK_DIR ?= sdk
|
||||||
|
OUT_DIR ?= build
|
||||||
|
LIB_DIR ?= lib
|
||||||
|
DESTDIR ?=
|
||||||
|
|
||||||
|
# Project definition
|
||||||
|
include project.mk
|
||||||
|
|
||||||
|
# Examples submodule
|
||||||
|
include examples.mk
|
||||||
|
|
||||||
|
# CFLAGS
|
||||||
|
|
||||||
|
CFLAGS := \
|
||||||
|
$(PROJECT_INCLUDES) \
|
||||||
|
-std=c99 \
|
||||||
|
-pedantic \
|
||||||
|
-Wall \
|
||||||
|
-Wmissing-prototypes \
|
||||||
|
-Wstrict-prototypes \
|
||||||
|
-Wold-style-definition \
|
||||||
|
-O2 \
|
||||||
|
-s \
|
||||||
|
-fvisibility=hidden \
|
||||||
|
-DVERSION_MAJOR=$(PROJECT_VERSION_MAJOR) \
|
||||||
|
-DVERSION_MINOR=$(PROJECT_VERSION_MINOR) \
|
||||||
|
-DVERSION_TWEAK=$(PROJECT_VERSION_TWEAK) \
|
||||||
|
-DVERSION_LABEL=$(PROJECT_VERSION_LABEL) \
|
||||||
|
$(CPPFLAGS) \
|
||||||
|
$(CFLAGS) \
|
||||||
|
$(PROJECT_CFLAGS) \
|
||||||
|
|
||||||
|
|
||||||
|
LDFLAGS := \
|
||||||
|
$(PROJECT_LIBRARIES) \
|
||||||
|
$(LDFLAGS) \
|
||||||
|
$(PROJECT_LDFLAGS) \
|
||||||
|
|
||||||
|
|
||||||
|
SOURCE_LIST := $(wildcard $(PROJECT_SOURCES))
|
||||||
|
OBJECT_FILES := $(addprefix $(OUT_DIR)/o_, ${SOURCE_LIST:.c=.o})
|
||||||
|
|
||||||
|
|
||||||
|
library: dirs $(OBJECT_FILES)
|
||||||
|
$(info linking target: $@)
|
||||||
|
@$(CC) -shared -o $(OUT_DIR)/lib$(PROJECT_NAME).so $(OBJECT_FILES) $(LDFLAGS)
|
||||||
|
$(info done: $@)
|
||||||
|
|
||||||
|
|
||||||
|
build/o_%.o: %.c
|
||||||
|
$(info compiling file: $<)
|
||||||
|
@$(CC) $(CFLAGS) -fpic -c $< -o $@
|
||||||
|
|
||||||
|
dirs:
|
||||||
|
$(info preparing: build folders)
|
||||||
|
@mkdir -p $(OUT_DIR)/o_$(SRC_DIR)
|
||||||
|
@mkdir -p $(OUT_DIR)/o_$(SDK_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(info cleaning: build files)
|
||||||
|
@rm -Rf $(OUT_DIR)
|
||||||
|
@rm -Rf ./vgcore.*
|
||||||
|
|
||||||
|
doc:
|
||||||
|
doxygen doc/api-reference.conf
|
||||||
|
|
||||||
|
all: library examples doc
|
||||||
|
|
||||||
|
default: library
|
||||||
|
.PHONY: all dirs clean doc install
|
||||||
|
|
||||||
|
install: library
|
||||||
|
$(info installing files)
|
||||||
|
@mkdir -p $(DESTDIR)$(PREFIX)/$(LIB_DIR)
|
||||||
|
@mkdir -p $(DESTDIR)$(PREFIX)/include
|
||||||
|
|
||||||
|
@cp $(OUT_DIR)/lib$(PROJECT_NAME).so $(DESTDIR)$(PREFIX)/$(LIB_DIR)
|
||||||
|
@cp -R include/aisl $(DESTDIR)$(PREFIX)/include
|
||||||
|
|
||||||
|
# vim:ft=make
|
||||||
|
#
|
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}
|
|
||||||
)
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
||||||
|
<!-- HTML footer for doxygen 1.8.14-->
|
||||||
|
<!-- start footer part -->
|
||||||
|
<!--BEGIN GENERATE_TREEVIEW-->
|
||||||
|
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||||
|
<ul>
|
||||||
|
$navpath
|
||||||
|
<li class="footer">$generatedby
|
||||||
|
<a href="http://www.doxygen.org/index.html">
|
||||||
|
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!--END GENERATE_TREEVIEW-->
|
||||||
|
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||||
|
<hr class="footer"/><address class="footer"><small>
|
||||||
|
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||||
|
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
|
||||||
|
</a> $doxygenversion
|
||||||
|
</small></address>
|
||||||
|
<!--END !GENERATE_TREEVIEW-->
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!-- HTML header for doxygen 1.8.14-->
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||||
|
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||||
|
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||||
|
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||||
|
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||||
|
$treeview
|
||||||
|
$search
|
||||||
|
$mathjax
|
||||||
|
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||||
|
$extrastylesheet
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||||
|
|
||||||
|
<!--BEGIN TITLEAREA-->
|
||||||
|
<div id="titlearea">
|
||||||
|
<table cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr style="height: 56px;">
|
||||||
|
<!--BEGIN PROJECT_LOGO-->
|
||||||
|
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||||
|
<!--END PROJECT_LOGO-->
|
||||||
|
<!--BEGIN PROJECT_NAME-->
|
||||||
|
<td id="projectalign" style="padding-left: 0.5em;">
|
||||||
|
<div id="projectname">$projectname
|
||||||
|
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||||
|
</div>
|
||||||
|
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||||
|
</td>
|
||||||
|
<!--END PROJECT_NAME-->
|
||||||
|
<!--BEGIN !PROJECT_NAME-->
|
||||||
|
<!--BEGIN PROJECT_BRIEF-->
|
||||||
|
<td style="padding-left: 0.5em;">
|
||||||
|
<div id="projectbrief">$projectbrief</div>
|
||||||
|
</td>
|
||||||
|
<!--END PROJECT_BRIEF-->
|
||||||
|
<!--END !PROJECT_NAME-->
|
||||||
|
<!--BEGIN DISABLE_INDEX-->
|
||||||
|
<!--BEGIN SEARCHENGINE-->
|
||||||
|
<td>$searchbox</td>
|
||||||
|
<!--END SEARCHENGINE-->
|
||||||
|
<!--END DISABLE_INDEX-->
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!--END TITLEAREA-->
|
||||||
|
<!-- end header part -->
|
File diff suppressed because it is too large
Load Diff
|
@ -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,99 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file hello-world.c
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief AISL usage example: Hello World
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Include library meta header */
|
||||||
|
#include <aisl/aisl.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFAULT_HTTP_PORT 8080 /**< Default HTTP server port */
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hello_world(const struct aisl_evt *evt, void *p_ctx)
|
||||||
|
{
|
||||||
|
if (evt->code != AISL_EVENT_STREAM_REQUEST)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AislStatus status;
|
||||||
|
|
||||||
|
AislStream s = evt->source;
|
||||||
|
|
||||||
|
const char html[] =
|
||||||
|
"<html>"
|
||||||
|
"<head>"
|
||||||
|
"<title>Hello World</title>"
|
||||||
|
"</head>"
|
||||||
|
"<body>"
|
||||||
|
"<h1>Hello World</h1>"
|
||||||
|
"<p>Powered by AISL</p>"
|
||||||
|
"</body>"
|
||||||
|
"</html>";
|
||||||
|
|
||||||
|
status = aisl_response(s, AISL_HTTP_OK, sizeof (html)-1);
|
||||||
|
|
||||||
|
if (status == AISL_SUCCESS)
|
||||||
|
{
|
||||||
|
if (aisl_write(s, html, sizeof (html)-1) != -1)
|
||||||
|
{
|
||||||
|
aisl_flush(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
aisl_reject(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) p_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
AislInstance aisl; /**< AISL instance pointer */
|
||||||
|
AislStatus status; /**< AISL status code */
|
||||||
|
struct aisl_cfg cfg = AISL_CFG_DEFAULT;
|
||||||
|
struct aisl_cfg_srv srv = {
|
||||||
|
.host = "0.0.0.0",
|
||||||
|
.port = DEFAULT_HTTP_PORT,
|
||||||
|
.secure = false
|
||||||
|
};
|
||||||
|
|
||||||
|
cfg.srv = &srv;
|
||||||
|
cfg.srv_cnt = 1;
|
||||||
|
cfg.callback = hello_world;
|
||||||
|
|
||||||
|
/* Initialize instance */
|
||||||
|
if ( (aisl = aisl_new(&cfg)) != NULL )
|
||||||
|
{
|
||||||
|
/* launch application loop */
|
||||||
|
fprintf(stdout, "Entering main loop" );
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
status = aisl_run_cycle(aisl);
|
||||||
|
|
||||||
|
if ( status != AISL_SUCCESS )
|
||||||
|
aisl_sleep(aisl, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
aisl_free(aisl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Failed to initialize AISL");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,107 +1,37 @@
|
||||||
/* ----------------------------------------------------------------------------
|
/******************************************************************************
|
||||||
* aisl.h - header file for AISL library, part of AISLing Technology
|
|
||||||
*
|
*
|
||||||
* Copyright (c) 2017 by Löwenware Ltd. (https://lowenware.com/)
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
*
|
*
|
||||||
* Authors and maintainers:
|
******************************************************************************/
|
||||||
* Ilja Kartaschoff <ik@lowenware.com>
|
|
||||||
|
/**
|
||||||
|
* @file aisl/aisl.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Meta header file of AISL
|
||||||
*
|
*
|
||||||
* DOCUMENTATION
|
* @see https://lowenware.com/aisl/
|
||||||
* This file is not designed to be used as a documentation, but for looking at
|
*/
|
||||||
* the precise values of constants and definitions.
|
|
||||||
* Please, for documentation refer to web page https://lowenware.com/aisling/ or
|
|
||||||
* file READEME.md from library source package.
|
|
||||||
*
|
|
||||||
* LICENSE and DISCLAIMER
|
|
||||||
*
|
|
||||||
* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
#ifndef _AISL_H_
|
#ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
|
||||||
#define _AISL_H_
|
#define AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279
|
||||||
|
|
||||||
/* system includes ---------------------------------------------------------- */
|
/* AISL configuration structure */
|
||||||
|
#include <aisl/config.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
/* AISL types and stringifiers */
|
||||||
#include <stdbool.h>
|
#include <aisl/types.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
/* aisl includes ------------------------------------------------------------ */
|
/* AISL instancing, initialization and processing */
|
||||||
|
#include <aisl/instance.h>
|
||||||
|
|
||||||
#include <aisl/status.h>
|
/* Embedded HTTP(s) servers */
|
||||||
#include <aisl/event.h>
|
#include <aisl/server.h>
|
||||||
|
|
||||||
|
/* HTTP(s) clients */
|
||||||
|
#include <aisl/client.h>
|
||||||
|
|
||||||
|
/* HTTP(s) streaming */
|
||||||
#include <aisl/stream.h>
|
#include <aisl/stream.h>
|
||||||
#include <aisl/handle.h>
|
|
||||||
#include <aisl/http.h>
|
|
||||||
|
|
||||||
|
#endif /* !AISL_H */
|
||||||
|
|
||||||
/* Control calls ------------------------------------------------------------ */
|
|
||||||
|
|
||||||
/* DEPRECATED, use aisl_handle_new instead
|
|
||||||
* */
|
|
||||||
aisl_status_t
|
|
||||||
aisl_init();
|
|
||||||
|
|
||||||
/* DEPRECATED, use aisl_handle_free instead
|
|
||||||
* */
|
|
||||||
void
|
|
||||||
aisl_release();
|
|
||||||
|
|
||||||
/* Tell library what socket should be opened. Could be called multiple times.
|
|
||||||
* This function only save passed data. In fact, sockets are being opened only
|
|
||||||
* inside aisl_run loop.
|
|
||||||
* @address : host or IP to listen
|
|
||||||
* @port : port to listen
|
|
||||||
* */
|
|
||||||
|
|
||||||
aisl_status_t
|
|
||||||
aisl_select(const char *address, int port);
|
|
||||||
|
|
||||||
/* Start main loop
|
|
||||||
* @result : exit code
|
|
||||||
* */
|
|
||||||
aisl_status_t
|
|
||||||
aisl_run( int * flags );
|
|
||||||
|
|
||||||
/* Event calls -------------------------------------------------------------- */
|
|
||||||
|
|
||||||
/* Add callback to be executed after timeout. If callback function will return
|
|
||||||
* true, callback will be kept in main loop and raised again, otherwise it will
|
|
||||||
* be removed
|
|
||||||
* @cb : callback function: bool callback (void * u_data)
|
|
||||||
* @usec : delay in milliseconds
|
|
||||||
* @data : user-defined data to be passed to callback
|
|
||||||
* */
|
|
||||||
aisl_status_t
|
|
||||||
aisl_delay(aisl_callback_t cb, uint32_t msec, void *u_data);
|
|
||||||
|
|
||||||
/* Add event listener
|
|
||||||
* @source : pointer to event source
|
|
||||||
* @e_id : event identifier
|
|
||||||
* @cb : callback to be executed
|
|
||||||
* */
|
|
||||||
aisl_status_t
|
|
||||||
aisl_listen(void *source, aisl_event_t e_id, aisl_callback_t cb);
|
|
||||||
|
|
||||||
/* Raise event
|
|
||||||
* @source : pointer to event source data
|
|
||||||
* @e_id : event identifier
|
|
||||||
* @... : custom event data
|
|
||||||
* @result : true if event was handled, false otherwise
|
|
||||||
* */
|
|
||||||
bool
|
|
||||||
aisl_raise(void *source, aisl_event_t e_id, ... );
|
|
||||||
|
|
||||||
/* input stream functions --------------------------------------------------- */
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
aisl_header_get(aisl_stream_t stream, const char *key);
|
|
||||||
|
|
||||||
const char *
|
|
||||||
aisl_header_get_by_index(aisl_stream_t stream, const char **key, uint32_t i);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file aisl/client.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Declarations of #AislCLient functions
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
|
||||||
|
#define AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
|
||||||
|
|
||||||
|
#include <aisl/types.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets #AislServer instance associated with client.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return an associated #AislServer pointer.
|
||||||
|
*/
|
||||||
|
AislServer
|
||||||
|
aisl_client_get_server(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets security connection status.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return true if SSL is enabled and false if disabled.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
aisl_client_is_secure(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets client's connection state.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return true if client is online and false if is offline.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
aisl_client_is_online(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Forcefully closes client's connection.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
aisl_client_disconnect(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets HTTP protocol version.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return HTTP protocol version
|
||||||
|
*/
|
||||||
|
AislHttpVersion
|
||||||
|
aisl_client_get_http_version(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_client_get_address(AislClient client, struct sockaddr_in *address);
|
||||||
|
|
||||||
|
#endif /* !AISL_CLIENT_H */
|
|
@ -0,0 +1,66 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file aisl/config.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Declarations of AISL configuration structures
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
|
||||||
|
#define AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
|
||||||
|
|
||||||
|
#include <aisl/types.h>
|
||||||
|
|
||||||
|
#define AISL_CFG_DEFAULT { \
|
||||||
|
.callback = NULL \
|
||||||
|
, .p_ctx = NULL \
|
||||||
|
, .srv = NULL \
|
||||||
|
, .ssl = NULL \
|
||||||
|
, .srv_cnt = 0 \
|
||||||
|
, .ssl_cnt = 0 \
|
||||||
|
, .client_spool_size = 32 \
|
||||||
|
, .initial_buffer_size = 16536 \
|
||||||
|
, .client_accept_limit = 1024 \
|
||||||
|
, .client_silence_timeout = 30 \
|
||||||
|
} \
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_cfg_srv {
|
||||||
|
const char * host;
|
||||||
|
uint16_t port;
|
||||||
|
bool secure;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct aisl_cfg_ssl {
|
||||||
|
const char * host;
|
||||||
|
const char * key_file;
|
||||||
|
const char * crt_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_cfg
|
||||||
|
{
|
||||||
|
/* event handlers */
|
||||||
|
AislCallback callback;
|
||||||
|
void *p_ctx;
|
||||||
|
|
||||||
|
struct aisl_cfg_srv *srv;
|
||||||
|
struct aisl_cfg_ssl *ssl;
|
||||||
|
|
||||||
|
int srv_cnt;
|
||||||
|
int ssl_cnt;
|
||||||
|
|
||||||
|
int client_spool_size;
|
||||||
|
int initial_buffer_size;
|
||||||
|
int client_accept_limit;
|
||||||
|
int client_silence_timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !AISL_CONFIG_H */
|
|
@ -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,106 @@
|
||||||
#ifndef _AISL_STREAM_H_
|
/******************************************************************************
|
||||||
#define _AISL_STREAM_H_
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
/**
|
||||||
/* Library statuses */
|
* @file aisl/stream.h
|
||||||
/* HTTP requests */
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
#include <aisl/http.h>
|
* @brief Declarations of #AislStream functions
|
||||||
#include <aisl/status.h>
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
#ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
|
||||||
|
#define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <aisl/types.h>
|
||||||
|
|
||||||
typedef struct sockaddr_in * aisl_server_t;
|
|
||||||
typedef struct sockaddr_in * aisl_client_t;
|
|
||||||
|
|
||||||
struct aisl_stream
|
bool
|
||||||
{
|
aisl_is_secure(AislStream stream);
|
||||||
/* DO NOT USE PROPERTIES DIRECTLY IN NEW CODE */
|
|
||||||
|
|
||||||
struct sockaddr_in *client;
|
|
||||||
|
|
||||||
const char *host;
|
AislClient
|
||||||
const char *path;
|
aisl_get_client(AislStream stream);
|
||||||
const char *query;
|
|
||||||
const char *scheme;
|
|
||||||
|
|
||||||
void *u_ptr; /* pointer to bind custom data to stream */
|
|
||||||
|
|
||||||
aisl_http_method_t request_method;
|
AislServer
|
||||||
};
|
aisl_get_server(AislStream stream);
|
||||||
|
|
||||||
/* pointer to stream descriptor */
|
|
||||||
typedef struct aisl_stream * aisl_stream_t;
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
AislHttpVersion
|
||||||
|
aisl_get_http_version(AislStream stream);
|
||||||
|
|
||||||
/* 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
|
AislInstance
|
||||||
* ALL responses should always be finished with calling of this method
|
aisl_stream_get_instance(AislStream s);
|
||||||
* @stream : stream instance
|
|
||||||
* */
|
|
||||||
aisl_status_t
|
void *
|
||||||
aisl_flush(aisl_stream_t stream);
|
aisl_get_context(AislStream stream);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_set_context(AislStream stream, void *context);
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
aisl_flush(AislStream stream);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_reject(AislStream stream);
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
aisl_response(AislStream stream,
|
||||||
|
AislHttpResponse status_code,
|
||||||
|
uint64_t content_length);
|
||||||
|
|
||||||
/* 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
|
int
|
||||||
aisl_header(aisl_stream_t stream, const char *key, const char *value);
|
aisl_header(AislStream 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
|
int
|
||||||
aisl_header_vprintf(aisl_stream_t stream, const char *key,
|
aisl_header_printf(AislStream stream,
|
||||||
|
const char *key,
|
||||||
|
const char *format,
|
||||||
|
... );
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
aisl_header_vprintf(AislStream stream,
|
||||||
|
const char *key,
|
||||||
const char *format,
|
const char *format,
|
||||||
va_list args );
|
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
|
int
|
||||||
aisl_printf(aisl_stream_t stream, const char *format, ...);
|
aisl_printf(AislStream 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
|
int
|
||||||
aisl_vprintf(aisl_stream_t stream, const char *format, va_list args);
|
aisl_vprintf(AislStream 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
|
int
|
||||||
aisl_write(aisl_stream_t s, const char *data, int d_len);
|
aisl_write(AislStream stream, const char *data, int d_len);
|
||||||
|
|
||||||
|
|
||||||
/* response string to client
|
|
||||||
* @string : string to be sent
|
|
||||||
* @stream : stream instance
|
|
||||||
* @result : number of responed bytes
|
|
||||||
* */
|
|
||||||
int
|
int
|
||||||
aisl_puts(const char *string, aisl_stream_t stream);
|
aisl_puts(const char *str_data, AislStream stream);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
aisl_cancel(aisl_stream_t s);
|
aisl_set_output_event(AislStream stream, bool value);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
aisl_is_secure(aisl_stream_t s);
|
aisl_get_output_event(AislStream stream);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
#endif /* !AISL_STREAM_H */
|
||||||
|
|
||||||
void *
|
|
||||||
aisl_get_context(aisl_stream_t s);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
void
|
|
||||||
aisl_set_context(aisl_stream_t s, void * u_ptr);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
aisl_client_t
|
|
||||||
aisl_get_client(aisl_stream_t s);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
aisl_server_t
|
|
||||||
aisl_get_server(aisl_stream_t s);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
aisl_http_version_t
|
|
||||||
aisl_get_http_version(aisl_stream_t s);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
void
|
|
||||||
aisl_reject( aisl_stream_t s);
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file aisl/types.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Declarations of AISL types
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
|
||||||
|
#define AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define AISL_AUTO_LENGTH (~0)
|
||||||
|
|
||||||
|
/** type casts */
|
||||||
|
#define AISL_CALLBACK(x) ((AislCallback) x)
|
||||||
|
|
||||||
|
|
||||||
|
/** AISL Instance */
|
||||||
|
typedef struct aisl_instance * AislInstance;
|
||||||
|
|
||||||
|
/** HTTP(s) Server */
|
||||||
|
typedef struct aisl_server * AislServer;
|
||||||
|
|
||||||
|
/** HTTP(s) Client */
|
||||||
|
typedef struct aisl_client * AislClient;
|
||||||
|
|
||||||
|
/** Server<->Client Stream */
|
||||||
|
typedef struct aisl_stream * AislStream;
|
||||||
|
|
||||||
|
|
||||||
|
/** status return codes */
|
||||||
|
typedef enum {
|
||||||
|
AISL_INPUT_ERROR = -4
|
||||||
|
, AISL_EXTCALL_ERROR = -3
|
||||||
|
, AISL_SYSCALL_ERROR = -2
|
||||||
|
, AISL_MALLOC_ERROR = -1
|
||||||
|
|
||||||
|
, AISL_SUCCESS = 0
|
||||||
|
, AISL_IDLE = 1
|
||||||
|
} AislStatus;
|
||||||
|
|
||||||
|
#ifndef WITHOUT_STRINGIFIERS
|
||||||
|
|
||||||
|
const char *
|
||||||
|
aisl_status_to_string(AislStatus status);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** Generic HTTP Enumerations */
|
||||||
|
typedef enum {
|
||||||
|
AISL_HTTP_0_9 = 0x0009
|
||||||
|
, AISL_HTTP_1_0 = 0x0100
|
||||||
|
, AISL_HTTP_1_1 = 0x0101
|
||||||
|
, AISL_HTTP_2_0 = 0x0200
|
||||||
|
} AislHttpVersion;
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
aisl_http_version_to_string(AislHttpVersion version);
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AISL_HTTP_METHOD_UNKNOWN
|
||||||
|
, AISL_HTTP_GET
|
||||||
|
, AISL_HTTP_PUT
|
||||||
|
, AISL_HTTP_POST
|
||||||
|
, AISL_HTTP_HEAD
|
||||||
|
, AISL_HTTP_TRACE
|
||||||
|
, AISL_HTTP_DELETE
|
||||||
|
, AISL_HTTP_OPTIONS
|
||||||
|
, AISL_HTTP_CONNECT
|
||||||
|
, AISL_HTTP_PRI
|
||||||
|
} AislHttpMethod;
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
aisl_http_method_to_string( AislHttpMethod method );
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AISL_HTTP_CONTINUE = 100
|
||||||
|
, AISL_HTTP_SWITCHING_PROTOCOLS
|
||||||
|
|
||||||
|
, AISL_HTTP_OK = 200
|
||||||
|
, AISL_HTTP_CREATED
|
||||||
|
, AISL_HTTP_ACCEPTED
|
||||||
|
, AISL_HTTP_NON_AUTHORITATIVE_INFORMATION
|
||||||
|
, AISL_HTTP_NO_CONTENT
|
||||||
|
, AISL_HTTP_RESET_CONTENT
|
||||||
|
, AISL_HTTP_PARTIAL_CONTENT
|
||||||
|
|
||||||
|
, AISL_HTTP_MULTIPLE_CHOICES = 300
|
||||||
|
, AISL_HTTP_MOVED_PERMANENTLY
|
||||||
|
, AISL_HTTP_FOUND
|
||||||
|
, AISL_HTTP_SEE_OTHER
|
||||||
|
, AISL_HTTP_NOT_MODIFIED
|
||||||
|
, AISL_HTTP_USE_PROXY
|
||||||
|
, AISL_HTTP_UNUSED
|
||||||
|
, AISL_HTTP_TEMPORARY_REDIRECT
|
||||||
|
|
||||||
|
, AISL_HTTP_BAD_REQUEST = 400
|
||||||
|
, AISL_HTTP_UNAUTHORIZED
|
||||||
|
, AISL_HTTP_PAYMENT_REQUIRED
|
||||||
|
, AISL_HTTP_FORBIDDEN
|
||||||
|
, AISL_HTTP_NOT_FOUND
|
||||||
|
, AISL_HTTP_METHOD_NOT_ALLOWED
|
||||||
|
, AISL_HTTP_NOT_ACCEPTABLE
|
||||||
|
, AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED
|
||||||
|
, AISL_HTTP_REQUEST_TIMEOUT
|
||||||
|
, AISL_HTTP_CONFLICT
|
||||||
|
, AISL_HTTP_GONE
|
||||||
|
, AISL_HTTP_LENGTH_REQUIRED
|
||||||
|
, AISL_HTTP_PRECONDITION_FAILED
|
||||||
|
, AISL_HTTP_REQUEST_ENTITY_TOO_LARGE
|
||||||
|
, AISL_HTTP_REQUEST_URI_TOO_LONG
|
||||||
|
, AISL_HTTP_UNSUPPORTED_MEDIA_TYPE
|
||||||
|
, AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
|
||||||
|
, AISL_HTTP_EXPECTATION_FAILED
|
||||||
|
|
||||||
|
, AISL_HTTP_INTERNAL_SERVER_ERROR = 500
|
||||||
|
, AISL_HTTP_NOT_IMPLEMENTED
|
||||||
|
, AISL_HTTP_BAD_GATEWAY
|
||||||
|
, AISL_HTTP_SERVICE_UNAVAILABLE
|
||||||
|
, AISL_HTTP_GATEWAY_TIMEOUT
|
||||||
|
, AISL_HTTP_VERSION_NOT_SUPPORTED
|
||||||
|
} AislHttpResponse;
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
aisl_http_response_to_string( AislHttpResponse code );
|
||||||
|
|
||||||
|
|
||||||
|
/** Codes of AISL events */
|
||||||
|
typedef enum {
|
||||||
|
AISL_EVENT_SERVER_READY = 100
|
||||||
|
, AISL_EVENT_SERVER_ERROR = 190
|
||||||
|
|
||||||
|
, AISL_EVENT_CLIENT_CONNECT = 200
|
||||||
|
, AISL_EVENT_CLIENT_DISCONNECT = 210
|
||||||
|
|
||||||
|
, AISL_EVENT_STREAM_OPEN = 300
|
||||||
|
, AISL_EVENT_STREAM_HEADER = 310
|
||||||
|
, AISL_EVENT_STREAM_INPUT = 320
|
||||||
|
, AISL_EVENT_STREAM_REQUEST = 330
|
||||||
|
, AISL_EVENT_STREAM_OUTPUT = 340
|
||||||
|
, AISL_EVENT_STREAM_CLOSE = 350
|
||||||
|
, AISL_EVENT_STREAM_ERROR = 390
|
||||||
|
} AislEvent;
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_evt {
|
||||||
|
void *source;
|
||||||
|
AislEvent code;
|
||||||
|
AislStatus status;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* void type event callback */
|
||||||
|
typedef void
|
||||||
|
(* AislCallback) (const struct aisl_evt *evt, void *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_evt_open {
|
||||||
|
struct aisl_evt evt;
|
||||||
|
const char *path;
|
||||||
|
const char *query;
|
||||||
|
AislHttpMethod http_method;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_evt_header {
|
||||||
|
struct aisl_evt evt;
|
||||||
|
const char *key;
|
||||||
|
const char *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_evt_input {
|
||||||
|
struct aisl_evt evt;
|
||||||
|
const char *data;
|
||||||
|
int32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WITHOUT_STRINGIFIERS
|
||||||
|
|
||||||
|
const char *
|
||||||
|
aisl_event_to_string(AislEvent evt);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !AISL_TYPES_H */
|
|
@ -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,63 @@
|
||||||
|
#
|
||||||
|
# config.mk
|
||||||
|
# Löwenware Makefile Config, 2019-03-02 17:35
|
||||||
|
#
|
||||||
|
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
PKG_CONFIG ?= pkg-config
|
||||||
|
|
||||||
|
PROJECT_NAME = aisl
|
||||||
|
|
||||||
|
# Version
|
||||||
|
|
||||||
|
PROJECT_VERSION_MAJOR = 1
|
||||||
|
PROJECT_VERSION_MINOR = 0
|
||||||
|
PROJECT_VERSION_TWEAK = 0
|
||||||
|
PROJECT_VERSION_LABEL = 0
|
||||||
|
|
||||||
|
#SRC_DIR = src
|
||||||
|
#SDK_DIR = sdk
|
||||||
|
#OUT_DIR = ./build
|
||||||
|
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
|
||||||
|
PROJECT_SOURCES := \
|
||||||
|
$(SRC_DIR)/instance.c \
|
||||||
|
$(SRC_DIR)/server.c \
|
||||||
|
$(SRC_DIR)/client.c \
|
||||||
|
$(SRC_DIR)/stream.c \
|
||||||
|
$(SRC_DIR)/http.c \
|
||||||
|
$(SRC_DIR)/ssl.c \
|
||||||
|
$(SRC_DIR)/list.c \
|
||||||
|
$(SRC_DIR)/str-utils.c \
|
||||||
|
$(SRC_DIR)/buffer.c \
|
||||||
|
$(SRC_DIR)/types.c \
|
||||||
|
|
||||||
|
|
||||||
|
# includes
|
||||||
|
PROJECT_INCLUDES = -I./ \
|
||||||
|
-I./include \
|
||||||
|
`$(PKG_CONFIG) --cflags openssl` \
|
||||||
|
|
||||||
|
# libraries
|
||||||
|
PROJECT_LIBRARIES = \
|
||||||
|
`$(PKG_CONFIG) --libs openssl` \
|
||||||
|
|
||||||
|
|
||||||
|
# compilation macro options:
|
||||||
|
# AISL_WITHOUT_SSL - exclude HTTPS support
|
||||||
|
# AISL_WITHOUT_STRINGIFIERS - exclude several *_to_string functions not
|
||||||
|
|
||||||
|
# flags
|
||||||
|
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
|
||||||
|
#PROJECT_CFLAGS += -DDEBUG
|
||||||
|
#PROJECT_CFLAGS += -DAISL_WITHOUT_SSL
|
||||||
|
#PROJECT_CFLAGS += -DAISL_WITHOUT_STRINGIFIERS
|
||||||
|
|
||||||
|
|
||||||
|
# PROJECT_LDFLAGS = -L
|
||||||
|
|
||||||
|
|
||||||
|
# vim:ft=make
|
||||||
|
#
|
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
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
|
@ -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,175 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file buffer.c
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Buffer module source file
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t
|
||||||
|
buffer_set_size(struct buffer *buffer, int32_t new_size)
|
||||||
|
{
|
||||||
|
if (new_size != buffer->size) {
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
if (new_size) {
|
||||||
|
int32_t s = new_size / 1024;
|
||||||
|
|
||||||
|
if ( new_size % 1024 ) {
|
||||||
|
new_size = (s+1) * 1024;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_size = 16*1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data = realloc(buffer->data, new_size)) != NULL) {
|
||||||
|
buffer->data = data;
|
||||||
|
buffer->size = new_size;
|
||||||
|
} else {
|
||||||
|
new_size = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_init(struct buffer *buffer, int32_t size)
|
||||||
|
{
|
||||||
|
if ( (size = buffer_set_size(buffer, size)) != -1)
|
||||||
|
buffer->used = 0;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
buffer_release(struct buffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer->data) {
|
||||||
|
free(buffer->data);
|
||||||
|
buffer->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->used = 0;
|
||||||
|
buffer->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t
|
||||||
|
buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size)
|
||||||
|
{
|
||||||
|
int32_t to_move = buffer->used - offset;
|
||||||
|
|
||||||
|
if (to_move < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (to_move) {
|
||||||
|
memmove(&buffer->data[offset+size], &buffer->data[offset], to_move);
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_insert(struct buffer *buffer,
|
||||||
|
int32_t offset,
|
||||||
|
const char *data,
|
||||||
|
int32_t size)
|
||||||
|
{
|
||||||
|
int32_t result;
|
||||||
|
|
||||||
|
if ( (result = buffer_set_size(buffer, size)) != -1) {
|
||||||
|
if ((result = buffer_move_offset(buffer, offset, size)) != -1) {
|
||||||
|
memcpy(&buffer->data[offset], data, size);
|
||||||
|
buffer->used += result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_append_printf(struct buffer *buffer, const char *format, ...)
|
||||||
|
{
|
||||||
|
int32_t result;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
result = buffer_append_vprintf(buffer, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
int32_t space, result;
|
||||||
|
va_list cp_args;
|
||||||
|
|
||||||
|
va_copy(cp_args, args);
|
||||||
|
space = buffer->size - buffer->used,
|
||||||
|
result = vsnprintf(&buffer->data[buffer->used], space, format, args);
|
||||||
|
|
||||||
|
if (result < space) { /* enough space */
|
||||||
|
buffer->used += result;
|
||||||
|
} else {
|
||||||
|
result = buffer_set_size(buffer, buffer->size + result - space);
|
||||||
|
if (result != -1)
|
||||||
|
result = buffer_append_vprintf(buffer, format, cp_args);
|
||||||
|
}
|
||||||
|
va_end(cp_args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_append(struct buffer *buffer, const char *data, int32_t size)
|
||||||
|
{
|
||||||
|
int32_t used, space;
|
||||||
|
|
||||||
|
used = buffer->used,
|
||||||
|
space = buffer->size - used;
|
||||||
|
|
||||||
|
if (size > space) { /* enough space */
|
||||||
|
if (buffer_set_size(buffer, buffer->size + size - space) == -1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buffer->data[used], data, size);
|
||||||
|
buffer->used += size;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
buffer_shift(struct buffer *buffer, int32_t offset)
|
||||||
|
{
|
||||||
|
int32_t used = buffer->used - offset;
|
||||||
|
|
||||||
|
if (offset > 0) {
|
||||||
|
if (offset < used) {
|
||||||
|
memmove(buffer->data, &buffer->data[offset], used);
|
||||||
|
} else {
|
||||||
|
used = 0;
|
||||||
|
}
|
||||||
|
buffer->used = used;
|
||||||
|
}
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <aisl/aisl.h>
|
||||||
|
#include "debug.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "instance.h"
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
#define FLAG_KEEPALIVE (1<<0)
|
||||||
|
#define FLAG_HANDSHAKE (1<<1)
|
||||||
|
#define FLAG_CAN_READ (1<<2)
|
||||||
|
#define FLAG_CAN_WRITE (1<<3)
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (16*1024)
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
aisl_client_close(AislClient client, AislStatus status)
|
||||||
|
{
|
||||||
|
if (client->fd != -1) {
|
||||||
|
aisl_raise(client->server->instance, (void *)client,
|
||||||
|
AISL_EVENT_CLIENT_DISCONNECT, status );
|
||||||
|
|
||||||
|
close(client->fd);
|
||||||
|
shutdown(client->fd, SHUT_RDWR);
|
||||||
|
client->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static AislStatus
|
||||||
|
aisl_client_parse(AislClient client, char *data, int32_t size)
|
||||||
|
{
|
||||||
|
AislStatus result = AISL_SUCCESS;
|
||||||
|
AislStream s = client->stream;
|
||||||
|
ParserStatus p = HTTP_PARSER_SUCCESS;
|
||||||
|
|
||||||
|
int32_t bytes_left = size;
|
||||||
|
|
||||||
|
switch (client->http_version) {
|
||||||
|
case AISL_HTTP_0_9:
|
||||||
|
case AISL_HTTP_1_0:
|
||||||
|
case AISL_HTTP_1_1:
|
||||||
|
while (p == HTTP_PARSER_SUCCESS) {
|
||||||
|
switch (aisl_stream_get_state(s)) {
|
||||||
|
case AISL_STREAM_STATE_IDLE:
|
||||||
|
p = http_10_parse_request(data, &size, client->stream);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AISL_STREAM_STATE_WAIT_HEADER:
|
||||||
|
p = http_10_parse_header(data, &size, client->stream);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AISL_STREAM_STATE_WAIT_BODY:
|
||||||
|
p = http_10_parse_body(data, &size, client->stream);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* has input data, but request was already parsed */
|
||||||
|
p = HTTP_PARSER_ERROR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// size now has number of parsed bytes
|
||||||
|
data += size;
|
||||||
|
bytes_left -= size;
|
||||||
|
size = bytes_left;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AISL_HTTP_2_0:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(p) {
|
||||||
|
case HTTP_PARSER_READY:
|
||||||
|
client->flags &= ~FLAG_CAN_READ;
|
||||||
|
client->flags |= FLAG_CAN_WRITE;
|
||||||
|
client->stream->content_length = AISL_AUTO_LENGTH;
|
||||||
|
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST,
|
||||||
|
result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_PARSER_ERROR:
|
||||||
|
/* reply Bad Request here */
|
||||||
|
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
|
||||||
|
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR,
|
||||||
|
result);
|
||||||
|
|
||||||
|
aisl_client_close(client, result);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
|
||||||
|
* parsed. If it is not NULL, then stream expects additional data -> same like
|
||||||
|
* in mono stream HTTP 1.0
|
||||||
|
*/
|
||||||
|
static AislStatus
|
||||||
|
aisl_client_input(AislClient client)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
char *data = &client->in.data[ client->in.used ];
|
||||||
|
int32_t size = client->in.size - client->in.used;
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (client->ssl) {
|
||||||
|
DPRINTF("SSL_read");
|
||||||
|
if (!(client->flags & FLAG_HANDSHAKE)) {
|
||||||
|
if ( (l = SSL_accept(client->ssl)) != 1 ) {
|
||||||
|
l = SSL_get_error(client->ssl, l);
|
||||||
|
|
||||||
|
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
|
||||||
|
return AISL_IDLE;
|
||||||
|
|
||||||
|
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
|
||||||
|
|
||||||
|
aisl_client_close(client, AISL_EXTCALL_ERROR);
|
||||||
|
return AISL_EXTCALL_ERROR;
|
||||||
|
}
|
||||||
|
client->flags &= ~FLAG_HANDSHAKE;
|
||||||
|
}
|
||||||
|
l = SSL_read(client->ssl, data, size);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
l = recv( client->fd, data, size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l > 0) {
|
||||||
|
DPRINTF("%d bytes received from client", l);
|
||||||
|
|
||||||
|
data = client->in.data;
|
||||||
|
size = client->in.used + l;
|
||||||
|
client->in.used = size;
|
||||||
|
return aisl_client_parse(client, data, size);
|
||||||
|
} else if (l<0) {
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (client->ssl) {
|
||||||
|
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
|
||||||
|
return AISL_IDLE;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
if(errno == EWOULDBLOCK)
|
||||||
|
return AISL_IDLE;
|
||||||
|
DPRINTF("client - %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* both: client disconnect + on read error */
|
||||||
|
/* todo: raise client error here */
|
||||||
|
aisl_client_close(client, AISL_SYSCALL_ERROR);
|
||||||
|
|
||||||
|
return AISL_SYSCALL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static AislStatus
|
||||||
|
aisl_client_output(AislClient client)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
AislStream s = client->stream;
|
||||||
|
|
||||||
|
/* while stream is not flushed, we should raise event */
|
||||||
|
if(aisl_get_output_event(s)) {
|
||||||
|
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
|
||||||
|
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
|
||||||
|
* buffer->size will be used to carry amount of stored bytes
|
||||||
|
* */
|
||||||
|
l = aisl_stream_get_buffer_space(s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (bsz < OUTPUT_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s->buffer->size = bsz;
|
||||||
|
bsz = OUTPUT_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!(l < aisl_stream_get_buffer_size(s) / 2)) {
|
||||||
|
aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT,
|
||||||
|
AISL_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = aisl_stream_get_data(s, &l);
|
||||||
|
|
||||||
|
if ( !l )
|
||||||
|
return AISL_IDLE;
|
||||||
|
|
||||||
|
#ifdef AISL_WITHOUT_SSL
|
||||||
|
l = send( client->fd, data, l, 0);
|
||||||
|
#else
|
||||||
|
l = (client->ssl) ?
|
||||||
|
SSL_write(client->ssl, data, l) :
|
||||||
|
send( client->fd, data, l, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (l > 0) {
|
||||||
|
aisl_stream_shift(s, l);
|
||||||
|
if ( aisl_stream_is_done(s) ) {
|
||||||
|
/* data has been sent */
|
||||||
|
if (client->flags & FLAG_KEEPALIVE) {
|
||||||
|
aisl_stream_free(s);
|
||||||
|
|
||||||
|
client->stream = aisl_stream_new(client, client->next_id++);
|
||||||
|
if (client->stream != NULL )
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
|
||||||
|
/* in case of malloc error it will not be error as long as request was
|
||||||
|
* handled and we just close the connection.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
aisl_client_close(client, AISL_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* l < 0 */
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (client->ssl) {
|
||||||
|
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE)
|
||||||
|
return AISL_IDLE;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (errno == EWOULDBLOCK)
|
||||||
|
return AISL_IDLE;
|
||||||
|
}
|
||||||
|
aisl_client_close(client, AISL_SYSCALL_ERROR);
|
||||||
|
|
||||||
|
return AISL_SYSCALL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislClient
|
||||||
|
aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr)
|
||||||
|
{
|
||||||
|
AislClient client;
|
||||||
|
AislStream stream;
|
||||||
|
|
||||||
|
if ((client = calloc(1, sizeof (struct aisl_client))) != NULL) {
|
||||||
|
DPRINTF("client alocated");
|
||||||
|
memcpy(&client->address, addr, sizeof (struct sockaddr_in));
|
||||||
|
client->server = server;
|
||||||
|
client->fd = fd;
|
||||||
|
client->next_id = 2;
|
||||||
|
client->http_version = AISL_HTTP_1_0;
|
||||||
|
client->timestamp = time(NULL);
|
||||||
|
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
|
||||||
|
|
||||||
|
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) {
|
||||||
|
DPRINTF("client buffer alocated");
|
||||||
|
memcpy(&client->out, &client->in, sizeof (struct buffer));
|
||||||
|
|
||||||
|
stream = aisl_stream_new(client, 0);
|
||||||
|
|
||||||
|
if (stream != NULL) {
|
||||||
|
client->stream = stream;
|
||||||
|
DPRINTF("client stream alocated");
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (server->ssl) {
|
||||||
|
SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
|
||||||
|
|
||||||
|
if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) {
|
||||||
|
SSL_set_fd(client->ssl, fd);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return client;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aisl_client_free(client);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_client_free(AislClient client)
|
||||||
|
{
|
||||||
|
aisl_client_close(client, AISL_SUCCESS);
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (client->ssl)
|
||||||
|
SSL_free(client->ssl);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (client->in.data)
|
||||||
|
free(client->in.data);
|
||||||
|
|
||||||
|
if (client->stream)
|
||||||
|
aisl_stream_free(client->stream);
|
||||||
|
|
||||||
|
free(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStatus
|
||||||
|
aisl_client_touch(AislClient client, int32_t timeout)
|
||||||
|
{
|
||||||
|
AislStatus result, status;
|
||||||
|
|
||||||
|
result = AISL_IDLE;
|
||||||
|
status = AISL_IDLE;
|
||||||
|
|
||||||
|
/* input */
|
||||||
|
if (client->flags & FLAG_CAN_READ) {
|
||||||
|
if ( (result = aisl_client_input(client)) < 0 )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output */
|
||||||
|
if (client->flags & FLAG_CAN_WRITE) {
|
||||||
|
if ( (status = aisl_client_output(client)) < 0 )
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == AISL_IDLE)
|
||||||
|
result = status;
|
||||||
|
|
||||||
|
if (result != AISL_SUCCESS) {
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
|
if (!(now - client->timestamp < timeout)) {
|
||||||
|
aisl_client_close(client, result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client->timestamp = time(NULL);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
aisl_client_get_socket(AislClient client)
|
||||||
|
{
|
||||||
|
return client->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
aisl_client_get_keepalive(AislClient client)
|
||||||
|
{
|
||||||
|
return (client->flags & FLAG_KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_client_set_keepalive(AislClient client, bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
client->flags |= FLAG_KEEPALIVE;
|
||||||
|
else
|
||||||
|
client->flags &= ~FLAG_KEEPALIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* API Level ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislServer
|
||||||
|
aisl_client_get_server(AislClient client)
|
||||||
|
{
|
||||||
|
return client->server;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
bool
|
||||||
|
aisl_client_is_secure(AislClient client)
|
||||||
|
{
|
||||||
|
#ifdef AISL_WITHOUT_SSL
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return (client->ssl == NULL) ? false : true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
bool
|
||||||
|
aisl_client_is_online(AislClient client)
|
||||||
|
{
|
||||||
|
return (client->fd == -1) ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_client_disconnect(AislClient client)
|
||||||
|
{
|
||||||
|
aisl_client_close(client, AISL_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislHttpVersion
|
||||||
|
aisl_client_get_http_version(AislClient client)
|
||||||
|
{
|
||||||
|
return client->http_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_client_get_address(AislClient client, struct sockaddr_in *address)
|
||||||
|
{
|
||||||
|
memcpy(address, &client->address, sizeof (struct sockaddr_in));
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file client.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Declarations of aisl_client structure and functions
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
#ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
|
||||||
|
#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <aisl/client.h>
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
#define AISL_CLIENT(x) ((AislClient) x)
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_client {
|
||||||
|
struct sockaddr_in address; /**< Client's address structure. */
|
||||||
|
struct buffer in; /**< Client's input buffer. */
|
||||||
|
struct buffer out; /**< Client's output buffer. */
|
||||||
|
AislServer server; /**< Server instance. */
|
||||||
|
AislStream stream; /**< Pending client's stream. */
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
SSL *ssl; /**< SSL pointer for HTTPS. */
|
||||||
|
#endif
|
||||||
|
time_t timestamp; /**< Last communication timestamp. */
|
||||||
|
int next_id; /**< Stream id generator (even). */
|
||||||
|
int flags; /**< Client's flag bitmask. */
|
||||||
|
int fd; /**< Client's socket descriptor. */
|
||||||
|
AislHttpVersion http_version; /**< Client's http_version version. */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor for #AislClient instance.
|
||||||
|
* @param server an #AislServer instance pointer.
|
||||||
|
* @param fd a client socket descriptor.
|
||||||
|
* @param addr a pointer to client's address structure.
|
||||||
|
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
|
||||||
|
*/
|
||||||
|
AislClient
|
||||||
|
aisl_client_new(AislServer server,
|
||||||
|
int fd,
|
||||||
|
struct sockaddr_in *addr );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor for #AislClient instance.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
aisl_client_free(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Does all HTTP client routines.
|
||||||
|
* Reads and parses requests, writes responses.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @param timeout an allowed client silence time in seconds.
|
||||||
|
* @return #AislStatus code.
|
||||||
|
*/
|
||||||
|
AislStatus
|
||||||
|
aisl_client_touch(AislClient client, int32_t timeout);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Brief Checks if client is about to keep connection alive.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return true if keepalive mode is on, otherwise false.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
aisl_client_get_keepalive(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Brief Sets if connection with client must be kept alive.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @param value a true to enable keepalive mode, false to disable.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
aisl_client_set_keepalive(AislClient client, bool value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets socket descriptor associated with #AislClient instance.
|
||||||
|
* @param client an #AislClient instance pointer.
|
||||||
|
* @return a client socket descriptor.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
aisl_client_get_socket(AislClient client);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !AISL_CLIENT_H */
|
|
@ -0,0 +1,37 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file debug.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief AISL debug module
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||||
|
#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define DPRINTF(...) do { \
|
||||||
|
fprintf(stderr, "* AISL: "); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n"); \
|
||||||
|
} while(0) \
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define DPRINTF(...)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !AISL_DEBUG_H */
|
|
@ -0,0 +1,397 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file http.c
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief HTTP module source file
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "client.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
|
||||||
|
static AislHttpMethod
|
||||||
|
http_method_from_string(const char *method, int32_t length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
AislHttpMethod methods[3] = {0, 0, 0};
|
||||||
|
|
||||||
|
switch(length) {
|
||||||
|
case 3:
|
||||||
|
methods[0] = AISL_HTTP_GET;
|
||||||
|
methods[1] = AISL_HTTP_PUT;
|
||||||
|
methods[2] = AISL_HTTP_PRI;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
methods[0] = AISL_HTTP_POST;
|
||||||
|
methods[1] = AISL_HTTP_HEAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
methods[0] = AISL_HTTP_TRACE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
methods[0] = AISL_HTTP_DELETE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
methods[0] = AISL_HTTP_OPTIONS;
|
||||||
|
methods[1] = AISL_HTTP_CONNECT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<sizeof (methods)/sizeof (AislHttpMethod); i++) {
|
||||||
|
if (!(methods[i]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
|
||||||
|
return methods[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return AISL_HTTP_METHOD_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static AislHttpVersion
|
||||||
|
http_version_from_string(const char *version_string)
|
||||||
|
{
|
||||||
|
if (strncmp(version_string, "HTTP/", 5)==0) {
|
||||||
|
if (strncmp(&version_string[5], "0.9", 3)==0) return AISL_HTTP_0_9;
|
||||||
|
if (strncmp(&version_string[5], "1.0", 3)==0) return AISL_HTTP_1_0;
|
||||||
|
if (strncmp(&version_string[5], "1.1", 3)==0) return AISL_HTTP_1_1;
|
||||||
|
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Library Level */
|
||||||
|
|
||||||
|
ParserStatus
|
||||||
|
http_10_parse_request(char *data, int32_t *p_size, AislStream stream)
|
||||||
|
{
|
||||||
|
/* STEP 1. Split data according to HTTP request format
|
||||||
|
*
|
||||||
|
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
|
||||||
|
* ^ ^ ^ ^ ^ ^ ^ ^
|
||||||
|
* | | | | | | | |
|
||||||
|
* | | | | | | | +--- newline
|
||||||
|
* | | | | | | +------------- version
|
||||||
|
* | | | | | +------------------------- query
|
||||||
|
* | | | | +------------------------------------- path
|
||||||
|
* | | | +--------------------------------------- port
|
||||||
|
* | | +----------------------------------------------------- host
|
||||||
|
* | +------------------------------------------------------------ uri
|
||||||
|
* +---------------------------------------------------------------- method
|
||||||
|
*/
|
||||||
|
char *uri = NULL,
|
||||||
|
*uri_end = NULL,
|
||||||
|
*host = NULL,
|
||||||
|
*port = NULL,
|
||||||
|
*path = NULL,
|
||||||
|
*query = NULL,
|
||||||
|
*version = NULL,
|
||||||
|
*newline = NULL,
|
||||||
|
*method = data,
|
||||||
|
*method_end = NULL;
|
||||||
|
|
||||||
|
AislHttpMethod http_method;
|
||||||
|
AislHttpVersion http_version;
|
||||||
|
|
||||||
|
int32_t size = *p_size;
|
||||||
|
|
||||||
|
while(!newline && size--) {
|
||||||
|
switch(*data)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
if (!method_end)
|
||||||
|
method_end = data;
|
||||||
|
else if (path && !uri_end)
|
||||||
|
uri_end = data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
if (uri && !host)
|
||||||
|
host = data+3;
|
||||||
|
else if (host && !port)
|
||||||
|
port = data+1;
|
||||||
|
else if (version)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
if (!path && data > host) {
|
||||||
|
path = data;
|
||||||
|
if (!uri)
|
||||||
|
uri = path;
|
||||||
|
} else if (version && data-version != 4) {
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
if (!query)
|
||||||
|
query = data+1;
|
||||||
|
else if (version)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
newline = data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
if (!version)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!uri && method_end)
|
||||||
|
uri = data;
|
||||||
|
else if (!version && uri_end)
|
||||||
|
version = data;
|
||||||
|
else if (version && data-version > 7)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
}
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STEP 2. Verifly splitting was completed */
|
||||||
|
|
||||||
|
/* Was request sent? */
|
||||||
|
if (!newline)
|
||||||
|
return HTTP_PARSER_HUNGRY;
|
||||||
|
|
||||||
|
/* Check mandatory parts presence */
|
||||||
|
if (!method_end || !path || !uri_end || !version)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
|
||||||
|
*method_end = 0;
|
||||||
|
*newline = 0;
|
||||||
|
*uri_end = 0;
|
||||||
|
|
||||||
|
http_method = http_method_from_string(method, method_end - method);
|
||||||
|
if (http_method == AISL_HTTP_METHOD_UNKNOWN)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
|
||||||
|
if ((http_version = http_version_from_string(version))==0)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
*(query-1)=0;
|
||||||
|
} else {
|
||||||
|
query = uri_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host) {
|
||||||
|
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8))
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
|
||||||
|
if (port)
|
||||||
|
*(port-1)=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->client->http_version = http_version;
|
||||||
|
aisl_stream_set_request(stream, http_method, path, query);
|
||||||
|
|
||||||
|
if (host)
|
||||||
|
aisl_stream_set_header(stream, "host", host);
|
||||||
|
/* how many characters has been read */
|
||||||
|
*(p_size)-=size;
|
||||||
|
return HTTP_PARSER_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ParserStatus
|
||||||
|
http_10_parse_header(char *data, int32_t *p_size, AislStream stream)
|
||||||
|
{
|
||||||
|
int32_t size = *p_size;
|
||||||
|
char *key = data,
|
||||||
|
*colon = NULL,
|
||||||
|
*val = NULL,
|
||||||
|
*val_end = NULL,
|
||||||
|
*newline = NULL;
|
||||||
|
|
||||||
|
while(!newline && size-- ) {
|
||||||
|
switch(*data) {
|
||||||
|
case ' ':
|
||||||
|
if (val && !val_end)
|
||||||
|
val_end = data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
if (!colon) {
|
||||||
|
if (colon == key)
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
|
||||||
|
colon = data;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
newline = data;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
if (!val_end && val)
|
||||||
|
val_end = data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!colon) {
|
||||||
|
*data = tolower(*data);
|
||||||
|
} else if (!val) {
|
||||||
|
if (colon)
|
||||||
|
val = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val_end)
|
||||||
|
val_end = NULL;
|
||||||
|
}
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newline)
|
||||||
|
return HTTP_PARSER_HUNGRY;
|
||||||
|
|
||||||
|
if (colon && val && val_end) {
|
||||||
|
*colon = 0;
|
||||||
|
*val_end = 0;
|
||||||
|
aisl_stream_set_header(stream, key, val);
|
||||||
|
*p_size -= size;
|
||||||
|
return HTTP_PARSER_SUCCESS;
|
||||||
|
} else if (newline == key || (newline == key+1 && *key == '\r')) {
|
||||||
|
*p_size -= size;
|
||||||
|
return (aisl_stream_set_end_of_headers(stream) == 0) ?
|
||||||
|
HTTP_PARSER_SUCCESS : HTTP_PARSER_READY;
|
||||||
|
}
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ParserStatus
|
||||||
|
http_10_parse_body(char *data, int32_t *p_size, AislStream stream)
|
||||||
|
{
|
||||||
|
int32_t size = *p_size;
|
||||||
|
|
||||||
|
if (!size)
|
||||||
|
return HTTP_PARSER_HUNGRY;
|
||||||
|
|
||||||
|
switch (aisl_stream_set_body(stream, data, size)) {
|
||||||
|
case 0:
|
||||||
|
return HTTP_PARSER_READY;
|
||||||
|
case -1:
|
||||||
|
return HTTP_PARSER_ERROR;
|
||||||
|
default:
|
||||||
|
return HTTP_PARSER_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* API Level */
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
const char *
|
||||||
|
aisl_http_version_to_string(AislHttpVersion version)
|
||||||
|
{
|
||||||
|
switch (version) {
|
||||||
|
case AISL_HTTP_0_9:
|
||||||
|
return "HTTP/0.9";
|
||||||
|
case AISL_HTTP_1_0:
|
||||||
|
return "HTTP/1.0";
|
||||||
|
case AISL_HTTP_1_1:
|
||||||
|
return "HTTP/1.1";
|
||||||
|
case AISL_HTTP_2_0:
|
||||||
|
return "HTTP/2.0";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
const char *
|
||||||
|
aisl_http_response_to_string(AislHttpResponse code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
/* most common for faster behavior */
|
||||||
|
case AISL_HTTP_OK: return "OK";
|
||||||
|
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
|
||||||
|
/* informational */
|
||||||
|
case AISL_HTTP_CONTINUE: return "Continue";
|
||||||
|
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
|
||||||
|
/* Successful */
|
||||||
|
case AISL_HTTP_CREATED: return "Created";
|
||||||
|
case AISL_HTTP_ACCEPTED: return "Accepted";
|
||||||
|
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
|
||||||
|
case AISL_HTTP_NO_CONTENT: return "No Content";
|
||||||
|
case AISL_HTTP_RESET_CONTENT: return "Reset Content";
|
||||||
|
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
|
||||||
|
/* redirection */
|
||||||
|
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
|
||||||
|
case AISL_HTTP_FOUND: return "Found";
|
||||||
|
case AISL_HTTP_SEE_OTHER: return "See other";
|
||||||
|
case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
|
||||||
|
case AISL_HTTP_USE_PROXY: return "Use Proxy";
|
||||||
|
case AISL_HTTP_UNUSED: return "(unused)";
|
||||||
|
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
|
||||||
|
/* client error */
|
||||||
|
case AISL_HTTP_BAD_REQUEST: return "Bad Request";
|
||||||
|
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
|
||||||
|
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
|
||||||
|
case AISL_HTTP_FORBIDDEN: return "Forbidden";
|
||||||
|
case AISL_HTTP_NOT_FOUND: return "Not Found";
|
||||||
|
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
|
||||||
|
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
|
||||||
|
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
|
||||||
|
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
|
||||||
|
case AISL_HTTP_CONFLICT: return "Conflict";
|
||||||
|
case AISL_HTTP_GONE: return "Gone";
|
||||||
|
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
|
||||||
|
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
|
||||||
|
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
|
||||||
|
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
|
||||||
|
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
|
||||||
|
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
|
||||||
|
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
|
||||||
|
/* server error */
|
||||||
|
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
|
||||||
|
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
|
||||||
|
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
|
||||||
|
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
|
||||||
|
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
|
||||||
|
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
const char *
|
||||||
|
aisl_http_method_to_string( AislHttpMethod method )
|
||||||
|
{
|
||||||
|
switch(method) {
|
||||||
|
case AISL_HTTP_GET: return "GET";
|
||||||
|
case AISL_HTTP_PUT: return "PUT";
|
||||||
|
case AISL_HTTP_POST: return "POST";
|
||||||
|
case AISL_HTTP_HEAD: return "HEAD";
|
||||||
|
case AISL_HTTP_TRACE: return "TRACE";
|
||||||
|
case AISL_HTTP_DELETE: return "DELETE";
|
||||||
|
case AISL_HTTP_OPTIONS: return "OPTIONS";
|
||||||
|
case AISL_HTTP_CONNECT: return "CONNECT";
|
||||||
|
case AISL_HTTP_PRI: return "PRI";
|
||||||
|
case AISL_HTTP_METHOD_UNKNOWN: break;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include "str-utils.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "instance.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
|
||||||
|
static uint32_t m_instances = 0;
|
||||||
|
|
||||||
|
static struct aisl_ssl *
|
||||||
|
aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl)
|
||||||
|
{
|
||||||
|
SSL_CTX *ssl_ctx = NULL;
|
||||||
|
struct aisl_ssl **list, *ssl;
|
||||||
|
|
||||||
|
list = instance->ssl;
|
||||||
|
|
||||||
|
/* lookup for existing contexts */
|
||||||
|
while ((ssl = *list)) {
|
||||||
|
if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 &&
|
||||||
|
ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0
|
||||||
|
) {
|
||||||
|
ssl_ctx = ssl->ctx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file,
|
||||||
|
ssl_ctx);
|
||||||
|
|
||||||
|
if (ssl) {
|
||||||
|
if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) {
|
||||||
|
aisl_ssl_free(ssl);
|
||||||
|
ssl = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ssl;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialization functions */
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislInstance
|
||||||
|
aisl_new(const struct aisl_cfg *cfg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
AislInstance instance;
|
||||||
|
|
||||||
|
/* allocate root structure */
|
||||||
|
if (!(instance = calloc(1, sizeof (struct aisl_instance))))
|
||||||
|
goto finally;
|
||||||
|
|
||||||
|
/* allocate servers */
|
||||||
|
if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer))))
|
||||||
|
goto release;
|
||||||
|
|
||||||
|
for (i = 0; i < cfg->srv_cnt; i++) {
|
||||||
|
DPRINTF("new srv %d", i);
|
||||||
|
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if ((m_instances++) == 0) {
|
||||||
|
SSL_load_error_strings();
|
||||||
|
OpenSSL_add_ssl_algorithms();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl))))
|
||||||
|
goto release;
|
||||||
|
|
||||||
|
for (i=0; i<cfg->ssl_cnt; i++) {
|
||||||
|
DPRINTF("new ssl %d", i);
|
||||||
|
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (list_init(&instance->client_spool, cfg->client_spool_size) == -1)
|
||||||
|
goto release;
|
||||||
|
|
||||||
|
instance->accept_limit = cfg->client_accept_limit;
|
||||||
|
instance->silence_timeout = cfg->client_silence_timeout;
|
||||||
|
instance->callback = cfg->callback;
|
||||||
|
instance->p_ctx = cfg->p_ctx;
|
||||||
|
|
||||||
|
goto finally;
|
||||||
|
|
||||||
|
release:
|
||||||
|
aisl_free(instance);
|
||||||
|
instance = NULL;
|
||||||
|
|
||||||
|
finally:
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_free(AislInstance instance)
|
||||||
|
{
|
||||||
|
if (instance->srv) {
|
||||||
|
AislServer * srv = instance->srv;
|
||||||
|
|
||||||
|
while (*srv) {
|
||||||
|
aisl_server_free(*(srv++));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(instance->srv);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_release(&instance->client_spool, (list_destructor_t)aisl_client_free);
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
if (instance->ssl) {
|
||||||
|
struct aisl_ssl **ssl = instance->ssl;
|
||||||
|
|
||||||
|
while (*ssl) {
|
||||||
|
aisl_ssl_free(*(ssl++));
|
||||||
|
}
|
||||||
|
free(instance->ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((--m_instances) == 0) {
|
||||||
|
EVP_cleanup();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
|
||||||
|
SSL_CTX *
|
||||||
|
aisl_get_ssl_ctx(AislInstance instance, const char * host)
|
||||||
|
{
|
||||||
|
struct aisl_ssl **list, *ssl;
|
||||||
|
|
||||||
|
list = instance->ssl;
|
||||||
|
|
||||||
|
if (host) {
|
||||||
|
while ((ssl = *list)) {
|
||||||
|
if (str_cmpi(ssl->host, host) == 0) {
|
||||||
|
return ssl->ctx;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt)
|
||||||
|
{
|
||||||
|
#ifdef AISL_WITHOUT_STRINGIFIERS
|
||||||
|
DPRINTF("! %d", evt->code);
|
||||||
|
#else
|
||||||
|
DPRINTF("! %s", aisl_event_to_string(evt->code));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (instance->callback)
|
||||||
|
instance->callback(evt, instance->p_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_raise(AislInstance instance,
|
||||||
|
void *source,
|
||||||
|
AislEvent code,
|
||||||
|
AislStatus status)
|
||||||
|
{
|
||||||
|
struct aisl_evt evt;
|
||||||
|
|
||||||
|
evt.source = source;
|
||||||
|
evt.code = code;
|
||||||
|
evt.status = status;
|
||||||
|
|
||||||
|
aisl_raise_evt(instance, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislStatus
|
||||||
|
aisl_run_cycle(AislInstance instance)
|
||||||
|
{
|
||||||
|
AislStatus result = AISL_IDLE;
|
||||||
|
AislServer *list, srv;
|
||||||
|
AislClient cli;
|
||||||
|
int32_t i;
|
||||||
|
|
||||||
|
list = instance->srv;
|
||||||
|
|
||||||
|
while ((srv = *list)) {
|
||||||
|
cli = NULL;
|
||||||
|
|
||||||
|
if (aisl_server_touch(srv, &cli) != AISL_IDLE)
|
||||||
|
result = AISL_SUCCESS;
|
||||||
|
|
||||||
|
if (cli) {
|
||||||
|
DPRINTF("Accepted %p", (void*)cli);
|
||||||
|
if (list_append(&instance->client_spool, cli) == -1)
|
||||||
|
aisl_client_free(cli);
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i < instance->client_spool.count; i++) {
|
||||||
|
cli = LIST_INDEX(instance->client_spool, i);
|
||||||
|
|
||||||
|
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
|
||||||
|
result = AISL_SUCCESS;
|
||||||
|
|
||||||
|
if (!aisl_client_is_online(cli)) {
|
||||||
|
aisl_client_free( cli );
|
||||||
|
list_remove_index(&instance->client_spool, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislStatus
|
||||||
|
aisl_sleep(AislInstance instance, uint32_t usec)
|
||||||
|
{
|
||||||
|
AislServer *list, srv;
|
||||||
|
int sd, maxfd = 0;
|
||||||
|
size_t i;
|
||||||
|
struct timeval timeout = {0,usec};
|
||||||
|
|
||||||
|
memset(&timeout, 0, sizeof (struct timeval));
|
||||||
|
timeout.tv_usec = usec;
|
||||||
|
|
||||||
|
fd_set fs;
|
||||||
|
FD_ZERO (&fs);
|
||||||
|
|
||||||
|
list = instance->srv;
|
||||||
|
|
||||||
|
while ((srv = *list)) {
|
||||||
|
sd = aisl_server_get_socket(srv);
|
||||||
|
|
||||||
|
if (sd != -1) {
|
||||||
|
FD_SET(sd, &fs);
|
||||||
|
if (sd > maxfd) maxfd = sd;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<instance->client_spool.count; i++) {
|
||||||
|
AislClient c = LIST_INDEX(instance->client_spool, i);
|
||||||
|
sd = aisl_client_get_socket(c);
|
||||||
|
if (sd != -1) {
|
||||||
|
FD_SET(sd, &fs);
|
||||||
|
if (sd > maxfd) maxfd = sd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) {
|
||||||
|
case -1:
|
||||||
|
return AISL_SYSCALL_ERROR;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
return AISL_IDLE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||||
|
* Please, refer LICENSE file for legal information
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file src/instance.h
|
||||||
|
* @author Ilja Kartašov <ik@lowenware.com>
|
||||||
|
* @brief Declarations of aisl_instance structure and functions
|
||||||
|
*
|
||||||
|
* @see https://lowenware.com/aisl/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
|
||||||
|
#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <aisl/instance.h>
|
||||||
|
#include "ssl.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct aisl_instance {
|
||||||
|
AislServer *srv;
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
struct aisl_ssl * *ssl;
|
||||||
|
#endif
|
||||||
|
struct list client_spool;
|
||||||
|
AislCallback callback;
|
||||||
|
void *p_ctx;
|
||||||
|
|
||||||
|
uint32_t accept_limit;
|
||||||
|
uint32_t silence_timeout;
|
||||||
|
uint32_t buffer_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
/**
|
||||||
|
* @brief Gets SSL context for appropriate server name.
|
||||||
|
* @param instance a pointer to #AislInstance instance.
|
||||||
|
* @param server_name a null-terminated string with server name or NULL.
|
||||||
|
* @return a pointer to SSL context
|
||||||
|
*/
|
||||||
|
SSL_CTX *
|
||||||
|
aisl_get_ssl_ctx(AislInstance instance, const char *server_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raises event from source.
|
||||||
|
* @param instance a pointer to #AislInstance instance.
|
||||||
|
* @param evt a pointer to event structure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_raise(AislInstance instance,
|
||||||
|
void *source,
|
||||||
|
AislEvent code,
|
||||||
|
AislStatus status);
|
||||||
|
|
||||||
|
#endif /* !AISL_INSTANCE_H */
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
|
||||||
|
__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"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AISL_WITHOUT_SSL
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance )
|
||||||
|
{
|
||||||
|
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||||
|
SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name);
|
||||||
|
|
||||||
|
if (ctx)
|
||||||
|
SSL_set_SSL_CTX(ssl, ctx);
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SSL_CTX *
|
||||||
|
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance)
|
||||||
|
{
|
||||||
|
SSL_CTX * ctx;
|
||||||
|
|
||||||
|
if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) {
|
||||||
|
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||||
|
SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx );
|
||||||
|
SSL_CTX_set_tlsext_servername_arg( ctx, p_instance );
|
||||||
|
|
||||||
|
if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0))
|
||||||
|
goto except;
|
||||||
|
|
||||||
|
if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0))
|
||||||
|
goto except;
|
||||||
|
|
||||||
|
ssl->ctx = ctx;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
except:
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct aisl_ssl *
|
||||||
|
aisl_ssl_new(const char *key_file,
|
||||||
|
const char *crt_file,
|
||||||
|
const char *host,
|
||||||
|
SSL_CTX *ctx)
|
||||||
|
{
|
||||||
|
struct aisl_ssl *ssl;
|
||||||
|
|
||||||
|
if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) {
|
||||||
|
if ((ssl->host = str_copy( host ? host : "*" )) != NULL) {
|
||||||
|
if (ctx) {
|
||||||
|
ssl->ctx = ctx;
|
||||||
|
return ssl;
|
||||||
|
} else {
|
||||||
|
if ((ssl->key_file = str_copy(key_file)) != NULL) {
|
||||||
|
if ((ssl->crt_file = str_copy(crt_file)) != NULL) {
|
||||||
|
return ssl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aisl_ssl_free(ssl);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_ssl_free( struct aisl_ssl *ssl )
|
||||||
|
{
|
||||||
|
if (ssl->host)
|
||||||
|
free(ssl->host);
|
||||||
|
|
||||||
|
if (ssl->key_file) {
|
||||||
|
free(ssl->key_file);
|
||||||
|
SSL_CTX_free(ssl->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssl->crt_file)
|
||||||
|
free(ssl->crt_file);
|
||||||
|
|
||||||
|
free(ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -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,605 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "instance.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "str-utils.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
#define FLAG_FLUSHED (1<<0)
|
||||||
|
#define FLAG_OUTPUT_CHUNKED (1<<1)
|
||||||
|
#define FLAG_SERVER_HEADER_SENT (1<<2)
|
||||||
|
#define FLAG_CONTENT_TYPE_HEADER_SENT (1<<3)
|
||||||
|
#define FLAG_CONTENT_LENGTH_HEADER_SENT (1<<4)
|
||||||
|
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
|
||||||
|
|
||||||
|
|
||||||
|
/* Library level */
|
||||||
|
|
||||||
|
static void
|
||||||
|
aisl_stream_reset(AislStream stream, bool initial)
|
||||||
|
{
|
||||||
|
if (!initial) {
|
||||||
|
aisl_raise(aisl_stream_get_instance(stream), (void*) stream,
|
||||||
|
AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_release(&stream->buffer);
|
||||||
|
|
||||||
|
stream->u_ptr = NULL;
|
||||||
|
stream->content_length = AISL_AUTO_LENGTH;
|
||||||
|
stream->head_offset = 0;
|
||||||
|
stream->flags = 0;
|
||||||
|
stream->state = AISL_STREAM_STATE_IDLE;
|
||||||
|
stream->http_response = AISL_HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStream
|
||||||
|
aisl_stream_new(AislClient client, int id)
|
||||||
|
{
|
||||||
|
AislStream stream = calloc(1, sizeof (struct aisl_stream));
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
stream->id = id;
|
||||||
|
stream->client = client;
|
||||||
|
aisl_stream_reset(stream, true);
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_stream_free(AislStream stream)
|
||||||
|
{
|
||||||
|
aisl_stream_reset(stream, false);
|
||||||
|
free(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
aisl_stream_get_buffer_space(AislStream stream)
|
||||||
|
{
|
||||||
|
return stream->buffer.size - stream->buffer.used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
aisl_stream_get_buffer_size(AislStream stream)
|
||||||
|
{
|
||||||
|
return stream->buffer.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
aisl_stream_get_data(AislStream stream, int32_t *p_length)
|
||||||
|
{
|
||||||
|
*p_length = stream->buffer.used;
|
||||||
|
|
||||||
|
return stream->buffer.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_stream_shift(AislStream stream, int32_t offset)
|
||||||
|
{
|
||||||
|
buffer_shift(&stream->buffer, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
aisl_stream_is_done(AislStream stream)
|
||||||
|
{
|
||||||
|
return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AislStreamState
|
||||||
|
aisl_stream_get_state(AislStream stream)
|
||||||
|
{
|
||||||
|
return stream->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_stream_set_request(AislStream stream,
|
||||||
|
AislHttpMethod http_method,
|
||||||
|
const char *path,
|
||||||
|
const char *query)
|
||||||
|
{
|
||||||
|
struct aisl_evt_open on_open;
|
||||||
|
|
||||||
|
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
|
||||||
|
|
||||||
|
DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method),
|
||||||
|
path, query);
|
||||||
|
|
||||||
|
on_open.evt.code = AISL_EVENT_STREAM_OPEN;
|
||||||
|
on_open.evt.source = (void *) stream;
|
||||||
|
on_open.evt.status = AISL_SUCCESS;
|
||||||
|
on_open.http_method = http_method;
|
||||||
|
on_open.path = path;
|
||||||
|
on_open.query = query;
|
||||||
|
|
||||||
|
aisl_raise_evt(aisl_stream_get_instance(stream), (struct aisl_evt *)&on_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
aisl_stream_set_header(AislStream stream, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
struct aisl_evt_header on_header;
|
||||||
|
|
||||||
|
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strcmp(key, "content-length") == 0) {
|
||||||
|
stream->content_length = strtoll(value, NULL, 10);
|
||||||
|
} else if (strcmp(key, "connection") == 0) {
|
||||||
|
aisl_client_set_keepalive(stream->client,
|
||||||
|
(str_cmpi(value, "close")==0) ? false : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF("%s: %s", key, value);
|
||||||
|
|
||||||
|
on_header.evt.code = AISL_EVENT_STREAM_HEADER;
|
||||||
|
on_header.evt.source = (void *) stream;
|
||||||
|
on_header.evt.status = AISL_SUCCESS;
|
||||||
|
on_header.key = key;
|
||||||
|
on_header.value = value;
|
||||||
|
|
||||||
|
aisl_raise_evt(aisl_stream_get_instance(stream),
|
||||||
|
(struct aisl_evt *) &on_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
aisl_stream_set_end_of_headers(AislStream stream)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
|
||||||
|
stream->state = AISL_STREAM_STATE_WAIT_BODY;
|
||||||
|
result = (stream->content_length == 0);
|
||||||
|
} else {
|
||||||
|
result = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
aisl_stream_set_body(AislStream stream, const char *data, int32_t size)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
if (stream->state == AISL_STREAM_STATE_WAIT_BODY) {
|
||||||
|
if (!(stream->content_length < size)) {
|
||||||
|
struct aisl_evt_input on_input;
|
||||||
|
|
||||||
|
stream->content_length -= size;
|
||||||
|
|
||||||
|
if (stream->content_length == 0) {
|
||||||
|
stream->state = AISL_STREAM_STATE_READY;
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_input.evt.code = AISL_EVENT_STREAM_INPUT;
|
||||||
|
on_input.evt.source = (void *)stream;
|
||||||
|
on_input.evt.status = AISL_SUCCESS;
|
||||||
|
on_input.data = data;
|
||||||
|
on_input.size = size;
|
||||||
|
|
||||||
|
aisl_raise_evt(stream->client->server->instance,
|
||||||
|
(struct aisl_evt *) &on_input);
|
||||||
|
} else {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = 2;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* API Level */
|
||||||
|
|
||||||
|
/* Why it was here?
|
||||||
|
static int
|
||||||
|
aisl_stream_write(AislStream stream, const char * data, uint32_t d_len)
|
||||||
|
{
|
||||||
|
return buffer_add( &stream->buffer, data, d_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_cancel(AislStream stream)
|
||||||
|
{
|
||||||
|
aisl_client_close( stream->client );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void *
|
||||||
|
aisl_get_context(AislStream s)
|
||||||
|
{
|
||||||
|
return s->u_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_set_context(AislStream s, void *u_ptr)
|
||||||
|
{
|
||||||
|
s->u_ptr = u_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislClient
|
||||||
|
aisl_get_client(AislStream s)
|
||||||
|
{
|
||||||
|
return s->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislServer
|
||||||
|
aisl_get_server(AislStream s)
|
||||||
|
{
|
||||||
|
return aisl_client_get_server(s->client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislHttpVersion
|
||||||
|
aisl_get_http_version(AislStream s)
|
||||||
|
{
|
||||||
|
return aisl_client_get_http_version(s->client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_reject(AislStream s)
|
||||||
|
{
|
||||||
|
aisl_client_disconnect( s->client );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static AislStatus
|
||||||
|
aisl_start_response(AislStream stream)
|
||||||
|
{
|
||||||
|
return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static AislStatus
|
||||||
|
aisl_stream_close_headers(AislStream stream)
|
||||||
|
{
|
||||||
|
int32_t l;
|
||||||
|
|
||||||
|
if (aisl_start_response(stream) == AISL_MALLOC_ERROR)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) {
|
||||||
|
l = buffer_append(&stream->buffer, "Server: AISL\r\n", 14);
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->flags |= FLAG_SERVER_HEADER_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
|
||||||
|
l = buffer_append(&stream->buffer,
|
||||||
|
"Content-Type: text/html; encoding=utf-8\r\n", 41);
|
||||||
|
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||||
|
if (stream->content_length != AISL_AUTO_LENGTH) {
|
||||||
|
l = buffer_append_printf(&stream->buffer, "Content-Length: %"PRIu64"\r\n",
|
||||||
|
stream->content_length);
|
||||||
|
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
|
||||||
|
l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n",
|
||||||
|
(aisl_client_get_keepalive(stream->client) ? "keepalive" : "close"));
|
||||||
|
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->body_offset = stream->buffer.used;
|
||||||
|
stream->state = AISL_STREAM_STATE_SEND_BODY;
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislStatus
|
||||||
|
aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len)
|
||||||
|
{
|
||||||
|
int32_t l;
|
||||||
|
|
||||||
|
/* check if those headers were already sent */
|
||||||
|
if (stream->state > AISL_STREAM_STATE_READY)
|
||||||
|
return AISL_IDLE;
|
||||||
|
|
||||||
|
stream->http_response = rs_code;
|
||||||
|
stream->content_length = c_len;
|
||||||
|
|
||||||
|
buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0);
|
||||||
|
|
||||||
|
l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n",
|
||||||
|
aisl_http_version_to_string(stream->client->http_version), rs_code,
|
||||||
|
aisl_http_response_to_string(rs_code));
|
||||||
|
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
|
||||||
|
stream->state = AISL_STREAM_STATE_SEND_HEADER;
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislStatus
|
||||||
|
aisl_flush(AislStream s)
|
||||||
|
{
|
||||||
|
if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||||
|
char hdr[ 40 ];
|
||||||
|
uint64_t c_len;
|
||||||
|
int32_t l;
|
||||||
|
|
||||||
|
if (s->body_offset) {
|
||||||
|
c_len = s->buffer.used - s->body_offset;
|
||||||
|
l = snprintf(hdr, sizeof (hdr), "Content-Length: %"PRIu64"\r\n", c_len);
|
||||||
|
l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l);
|
||||||
|
if (l == -1)
|
||||||
|
return AISL_MALLOC_ERROR;
|
||||||
|
} else {
|
||||||
|
aisl_stream_close_headers(s);
|
||||||
|
}
|
||||||
|
s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = AISL_STREAM_STATE_DONE;
|
||||||
|
s->flags |= FLAG_FLUSHED;
|
||||||
|
|
||||||
|
return AISL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t
|
||||||
|
aisl_stream_verify_header(AislStream stream, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
if (stream->state < AISL_STREAM_STATE_SEND_HEADER) {
|
||||||
|
if (aisl_start_response(stream) != AISL_SUCCESS)
|
||||||
|
return -1;
|
||||||
|
} else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
|
||||||
|
if (str_cmpi(key, "connection")==0) {
|
||||||
|
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
|
||||||
|
if (value) {
|
||||||
|
aisl_client_set_keepalive(stream->client,
|
||||||
|
(str_cmpi(value, "keepalive") == 0));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
|
||||||
|
if (str_cmpi(key, "content-type") == 0) {
|
||||||
|
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||||
|
if (str_cmpi(key, "content-length") == 0) {
|
||||||
|
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
|
||||||
|
if (str_cmpi(key, "content-length")==0) {
|
||||||
|
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int32_t
|
||||||
|
aisl_header(AislStream stream, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
int32_t result;
|
||||||
|
|
||||||
|
if ( (result = aisl_stream_verify_header( stream, key, value )) != 1)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* For debug purposes
|
||||||
|
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL )
|
||||||
|
{
|
||||||
|
ret = strlen(pch);
|
||||||
|
if ( buffer_insert(
|
||||||
|
&stream->buffer,
|
||||||
|
stream->end_of_headers,
|
||||||
|
pch,
|
||||||
|
ret
|
||||||
|
) == -1 )
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stream->end_of_headers += ret;
|
||||||
|
|
||||||
|
free(pch);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = -1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int32_t
|
||||||
|
aisl_header_printf(AislStream stream, const char *key, const char *format, ...)
|
||||||
|
{
|
||||||
|
int32_t result;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
result = aisl_header_vprintf( stream, key, format, args );
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int32_t
|
||||||
|
aisl_header_vprintf(AislStream stream,
|
||||||
|
const char *key,
|
||||||
|
const char *format,
|
||||||
|
va_list args)
|
||||||
|
{
|
||||||
|
int32_t result, l;
|
||||||
|
|
||||||
|
if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = buffer_append_printf( &stream->buffer, "%s: ", key );
|
||||||
|
|
||||||
|
if (result != -1) {
|
||||||
|
l = buffer_append_vprintf( &stream->buffer, format, args );
|
||||||
|
|
||||||
|
if (l != -1) {
|
||||||
|
result += l;
|
||||||
|
if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) {
|
||||||
|
result += l;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int
|
||||||
|
aisl_printf(AislStream stream, const char *format, ...)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, format);
|
||||||
|
result = aisl_vprintf(stream, format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int32_t
|
||||||
|
aisl_vprintf(AislStream stream, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||||
|
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return buffer_append_vprintf(&stream->buffer, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int32_t
|
||||||
|
aisl_write(AislStream stream, const char *data, int32_t d_len)
|
||||||
|
{
|
||||||
|
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||||
|
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d_len == -1)
|
||||||
|
d_len = strlen(data);
|
||||||
|
|
||||||
|
return buffer_append(&stream->buffer, data, d_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
int
|
||||||
|
aisl_puts(const char *str, AislStream stream)
|
||||||
|
{
|
||||||
|
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
|
||||||
|
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return aisl_write( stream, str, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
AislInstance
|
||||||
|
aisl_stream_get_instance(AislStream stream)
|
||||||
|
{
|
||||||
|
return stream->client->server->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
void
|
||||||
|
aisl_set_output_event(AislStream stream, bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
stream->flags |= FLAG_OUTPUT_CHUNKED;
|
||||||
|
else if (stream->flags & FLAG_OUTPUT_CHUNKED)
|
||||||
|
stream->flags &= ~FLAG_OUTPUT_CHUNKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
bool
|
||||||
|
aisl_get_output_event(AislStream stream)
|
||||||
|
{
|
||||||
|
return (stream->flags & FLAG_OUTPUT_CHUNKED);
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WITHOUT_STRINGIFIERS
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
const char *
|
||||||
|
aisl_status_to_string(AislStatus status)
|
||||||
|
{
|
||||||
|
switch(status) {
|
||||||
|
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
|
||||||
|
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
|
||||||
|
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
|
||||||
|
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
|
||||||
|
case AISL_SUCCESS: return "AISL_SUCCESS";
|
||||||
|
case AISL_IDLE: return "AISL_IDLE";
|
||||||
|
}
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((visibility ("default") ))
|
||||||
|
const char *
|
||||||
|
aisl_event_to_string(AislEvent evt_code)
|
||||||
|
{
|
||||||
|
switch(evt_code) {
|
||||||
|
case AISL_EVENT_SERVER_READY: return "SERVER READY";
|
||||||
|
case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR";
|
||||||
|
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT";
|
||||||
|
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT";
|
||||||
|
case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN";
|
||||||
|
case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER";
|
||||||
|
case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT";
|
||||||
|
case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST";
|
||||||
|
case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT";
|
||||||
|
case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE";
|
||||||
|
case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue