Switch to new coding style

This commit is contained in:
Ilja Kartašov 2019-04-19 21:43:02 +02:00
parent ee90cbc37c
commit 12be39c747
28 changed files with 2466 additions and 2213 deletions

View File

@ -24,14 +24,14 @@
static void static void
hello_world(aisl_evt_t const evt, void * p_ctx) hello_world(const struct aisl_evt *evt, void *p_ctx)
{ {
if (evt->code != AISL_EVENT_STREAM_REQUEST) if (evt->code != AISL_EVENT_STREAM_REQUEST)
return; return;
aisl_status_t status; AislStatus status;
aisl_stream_t s = evt->source; AislStream s = evt->source;
const char html[] = const char html[] =
"<html>" "<html>"
@ -44,11 +44,11 @@ hello_world(aisl_evt_t const evt, void * p_ctx)
"</body>" "</body>"
"</html>"; "</html>";
status = aisl_response(s, AISL_HTTP_OK, sizeof(html)-1); status = aisl_response(s, AISL_HTTP_OK, sizeof (html)-1);
if (status == AISL_SUCCESS) if (status == AISL_SUCCESS)
{ {
if (aisl_write(s, html, sizeof(html)-1) != -1) if (aisl_write(s, html, sizeof (html)-1) != -1)
{ {
aisl_flush(s); aisl_flush(s);
} }
@ -63,8 +63,8 @@ hello_world(aisl_evt_t const evt, void * p_ctx)
int int
main(int argc, char ** argv) main(int argc, char ** argv)
{ {
aisl_t aisl; /**< AISL instance pointer */ AislInstance aisl; /**< AISL instance pointer */
aisl_status_t status; /**< AISL status code */ AislStatus status; /**< AISL status code */
struct aisl_cfg cfg = AISL_CFG_DEFAULT; struct aisl_cfg cfg = AISL_CFG_DEFAULT;
struct aisl_cfg_srv srv = { struct aisl_cfg_srv srv = {
.host = "0.0.0.0", .host = "0.0.0.0",

View File

@ -1,10 +1,16 @@
/* /******************************************************************************
* <aisl/aisl.h>
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
* *
* Project homepage: https://lowenware.com/aisl/ ******************************************************************************/
/**
* @file aisl/aisl.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Meta header file of AISL
* *
* @see https://lowenware.com/aisl/
*/ */
#ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279 #ifndef AISL_H_17EF1616_A00F_49C9_92B6_273AB13BF279

View File

@ -1,10 +1,16 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/** /**
* @file aisl/client.h * @file aisl/client.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislCLient functions
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * @see https://lowenware.com/aisl/
*
* Project homepage: https://lowenware.com/aisl/
*
*/ */
#ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08 #ifndef AISL_CLIENT_H_A6C37DCF_2183_4F22_A5A0_668311757A08
@ -14,50 +20,50 @@
/** /**
* @brief Gets #aisl_server_t instance associated with client. * @brief Gets #AislServer instance associated with client.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return an associated #aisl_server_t pointer. * @return an associated #AislServer pointer.
*/ */
aisl_server_t AislServer
aisl_client_get_server(aisl_client_t client); aisl_client_get_server(AislClient client);
/** /**
* @brief Gets security connection status. * @brief Gets security connection status.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return true if SSL is enabled and false if disabled. * @return true if SSL is enabled and false if disabled.
*/ */
bool bool
aisl_client_is_secure(aisl_client_t client); aisl_client_is_secure(AislClient client);
/** /**
* @brief Gets client's connection state. * @brief Gets client's connection state.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return true if client is online and false if is offline. * @return true if client is online and false if is offline.
*/ */
bool bool
aisl_client_is_online(aisl_client_t client); aisl_client_is_online(AislClient client);
/** /**
* @brief Forcefully closes client's connection. * @brief Forcefully closes client's connection.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
*/ */
void void
aisl_client_disconnect(aisl_client_t client); aisl_client_disconnect(AislClient client);
/** /**
* @brief Gets HTTP protocol version. * @brief Gets HTTP protocol version.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return HTTP protocol version * @return HTTP protocol version
*/ */
aisl_http_version_t AislHttpVersion
aisl_client_get_http_version(aisl_client_t client); aisl_client_get_http_version(AislClient client);
void void
aisl_client_get_address( aisl_client_t client, struct sockaddr_in * address); aisl_client_get_address(AislClient client, struct sockaddr_in *address);
#endif /* !AISL_CLIENT_H */ #endif /* !AISL_CLIENT_H */

View File

@ -1,10 +1,16 @@
/* /******************************************************************************
* <aisl/config.h>
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
* *
* Project homepage: https://lowenware.com/aisl/ ******************************************************************************/
/**
* @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 #ifndef AISL_CONFIG_H_DB67A89B_5CAF_4A5F_AEB1_6DB9F84827D6
@ -12,59 +18,49 @@
#include <aisl/types.h> #include <aisl/types.h>
#define AISL_CFG_DEFAULT \ #define AISL_CFG_DEFAULT { \
{ \ .callback = NULL \
.callback = NULL \ , .p_ctx = NULL \
, .p_ctx = NULL \ , .srv = NULL \
, .srv = NULL \ , .ssl = NULL \
, .ssl = NULL \ , .srv_cnt = 0 \
, .srv_cnt = 0 \ , .ssl_cnt = 0 \
, .ssl_cnt = 0 \ , .client_spool_size = 32 \
, .client_spool_size = 32 \ , .initial_buffer_size = 16536 \
, .initial_buffer_size = 16536 \ , .client_accept_limit = 1024 \
, .client_accept_limit = 1024 \ , .client_silence_timeout = 30 \
, .client_silence_timeout = 30 \
} \ } \
struct aisl_cfg_srv struct aisl_cfg_srv {
{ const char * host;
const char * host; uint16_t port;
uint16_t port; bool secure;
bool secure;
}; };
typedef struct aisl_cfg_srv * aisl_cfg_srv_t; struct aisl_cfg_ssl {
const char * host;
const char * key_file;
struct aisl_cfg_ssl const char * crt_file;
{
const char * host;
const char * key_file;
const char * crt_file;
}; };
typedef struct aisl_cfg_ssl * aisl_cfg_ssl_t;
struct aisl_cfg struct aisl_cfg
{ {
/* event handlers */ /* event handlers */
aisl_callback_t callback; AislCallback callback;
void * p_ctx; void *p_ctx;
aisl_cfg_srv_t srv; struct aisl_cfg_srv *srv;
aisl_cfg_ssl_t ssl; struct aisl_cfg_ssl *ssl;
int srv_cnt; int srv_cnt;
int ssl_cnt; int ssl_cnt;
int client_spool_size; int client_spool_size;
int initial_buffer_size; int initial_buffer_size;
int client_accept_limit; int client_accept_limit;
int client_silence_timeout; int client_silence_timeout;
}; };
typedef struct aisl_cfg * aisl_cfg_t;
#endif /* !AISL_CONFIG_H */ #endif /* !AISL_CONFIG_H */

View File

@ -1,10 +1,17 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/** /**
* @file aisl/instance.h * @file aisl/instance.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislInstance functions
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * @see https://lowenware.com/aisl/
*
* Project homepage: https://lowenware.com/aisl/
*
*/ */
#ifndef AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230 #ifndef AISL_INSTANCE_H_60576F41_454C_4189_B91A_F40501132230
@ -21,39 +28,39 @@
* @brief Allocates new AISL instance. * @brief Allocates new AISL instance.
* *
* @param cfg a pointer to #aisl_cfg_t structure. * @param cfg a pointer to #aisl_cfg_t structure.
* @return an #aisl_t instance pointer. * @return an #AislInstance instance pointer.
*/ */
aisl_t AislInstance
aisl_new( aisl_cfg_t cfg ); aisl_new(const struct aisl_cfg *cfg);
/** /**
* @brief Frees previously allocated pointer of AISL instance. * @brief Frees previously allocated pointer of AISL instance.
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
*/ */
void void
aisl_free( aisl_t instance ); aisl_free(AislInstance instance);
/** /**
* @brief A core function doing all the library routines. * @brief A core function doing all the library routines.
* Designed to be called inside application main loop * Designed to be called inside application main loop
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
* @return #aisl_status_t code. * @return #AislStatus code.
*/ */
aisl_status_t AislStatus
aisl_run_cycle( aisl_t instance ); aisl_run_cycle(AislInstance instance);
/** /**
* @brief Function to sleep CPU if nothing to do. * @brief Function to sleep CPU if nothing to do.
* Calls select on all the opened sockets inside. * Calls select on all the opened sockets inside.
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
* @param usec a number of miliseconds to wait for any data on sockets. * @param usec a number of miliseconds to wait for any data on sockets.
* @return #aisl_status_t code. * @return #AislStatus code.
*/ */
aisl_status_t AislStatus
aisl_sleep( aisl_t instance, uint32_t usec ); aisl_sleep(AislInstance instance, uint32_t usec);
#endif /* !AISL_INSTANCE_H */ #endif /* !AISL_INSTANCE_H */

View File

@ -1,10 +1,16 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/** /**
* @file aisl/server.h * @file aisl/server.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislServer functions
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * @see https://lowenware.com/aisl/
*
* Project homepage: https://lowenware.com/aisl/
*
*/ */
#ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768 #ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
@ -15,28 +21,28 @@
/** /**
* @brief Function to get appropriate AISL instance pointer from server pointer. * @brief Function to get appropriate AISL instance pointer from server pointer.
* @param server an #aisl_server_t pointer. * @param server an #AislServer pointer.
* @return an #aisl_t instance pointer. * @return an #AislInstance instance pointer.
*/ */
aisl_t AislInstance
aisl_server_get_instance( aisl_server_t server ); aisl_server_get_instance(AislServer server);
/** /**
* @brief Copies server listen address information to sockaddr_in structure. * @brief Copies server listen address information to sockaddr_in structure.
* @param server an #aisl_server_t pointer. * @param server an #AislServer pointer.
* @param address a pointer to sockaddr_in structure. * @param address a pointer to sockaddr_in structure.
*/ */
void void
aisl_server_get_address( aisl_server_t server, struct sockaddr_in * address); aisl_server_get_address(AislServer server, struct sockaddr_in *address);
/** /**
* @brief Function to get on and off status of SSL for the #aisl_server_t. * @brief Function to get on and off status of SSL for the #AislServer.
* @param server an #aisl_server_t pointer. * @param server an #AislServer pointer.
* @return a boolean value representing SSL enabled/disabled state. * @return a boolean value representing SSL enabled/disabled state.
*/ */
bool bool
aisl_server_get_ssl( aisl_server_t server ); aisl_server_get_ssl(AislServer server);
#endif /* !AISL_SERVER_H */ #endif /* !AISL_SERVER_H */

View File

@ -1,12 +1,17 @@
/* /******************************************************************************
* <aisl/stream.h>
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
* *
* Project homepage: https://lowenware.com/aisl/ ******************************************************************************/
*
*/
/**
* @file aisl/stream.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of #AislStream functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC #ifndef AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
#define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC #define AISL_STREAM_H_4D8EB622_3CE0_4F1B_AC1F_B27CCB5C2EDC
@ -15,96 +20,87 @@
#include <aisl/types.h> #include <aisl/types.h>
/* Stream helpers */
bool bool
aisl_is_secure( aisl_stream_t stream ); aisl_is_secure(AislStream stream);
aisl_client_t AislClient
aisl_get_client( aisl_stream_t stream ); aisl_get_client(AislStream stream);
aisl_server_t AislServer
aisl_get_server( aisl_stream_t stream ); aisl_get_server(AislStream stream);
aisl_http_version_t AislHttpVersion
aisl_get_http_version( aisl_stream_t stream ); aisl_get_http_version(AislStream stream);
aisl_t AislInstance
aisl_stream_get_instance( aisl_stream_t s ); aisl_stream_get_instance(AislStream s);
/* Context functions */
void * void *
aisl_get_context( aisl_stream_t stream ); aisl_get_context(AislStream stream);
void void
aisl_set_context( aisl_stream_t stream, void * context ); aisl_set_context(AislStream stream, void *context);
/* Stream control functions */ AislStatus
aisl_flush(AislStream stream);
aisl_status_t
aisl_flush( aisl_stream_t stream );
void void
aisl_reject( aisl_stream_t stream ); aisl_reject(AislStream stream);
/* Response functions */ AislStatus
aisl_response(AislStream stream,
aisl_status_t AislHttpResponse status_code,
aisl_response( aisl_stream_t stream, uint64_t content_length);
aisl_http_response_t status_code,
uint64_t content_length );
int int
aisl_header( aisl_stream_t stream, const char *key, const char *value ); aisl_header(AislStream stream, const char *key, const char *value );
int int
aisl_header_printf( aisl_stream_t stream, aisl_header_printf(AislStream stream,
const char * key, const char *key,
const char * format, const char *format,
... ); ... );
int int
aisl_header_vprintf( aisl_stream_t stream, aisl_header_vprintf(AislStream stream,
const char * key, const char *key,
const char * format, const char *format,
va_list args ); va_list args );
int int
aisl_printf( aisl_stream_t stream, const char * format, ... ); aisl_printf(AislStream stream, const char *format, ...);
int int
aisl_vprintf( aisl_stream_t stream, const char * format, va_list args ); aisl_vprintf(AislStream stream, const char *format, va_list args);
int int
aisl_write( aisl_stream_t stream, const char * data, int d_len ); aisl_write(AislStream stream, const char *data, int d_len);
int int
aisl_puts( const char * str_data, aisl_stream_t stream ); aisl_puts(const char *str_data, AislStream stream);
void void
aisl_set_output_event(aisl_stream_t stream, bool value); aisl_set_output_event(AislStream stream, bool value);
bool bool
aisl_get_output_event(aisl_stream_t stream); aisl_get_output_event(AislStream stream);
#endif /* !AISL_STREAM_H */ #endif /* !AISL_STREAM_H */

View File

@ -1,10 +1,16 @@
/* /******************************************************************************
* <aisl/types.h>
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
* *
* Project homepage: https://lowenware.com/aisl/ ******************************************************************************/
/**
* @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 #ifndef AISL_TYPES_H_86A9DBA7_C0E6_4CF4_8A64_DAAD4A81031B
@ -20,206 +26,182 @@
#define AISL_AUTO_LENGTH (~0) #define AISL_AUTO_LENGTH (~0)
/* type casts */ /** type casts */
#define AISL_CALLBACK(x) ((aisl_callback_t) x) #define AISL_CALLBACK(x) ((AislCallback) x)
/* AISL Instance */ /** AISL Instance */
typedef struct aisl * aisl_t; typedef struct aisl_instance * AislInstance;
/* HTTP(s) Server */ /** HTTP(s) Server */
typedef struct aisl_server * aisl_server_t; typedef struct aisl_server * AislServer;
/* HTTP(s) Client */ /** HTTP(s) Client */
typedef struct aisl_client * aisl_client_t; typedef struct aisl_client * AislClient;
/* Server<->Client Stream */ /** Server<->Client Stream */
typedef struct aisl_stream * aisl_stream_t; typedef struct aisl_stream * AislStream;
/* status return codes */ /** status return codes */
typedef enum typedef enum {
{ AISL_INPUT_ERROR = -4
AISL_INPUT_ERROR = -4 , AISL_EXTCALL_ERROR = -3
, AISL_EXTCALL_ERROR = -3 , AISL_SYSCALL_ERROR = -2
, AISL_SYSCALL_ERROR = -2 , AISL_MALLOC_ERROR = -1
, AISL_MALLOC_ERROR = -1
, AISL_SUCCESS = 0 , AISL_SUCCESS = 0
, AISL_IDLE = 1 , AISL_IDLE = 1
} AislStatus;
} aisl_status_t;
#ifndef WITHOUT_STRINGIFIERS #ifndef WITHOUT_STRINGIFIERS
const char * const char *
aisl_status_to_string(aisl_status_t status); aisl_status_to_string(AislStatus status);
#endif #endif
/* Generic HTTP Enumerations */ /** Generic HTTP Enumerations */
typedef enum {
typedef enum AISL_HTTP_0_9 = 0x0009
{ , AISL_HTTP_1_0 = 0x0100
AISL_HTTP_0_9 = 0x0009 , AISL_HTTP_1_1 = 0x0101
, AISL_HTTP_1_0 = 0x0100 , AISL_HTTP_2_0 = 0x0200
, AISL_HTTP_1_1 = 0x0101 } AislHttpVersion;
, AISL_HTTP_2_0 = 0x0200
} aisl_http_version_t;
const char * const char *
aisl_http_version_to_string( aisl_http_version_t version ); aisl_http_version_to_string(AislHttpVersion version);
typedef enum typedef enum {
{ AISL_HTTP_METHOD_UNKNOWN
AISL_HTTP_METHOD_UNKNOWN , AISL_HTTP_GET
, AISL_HTTP_GET , AISL_HTTP_PUT
, AISL_HTTP_PUT , AISL_HTTP_POST
, AISL_HTTP_POST , AISL_HTTP_HEAD
, AISL_HTTP_HEAD , AISL_HTTP_TRACE
, AISL_HTTP_TRACE , AISL_HTTP_DELETE
, AISL_HTTP_DELETE , AISL_HTTP_OPTIONS
, AISL_HTTP_OPTIONS , AISL_HTTP_CONNECT
, AISL_HTTP_CONNECT , AISL_HTTP_PRI
} AislHttpMethod;
, AISL_HTTP_PRI
} aisl_http_method_t;
const char * const char *
aisl_http_method_to_string( aisl_http_method_t method ); aisl_http_method_to_string( AislHttpMethod method );
typedef enum typedef enum {
{ AISL_HTTP_CONTINUE = 100
AISL_HTTP_CONTINUE = 100 , AISL_HTTP_SWITCHING_PROTOCOLS
, AISL_HTTP_SWITCHING_PROTOCOLS
, AISL_HTTP_OK = 200 , AISL_HTTP_OK = 200
, AISL_HTTP_CREATED , AISL_HTTP_CREATED
, AISL_HTTP_ACCEPTED , AISL_HTTP_ACCEPTED
, AISL_HTTP_NON_AUTHORITATIVE_INFORMATION , AISL_HTTP_NON_AUTHORITATIVE_INFORMATION
, AISL_HTTP_NO_CONTENT , AISL_HTTP_NO_CONTENT
, AISL_HTTP_RESET_CONTENT , AISL_HTTP_RESET_CONTENT
, AISL_HTTP_PARTIAL_CONTENT , AISL_HTTP_PARTIAL_CONTENT
, AISL_HTTP_MULTIPLE_CHOICES = 300 , AISL_HTTP_MULTIPLE_CHOICES = 300
, AISL_HTTP_MOVED_PERMANENTLY , AISL_HTTP_MOVED_PERMANENTLY
, AISL_HTTP_FOUND , AISL_HTTP_FOUND
, AISL_HTTP_SEE_OTHER , AISL_HTTP_SEE_OTHER
, AISL_HTTP_NOT_MODIFIED , AISL_HTTP_NOT_MODIFIED
, AISL_HTTP_USE_PROXY , AISL_HTTP_USE_PROXY
, AISL_HTTP_UNUSED , AISL_HTTP_UNUSED
, AISL_HTTP_TEMPORARY_REDIRECT , AISL_HTTP_TEMPORARY_REDIRECT
, AISL_HTTP_BAD_REQUEST = 400 , AISL_HTTP_BAD_REQUEST = 400
, AISL_HTTP_UNAUTHORIZED , AISL_HTTP_UNAUTHORIZED
, AISL_HTTP_PAYMENT_REQUIRED , AISL_HTTP_PAYMENT_REQUIRED
, AISL_HTTP_FORBIDDEN , AISL_HTTP_FORBIDDEN
, AISL_HTTP_NOT_FOUND , AISL_HTTP_NOT_FOUND
, AISL_HTTP_METHOD_NOT_ALLOWED , AISL_HTTP_METHOD_NOT_ALLOWED
, AISL_HTTP_NOT_ACCEPTABLE , AISL_HTTP_NOT_ACCEPTABLE
, AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED , AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED
, AISL_HTTP_REQUEST_TIMEOUT , AISL_HTTP_REQUEST_TIMEOUT
, AISL_HTTP_CONFLICT , AISL_HTTP_CONFLICT
, AISL_HTTP_GONE , AISL_HTTP_GONE
, AISL_HTTP_LENGTH_REQUIRED , AISL_HTTP_LENGTH_REQUIRED
, AISL_HTTP_PRECONDITION_FAILED , AISL_HTTP_PRECONDITION_FAILED
, AISL_HTTP_REQUEST_ENTITY_TOO_LARGE , AISL_HTTP_REQUEST_ENTITY_TOO_LARGE
, AISL_HTTP_REQUEST_URI_TOO_LONG , AISL_HTTP_REQUEST_URI_TOO_LONG
, AISL_HTTP_UNSUPPORTED_MEDIA_TYPE , AISL_HTTP_UNSUPPORTED_MEDIA_TYPE
, AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE , AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
, AISL_HTTP_EXPECTATION_FAILED , AISL_HTTP_EXPECTATION_FAILED
, AISL_HTTP_INTERNAL_SERVER_ERROR = 500 , AISL_HTTP_INTERNAL_SERVER_ERROR = 500
, AISL_HTTP_NOT_IMPLEMENTED , AISL_HTTP_NOT_IMPLEMENTED
, AISL_HTTP_BAD_GATEWAY , AISL_HTTP_BAD_GATEWAY
, AISL_HTTP_SERVICE_UNAVAILABLE , AISL_HTTP_SERVICE_UNAVAILABLE
, AISL_HTTP_GATEWAY_TIMEOUT , AISL_HTTP_GATEWAY_TIMEOUT
, AISL_HTTP_VERSION_NOT_SUPPORTED , AISL_HTTP_VERSION_NOT_SUPPORTED
} AislHttpResponse;
} aisl_http_response_t;
const char * const char *
aisl_http_response_to_string( aisl_http_response_t code ); aisl_http_response_to_string( AislHttpResponse code );
/* Events */ /** Codes of AISL events */
typedef enum {
AISL_EVENT_SERVER_READY = 100
, AISL_EVENT_SERVER_ERROR = 190
typedef enum , AISL_EVENT_CLIENT_CONNECT = 200
{ , AISL_EVENT_CLIENT_DISCONNECT = 210
AISL_EVENT_SERVER_OPEN = 100
, AISL_EVENT_SERVER_ERROR = 190
, AISL_EVENT_CLIENT_CONNECT = 200 , AISL_EVENT_STREAM_OPEN = 300
, AISL_EVENT_CLIENT_DISCONNECT = 210 , AISL_EVENT_STREAM_HEADER = 310
, AISL_EVENT_STREAM_INPUT = 320
, AISL_EVENT_STREAM_OPEN = 300 , AISL_EVENT_STREAM_REQUEST = 330
, AISL_EVENT_STREAM_HEADER = 310 , AISL_EVENT_STREAM_OUTPUT = 340
, AISL_EVENT_STREAM_INPUT = 320 , AISL_EVENT_STREAM_CLOSE = 350
, AISL_EVENT_STREAM_REQUEST = 330 , AISL_EVENT_STREAM_ERROR = 390
, AISL_EVENT_STREAM_OUTPUT = 340 } AislEvent;
, AISL_EVENT_STREAM_CLOSE = 350
, AISL_EVENT_STREAM_ERROR = 390
} aisl_evt_code_t;
struct aisl_evt struct aisl_evt {
{ void *source;
void * source; AislEvent code;
aisl_evt_code_t code; AislStatus status;
aisl_status_t status;
}; };
typedef struct aisl_evt * aisl_evt_t;
/* void type event callback */ /* void type event callback */
typedef void typedef void
(* aisl_callback_t) (aisl_evt_t const evt, void * p_ctx); (* AislCallback) (const struct aisl_evt *evt, void *ctx);
struct aisl_evt_stream_open struct aisl_evt_open {
{ struct aisl_evt evt;
struct aisl_evt evt; const char *path;
const char * path; const char *query;
const char * query; AislHttpMethod http_method;
aisl_http_method_t http_method;
}; };
typedef struct aisl_evt_stream_open * aisl_evt_stream_open_t;
struct aisl_evt_header {
struct aisl_evt_stream_header struct aisl_evt evt;
{ const char *key;
struct aisl_evt evt; const char *value;
const char * key;
const char * value;
}; };
typedef struct aisl_evt_stream_header * aisl_evt_stream_header_t;
struct aisl_evt_input {
struct aisl_evt_stream_input struct aisl_evt evt;
{ const char *data;
struct aisl_evt evt; int32_t size;
const char * data;
int32_t size;
}; };
typedef struct aisl_evt_stream_input * aisl_evt_stream_input_t;
#ifndef WITHOUT_STRINGIFIERS #ifndef WITHOUT_STRINGIFIERS
const char * const char *
aisl_evt_code_to_string( aisl_evt_code_t evt_code ); aisl_event_to_string(AislEvent evt);
#endif #endif

539
src/) Normal file
View File

@ -0,0 +1,539 @@
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef AISL_WITHOUT_SSL
#include <openssl/err.h>
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
#define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
#define BUFFER_SIZE (16*1024)
static void
aisl_client_close(AislClient client, AislStatus status)
{
if (client->fd != -1)
{
aisl_raise(
client->server->instance
, (void *)client
, AISL_EVENT_CLIENT_DISCONNECT
, status
);
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
static AislStatus
aisl_client_parse(AislClient client, char * data, int32_t size)
{
AislStatus result = AISL_SUCCESS;
AislStream s = client->stream;
ParserStatus p = HTTP_PARSER_SUCCESS;
int32_t bytes_left = size;
switch (client->http_version)
{
case AISL_HTTP_0_9:
case AISL_HTTP_1_0:
case AISL_HTTP_1_1:
/* s = client->stream; */
while ( p == HTTP_PARSER_SUCCESS )
{
switch ( aisl_stream_get_state(s) )
{
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
p = HTTP_PARSER_ERROR;
continue;
}
// size now has number of parsed bytes
data += size;
bytes_left -= size;
size = bytes_left;
}
break;
case AISL_HTTP_2_0:
break;
}
switch(p)
{
case HTTP_PARSER_READY:
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_REQUEST
, result
);
break;
case HTTP_PARSER_ERROR:
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_ERROR
, result
);
aisl_client_close(client, result);
return result;
default:
break;
}
if (size)
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
return result;
}
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
* parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0
*/
static AislStatus
aisl_client_input(AislClient client)
{
int l;
char * data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
DPRINTF("SSL_read");
if ( !(client->flags & FLAG_HANDSHAKE) )
{
if ( (l = SSL_accept(client->ssl)) != 1 )
{
l = SSL_get_error(client->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client, AISL_EXTCALL_ERROR);
return AISL_EXTCALL_ERROR;
}
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size) ;
}
else
#endif
l = recv( client->fd, data, size, 0);
if (l > 0)
{
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
}
else if (l<0)
{
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
return AISL_IDLE;
}
else
#endif
{
if(errno == EWOULDBLOCK)
return AISL_IDLE;
DPRINTF("client - %s", strerror(errno));
}
}
/* both: client disconnect + on read error */
/* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
static AislStatus
aisl_client_output(AislClient client)
{
int l;
char * data;
AislStream s = client->stream;
/* while stream is not flushed, we should raise event */
if( aisl_get_output_event(s) )
{
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* buffer->size will be used to carry amount of stored bytes
* */
l = aisl_stream_get_buffer_space(s);
/*
if (bsz < OUTPUT_BUFFER_SIZE)
{
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false;
s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE;
}
*/
if ( !(l < aisl_stream_get_buffer_size(s) / 2) )
{
aisl_raise(
client->server->instance
, (void*)s
, AISL_EVENT_STREAM_OUTPUT
, AISL_SUCCESS
);
}
}
data = aisl_stream_get_data(s, &l);
if ( !l )
return AISL_IDLE;
#ifdef AISL_WITHOUT_SSL
l = send( client->fd, data, l, 0);
#else
l = (client->ssl) ?
SSL_write(client->ssl, data, l) :
send( client->fd, data, l, 0);
#endif
if (l > 0)
{
aisl_stream_shift(s, l);
/*
if (s->state == STREAM_RESPONSE_READY && / * flushed * /
s->buffer->size == 0) / * all sent * /
*/
if ( aisl_stream_is_done(s) )
{
/* buffer_clear(s->buffer, 0); */
/* data has been sent */
if (client->flags & FLAG_KEEPALIVE)
{
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++);
if (client->stream != NULL )
return AISL_SUCCESS;
/* in case of malloc error it will not be error as long as request was
* handled and we just close the connection.
*/
}
aisl_client_close(client, AISL_SUCCESS);
}
return AISL_SUCCESS;
}
/* l < 0 */
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
if ( SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE )
return AISL_IDLE;
}
else
#endif
{
if (errno == EWOULDBLOCK)
return AISL_IDLE;
}
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
AislClient
aisl_client_new( AislServer server,
int fd,
struct sockaddr_in * addr )
{
AislClient client;
AislStream stream;
if ( (client = calloc(1, sizeof (struct aisl_client))) != NULL )
{
DPRINTF("client alocated");
memcpy(&client->address, addr, sizeof (struct sockaddr_in));
client->server = server;
client->fd = fd;
client->next_id = 2;
client->http_version = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1)
{
DPRINTF("client buffer alocated");
memcpy(&client->out, &client->in, sizeof (struct buffer));
stream = aisl_stream_new(client, 0);
if (stream != NULL)
{
client->stream = stream;
DPRINTF("client stream alocated");
#ifdef AISL_WITHOUT_SSL
return client;
#else
SSL_CTX * ssl_ctx;
if ( !server->ssl )
return client;
ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
if ((client->ssl = SSL_new(ssl_ctx)) != NULL )
{
SSL_set_fd(client->ssl, fd);
return client;
}
#endif
}
}
aisl_client_free(client);
}
return NULL;
}
void
aisl_client_free(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
SSL_free(client->ssl);
#endif
if (client->in.data)
free(client->in.data);
/* out buffer is a shared part of input buffer, so no need to free it */
if (client->stream)
aisl_stream_free(client->stream);
free(client);
}
AislStatus
AislClientouch(AislClient client, int32_t timeout)
{
AislStatus result = AISL_IDLE,
status = AISL_IDLE;
/* input */
if (client->flags & FLAG_CAN_READ)
{
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */
if (client->flags & FLAG_CAN_WRITE)
{
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
/*
if ((client->http_version==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) &&
(client_input(client)) ) result = true;
*/
/* output */
/*
s = list_index(client->streams, client->ostream);
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) )
result = client_output(client);
*/
/* update timestamp */
if (result == AISL_IDLE)
result = status;
if (result == AISL_SUCCESS)
client->timestamp = time(NULL);
else
{
time_t now;
time(&now);
if ( !(now - client->timestamp < timeout) )
{
aisl_client_close(client, result);
}
}
return result;
}
int
aisl_client_get_socket(AislClient client)
{
return client->fd;
}
bool
aisl_client_get_keepalive(AislClient client)
{
return (client->flags & FLAG_KEEPALIVE);
}
void
aisl_client_set_keepalive(AislClient client, bool value)
{
if (value)
client->flags |= FLAG_KEEPALIVE;
else
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
AislServer
aisl_client_get_server(AislClient client)
{
return client->server;
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_secure(AislClient client)
{
#ifdef AISL_WITHOUT_SSL
return false;
#else
return (client->ssl == NULL) ? false : true;
#endif
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_online(AislClient client)
{
return (client->fd == -1) ? false : true;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_disconnect(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_client_get_http_version(AislClient client)
{
return client->http_version;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_get_address( AislClient client, struct sockaddr_in * address)
{
memcpy(address, &client->address, sizeof (struct sockaddr_in));
}

View File

@ -19,169 +19,157 @@
static int32_t static int32_t
buffer_set_size(buffer_t buffer, int32_t new_size) buffer_set_size(struct buffer *buffer, int32_t new_size)
{ {
if ( new_size != buffer->size ) if (new_size != buffer->size) {
{ char *data;
if (new_size)
{
int32_t s = new_size / 1024;
if ( new_size % 1024 ) if (new_size) {
{ int32_t s = new_size / 1024;
new_size = (s+1) * 1024;
}
}
else
new_size = 16*1024;
char * data = realloc(buffer->data, new_size); if ( new_size % 1024 ) {
new_size = (s+1) * 1024;
}
} else {
new_size = 16*1024;
}
if (data) if ((data = realloc(buffer->data, new_size)) != NULL) {
{ buffer->data = data;
buffer->data = data; buffer->size = new_size;
buffer->size = new_size; } else {
} new_size = -1;
else }
new_size = -1; }
}
return new_size; return new_size;
} }
int32_t int32_t
buffer_init( buffer_t buffer, int32_t size ) buffer_init(struct buffer *buffer, int32_t size)
{ {
if ( (size = buffer_set_size(buffer, size)) != -1) if ( (size = buffer_set_size(buffer, size)) != -1)
buffer->used = 0; buffer->used = 0;
return size; return size;
} }
void void
buffer_release( buffer_t buffer ) buffer_release(struct buffer *buffer)
{ {
if (buffer->data) if (buffer->data) {
{ free(buffer->data);
free(buffer->data); buffer->data = NULL;
buffer->data = NULL; }
}
buffer->used = 0; buffer->used = 0;
buffer->size = 0; buffer->size = 0;
} }
static int32_t static int32_t
buffer_move_offset( buffer_t buffer, int32_t offset, int32_t size ) buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size)
{ {
int32_t to_move = buffer->used - offset; int32_t to_move = buffer->used - offset;
if (to_move < 0) if (to_move < 0) {
return -1; return -1;
else if (to_move) } else if (to_move) {
memmove(&buffer->data[offset+size], &buffer->data[offset], to_move); memmove(&buffer->data[offset+size], &buffer->data[offset], to_move);
}
return size; return size;
} }
int32_t int32_t
buffer_insert( buffer_t buffer buffer_insert(struct buffer *buffer,
, int32_t offset int32_t offset,
, const char * data const char *data,
, int32_t size ) int32_t size)
{ {
int32_t result; int32_t result;
if ( (result = buffer_set_size(buffer, size)) != -1) if ( (result = buffer_set_size(buffer, size)) != -1) {
{ if ((result = buffer_move_offset(buffer, offset, size)) != -1) {
if ((result = buffer_move_offset(buffer, offset, size)) != -1) memcpy(&buffer->data[offset], data, size);
{ buffer->used += result;
memcpy(&buffer->data[offset], data, size); }
buffer->used += result; }
}
}
return result; return result;
} }
int32_t int32_t
buffer_append_printf( buffer_t buffer, const char * format, ... ) buffer_append_printf(struct buffer *buffer, const char *format, ...)
{ {
int32_t result; int32_t result;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
result = buffer_append_vprintf( buffer, format, args); result = buffer_append_vprintf(buffer, format, args);
va_end(args); va_end(args);
return result; return result;
} }
int32_t int32_t
buffer_append_vprintf( buffer_t buffer, const char * format, va_list args ) buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args)
{ {
int32_t space = buffer->size - buffer->used, int32_t space, result;
result; va_list cp_args;
va_list cp_args; va_copy(cp_args, args);
va_copy(cp_args, args); space = buffer->size - buffer->used,
result = vsnprintf(&buffer->data[buffer->used], space, format, args);
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);
if ( result < space ) /* enough space */ return result;
{
buffer->used += result;
}
else
{
if ((result = buffer_set_size(buffer, buffer->size + result - space)) != -1)
result = buffer_append_vprintf(buffer, format, cp_args);
}
va_end(cp_args);
return result;
} }
int32_t int32_t
buffer_append( buffer_t buffer, const char * data, int32_t size ) buffer_append(struct buffer *buffer, const char *data, int32_t size)
{ {
int32_t used = buffer->used, int32_t used, space;
space = buffer->size - used;
if ( size > space ) /* enough space */ used = buffer->used,
{ space = buffer->size - used;
if ( buffer_set_size(buffer, buffer->size + size - space) == -1)
return -1;
}
memcpy(&buffer->data[used], data, size); if (size > space) { /* enough space */
buffer->used += size; if (buffer_set_size(buffer, buffer->size + size - space) == -1)
return -1;
}
return size; memcpy(&buffer->data[used], data, size);
buffer->used += size;
return size;
} }
int32_t int32_t
buffer_shift( buffer_t buffer, int32_t offset ) buffer_shift(struct buffer *buffer, int32_t offset)
{ {
int32_t used = buffer->used - offset; int32_t used = buffer->used - offset;
if (offset > 0) if (offset > 0) {
{ if (offset < used) {
if (offset < used) memmove(buffer->data, &buffer->data[offset], used);
{ } else {
memmove(buffer->data, &buffer->data[offset], used); used = 0;
} }
else buffer->used = used;
used = 0; }
return used;
buffer->used = used;
}
return used;
} }

View File

@ -6,9 +6,9 @@
******************************************************************************/ ******************************************************************************/
/** /**
* @file buffer.h * @file src/buffer.h
* @author Ilja Kartašov <ik@lowenware.com> * @author Ilja Kartašov <ik@lowenware.com>
* @brief Buffer module header file * @brief Declarations of struct buffer and functions
* *
* @see https://lowenware.com/aisl/ * @see https://lowenware.com/aisl/
*/ */
@ -20,42 +20,39 @@
#include <stdarg.h> #include <stdarg.h>
struct buffer struct buffer {
{ char *data;
char * data; int32_t size;
int32_t size; int32_t used;
int32_t used;
}; };
typedef struct buffer * buffer_t;
int32_t int32_t
buffer_init( buffer_t buffer, int32_t size ); buffer_init(struct buffer *bs, int32_t size);
void void
buffer_release( buffer_t buffer ); buffer_release(struct buffer *bs );
int32_t int32_t
buffer_insert(buffer_t buffer, int32_t offset, const char * data, int32_t size); buffer_insert(struct buffer *bs, int32_t offset, const char *data, int32_t size);
int32_t int32_t
buffer_append_printf( buffer_t buffer, const char * format, ... ); buffer_append_printf(struct buffer *bs, const char *format, ...);
int32_t int32_t
buffer_append_vprintf( buffer_t buffer, const char * format, va_list args ); buffer_append_vprintf(struct buffer *bs, const char *format, va_list args);
int32_t int32_t
buffer_append( buffer_t buffer, const char * data, int32_t size ); buffer_append(struct buffer *bs, const char *data, int32_t size);
int32_t int32_t
buffer_shift( buffer_t buffer, int32_t size ); buffer_shift(struct buffer *bs, int32_t size);

View File

@ -27,115 +27,87 @@
static void static void
aisl_client_close(aisl_client_t client, aisl_status_t status) aisl_client_close(AislClient client, AislStatus status)
{ {
if (client->fd != -1) if (client->fd != -1) {
{ aisl_raise(client->server->instance, (void *)client,
aisl_raise( AISL_EVENT_CLIENT_DISCONNECT, status );
client->server->instance
, (void *)client
, AISL_EVENT_CLIENT_DISCONNECT
, status
);
close(client->fd); close(client->fd);
shutdown(client->fd, SHUT_RDWR); shutdown(client->fd, SHUT_RDWR);
client->fd = -1; client->fd = -1;
} }
} }
static aisl_status_t static AislStatus
aisl_client_parse(aisl_client_t client, char * data, int32_t size) aisl_client_parse(AislClient client, char *data, int32_t size)
{ {
aisl_status_t result = AISL_SUCCESS; AislStatus result = AISL_SUCCESS;
aisl_stream_t s = client->stream; AislStream s = client->stream;
http_parser_t p = HTTP_PARSER_SUCCESS; ParserStatus p = HTTP_PARSER_SUCCESS;
int32_t bytes_left = size;
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;
switch (client->http_version) case AISL_STREAM_STATE_WAIT_HEADER:
{ p = http_10_parse_header(data, &size, client->stream);
case AISL_HTTP_0_9: break;
case AISL_HTTP_1_0:
case AISL_HTTP_1_1:
/* s = client->stream; */ case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
while ( p == HTTP_PARSER_SUCCESS ) 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;
switch ( aisl_stream_get_state(s) ) case AISL_HTTP_2_0:
{ break;
case AISL_STREAM_STATE_IDLE: }
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER: switch(p) {
p = http_10_parse_header(data, &size, client->stream); case HTTP_PARSER_READY:
break; client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
case AISL_STREAM_STATE_WAIT_BODY: aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST,
p = http_10_parse_body(data, &size, client->stream); result);
break; break;
default: /* has input data, but request was already parsed */ case HTTP_PARSER_ERROR:
p = HTTP_PARSER_ERROR; /* reply Bad Request here */
continue; client->stream->http_response = AISL_HTTP_BAD_REQUEST;
} aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR,
// size now has number of parsed bytes result);
data += size;
bytes_left -= size;
size = bytes_left;
}
aisl_client_close(client, result);
return result;
default:
break;
}
break; if (size)
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
case AISL_HTTP_2_0: return result;
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;
} }
@ -143,344 +115,279 @@ aisl_client_parse(aisl_client_t client, char * data, int32_t size)
* parsed. If it is not NULL, then stream expects additional data -> same like * parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0 * in mono stream HTTP 1.0
*/ */
static aisl_status_t static AislStatus
aisl_client_input(aisl_client_t client) aisl_client_input(AislClient client)
{ {
int l; int l;
char *data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
char * data = &client->in.data[ client->in.used ]; #ifndef AISL_WITHOUT_SSL
int32_t size = client->in.size - client->in.used; 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);
#ifndef AISL_WITHOUT_SSL if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
if (client->ssl) return AISL_IDLE;
{
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) DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
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);
}
aisl_client_close(client, AISL_EXTCALL_ERROR); if (l > 0) {
return AISL_EXTCALL_ERROR; DPRINTF("%d bytes received from client", l);
}
client->flags &= ~FLAG_HANDSHAKE; data = client->in.data;
} size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
} else if (l<0) {
l = SSL_read(client->ssl, data, size) ; #ifndef AISL_WITHOUT_SSL
} if (client->ssl) {
else if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
#endif return AISL_IDLE;
l = recv( client->fd, data, size, 0); } else
#endif
{
if (l > 0) if(errno == EWOULDBLOCK)
{ return AISL_IDLE;
DPRINTF("%d bytes received from client", l); DPRINTF("client - %s", strerror(errno));
}
}
data = client->in.data; /* both: client disconnect + on read error */
size = client->in.used + l; /* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
client->in.used = size; return AISL_SYSCALL_ERROR;
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 aisl_status_t static AislStatus
aisl_client_output(aisl_client_t client) aisl_client_output(AislClient client)
{ {
int l; int l;
char * data; char *data;
aisl_stream_t s = client->stream; AislStream s = client->stream;
/* while stream is not flushed, we should raise event */ /* while stream is not flushed, we should raise event */
if( aisl_get_output_event(s) ) if(aisl_get_output_event(s)) {
{ /* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event ) * stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but * buffer->size will be used to carry amount of stored bytes
* buffer->size will be used to carry amount of stored bytes * */
* */ l = aisl_stream_get_buffer_space(s);
l = aisl_stream_get_buffer_space(s);
/* /*
if (bsz < OUTPUT_BUFFER_SIZE) if (bsz < OUTPUT_BUFFER_SIZE)
{ {
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0) if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false; return false;
s->buffer->size = bsz; s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE; bsz = OUTPUT_BUFFER_SIZE;
} }
*/ */
if ( !(l < aisl_stream_get_buffer_size(s) / 2) ) if (!(l < aisl_stream_get_buffer_size(s) / 2)) {
{ aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT,
aisl_raise( AISL_SUCCESS);
client->server->instance }
, (void*)s }
, AISL_EVENT_STREAM_OUTPUT
, AISL_SUCCESS
);
}
}
data = aisl_stream_get_data(s, &l); data = aisl_stream_get_data(s, &l);
if ( !l ) if ( !l )
return AISL_IDLE; return AISL_IDLE;
#ifdef AISL_WITHOUT_SSL #ifdef AISL_WITHOUT_SSL
l = send( client->fd, data, l, 0); l = send( client->fd, data, l, 0);
#else #else
l = (client->ssl) ? l = (client->ssl) ?
SSL_write(client->ssl, data, l) : SSL_write(client->ssl, data, l) :
send( client->fd, data, l, 0); send( client->fd, data, l, 0);
#endif #endif
if (l > 0) if (l > 0) {
{ aisl_stream_shift(s, l);
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 (s->state == STREAM_RESPONSE_READY && / * flushed * / if (client->stream != NULL )
s->buffer->size == 0) / * all sent * / return AISL_SUCCESS;
*/
if ( aisl_stream_is_done(s) )
{
/* buffer_clear(s->buffer, 0); */
/* data has been sent */ /* in case of malloc error it will not be error as long as request was
* handled and we just close the connection.
*/
}
if (client->flags & FLAG_KEEPALIVE) aisl_client_close(client, AISL_SUCCESS);
{ }
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++); return AISL_SUCCESS;
if (client->stream != NULL ) }
return AISL_SUCCESS;
/* in case of malloc error it will not be error as long as request was /* l < 0 */
* handled and we just close the connection. #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);
aisl_client_close(client, AISL_SUCCESS); return AISL_SYSCALL_ERROR;
}
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;
} }
aisl_client_t AislClient
aisl_client_new( aisl_server_t server, aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr)
int fd,
struct sockaddr_in * addr )
{ {
aisl_client_t client; AislClient client;
aisl_stream_t stream; AislStream stream;
if ( (client = calloc(1, sizeof(struct aisl_client))) != NULL ) if ((client = calloc(1, sizeof (struct aisl_client))) != NULL) {
{ DPRINTF("client alocated");
DPRINTF("client alocated"); memcpy(&client->address, addr, sizeof (struct sockaddr_in));
memcpy(&client->address, addr, sizeof(struct sockaddr_in)); client->server = server;
client->server = server; client->fd = fd;
client->fd = fd; client->next_id = 2;
client->next_id = 2; client->http_version = AISL_HTTP_1_0;
client->http_version = AISL_HTTP_1_0; client->timestamp = time(NULL);
client->timestamp = time(NULL); client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) {
{ DPRINTF("client buffer alocated");
DPRINTF("client buffer alocated"); memcpy(&client->out, &client->in, sizeof (struct buffer));
memcpy(&client->out, &client->in, sizeof(struct buffer));
stream = aisl_stream_new(client, 0); stream = aisl_stream_new(client, 0);
if (stream != NULL) if (stream != NULL) {
{ client->stream = stream;
client->stream = stream; DPRINTF("client stream alocated");
DPRINTF("client stream alocated"); #ifndef AISL_WITHOUT_SSL
if (server->ssl) {
SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
#ifdef AISL_WITHOUT_SSL if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) {
SSL_set_fd(client->ssl, fd);
return client; return client;
}
#else } else {
return client;
SSL_CTX * ssl_ctx; }
#else
if ( !server->ssl ) return client;
return client; #endif
}
ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL); }
aisl_client_free(client);
if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) }
{ return NULL;
SSL_set_fd(client->ssl, fd);
return client;
}
#endif
}
}
aisl_client_free(client);
}
return NULL;
} }
void void
aisl_client_free(aisl_client_t client) aisl_client_free(AislClient client)
{ {
aisl_client_close(client, AISL_SUCCESS); aisl_client_close(client, AISL_SUCCESS);
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
if (client->ssl) if (client->ssl)
SSL_free(client->ssl); SSL_free(client->ssl);
#endif #endif
if (client->in.data) if (client->in.data)
free(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);
if (client->stream) free(client);
aisl_stream_free(client->stream);
free(client);
} }
aisl_status_t AislStatus
aisl_client_touch(aisl_client_t client, int32_t timeout) aisl_client_touch(AislClient client, int32_t timeout)
{ {
aisl_status_t result = AISL_IDLE, AislStatus result, status;
status = AISL_IDLE;
/* input */ result = AISL_IDLE;
if (client->flags & FLAG_CAN_READ) status = AISL_IDLE;
{
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */ /* input */
if (client->flags & FLAG_CAN_WRITE) if (client->flags & FLAG_CAN_READ) {
{ if ( (result = aisl_client_input(client)) < 0 )
if ( (status = aisl_client_output(client)) < 0 ) return result;
return status; }
}
/* output */
if (client->flags & FLAG_CAN_WRITE) {
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
/* if (result == AISL_IDLE)
if ((client->http_version==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) && result = status;
(client_input(client)) ) result = true;
*/
/* output */
/*
s = list_index(client->streams, client->ostream);
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) ) if (result != AISL_SUCCESS) {
result = client_output(client); time_t now;
*/ time(&now);
/* update timestamp */
if (result == AISL_IDLE) if (!(now - client->timestamp < timeout)) {
result = status; aisl_client_close(client, result);
}
if (result == AISL_SUCCESS) } else {
client->timestamp = time(NULL); client->timestamp = time(NULL);
else }
{ return result;
time_t now;
time(&now);
if ( !(now - client->timestamp < timeout) )
{
aisl_client_close(client, result);
}
}
return result;
} }
int int
aisl_client_get_socket(aisl_client_t client) aisl_client_get_socket(AislClient client)
{ {
return client->fd; return client->fd;
} }
bool bool
aisl_client_get_keepalive(aisl_client_t client) aisl_client_get_keepalive(AislClient client)
{ {
return (client->flags & FLAG_KEEPALIVE); return (client->flags & FLAG_KEEPALIVE);
} }
void void
aisl_client_set_keepalive(aisl_client_t client, bool value) aisl_client_set_keepalive(AislClient client, bool value)
{ {
if (value) if (value)
client->flags |= FLAG_KEEPALIVE; client->flags |= FLAG_KEEPALIVE;
else else
client->flags &= ~FLAG_KEEPALIVE; client->flags &= ~FLAG_KEEPALIVE;
} }
@ -488,52 +395,52 @@ aisl_client_set_keepalive(aisl_client_t client, bool value)
/* API Level ---------------------------------------------------------------- */ /* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_server_t AislServer
aisl_client_get_server(aisl_client_t client) aisl_client_get_server(AislClient client)
{ {
return client->server; return client->server;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_client_is_secure(aisl_client_t client) aisl_client_is_secure(AislClient client)
{ {
#ifdef AISL_WITHOUT_SSL #ifdef AISL_WITHOUT_SSL
return false; return false;
#else #else
return (client->ssl == NULL) ? false : true; return (client->ssl == NULL) ? false : true;
#endif #endif
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_client_is_online(aisl_client_t client) aisl_client_is_online(AislClient client)
{ {
return (client->fd == -1) ? false : true; return (client->fd == -1) ? false : true;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_client_disconnect(aisl_client_t client) aisl_client_disconnect(AislClient client)
{ {
aisl_client_close(client, AISL_SUCCESS); aisl_client_close(client, AISL_SUCCESS);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_http_version_t AislHttpVersion
aisl_client_get_http_version(aisl_client_t client) aisl_client_get_http_version(AislClient client)
{ {
return client->http_version; return client->http_version;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_client_get_address( aisl_client_t client, struct sockaddr_in * address) aisl_client_get_address(AislClient client, struct sockaddr_in *address)
{ {
memcpy(address, &client->address, sizeof(struct sockaddr_in)); memcpy(address, &client->address, sizeof (struct sockaddr_in));
} }

View File

@ -1,3 +1,17 @@
/******************************************************************************
*
* 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 #ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777 #define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
@ -6,86 +20,83 @@
#include <aisl/client.h> #include <aisl/client.h>
#include "buffer.h" #include "buffer.h"
#define AISL_CLIENT(x) ((aisl_client_t) x) #define AISL_CLIENT(x) ((AislClient) x)
struct aisl_client struct aisl_client {
{ struct sockaddr_in address; /**< Client's address structure. */
struct sockaddr_in address; /**< Client's address structure. */ struct buffer in; /**< Client's input buffer. */
aisl_server_t server; /**< Server instance. */ struct buffer out; /**< Client's output buffer. */
struct buffer in; /**< Client's input buffer. */ AislServer server; /**< Server instance. */
struct buffer out; /**< Client's output buffer. */ AislStream stream; /**< Pending client's stream. */
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
SSL * ssl; /**< SSL pointer for HTTPS. */ SSL *ssl; /**< SSL pointer for HTTPS. */
#endif #endif
time_t timestamp; /**< Last communication timestamp. */ time_t timestamp; /**< Last communication timestamp. */
int next_id; /**< Stream id generator (even). */
aisl_stream_t stream; /**< Pending client's stream. */ int flags; /**< Client's flag bitmask. */
int next_id; /**< Stream id generator (even). */ int fd; /**< Client's socket descriptor. */
int flags; /**< Client's flag bitmask. */ AislHttpVersion http_version; /**< Client's http_version version. */
int fd; /**< Client's socket descriptor. */
aisl_http_version_t http_version; /**< Client's http_version version. */
}; };
/** /**
* @brief Constructor for #aisl_client_t instance. * @brief Constructor for #AislClient instance.
* @param server an #aisl_server_t instance pointer. * @param server an #AislServer instance pointer.
* @param fd a client socket descriptor. * @param fd a client socket descriptor.
* @param addr a pointer to client's address structure. * @param addr a pointer to client's address structure.
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled * @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
*/ */
aisl_client_t AislClient
aisl_client_new( aisl_server_t server, aisl_client_new(AislServer server,
int fd, int fd,
struct sockaddr_in * addr ); struct sockaddr_in *addr );
/** /**
* @brief Destructor for #aisl_client_t instance. * @brief Destructor for #AislClient instance.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
*/ */
void void
aisl_client_free(aisl_client_t client); aisl_client_free(AislClient client);
/** /**
* @brief Does all HTTP client routines. * @brief Does all HTTP client routines.
* Reads and parses requests, writes responses. * Reads and parses requests, writes responses.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @param timeout an allowed client silence time in seconds. * @param timeout an allowed client silence time in seconds.
* @return #aisl_status_t code. * @return #AislStatus code.
*/ */
aisl_status_t AislStatus
aisl_client_touch(aisl_client_t client, int32_t timeout); aisl_client_touch(AislClient client, int32_t timeout);
/** /**
* @Brief Checks if client is about to keep connection alive. * @Brief Checks if client is about to keep connection alive.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return true if keepalive mode is on, otherwise false. * @return true if keepalive mode is on, otherwise false.
*/ */
bool bool
aisl_client_get_keepalive(aisl_client_t client); aisl_client_get_keepalive(AislClient client);
/** /**
* @Brief Sets if connection with client must be kept alive. * @Brief Sets if connection with client must be kept alive.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @param value a true to enable keepalive mode, false to disable. * @param value a true to enable keepalive mode, false to disable.
*/ */
void void
aisl_client_set_keepalive(aisl_client_t client, bool value); aisl_client_set_keepalive(AislClient client, bool value);
/** /**
* @brief Gets socket descriptor associated with #aisl_client_t instance. * @brief Gets socket descriptor associated with #AislClient instance.
* @param client an #aisl_client_t instance pointer. * @param client an #AislClient instance pointer.
* @return a client socket descriptor. * @return a client socket descriptor.
*/ */
int int
aisl_client_get_socket(aisl_client_t client); aisl_client_get_socket(AislClient client);
#endif /* !AISL_CLIENT_H */ #endif /* !AISL_CLIENT_H */

View File

@ -20,319 +20,277 @@
#include "debug.h" #include "debug.h"
#include "http.h" #include "http.h"
struct http_request
static AislHttpMethod
http_method_from_string(const char *method, int32_t length)
{ {
char * method; int i;
char * schema; AislHttpMethod methods[3] = {0, 0, 0};
char * host;
char * port;
char * path;
char * version;
char * newline;
int32_t method_len;
int32_t schema_len;
int32_t host_len;
int32_t port_len;
int32_t path_len;
int32_t version_len;
};
typedef struct http_request * http_request_t; switch(length) {
case 3:
methods[0] = AISL_HTTP_GET;
methods[1] = AISL_HTTP_PUT;
methods[2] = AISL_HTTP_PRI;
break;
static aisl_http_method_t case 4:
http_method_from_string( const char * method, int32_t length ) methods[0] = AISL_HTTP_POST;
{ methods[1] = AISL_HTTP_HEAD;
int i; break;
aisl_http_method_t methods[3] = {0, 0, 0};
switch(length) case 5:
{ methods[0] = AISL_HTTP_TRACE;
case 3: break;
methods[0] = AISL_HTTP_GET;
methods[1] = AISL_HTTP_PUT;
methods[2] = AISL_HTTP_PRI;
break;
case 4: case 6:
methods[0] = AISL_HTTP_POST; methods[0] = AISL_HTTP_DELETE;
methods[1] = AISL_HTTP_HEAD; break;
break;
case 5: case 7:
methods[0] = AISL_HTTP_TRACE; methods[0] = AISL_HTTP_OPTIONS;
break; methods[1] = AISL_HTTP_CONNECT;
break;
}
case 6: for (i=0; i<sizeof (methods)/sizeof (AislHttpMethod); i++) {
methods[0] = AISL_HTTP_DELETE; if (!(methods[i]))
break; break;
case 7: if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
methods[0] = AISL_HTTP_OPTIONS; return methods[i];
methods[1] = AISL_HTTP_CONNECT; }
break;
}
for (i=0; i<sizeof(methods)/sizeof(aisl_http_method_t); i++) return AISL_HTTP_METHOD_UNKNOWN;
{
if (!(methods[i]))
break;
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
return methods[i];
}
return AISL_HTTP_METHOD_UNKNOWN;
} }
static aisl_http_version_t static AislHttpVersion
http_version_from_string(const char * version_string) http_version_from_string(const char *version_string)
{ {
if (strncmp(version_string, "HTTP/", 5)==0) 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], "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.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], "1.1", 3)==0) return AISL_HTTP_1_1; if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0; }
}
return 0; return 0;
} }
/* Library Level */ /* Library Level */
http_parser_t ParserStatus
http_10_parse_request(char * data, int32_t * p_size, aisl_stream_t stream) http_10_parse_request(char *data, int32_t *p_size, AislStream stream)
{ {
/* STEP 1. Split data according to HTTP request format /* STEP 1. Split data according to HTTP request format
* *
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n * GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
* ^ ^ ^ ^ ^ ^ ^ ^ * ^ ^ ^ ^ ^ ^ ^ ^
* | | | | | | | | * | | | | | | | |
* | | | | | | | +--- newline * | | | | | | | +--- newline
* | | | | | | +------------- version * | | | | | | +------------- version
* | | | | | +------------------------- query * | | | | | +------------------------- query
* | | | | +------------------------------------- path * | | | | +------------------------------------- path
* | | | +--------------------------------------- port * | | | +--------------------------------------- port
* | | +----------------------------------------------------- host * | | +----------------------------------------------------- host
* | +------------------------------------------------------------ uri * | +------------------------------------------------------------ uri
* +---------------------------------------------------------------- method * +---------------------------------------------------------------- method
*/ */
char * uri = NULL, char *uri = NULL,
* uri_end = NULL, *uri_end = NULL,
* host = NULL, *host = NULL,
* port = NULL, *port = NULL,
* path = NULL, *path = NULL,
* query = NULL, *query = NULL,
* version = NULL, *version = NULL,
* newline = NULL, *newline = NULL,
* method = data, *method = data,
* method_end = NULL; *method_end = NULL;
aisl_http_method_t http_method; AislHttpMethod http_method;
aisl_http_version_t http_version; AislHttpVersion http_version;
int32_t size = *p_size; int32_t size = *p_size;
while(!newline && size--) while(!newline && size--) {
{ switch(*data)
switch(*data) {
{ case ' ':
case ' ': if (!method_end)
if (!method_end) method_end = data;
method_end = data; else if (path && !uri_end)
else if (path && !uri_end) uri_end = data;
uri_end = data; break;
break;
case ':': case ':':
if (uri && !host) if (uri && !host)
host = data+3; host = data+3;
else if (host && !port) else if (host && !port)
port = data+1; port = data+1;
else if (version) else if (version)
return HTTP_PARSER_ERROR; return HTTP_PARSER_ERROR;
break; break;
case '/': case '/':
if (!path && data > host) if (!path && data > host) {
{ path = data;
path = data; if (!uri)
if (!uri) uri = path;
uri = path; } else if (version && data-version != 4) {
} return HTTP_PARSER_ERROR;
else if (version && data-version != 4) }
return HTTP_PARSER_ERROR; break;
break;
case '?': case '?':
if (!query) if (!query)
query = data+1; query = data+1;
else if (version) else if (version)
return HTTP_PARSER_ERROR; return HTTP_PARSER_ERROR;
break; break;
case '\n': case '\n':
newline = data; newline = data;
break; break;
case '\r': case '\r':
if (!version) if (!version)
return HTTP_PARSER_ERROR; return HTTP_PARSER_ERROR;
break; break;
default: default:
if (!uri && method_end) if (!uri && method_end)
uri = data; uri = data;
else if (!version && uri_end) else if (!version && uri_end)
version = data; version = data;
else if (version && data-version > 7) else if (version && data-version > 7)
return HTTP_PARSER_ERROR; return HTTP_PARSER_ERROR;
}
data++;
}
} /* STEP 2. Verifly splitting was completed */
data++;
}
/* Was request sent? */
if (!newline)
return HTTP_PARSER_HUNGRY;
/* STEP 2. Verifly splitting was completed */ /* Check mandatory parts presence */
if (!method_end || !path || !uri_end || !version)
return HTTP_PARSER_ERROR;
/* Was request sent? */ *method_end = 0;
if (!newline) *newline = 0;
return HTTP_PARSER_HUNGRY; *uri_end = 0;
http_method = http_method_from_string(method, method_end - method);
if (http_method == AISL_HTTP_METHOD_UNKNOWN)
return HTTP_PARSER_ERROR;
/* Check mandatory parts presence */ if ((http_version = http_version_from_string(version))==0)
if (!method_end || !path || !uri_end || !version) return HTTP_PARSER_ERROR;
return HTTP_PARSER_ERROR;
*method_end = 0; if (query) {
*newline = 0; *(query-1)=0;
*uri_end = 0; } else {
query = uri_end;
}
http_method = http_method_from_string(method, method_end - method); if (host) {
if (http_method == AISL_HTTP_METHOD_UNKNOWN) if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8))
return HTTP_PARSER_ERROR; return HTTP_PARSER_ERROR;
if ((http_version = http_version_from_string(version))==0) if (port)
return HTTP_PARSER_ERROR; *(port-1)=0;
}
if (query) stream->client->http_version = http_version;
{ aisl_stream_set_request(stream, http_method, path, query);
*(query-1)=0;
}
else
query = uri_end;
if (host) if (host)
{ aisl_stream_set_header(stream, "host", host);
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) /* how many characters has been read */
return HTTP_PARSER_ERROR; *(p_size)-=size;
return HTTP_PARSER_SUCCESS;
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;
} }
http_parser_t ParserStatus
http_10_parse_header(char * data, int32_t * p_size, aisl_stream_t stream) http_10_parse_header(char *data, int32_t *p_size, AislStream stream)
{ {
int32_t size = *p_size; int32_t size = *p_size;
char * key = data, char *key = data,
* colon = NULL, *colon = NULL,
* val = NULL, *val = NULL,
* val_end = NULL, *val_end = NULL,
* newline = NULL; *newline = NULL;
while(!newline && size-- ) while(!newline && size-- ) {
{ switch(*data) {
switch(*data) case ' ':
{ if (val && !val_end)
case ' ': val_end = data;
if (val && !val_end) break;
val_end = data;
break;
case ':': case ':':
if (!colon) if (!colon) {
{ if (colon == key)
if (colon == key) return HTTP_PARSER_ERROR;
return HTTP_PARSER_ERROR;
colon = data; colon = data;
} }
break; break;
case '\n': case '\n':
newline = data; newline = data;
case '\r': case '\r':
if (!val_end && val) if (!val_end && val)
val_end = data; val_end = data;
break; break;
default: default:
if (!colon) if (!colon) {
{ *data = tolower(*data);
*data = tolower(*data); } else if (!val) {
} if (colon)
else if (!val) val = data;
{ }
if (colon)
val = data;
}
if (val_end) if (val_end)
val_end = NULL; val_end = NULL;
}
data++;
}
} if (!newline)
data++; return HTTP_PARSER_HUNGRY;
}
if (!newline) if (colon && val && val_end) {
return HTTP_PARSER_HUNGRY; *colon = 0;
*val_end = 0;
if (colon && val && val_end) aisl_stream_set_header(stream, key, val);
{ *p_size -= size;
*colon = 0; return HTTP_PARSER_SUCCESS;
*val_end = 0; } else if (newline == key || (newline == key+1 && *key == '\r')) {
return (aisl_stream_set_end_of_headers(stream) == 0) ?
aisl_stream_set_header(stream, key, val); HTTP_PARSER_READY : HTTP_PARSER_SUCCESS;
}
*p_size -= size; return HTTP_PARSER_ERROR;
return HTTP_PARSER_SUCCESS;
}
else if (newline == key || (newline == key+1 && *key == '\r'))
{
return (aisl_stream_set_end_of_headers(stream) == 0) ? HTTP_PARSER_READY :
HTTP_PARSER_SUCCESS;
}
return HTTP_PARSER_ERROR;
} }
http_parser_t ParserStatus
http_10_parse_body(char * data, int32_t * p_size, aisl_stream_t stream) http_10_parse_body(char *data, int32_t *p_size, AislStream stream)
{ {
switch (aisl_stream_set_body(stream, data, *p_size)) switch (aisl_stream_set_body(stream, data, *p_size)) {
{ case 0:
case 0: return HTTP_PARSER_READY; return HTTP_PARSER_READY;
case -1: return HTTP_PARSER_ERROR; case -1:
default: return HTTP_PARSER_SUCCESS; return HTTP_PARSER_ERROR;
} default:
return HTTP_PARSER_SUCCESS;
}
} }
@ -340,102 +298,94 @@ http_10_parse_body(char * data, int32_t * p_size, aisl_stream_t stream)
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
const char * const char *
aisl_http_version_to_string(aisl_http_version_t version) aisl_http_version_to_string(AislHttpVersion version)
{ {
switch (version) switch (version) {
{ case AISL_HTTP_0_9:
case AISL_HTTP_0_9: return "HTTP/0.9"; return "HTTP/0.9";
case AISL_HTTP_1_0: return "HTTP/1.0"; case AISL_HTTP_1_0:
case AISL_HTTP_1_1: return "HTTP/1.1"; return "HTTP/1.0";
case AISL_HTTP_2_0: return "HTTP/2.0"; case AISL_HTTP_1_1:
} return "HTTP/1.1";
return ""; case AISL_HTTP_2_0:
return "HTTP/2.0";
}
return "";
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
const char * const char *
aisl_http_response_to_string(aisl_http_response_t code) aisl_http_response_to_string(AislHttpResponse code)
{ {
switch (code) switch (code) {
{ /* most common for faster behavior */
/* most common for faster behavior */ case AISL_HTTP_OK: return "OK";
case AISL_HTTP_OK: return "OK"; case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; /* informational */
case AISL_HTTP_CONTINUE: return "Continue";
/* informational */ case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
case AISL_HTTP_CONTINUE: return "Continue"; /* Successful */
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols"; case AISL_HTTP_CREATED: return "Created";
/* Successful */ case AISL_HTTP_ACCEPTED: return "Accepted";
case AISL_HTTP_CREATED: return "Created"; case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
case AISL_HTTP_ACCEPTED: return "Accepted"; case AISL_HTTP_NO_CONTENT: return "No Content";
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information"; case AISL_HTTP_RESET_CONTENT: return "Reset Content";
case AISL_HTTP_NO_CONTENT: return "No Content"; case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
case AISL_HTTP_RESET_CONTENT: return "Reset Content"; /* redirection */
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content"; case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
/* redirection */ case AISL_HTTP_FOUND: return "Found";
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices"; case AISL_HTTP_SEE_OTHER: return "See other";
case AISL_HTTP_FOUND: return "Found"; case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
case AISL_HTTP_SEE_OTHER: return "See other"; case AISL_HTTP_USE_PROXY: return "Use Proxy";
case AISL_HTTP_NOT_MODIFIED: return "Not Modified"; case AISL_HTTP_UNUSED: return "(unused)";
case AISL_HTTP_USE_PROXY: return "Use Proxy"; case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
case AISL_HTTP_UNUSED: return "(unused)"; /* client error */
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect"; case AISL_HTTP_BAD_REQUEST: return "Bad Request";
/* client error */ case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
case AISL_HTTP_BAD_REQUEST: return "Bad Request"; case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized"; case AISL_HTTP_FORBIDDEN: return "Forbidden";
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required"; case AISL_HTTP_NOT_FOUND: return "Not Found";
case AISL_HTTP_FORBIDDEN: return "Forbidden"; case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
case AISL_HTTP_NOT_FOUND: return "Not Found"; case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed"; case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable"; case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required"; case AISL_HTTP_CONFLICT: return "Conflict";
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout"; case AISL_HTTP_GONE: return "Gone";
case AISL_HTTP_CONFLICT: return "Conflict"; case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
case AISL_HTTP_GONE: return "Gone"; case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required"; case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed"; case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large"; case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long"; case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable"; /* server error */
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed"; case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
/* server error */ case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented"; case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway"; case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable"; case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout"; }
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported"; return "";
}
return "";
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
const char * const char *
aisl_http_method_to_string( aisl_http_method_t method ) aisl_http_method_to_string( AislHttpMethod method )
{ {
switch(method) switch(method) {
{ case AISL_HTTP_GET: return "GET";
case AISL_HTTP_GET: return "GET"; case AISL_HTTP_PUT: return "PUT";
case AISL_HTTP_PUT: return "PUT"; case AISL_HTTP_POST: return "POST";
case AISL_HTTP_POST: return "POST"; case AISL_HTTP_HEAD: return "HEAD";
case AISL_HTTP_HEAD: return "HEAD"; case AISL_HTTP_TRACE: return "TRACE";
case AISL_HTTP_TRACE: return "TRACE"; case AISL_HTTP_DELETE: return "DELETE";
case AISL_HTTP_DELETE: return "DELETE"; case AISL_HTTP_OPTIONS: return "OPTIONS";
case AISL_HTTP_OPTIONS: return "OPTIONS"; case AISL_HTTP_CONNECT: return "CONNECT";
case AISL_HTTP_CONNECT: return "CONNECT"; case AISL_HTTP_PRI: return "PRI";
case AISL_HTTP_METHOD_UNKNOWN: break;
case AISL_HTTP_PRI: return "PRI"; }
return "";
case AISL_HTTP_METHOD_UNKNOWN: break;
}
return "";
} }

View File

@ -18,26 +18,24 @@
#include <aisl/types.h> #include <aisl/types.h>
typedef enum typedef enum {
{ HTTP_PARSER_SUCCESS
HTTP_PARSER_SUCCESS , HTTP_PARSER_READY
, HTTP_PARSER_READY , HTTP_PARSER_HUNGRY
, HTTP_PARSER_HUNGRY , HTTP_PARSER_ERROR
, HTTP_PARSER_ERROR } ParserStatus;
} http_parser_t;
http_parser_t ParserStatus
http_10_parse_request(char * data, int32_t * size, aisl_stream_t stream); http_10_parse_request(char *data, int32_t *size, AislStream stream);
http_parser_t ParserStatus
http_10_parse_header(char * data, int32_t * size, aisl_stream_t stream); http_10_parse_header(char *data, int32_t *size, AislStream stream);
http_parser_t ParserStatus
http_10_parse_body(char * data, int32_t * size, aisl_stream_t stream); http_10_parse_body(char *data, int32_t *size, AislStream stream);
#endif /* !AISL_HTTP_H */ #endif /* !AISL_HTTP_H */

View File

@ -22,10 +22,8 @@
#include "debug.h" #include "debug.h"
#include "str-utils.h" #include "str-utils.h"
#include "buffer.h" #include "buffer.h"
#include "client.h" #include "client.h"
#include "server.h" #include "server.h"
//#include "globals.h"
#include "stream.h" #include "stream.h"
#include "instance.h" #include "instance.h"
@ -34,42 +32,35 @@
static uint32_t m_instances = 0; static uint32_t m_instances = 0;
static struct aisl_ssl *
static const aisl_ssl_t aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl)
aisl_new_ssl( aisl_t instance, const aisl_cfg_ssl_t cfg_ssl)
{ {
SSL_CTX * ssl_ctx = NULL; SSL_CTX *ssl_ctx = NULL;
aisl_ssl_t * list = instance->ssl, struct aisl_ssl **list, *ssl;
ssl;
/* lookup for existing contexts */ list = instance->ssl;
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++; /* 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, ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file,
cfg_ssl->key_file, ssl_ctx);
cfg_ssl->crt_file,
ssl_ctx );
if (ssl) if (ssl) {
{ if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) {
if ( !ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) aisl_ssl_free(ssl);
{ ssl = NULL;
aisl_ssl_free(ssl); }
ssl = NULL; }
} return ssl;
}
return ssl;
} }
#endif #endif
@ -78,265 +69,233 @@ aisl_new_ssl( aisl_t instance, const aisl_cfg_ssl_t cfg_ssl)
/* Initialization functions */ /* Initialization functions */
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_t AislInstance
aisl_new( aisl_cfg_t cfg ) aisl_new(const struct aisl_cfg *cfg)
{ {
aisl_t instance; int i;
AislInstance instance;
/* allocate root structure */ /* allocate root structure */
if ( !(instance = calloc(1, sizeof(struct aisl))) ) if (!(instance = calloc(1, sizeof (struct aisl_instance))))
goto finally; goto finally;
/* allocate servers */ /* allocate servers */
if ( !(instance->srv = calloc(cfg->srv_cnt+1, sizeof(aisl_server_t))) ) if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer))))
goto release; goto release;
for (int i=0; i<cfg->srv_cnt; i++) for (i = 0; i < cfg->srv_cnt; i++) {
{ DPRINTF("new srv %d", i);
DPRINTF("new srv %d", i); if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance))) goto release;
goto release; }
}
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
if ((m_instances++) == 0) if ((m_instances++) == 0) {
{ SSL_load_error_strings();
SSL_load_error_strings(); OpenSSL_add_ssl_algorithms();
OpenSSL_add_ssl_algorithms(); }
}
if ( !(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof(aisl_ssl_t))) ) if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl))))
goto release; goto release;
for (int i=0; i<cfg->ssl_cnt; i++) for (i=0; i<cfg->ssl_cnt; i++) {
{ DPRINTF("new ssl %d", i);
DPRINTF("new ssl %d", i); if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i]))) goto release;
goto release; }
} #endif
#endif
if ( list_init(&instance->client_spool, cfg->client_spool_size) == -1 ) if (list_init(&instance->client_spool, cfg->client_spool_size) == -1)
goto release; goto release;
instance->accept_limit = cfg->client_accept_limit; instance->accept_limit = cfg->client_accept_limit;
instance->silence_timeout = cfg->client_silence_timeout; instance->silence_timeout = cfg->client_silence_timeout;
instance->callback = cfg->callback; instance->callback = cfg->callback;
instance->p_ctx = cfg->p_ctx; instance->p_ctx = cfg->p_ctx;
goto finally; goto finally;
release: release:
aisl_free(instance); aisl_free(instance);
instance = NULL; instance = NULL;
finally: finally:
return instance; return instance;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_free( aisl_t instance ) aisl_free(AislInstance instance)
{ {
if (instance->srv) if (instance->srv) {
{ AislServer * srv = instance->srv;
aisl_server_t * srv = instance->srv;
while (*srv) while (*srv) {
{ aisl_server_free(*(srv++));
aisl_server_free(*srv); }
srv++;
}
free(instance->srv); free(instance->srv);
} }
list_release(&instance->client_spool, (list_destructor_t) aisl_client_free ); list_release(&instance->client_spool, (list_destructor_t)aisl_client_free);
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
if (instance->ssl) if (instance->ssl) {
{ struct aisl_ssl **ssl = instance->ssl;
aisl_ssl_t * ssl = instance->ssl;
while (*ssl) while (*ssl) {
{ aisl_ssl_free(*(ssl++));
aisl_ssl_free(*ssl); }
ssl++; free(instance->ssl);
} }
free(instance->ssl); if ((--m_instances) == 0) {
} EVP_cleanup();
}
#endif
if ((--m_instances) == 0) free(instance);
{
EVP_cleanup();
}
#endif
free(instance);
} }
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
SSL_CTX * SSL_CTX *
aisl_get_ssl_ctx( aisl_t instance, const char * host ) aisl_get_ssl_ctx(AislInstance instance, const char * host)
{ {
aisl_ssl_t * list = instance->ssl, struct aisl_ssl **list, *ssl;
ssl;
if (host) list = instance->ssl;
{
while ( (ssl = *list) )
{
if (str_cmpi(ssl->host, host) == 0)
{
return ssl->ctx;
}
list++; if (host) {
} while ((ssl = *list)) {
} if (str_cmpi(ssl->host, host) == 0) {
return ssl->ctx;
return NULL; }
list++;
}
}
return NULL;
} }
#endif #endif
void void
aisl_raise_evt( aisl_t instance, aisl_evt_t const evt ) aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt)
{ {
#ifdef AISL_WITHOUT_STRINGIFIERS #ifdef AISL_WITHOUT_STRINGIFIERS
DPRINTF("! %d", evt->code); DPRINTF("! %d", evt->code);
#else #else
DPRINTF("! %s", aisl_evt_code_to_string(evt->code)); DPRINTF("! %s", aisl_event_to_string(evt->code));
#endif #endif
if (instance->callback) if (instance->callback)
instance->callback(evt, instance->p_ctx); instance->callback(evt, instance->p_ctx);
} }
void void
aisl_raise( aisl_t instance, aisl_raise(AislInstance instance,
void * source, void *source,
aisl_evt_code_t code, AislEvent code,
aisl_status_t status ) AislStatus status)
{ {
struct aisl_evt evt; struct aisl_evt evt;
evt.source = source; evt.source = source;
evt.code = code; evt.code = code;
evt.status = status; evt.status = status;
aisl_raise_evt(instance, &evt); aisl_raise_evt(instance, &evt);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_status_t AislStatus
aisl_run_cycle( aisl_t instance ) aisl_run_cycle(AislInstance instance)
{ {
aisl_status_t result = AISL_IDLE; AislStatus result = AISL_IDLE;
AislServer *list, srv;
AislClient cli;
int32_t i;
aisl_server_t * list = instance->srv, list = instance->srv;
srv;
aisl_client_t cli;
while ( (srv = *list) ) while ((srv = *list)) {
{ cli = NULL;
cli = NULL;
if (aisl_server_touch(srv, &cli) != AISL_IDLE) if (aisl_server_touch(srv, &cli) != AISL_IDLE)
result = AISL_SUCCESS; result = AISL_SUCCESS;
if (cli) if (cli) {
{ DPRINTF("Accepted %p", (void*)cli);
DPRINTF("Accepted %p", (void*)cli); if (list_append(&instance->client_spool, cli) == -1)
if ( list_append(&instance->client_spool, cli) == -1 ) aisl_client_free(cli);
aisl_client_free(cli); }
} list++;
}
list++; for (i=0; i < instance->client_spool.count; i++) {
} cli = LIST_INDEX(instance->client_spool, i);
for (int32_t i=0; i < instance->client_spool.count; i++) if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
{ result = AISL_SUCCESS;
cli = LIST_INDEX(instance->client_spool, i);
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE) if (!aisl_client_is_online(cli)) {
result = AISL_SUCCESS; aisl_client_free( cli );
list_remove_index(&instance->client_spool, i);
/* if (aisl_client_is_timed_out( c, instance->silence_timeout ) ) }
aisl_raise( instance, c, AISL_EVENT_CLIENT_TIMEOUT ); }
*/ return result;
if ( !aisl_client_is_online(cli) )
{
aisl_client_free( cli );
list_remove_index(&instance->client_spool, i);
}
}
return result;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_status_t AislStatus
aisl_sleep( aisl_t instance, uint32_t usec ) aisl_sleep(AislInstance instance, uint32_t usec)
{ {
int maxfd=0, AislServer *list, srv;
sd; int sd, maxfd = 0;
size_t i; size_t i;
struct timeval timeout = {0,usec}; struct timeval timeout = {0,usec};
memset(&timeout, 0, sizeof(struct timeval)); memset(&timeout, 0, sizeof (struct timeval));
timeout.tv_usec = usec; timeout.tv_usec = usec;
fd_set fs; fd_set fs;
FD_ZERO (&fs); FD_ZERO (&fs);
aisl_server_t * list = instance->srv, list = instance->srv;
srv;
while ( (srv = *list) ) while ((srv = *list)) {
{ sd = aisl_server_get_socket(srv);
sd = aisl_server_get_socket(srv);
if (sd != -1) if (sd != -1) {
{ FD_SET(sd, &fs);
FD_SET(sd, &fs); if (sd > maxfd) maxfd = sd;
if (sd > maxfd) maxfd = sd; }
} list++;
}
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;
}
}
for (i=0; i<instance->client_spool.count; i++) switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) {
{ case -1:
aisl_client_t c = LIST_INDEX(instance->client_spool, i); return AISL_SYSCALL_ERROR;
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 0:
{ return AISL_IDLE;
case -1:
return AISL_SYSCALL_ERROR;
case 0: default:
return AISL_IDLE; return AISL_SUCCESS;
}
default:
return AISL_SUCCESS;
}
} }

View File

@ -1,10 +1,16 @@
/* /******************************************************************************
* src/instance.h
* *
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com> * Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
* *
* Project homepage: https://lowenware.com/aisl/ ******************************************************************************/
/**
* @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 #ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
@ -19,47 +25,46 @@
#include "list.h" #include "list.h"
struct aisl struct aisl_instance {
{ AislServer *srv;
aisl_server_t * srv; #ifndef AISL_WITHOUT_SSL
#ifndef AISL_WITHOUT_SSL struct aisl_ssl * *ssl;
aisl_ssl_t * ssl; #endif
#endif struct list client_spool;
struct list client_spool; AislCallback callback;
aisl_callback_t callback; void *p_ctx;
void * p_ctx;
uint32_t accept_limit; uint32_t accept_limit;
uint32_t silence_timeout; uint32_t silence_timeout;
uint32_t buffer_size; uint32_t buffer_size;
}; };
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
/** /**
* @brief Gets SSL context for appropriate server name. * @brief Gets SSL context for appropriate server name.
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
* @param server_name a null-terminated string with server name or NULL. * @param server_name a null-terminated string with server name or NULL.
* @return a pointer to SSL context * @return a pointer to SSL context
*/ */
SSL_CTX * SSL_CTX *
aisl_get_ssl_ctx( aisl_t instance, const char * server_name ); aisl_get_ssl_ctx(AislInstance instance, const char *server_name);
#endif #endif
/** /**
* @brief Raises event from source. * @brief Raises event from source.
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
* @param evt a pointer to event structure. * @param evt a pointer to event structure.
*/ */
void void
aisl_raise_evt( aisl_t instance, aisl_evt_t const evt ); aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt);
void void
aisl_raise( aisl_t instance, aisl_raise(AislInstance instance,
void * source, void *source,
aisl_evt_code_t code, AislEvent code,
aisl_status_t status ); AislStatus status);
#endif /* !AISL_INSTANCE_H */ #endif /* !AISL_INSTANCE_H */

View File

@ -18,87 +18,80 @@
#include "list.h" #include "list.h"
int32_t int32_t
list_init(list_t list, int32_t size) list_init(struct list *list, int32_t size)
{ {
if ((list->data = calloc(size, sizeof(void*))) != NULL) if ((list->data = calloc(size, sizeof (void*))) != NULL) {
{ list->size = size;
list->size = size; list->count = 0;
list->count = 0; return 0;
return 0; }
} return -1;
return -1;
} }
void void
list_release(list_t list, list_destructor_t destructor) list_release(struct list *list, list_destructor_t destructor)
{ {
if (list->data) if (list->data) {
{ if (destructor) {
if (destructor) int32_t i;
{
int32_t i;
for (i=0; i<list->count; i++)
{
void * ptr;
if ( (ptr = list->data[i]) != NULL) for (i=0; i<list->count; i++) {
destructor( list->data[i] ); void *ptr;
}
} if ((ptr = list->data[i]) != NULL)
free(list->data); destructor(list->data[i]);
} }
}
free(list->data);
}
} }
int32_t int32_t
list_append(list_t list, void * entry) list_append(struct list *list, void *entry)
{ {
int32_t pos = list->count; int32_t pos = list->count;
DPRINTF("pos = %d", pos); DPRINTF("pos = %d", pos);
if ( !(pos < list->size) ) if (!(pos < list->size)) {
{ DPRINTF("extending, size = %d", list->size);
DPRINTF("extending, size = %d", list->size); void **new_list;
void ** new_list; int32_t new_size = pos + 1;
int32_t new_size = pos + 1;
DPRINTF("extending, ptr = %p", (void*)list->data); DPRINTF("extending, ptr = %p", (void*)list->data);
if ( (new_list = realloc( list->data, new_size*sizeof(void*) )) == NULL ) if ((new_list = realloc( list->data, new_size*sizeof (void*) )) == NULL)
return -1; return -1;
list->data = new_list; list->data = new_list;
list->size = new_size; list->size = new_size;
} }
list->data[pos]=entry; list->data[pos]=entry;
list->count++; list->count++;
return pos; return pos;
} }
void * void *
list_remove_index(list_t list, int32_t index) list_remove_index(struct list *list, int32_t index)
{ {
void * result; void *result;
if (index < list->count) if (index < list->count) {
{ int32_t i, c = --list->count;
int32_t i, c = --list->count;
result = list->data[index]; result = list->data[index];
for (i=index; i<c; i++) for (i = index; i<c; i++) {
{ list->data[i]=list->data[i+1];
list->data[i]=list->data[i+1]; }
} }
} else
else result = NULL;
result = NULL;
return result; return result;
} }

View File

@ -6,11 +6,11 @@
******************************************************************************/ ******************************************************************************/
/** /**
* @file list.h * @file src/list.h
* @author Ilja Kartašov <ik@lowenware.com> * @author Ilja Kartašov <ik@lowenware.com>
* @brief List module header file * @brief List module header file
* *
* @see https://lowenware.com/ * @see https://lowenware.com/aisl/
*/ */
#ifndef AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1 #ifndef AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1
@ -21,34 +21,31 @@
#define LIST_INDEX(L, I) ( L.data[ I ] ) #define LIST_INDEX(L, I) ( L.data[ I ] )
struct list struct list {
{ void **data;
void ** data; int32_t size;
int32_t size; int32_t count;
int32_t count;
}; };
typedef struct list * list_t;
typedef void typedef void
(* list_destructor_t)(void * list_item); (* list_destructor_t)(void *list_item);
int32_t int32_t
list_init(list_t list, int32_t size); list_init(struct list *lst, int32_t size);
void void
list_release(list_t list, list_destructor_t destructor); list_release(struct list *lst, list_destructor_t destructor);
int32_t int32_t
list_append(list_t list, void * entry); list_append(struct list *lst, void * entry);
void * void *
list_remove_index(list_t list, int32_t index); list_remove_index(struct list *lst, int32_t index);
#endif /* !AISL_LIST_H */ #endif /* !AISL_LIST_H */

View File

@ -6,15 +6,12 @@
#include <unistd.h> #include <unistd.h>
#include <netdb.h> #include <netdb.h>
#include <fcntl.h> #include <fcntl.h>
#include <openssl/ssl.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/ioctl.h> #include <sys/ioctl.h>
#endif #endif
#include <openssl/ssl.h>
#include "debug.h" #include "debug.h"
#include "str-utils.h" #include "str-utils.h"
#include "instance.h" #include "instance.h"
@ -24,151 +21,128 @@
/** /**
* @brief Creates TCP server socket, binds to address and starts to listen. * @brief Creates TCP server socket, binds to address and starts to listen.
* @param server a pointer to #aisl_server_t instance. * @param server a pointer to #AislServer instance.
* @return #aisl_status_t code. * @return #AislStatus code.
*/ */
static aisl_status_t static AislStatus
aisl_server_open(aisl_server_t server) aisl_server_open(AislServer server)
{ {
int fd, s_opt = 1; int fd, s_opt = 1;
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd != -1) if (fd != -1) {
{ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt,
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int)); sizeof (int));
#ifdef __APPLE__ #ifdef __APPLE__
ioctl(fd, FIONBIO, (char *)&s_opt); ioctl(fd, FIONBIO, (char *)&s_opt);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif #endif
s_opt = sizeof(struct sockaddr_in); s_opt = sizeof (struct sockaddr_in);
if (bind(fd, (struct sockaddr *) &server->address, s_opt)==0) if (bind(fd, (struct sockaddr *) &server->address, s_opt) == 0) {
{ if (listen(fd, SOMAXCONN) == 0) {
if (listen(fd, SOMAXCONN) == 0) server->fd = fd;
{ return AISL_SUCCESS;
server->fd = fd; }
return AISL_SUCCESS; }
} close(fd);
} }
close(fd); return AISL_SYSCALL_ERROR;
}
return AISL_SYSCALL_ERROR;
} }
/** /**
* @brief Tries to accept a new client. * @brief Tries to accept a new client.
* @param server a pointer to #aisl_server_t instance. * @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #aisl_client_t instance pointer. * @param p_client a pointer to store #AislClient instance pointer.
* @return #aisl_status_t code. * @return #AislStatus code.
*/ */
static aisl_status_t static AislStatus
aisl_server_accept( aisl_server_t server, aisl_server_accept(AislServer server, AislClient *p_client )
aisl_client_t * p_client )
{ {
int fd; int fd;
struct sockaddr_in addr; struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in); socklen_t len = sizeof (struct sockaddr_in);
fd = accept(server->fd, (struct sockaddr *) &addr, &len); fd = accept(server->fd, (struct sockaddr *)&addr, &len);
if (fd != -1) if (fd != -1) {
{ int flags;
int flags;
DPRINTF("accepted fd=%d", fd); DPRINTF("accepted fd=%d", fd);
if ((flags = fcntl(fd, F_GETFL, 0)) != -1) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) {
{ flags |= O_NONBLOCK;
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == 0) if (fcntl(fd, F_SETFL, flags) == 0) {
{ return (!(*p_client = aisl_client_new(server, fd, &addr))) ?
return (!(*p_client = aisl_client_new(server, fd, &addr))) ? AISL_MALLOC_ERROR : AISL_SUCCESS;
AISL_MALLOC_ERROR : AISL_SUCCESS; }
} }
} close(fd);
close(fd); } else if (errno == EWOULDBLOCK) {
} return AISL_IDLE;
else if (errno == EWOULDBLOCK) }
return AISL_IDLE; return AISL_SYSCALL_ERROR;
return AISL_SYSCALL_ERROR;
} }
/* Library Level ------------------------------------------------------------ */ /* Library Level ------------------------------------------------------------ */
aisl_status_t AislStatus
aisl_server_touch( aisl_server_t server, aisl_server_touch(AislServer server, AislClient *p_client)
aisl_client_t * p_client )
{ {
aisl_status_t result; AislStatus result;
if (server->fd == -1) if (server->fd == -1) {
{ if ((result = aisl_server_open(server)) != AISL_IDLE) {
aisl_raise(server->instance, server, ((result == AISL_SUCCESS) ?
if ((result = aisl_server_open(server)) != AISL_IDLE) AISL_EVENT_SERVER_READY : AISL_EVENT_SERVER_ERROR), result);
{ }
aisl_raise( } else {
server->instance, result = aisl_server_accept(server, p_client);
server, }
((result == AISL_SUCCESS) ? AISL_EVENT_SERVER_OPEN return result;
: AISL_EVENT_SERVER_ERROR),
result
);
}
}
else
result = aisl_server_accept(server, p_client);
return result;
} }
int int
aisl_server_get_socket( aisl_server_t server ) aisl_server_get_socket(AislServer server)
{ {
return server->fd; return server->fd;
} }
aisl_server_t AislServer
aisl_server_new(aisl_cfg_srv_t const cfg_srv, aisl_t instance) aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance)
{ {
aisl_server_t server; AislServer server;
if ( (server = calloc(1, sizeof(struct aisl_server))) != NULL ) if ((server = calloc(1, sizeof (struct aisl_server))) != NULL) {
{ server->instance = instance;
server->instance = instance; server->fd = -1;
server->fd = -1; server->address.sin_family = AF_INET;
server->address.sin_family = AF_INET; server->address.sin_addr.s_addr = inet_addr(cfg_srv->host);
server->address.sin_addr.s_addr = inet_addr(cfg_srv->host); server->address.sin_port = htons(cfg_srv->port);
server->address.sin_port = htons(cfg_srv->port); server->ssl = cfg_srv->secure;
server->ssl = cfg_srv->secure; }
} return server;
return server;
} }
void void
aisl_server_free(aisl_server_t server) aisl_server_free(AislServer server)
{ {
if (server) if (server) {
{ if ( server->fd != -1) {
if ( server->fd != -1) close(server->fd);
{ server->fd=-1;
close(server->fd); }
server->fd=-1; free(server);
} }
free(server);
}
} }
@ -177,9 +151,9 @@ aisl_server_free(aisl_server_t server)
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_server_get_address( aisl_server_t server, struct sockaddr_in * address) aisl_server_get_address(AislServer server, struct sockaddr_in *address)
{ {
memcpy(address, &server->address, sizeof(struct sockaddr_in)); memcpy(address, &server->address, sizeof (struct sockaddr_in));
} }
@ -187,9 +161,9 @@ aisl_server_get_address( aisl_server_t server, struct sockaddr_in * address)
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_server_get_ssl( aisl_server_t server ) aisl_server_get_ssl(AislServer server)
{ {
return server->ssl; return server->ssl;
} }
#endif #endif

View File

@ -1,4 +1,17 @@
/* /******************************************************************************
*
* 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 * @file src/server.h
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd.
@ -13,60 +26,58 @@
#include <aisl/config.h> #include <aisl/config.h>
#include <aisl/server.h> #include <aisl/server.h>
#define AISL_SERVER(x) ((aisl_server_t) x) #define AISL_SERVER(x) ((AislServer) x)
/** /**
* @brief HTTP(s) server data structure represented by #aisl_server_t pointer. * @brief HTTP(s) server data structure represented by #AislServer pointer.
*/ */
struct aisl_server struct aisl_server {
{ struct sockaddr_in address; /**< TCP server address to listen to. */
struct sockaddr_in address; /**< TCP server address to listen to. */ AislInstance instance; /**< Associated AISL instance pointer. */
aisl_t instance; /**< Associated AISL instance pointer. */ int fd; /**< System socket descriptor. */
int fd; /**< System socket descriptor. */ bool ssl; /**< SSL enabled/disabled flag. */
bool ssl; /**< SSL enabled/disabled flag. */
}; };
/** /**
* @brief Allocates and instance of #aisl_server_t. * @brief Allocates and instance of #AislServer.
* @param cfg_srv a pointer to server configuration structure. * @param cfg_srv a pointer to server configuration structure.
* @param instance a pointer to #aisl_t instance. * @param instance a pointer to #AislInstance instance.
* @return a pointer to #aisl_server_t instance. * @return a pointer to #AislServer instance.
*/ */
aisl_server_t AislServer
aisl_server_new(aisl_cfg_srv_t const cfg_srv, aisl_t instance); aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance);
/** /**
* @brief Frees memory allocated for #aisl_server_t instance. * @brief Frees memory allocated for #AislServer instance.
* @param server a pointer to #aisl_server_t instance. * @param server a pointer to #AislServer instance.
*/ */
void void
aisl_server_free(aisl_server_t server); aisl_server_free(AislServer server);
/** /**
* @brief Does server routines. * @brief Does server routines.
* Tries to open server if it was not opened yet, otherwise tries to accept a * Tries to open server if it was not opened yet, otherwise tries to accept a
* new client connecting to the server. * new client connecting to the server.
* @param server a pointer to #aisl_server_t instance. * @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #aisl_client_t instance pointer. * @param p_client a pointer to store #AislClient instance pointer.
* @return #aisl_status_t code: * @return #AislStatus code:
* - AISL_SUCCESS if client connected, * - AISL_SUCCESS if client connected,
* - AISL_IDLE if there is no client to connect, * - AISL_IDLE if there is no client to connect,
* - AISL_SYSCALL_ERROR if error occured. * - AISL_SYSCALL_ERROR if error occured.
*/ */
aisl_status_t AislStatus
aisl_server_touch( aisl_server_t server, aisl_server_touch(AislServer server, AislClient *p_client);
aisl_client_t * p_client );
/** /**
* @brief Gets a socket descriptor associated with HTTP client. * @brief Gets a socket descriptor associated with HTTP client.
* @param server a pointer to #aisl_server_t instance. * @param server a pointer to #AislServer instance.
* @return a client socket descriptor. * @return a client socket descriptor.
*/ */
int int
aisl_server_get_socket(aisl_server_t server); aisl_server_get_socket(AislServer server);
#endif /* !AISL_SERVER_H */ #endif /* !AISL_SERVER_H */

123
src/ssl.c
View File

@ -15,103 +15,88 @@
static int static int
aisl_ssl_on_get_ctx( SSL * ssl, int * ptr, void * instance ) aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance )
{ {
const char * server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name);
SSL_CTX * ctx = aisl_get_ssl_ctx( (aisl_t) instance, server_name ); if (ctx)
SSL_set_SSL_CTX(ssl, ctx);
if (ctx) (void)ptr;
{
SSL_set_SSL_CTX(ssl, ctx);
}
(void)ptr; return SSL_TLSEXT_ERR_OK;
return SSL_TLSEXT_ERR_OK;
} }
SSL_CTX * SSL_CTX *
aisl_ssl_get_ctx(aisl_ssl_t ssl, void * p_instance) aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance)
{ {
SSL_CTX * ctx; SSL_CTX * ctx;
if ( (ctx = SSL_CTX_new(SSLv23_server_method())) != NULL ) if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) {
{ SSL_CTX_set_ecdh_auto(ctx, 1);
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 );
SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx ); if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0))
SSL_CTX_set_tlsext_servername_arg( ctx, p_instance ); goto except;
if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0)) if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0))
goto except; goto except;
if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0)) ssl->ctx = ctx;
goto except;
ssl->ctx = ctx; return ctx;
}
return ctx;
}
except: except:
SSL_CTX_free(ctx); SSL_CTX_free(ctx);
return NULL; return NULL;
} }
aisl_ssl_t struct aisl_ssl *
aisl_ssl_new( const char * key_file, aisl_ssl_new(const char *key_file,
const char * crt_file, const char *crt_file,
const char * host, const char *host,
SSL_CTX * ctx ) SSL_CTX *ctx)
{ {
aisl_ssl_t ssl; struct aisl_ssl *ssl;
if ((ssl = calloc(1, sizeof(struct aisl_ssl))) != NULL) if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) {
{ if ((ssl->host = str_copy( host ? host : "*" )) != NULL) {
if ((ssl->host = str_copy( host ? host : "*" )) != NULL) if (ctx) {
{ ssl->ctx = ctx;
if (ctx) return ssl;
{ } else {
ssl->ctx = ctx; if ((ssl->key_file = str_copy(key_file)) != NULL) {
return ssl; if ((ssl->crt_file = str_copy(crt_file)) != NULL) {
} return ssl;
else }
{ }
if ((ssl->key_file = str_copy(key_file)) != NULL) }
{ }
if ((ssl->crt_file = str_copy(crt_file)) != NULL) aisl_ssl_free(ssl);
{ }
return ssl; return NULL;
}
}
}
}
aisl_ssl_free(ssl);
}
return NULL;
} }
void void
aisl_ssl_free( aisl_ssl_t ssl ) aisl_ssl_free( struct aisl_ssl *ssl )
{ {
if (ssl->host) if (ssl->host)
free(ssl->host); free(ssl->host);
if (ssl->key_file) if (ssl->key_file) {
{ free(ssl->key_file);
free(ssl->key_file); SSL_CTX_free(ssl->ctx);
SSL_CTX_free(ssl->ctx); }
}
if (ssl->crt_file) if (ssl->crt_file)
free(ssl->crt_file); free(ssl->crt_file);
free(ssl); free(ssl);
} }
#endif #endif

View File

@ -1,4 +1,17 @@
/* /******************************************************************************
*
* 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 * src/ssl.h
* *
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com> * Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
@ -15,30 +28,27 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
struct aisl_ssl struct aisl_ssl {
{ char *key_file;
char * key_file; char *crt_file;
char * crt_file; char *host;
char * host; SSL_CTX *ctx;
SSL_CTX * ctx;
}; };
typedef struct aisl_ssl * aisl_ssl_t;
struct aisl_ssl *
aisl_ssl_t aisl_ssl_new( const char *key_file,
aisl_ssl_new( const char * key_file, const char *crt_file,
const char * crt_file, const char *host,
const char * host, SSL_CTX *ctx );
SSL_CTX * ctx );
SSL_CTX * SSL_CTX *
aisl_ssl_get_ctx(aisl_ssl_t ssl, void * p_instance); aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance);
void void
aisl_ssl_free( aisl_ssl_t ssl ); aisl_ssl_free(struct aisl_ssl *ssl);
#endif /* !AISL_SSL_H */ #endif /* !AISL_SSL_H */

View File

@ -20,34 +20,33 @@
char * char *
str_copy(const char * source) str_copy(const char *source)
{ {
int l = strlen(source); int l = strlen(source);
char * result = malloc(l+1); char *result = malloc(l+1);
if (result) if (result)
strcpy(result, source); strcpy(result, source);
return result; return result;
} }
int int
str_cmpi(const char * s1, const char * s2) str_cmpi(const char *s1, const char *s2)
{ {
char c1, c2, r = 0; char c1, c2, r = 0;
do do {
{ c1 = tolower(*(s1++));
c1 = tolower(*(s1++)); c2 = tolower(*(s2++));
c2 = tolower(*(s2++));
if ((r = c1-c2) != 0) if ((r = c1-c2) != 0)
break; break;
} while (c1 != 0); } while (c1 != 0);
return r; return r;
} }

View File

@ -18,9 +18,9 @@
char * char *
str_copy(const char * source); str_copy(const char *source);
int int
str_cmpi(const char * anycase, const char * lowcase); str_cmpi(const char *anycase, const char *lowcase);
#endif /* !STR_UTILS_H */ #endif /* !STR_UTILS_H */

View File

@ -21,657 +21,580 @@
/* Library level */ /* Library level */
static void static void
aisl_stream_reset(aisl_stream_t stream, bool initial) aisl_stream_reset(AislStream stream, bool initial)
{ {
if (!initial) if (!initial) {
{ aisl_raise(aisl_stream_get_instance(stream), (void*) stream,
aisl_raise( AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
aisl_stream_get_instance(stream) }
, (void*) stream
, AISL_EVENT_STREAM_CLOSE
, AISL_SUCCESS
);
}
buffer_release(&stream->buffer); buffer_release(&stream->buffer);
stream->u_ptr = NULL; stream->u_ptr = NULL;
stream->content_length = AISL_AUTO_LENGTH; stream->content_length = AISL_AUTO_LENGTH;
stream->head_offset = 0; stream->head_offset = 0;
stream->flags = 0; stream->flags = 0;
stream->state = AISL_STREAM_STATE_IDLE; stream->state = AISL_STREAM_STATE_IDLE;
stream->http_response = AISL_HTTP_OK; stream->http_response = AISL_HTTP_OK;
} }
aisl_stream_t AislStream
aisl_stream_new(aisl_client_t client, int id) aisl_stream_new(AislClient client, int id)
{ {
aisl_stream_t stream = calloc(1, sizeof(struct aisl_stream)); AislStream stream = calloc(1, sizeof (struct aisl_stream));
if (stream) if (stream) {
{ stream->id = id;
stream->id = id; stream->client = client;
stream->client = client; aisl_stream_reset(stream, true);
aisl_stream_reset(stream, true); }
} return stream;
return stream;
} }
void void
aisl_stream_free(aisl_stream_t stream) aisl_stream_free(AislStream stream)
{ {
aisl_stream_reset(stream, false); aisl_stream_reset(stream, false);
free(stream); free(stream);
} }
int32_t int32_t
aisl_stream_get_buffer_space(aisl_stream_t stream) aisl_stream_get_buffer_space(AislStream stream)
{ {
return stream->buffer.size - stream->buffer.used; return stream->buffer.size - stream->buffer.used;
} }
int32_t int32_t
aisl_stream_get_buffer_size(aisl_stream_t stream) aisl_stream_get_buffer_size(AislStream stream)
{ {
return stream->buffer.size; return stream->buffer.size;
} }
char * char *
aisl_stream_get_data(aisl_stream_t stream, int32_t * p_length) aisl_stream_get_data(AislStream stream, int32_t *p_length)
{ {
*p_length = stream->buffer.used; *p_length = stream->buffer.used;
return stream->buffer.data; return stream->buffer.data;
} }
void void
aisl_stream_shift(aisl_stream_t stream, int32_t offset) aisl_stream_shift(AislStream stream, int32_t offset)
{ {
buffer_shift(&stream->buffer, offset); buffer_shift(&stream->buffer, offset);
} }
bool bool
aisl_stream_is_done(aisl_stream_t stream) aisl_stream_is_done(AislStream stream)
{ {
return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE); return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE);
} }
aisl_stream_state_t AislStreamState
aisl_stream_get_state(aisl_stream_t stream) aisl_stream_get_state(AislStream stream)
{ {
return stream->state; return stream->state;
} }
void void
aisl_stream_set_request( aisl_stream_t stream, aisl_stream_set_request(AislStream stream,
aisl_http_method_t http_method, AislHttpMethod http_method,
const char * path, const char *path,
const char * query ) const char *query)
{ {
struct aisl_evt_stream_open on_open; struct aisl_evt_open on_open;
stream->state = AISL_STREAM_STATE_WAIT_HEADER; stream->state = AISL_STREAM_STATE_WAIT_HEADER;
DPRINTF( DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method),
"%s -> path: %s, query: %s" path, query);
, aisl_http_method_to_string(http_method)
, path
, query
);
on_open.evt.code = AISL_EVENT_STREAM_OPEN; on_open.evt.code = AISL_EVENT_STREAM_OPEN;
on_open.evt.source = (void *) stream; on_open.evt.source = (void *) stream;
on_open.evt.status = AISL_SUCCESS; on_open.evt.status = AISL_SUCCESS;
on_open.http_method = http_method; on_open.http_method = http_method;
on_open.path = path; on_open.path = path;
on_open.query = query; on_open.query = query;
aisl_raise_evt(aisl_stream_get_instance(stream), (aisl_evt_t)&on_open); aisl_raise_evt(aisl_stream_get_instance(stream), (struct aisl_evt *)&on_open);
} }
void void
aisl_stream_set_header( aisl_stream_t stream, aisl_stream_set_header(AislStream stream, const char *key, const char *value)
const char * key,
const char * value )
{ {
struct aisl_evt_stream_header on_header; struct aisl_evt_header on_header;
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER) if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
return; return;
if (strcmp(key, "content-length")==0) if (strcmp(key, "content-length") == 0) {
stream->content_length = strtoll(value, NULL, 10); stream->content_length = strtoll(value, NULL, 10);
else if (strcmp(key, "connection")==0) } else if (strcmp(key, "connection") == 0) {
aisl_client_set_keepalive( aisl_client_set_keepalive(stream->client,
stream->client (str_cmpi(value, "close")==0) ? false : true);
, (str_cmpi(value, "close")==0) ? false : true }
);
DPRINTF("%s: %s", key, value); DPRINTF("%s: %s", key, value);
on_header.evt.code = AISL_EVENT_STREAM_HEADER; on_header.evt.code = AISL_EVENT_STREAM_HEADER;
on_header.evt.source = (void *) stream; on_header.evt.source = (void *) stream;
on_header.evt.status = AISL_SUCCESS; on_header.evt.status = AISL_SUCCESS;
on_header.key = key; on_header.key = key;
on_header.value = value; on_header.value = value;
aisl_raise_evt(aisl_stream_get_instance(stream), (aisl_evt_t)&on_header); aisl_raise_evt(aisl_stream_get_instance(stream),
(struct aisl_evt *) &on_header);
} }
int int
aisl_stream_set_end_of_headers( aisl_stream_t stream ) aisl_stream_set_end_of_headers(AislStream stream)
{ {
int result; int result;
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
{ stream->state = AISL_STREAM_STATE_WAIT_BODY;
stream->state = AISL_STREAM_STATE_WAIT_BODY; result = (stream->content_length == 0);
result = (stream->content_length == 0); } else {
} result = 2;
else }
result = 2; return result;
return result;
} }
int int
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size ) aisl_stream_set_body(AislStream stream, const char *data, int32_t size)
{ {
int result; int result;
if (stream->state == AISL_STREAM_STATE_WAIT_BODY) if (stream->state == AISL_STREAM_STATE_WAIT_BODY) {
{ if (!(stream->content_length < size)) {
if ( !(stream->content_length < size) ) struct aisl_evt_input on_input;
{
struct aisl_evt_stream_input on_input;
if (stream->content_length == 0) if (stream->content_length == 0) {
{ stream->state = AISL_STREAM_STATE_READY;
stream->state = AISL_STREAM_STATE_READY; result = 0;
result = 0; } else {
} result = 1;
else }
result = 1;
on_input.evt.code = AISL_EVENT_STREAM_INPUT; on_input.evt.code = AISL_EVENT_STREAM_INPUT;
on_input.evt.source = (void *) stream; on_input.evt.source = (void *) stream;
on_input.evt.status = AISL_SUCCESS; on_input.evt.status = AISL_SUCCESS;
on_input.data = data; on_input.data = data;
on_input.size = size; on_input.size = size;
aisl_raise_evt(stream->client->server->instance, (aisl_evt_t)&on_input); aisl_raise_evt(stream->client->server->instance,
} (struct aisl_evt *) &on_input);
else } else {
result = -1; result = -1;
} }
else } else {
result = 2; result = 2;
}
return result; return result;
} }
/* API Level */ /* API Level */
/* Why it was here? /* Why it was here?
static int static int
aisl_stream_write(aisl_stream_t stream, const char * data, uint32_t d_len) aisl_stream_write(AislStream stream, const char * data, uint32_t d_len)
{ {
return buffer_add( &stream->buffer, data, d_len); return buffer_add( &stream->buffer, data, d_len);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_cancel(aisl_stream_t stream) aisl_cancel(AislStream stream)
{ {
aisl_client_close( stream->client ); aisl_client_close( stream->client );
} }
*/ */
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void * void *
aisl_get_context(aisl_stream_t s) aisl_get_context(AislStream s)
{ {
return s->u_ptr; return s->u_ptr;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_set_context(aisl_stream_t s, void * u_ptr) aisl_set_context(AislStream s, void *u_ptr)
{ {
s->u_ptr = u_ptr; s->u_ptr = u_ptr;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_client_t AislClient
aisl_get_client(aisl_stream_t s) aisl_get_client(AislStream s)
{ {
return s->client; return s->client;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_server_t AislServer
aisl_get_server(aisl_stream_t s) aisl_get_server(AislStream s)
{ {
return aisl_client_get_server(s->client); return aisl_client_get_server(s->client);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_http_version_t AislHttpVersion
aisl_get_http_version(aisl_stream_t s) aisl_get_http_version(AislStream s)
{ {
return aisl_client_get_http_version(s->client); return aisl_client_get_http_version(s->client);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_reject(aisl_stream_t s) aisl_reject(AislStream s)
{ {
aisl_client_disconnect( s->client ); aisl_client_disconnect( s->client );
} }
static aisl_status_t static AislStatus
aisl_start_response(aisl_stream_t stream) aisl_start_response(AislStream stream)
{ {
return aisl_response( stream return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH);
, AISL_HTTP_OK
, AISL_AUTO_LENGTH );
} }
static aisl_status_t static AislStatus
aisl_stream_close_headers(aisl_stream_t stream) aisl_stream_close_headers(AislStream stream)
{ {
int32_t l; int32_t l;
if (aisl_start_response(stream) == AISL_MALLOC_ERROR) if (aisl_start_response(stream) == AISL_MALLOC_ERROR)
return AISL_MALLOC_ERROR; return AISL_MALLOC_ERROR;
if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) {
{ l = buffer_append( &stream->buffer, "Server: AISL\r\n", 14);
l = buffer_append( &stream->buffer, "Server: AISL\r\n", 14); if (l == -1)
if (l == -1) return AISL_MALLOC_ERROR;
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_SERVER_HEADER_SENT; stream->flags |= FLAG_SERVER_HEADER_SENT;
} }
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
{ l = buffer_append(&stream->buffer,
l = buffer_append( &stream->buffer "Content-type: text/html; encoding=utf-8\r\n", 41);
, "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 (l == -1)
} return AISL_MALLOC_ERROR;
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) stream->flags |= FLAG_CONTENT_TYPE_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_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 (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) if (l == -1)
{ return AISL_MALLOC_ERROR;
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; stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
} }
}
if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1) if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
return AISL_MALLOC_ERROR; l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n",
(aisl_client_get_keepalive(stream->client) ? "keepalive" : "close"));
stream->body_offset = stream->buffer.used; if (l == -1)
return AISL_MALLOC_ERROR;
stream->state = AISL_STREAM_STATE_SEND_BODY; stream->flags |= FLAG_CONNECTION_HEADER_SENT;
}
return AISL_SUCCESS; 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") )) __attribute__ ((visibility ("default") ))
aisl_status_t AislStatus
aisl_response( aisl_stream_t stream aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len)
, aisl_http_response_t status_code
, uint64_t content_length )
{ {
int32_t l; int32_t l;
/* check if those headers were already sent */ /* check if those headers were already sent */
if (stream->state > AISL_STREAM_STATE_READY) return AISL_IDLE; if (stream->state > AISL_STREAM_STATE_READY)
return AISL_IDLE;
stream->http_response = status_code; stream->http_response = rs_code;
stream->content_length = content_length; stream->content_length = c_len;
buffer_init( &stream->buffer buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0);
, (content_length != AISL_AUTO_LENGTH) ? content_length : 0);
l = buffer_append_printf( l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n",
&stream->buffer aisl_http_version_to_string(stream->client->http_version), rs_code,
, "%s %d %s\r\n" aisl_http_response_to_string(rs_code));
, aisl_http_version_to_string(stream->client->http_version)
, status_code
, aisl_http_response_to_string(status_code)
);
if ( l == -1 ) if (l == -1)
return AISL_MALLOC_ERROR; return AISL_MALLOC_ERROR;
stream->head_offset = l; stream->head_offset = l;
stream->state = AISL_STREAM_STATE_SEND_HEADER;
stream->state = AISL_STREAM_STATE_SEND_HEADER; return AISL_SUCCESS;
return AISL_SUCCESS;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_status_t AislStatus
aisl_flush(aisl_stream_t s) aisl_flush(AislStream s)
{ {
if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
{ char hdr[ 40 ];
char hdr[ 40 ]; uint64_t c_len = s->buffer.used - s->body_offset;
int32_t l;
uint64_t c_len = s->buffer.used - s->body_offset; l = snprintf(hdr, sizeof (hdr), "Content-length: %"PRIu64"\r\n", c_len);
int32_t l; l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l);
l = snprintf(hdr, sizeof(hdr), "Content-length: %"PRIu64"\r\n", c_len); if (l == -1)
return AISL_MALLOC_ERROR;
l = buffer_insert( &s->buffer s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
, s->body_offset - 2 }
, hdr
, l );
if (l == -1) s->state = AISL_STREAM_STATE_DONE;
return AISL_MALLOC_ERROR; s->flags |= FLAG_FLUSHED;
s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; return AISL_SUCCESS;
}
s->state = AISL_STREAM_STATE_DONE;
s->flags |= FLAG_FLUSHED;
return AISL_SUCCESS;
} }
static int32_t static int32_t
aisl_stream_verify_header( aisl_stream_t stream, aisl_stream_verify_header(AislStream stream, const char *key, const char *value)
const char * key,
const char * value )
{ {
if (stream->state < AISL_STREAM_STATE_SEND_HEADER) if (stream->state < AISL_STREAM_STATE_SEND_HEADER) {
{ if (aisl_start_response(stream) != AISL_SUCCESS)
if (aisl_start_response(stream) != AISL_SUCCESS) return -1;
return -1; } else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) {
} return 0;
else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) }
return 0;
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
{ if (str_cmpi(key, "connection")==0) {
if (str_cmpi(key, "connection")==0) stream->flags |= FLAG_CONNECTION_HEADER_SENT;
{ if (value) {
stream->flags |= FLAG_CONNECTION_HEADER_SENT; aisl_client_set_keepalive(stream->client,
if (value) (str_cmpi(value, "keepalive") == 0));
{ }
aisl_client_set_keepalive( stream->client return 1;
, (str_cmpi(value, "keepalive")==0) ); }
} }
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
{ if (str_cmpi(key, "content-type") == 0) {
if (str_cmpi(key, "content-type")==0) stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
{ return 1;
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT; }
return 1; }
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
{ if (str_cmpi(key, "content-length") == 0) {
if (str_cmpi(key, "content-length")==0) stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
{ return 1;
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; }
return 1; }
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
{ if (str_cmpi(key, "content-length")==0) {
if (str_cmpi(key, "content-length")==0) stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
{ return 1;
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT; }
return 1; }
}
}
return 1; return 1;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int32_t int32_t
aisl_header(aisl_stream_t stream, const char *key, const char *value) aisl_header(AislStream stream, const char *key, const char *value)
{ {
int32_t result; int32_t result;
if ( (result = aisl_stream_verify_header( stream, key, value )) != 1) if ( (result = aisl_stream_verify_header( stream, key, value )) != 1)
return result; return result;
result = buffer_append_printf( &stream->buffer result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value);
, "%s: %s\r\n"
, key
, value );
return result;
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;
/* For debug purposes free(pch);
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL ) }
{ else
ret = strlen(pch); ret = -1;
if ( buffer_insert(
&stream->buffer,
stream->end_of_headers,
pch,
ret
) == -1 )
{
ret = -1;
}
else
stream->end_of_headers += ret;
free(pch); return ret;
} */
else
ret = -1;
return ret;
*/
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int32_t int32_t
aisl_header_printf(aisl_stream_t stream, const char *key, aisl_header_printf(AislStream stream, const char *key, const char *format, ...)
const char *format, ...)
{ {
int32_t result; int32_t result;
va_list args;
va_list args; va_start(args, format);
va_start(args, format); result = aisl_header_vprintf( stream, key, format, args );
va_end(args);
result = aisl_header_vprintf( stream, key, format, args ); return result;
va_end(args);
return result;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int32_t int32_t
aisl_header_vprintf(aisl_stream_t stream, const char *key, aisl_header_vprintf(AislStream stream,
const char *format, const char *key,
va_list args) const char *format,
va_list args)
{ {
int32_t result, int32_t result, l;
l;
if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1) if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1)
return result; return result;
result = buffer_append_printf( &stream->buffer, "%s: ", key ); result = buffer_append_printf( &stream->buffer, "%s: ", key );
if (result != -1) if (result != -1) {
{ l = buffer_append_vprintf( &stream->buffer, format, args );
l = buffer_append_vprintf( &stream->buffer, format, args );
if (l != -1) if (l != -1) {
{ result += l;
result += l; if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) {
if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) result += l;
{ return result;
result += l; }
}
return result; }
} return -1;
}
}
return -1;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int int
aisl_printf(aisl_stream_t stream, const char *format, ...) aisl_printf(AislStream stream, const char *format, ...)
{ {
va_list arg; int result;
va_start(arg, format); va_list arg;
int result = aisl_vprintf(stream, format, arg); va_start(arg, format);
result = aisl_vprintf(stream, format, arg);
va_end(arg); va_end(arg);
/* No need to update length there, because vprintf do that
*
* if (stream->c_length_unknown)
stream->c_length += result;
*/
return result;
return result;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int32_t int32_t
aisl_vprintf(aisl_stream_t stream, const char *format, va_list args) aisl_vprintf(AislStream stream, const char *format, va_list args)
{ {
if (stream->state < AISL_STREAM_STATE_SEND_BODY) if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
{ if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
if (aisl_stream_close_headers(stream) != AISL_SUCCESS) return -1;
return -1; }
} return buffer_append_vprintf(&stream->buffer, format, args);
return buffer_append_vprintf(&stream->buffer, format, args);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int32_t int32_t
aisl_write(aisl_stream_t stream, const char *data, int32_t d_len) aisl_write(AislStream stream, const char *data, int32_t d_len)
{ {
if (stream->state < AISL_STREAM_STATE_SEND_BODY) if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
{ if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
if (aisl_stream_close_headers(stream) != AISL_SUCCESS) return -1;
return -1; }
}
if (d_len == -1)
d_len = strlen(data);
return buffer_append(&stream->buffer, data, d_len); if (d_len == -1)
d_len = strlen(data);
return buffer_append(&stream->buffer, data, d_len);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int int
aisl_puts(const char *str, aisl_stream_t stream) aisl_puts(const char *str, AislStream stream)
{ {
if (stream->state < AISL_STREAM_STATE_SEND_BODY) if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
{ if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
if (aisl_stream_close_headers(stream) != AISL_SUCCESS) return -1;
return -1; }
} return aisl_write( stream, str, -1);
return aisl_write( stream, str, -1);
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_t AislInstance
aisl_stream_get_instance(aisl_stream_t stream) aisl_stream_get_instance(AislStream stream)
{ {
return stream->client->server->instance; return stream->client->server->instance;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_set_output_event(aisl_stream_t stream, bool value) aisl_set_output_event(AislStream stream, bool value)
{ {
if (value) if (value)
stream->flags |= FLAG_OUTPUT_CHUNKED; stream->flags |= FLAG_OUTPUT_CHUNKED;
else if (stream->flags & FLAG_OUTPUT_CHUNKED) else if (stream->flags & FLAG_OUTPUT_CHUNKED)
stream->flags &= ~FLAG_OUTPUT_CHUNKED; stream->flags &= ~FLAG_OUTPUT_CHUNKED;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_get_output_event(aisl_stream_t stream) aisl_get_output_event(AislStream stream)
{ {
return (stream->flags & FLAG_OUTPUT_CHUNKED); return (stream->flags & FLAG_OUTPUT_CHUNKED);
} }

View File

@ -1,96 +1,106 @@
#ifndef AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3 /******************************************************************************
*
* 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 #define AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3
#include <aisl/types.h> #include <aisl/types.h>
#include <aisl/stream.h> #include <aisl/stream.h>
#include "buffer.h" #include "buffer.h"
#define AISL_STREAM(x) ((aisl_stream_t) x) #define AISL_STREAM(x) ((AislStream) x)
typedef enum typedef enum
{ {
AISL_STREAM_STATE_IDLE AISL_STREAM_STATE_IDLE
, AISL_STREAM_STATE_WAIT_HEADER , AISL_STREAM_STATE_WAIT_HEADER
, AISL_STREAM_STATE_WAIT_BODY , AISL_STREAM_STATE_WAIT_BODY
, AISL_STREAM_STATE_READY , AISL_STREAM_STATE_READY
, AISL_STREAM_STATE_SEND_HEADER , AISL_STREAM_STATE_SEND_HEADER
, AISL_STREAM_STATE_SEND_BODY , AISL_STREAM_STATE_SEND_BODY
, AISL_STREAM_STATE_DONE , AISL_STREAM_STATE_DONE
} aisl_stream_state_t; } AislStreamState;
struct aisl_stream struct aisl_stream {
{ struct buffer buffer;
struct buffer buffer; AislClient client;
aisl_client_t client;
void * u_ptr; void *u_ptr;
uint64_t content_length; uint64_t content_length;
int32_t head_offset; int32_t head_offset;
int32_t body_offset; int32_t body_offset;
int id; int id;
int flags; int flags;
aisl_http_response_t http_response; AislHttpResponse http_response;
aisl_stream_state_t state; AislStreamState state;
}; };
aisl_stream_t AislStream
aisl_stream_new(aisl_client_t client, int id ); aisl_stream_new(AislClient client, int id);
void void
aisl_stream_free(aisl_stream_t stream); aisl_stream_free(AislStream stream);
int32_t int32_t
aisl_stream_get_buffer_space(aisl_stream_t stream); aisl_stream_get_buffer_space(AislStream stream);
int32_t int32_t
aisl_stream_get_buffer_size(aisl_stream_t stream); aisl_stream_get_buffer_size(AislStream stream);
char * char *
aisl_stream_get_data(aisl_stream_t stream, int32_t * p_length); aisl_stream_get_data(AislStream stream, int32_t *p_length);
void void
aisl_stream_shift(aisl_stream_t stream, int32_t offset); aisl_stream_shift(AislStream stream, int32_t offset);
bool bool
aisl_stream_is_done(aisl_stream_t stream); aisl_stream_is_done(AislStream stream);
aisl_stream_state_t AislStreamState
aisl_stream_get_state(aisl_stream_t stream); aisl_stream_get_state(AislStream stream);
void void
aisl_stream_set_request( aisl_stream_t stream, aisl_stream_set_request(AislStream stream,
aisl_http_method_t http_method, AislHttpMethod http_method,
const char * path, const char *path,
const char * query ); const char *query );
void void
aisl_stream_set_header( aisl_stream_t stream, aisl_stream_set_header(AislStream stream, const char *key, const char *value);
const char * key,
const char * value );
int int
aisl_stream_set_end_of_headers( aisl_stream_t stream ); aisl_stream_set_end_of_headers(AislStream stream);
int int
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size ); aisl_stream_set_body(AislStream stream, const char *data, int32_t size);
#endif /* !AISL_STREAM_H */ #endif /* !AISL_STREAM_H */

View File

@ -20,41 +20,39 @@
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
const char * const char *
aisl_status_to_string(aisl_status_t status) aisl_status_to_string(AislStatus status)
{ {
switch(status) switch(status) {
{ case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR"; case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR"; case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR"; case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR"; case AISL_SUCCESS: return "AISL_SUCCESS";
case AISL_SUCCESS: return "AISL_SUCCESS"; case AISL_IDLE: return "AISL_IDLE";
case AISL_IDLE: return "AISL_IDLE"; }
} return "UNKNOWN";
return "UNKNOWN";
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
const char * const char *
aisl_evt_code_to_string( aisl_evt_code_t evt_code ) aisl_event_to_string(AislEvent evt_code)
{ {
switch(evt_code) switch(evt_code) {
{ case AISL_EVENT_SERVER_READY: return "SERVER READY";
case AISL_EVENT_SERVER_OPEN: return "SERVER OPEN"; case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR";
case AISL_EVENT_SERVER_ERROR: return "SERVER ERROR"; case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT";
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT CONNECT"; case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT";
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT DISCONNECT"; case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN";
case AISL_EVENT_STREAM_OPEN: return "STREAM OPEN"; case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER";
case AISL_EVENT_STREAM_HEADER: return "STREAM HEADER"; case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT";
case AISL_EVENT_STREAM_INPUT: return "STREAM INPUT"; case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST";
case AISL_EVENT_STREAM_REQUEST: return "STREAM REQUEST"; case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT";
case AISL_EVENT_STREAM_OUTPUT: return "STREAM OUTPUT"; case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE";
case AISL_EVENT_STREAM_CLOSE: return "STREAM CLOSE"; case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR";
case AISL_EVENT_STREAM_ERROR: return "STREAM ERROR"; }
}
return "UNKNOWN"; return "UNKNOWN";
} }
#endif #endif