Add working Hello World example

This commit is contained in:
Ilja Kartašov 2019-03-22 20:24:09 +01:00
parent 8847f92528
commit d28f942cf7
28 changed files with 933 additions and 1981 deletions

View File

@ -58,9 +58,10 @@ hello_world(aisl_stream_t s)
int
main(int argc, char ** argv)
{
aisl_t aisl; /**< AISL instance pointer */
aisl_status_t status; /**< AISL status code */
uint16_t port = 0;
aisl_t aisl; /**< AISL instance pointer */
aisl_status_t status; /**< AISL status code */
struct aisl_config config = AISL_CONFIG_DEFAULT;
uint16_t port = 0;
/* Try to use first argument as a port number */
if (argc > 1)
@ -71,32 +72,24 @@ main(int argc, char ** argv)
port = DEFAULT_HTTP_PORT;
/* Initialize instance */
if ( (aisl = aisl_new(NULL)) != NULL )
if ( (aisl = aisl_new(&config)) != NULL )
{
if (aisl_listen( aisl, "0.0.0.0", port ) != NULL)
{
aisl_callback_t callback = AISL_CALLBACK(hello_world);
/* Set up request callback */
status = aisl_set_callback( aisl
, NULL
, AISL_EVENT_STREAM_REQUEST
, callback );
aisl_set_callback( aisl
, AISL_EVENT_STREAM_REQUEST
, AISL_CALLBACK(hello_world) );
/* if callback was set, launch application loop */
if ( status == AISL_SUCCESS )
/* launch application loop */
fprintf(stdout, "Entering main loop" );
for(;;)
{
fprintf(stdout, "Entering main loop" );
for(;;)
{
status = aisl_run_cycle(aisl);
status = aisl_run_cycle(aisl);
if ( status != AISL_SUCCESS )
aisl_sleep(aisl, 500);
}
if ( status != AISL_SUCCESS )
aisl_sleep(aisl, 500);
}
else
fprintf(stderr, "Failed to register callback" );
aisl_free(aisl);
}

View File

@ -12,13 +12,22 @@
#include <stdint.h>
#define AISL_CONFIG_DEFAULT \
{ \
.server_spool_size = 1 \
, .client_spool_size = 32 \
, .ssl_spool_size = 1 \
, .initial_buffer_size = 16536 \
, .client_accept_limit = 1024 \
, .client_silence_timeout = 30 \
} \
struct aisl_config
{
uint32_t server_spool_size;
uint32_t client_spool_size;
uint32_t ssl_spool_size;
uint32_t callback_spool_size;
uint32_t initial_buffer_size;
uint32_t client_accept_limit;
uint32_t client_silence_timeout;

View File

@ -76,52 +76,13 @@ aisl_set_ssl( aisl_t instance,
* - #aisl_stream_t;
*
* @param instance a pointer to #aisl_t instance.
* @param source a pointer to an event source.
* @param event a code of event.
* @param callback a pointer to function that will be triggered on event.
* @return #aisl_status_t code.
*/
aisl_status_t
aisl_set_callback( aisl_t instance,
void * source,
aisl_event_t event,
aisl_callback_t callback );
/**
* @brief Raises event from source.
* @param instance a pointer to #aisl_t instance.
* @param source a pointer to an event source.
* @param event a code of event.
* @param ... a list of arguments specific for event.
* @return true if event was handled by at least one callback, false otherwise.
*/
bool
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... );
/**
* @brief Raises event from source.
* @param instance a pointer to #aisl_t instance.
* @param source a pointer to an event source.
* @param event a code of event.
* @param args a list of arguments specific for event.
* @return true if event was handled by at least one callback, false otherwise.
*/
bool
aisl_raise_vl( aisl_t instance,
void * source,
aisl_event_t event,
va_list args );
/**
* @brief Unsets callbacks for specified source.
* @param instance a pointer to #aisl_t instance.
* @param source a pointer to an event source.
*/
void
aisl_unset_callbacks_for( aisl_t instance, void * source );
aisl_set_callback( aisl_t instance,
aisl_event_t event,
aisl_callback_t callback );
/**

View File

@ -68,23 +68,22 @@ aisl_status_to_string(aisl_status_t status);
typedef enum
{
AISL_HTTP_1_0
, AISL_HTTP_1_1
, AISL_HTTP_2_0
AISL_HTTP_0_9 = 0x0009
, AISL_HTTP_1_0 = 0x0100
, AISL_HTTP_1_1 = 0x0101
, AISL_HTTP_2_0 = 0x0200
} aisl_http_version_t;
#ifndef WITHOUT_STRINGIFIERS
const char *
aisl_http_version_to_string( aisl_http_version_t version );
#endif
typedef enum
{
AISL_HTTP_GET
AISL_HTTP_METHOD_UNKNOWN
, AISL_HTTP_GET
, AISL_HTTP_PUT
, AISL_HTTP_POST
, AISL_HTTP_HEAD
@ -97,13 +96,10 @@ typedef enum
} aisl_http_method_t;
#ifndef WITHOUT_STRINGIFIERS
const char *
aisl_http_method_to_string( aisl_http_method_t method );
#endif
typedef enum
{
@ -160,14 +156,6 @@ const char *
aisl_http_response_to_string( aisl_http_response_t code );
#ifndef WITHOUT_STRINGIFIERS
const char *
aisl_http_secure_to_string( bool is_secure );
#endif
/* Built-in Events IDs */
enum
@ -187,8 +175,6 @@ enum
, AISL_EVENT_STREAM_CLOSE = 350
, AISL_EVENT_STREAM_ERROR = 390
, AISL_EVENTS_CUSTOM = 1000
};
#ifndef WITHOUT_STRINGIFIERS
@ -231,7 +217,9 @@ typedef bool
const char * val );
typedef bool
(*aisl_on_stream_input_t)( aisl_stream_t stream, char * data, int32_t len );
(*aisl_on_stream_input_t)( aisl_stream_t stream,
const char * data,
int32_t len );
typedef bool
(*aisl_on_stream_request_t)( aisl_stream_t stream );
@ -245,7 +233,4 @@ typedef bool
typedef bool
(*aisl_on_stream_error_t)( aisl_stream_t stream );
typedef bool
(*aisl_on_custom_event_t)( void * source, va_list vl );
#endif /* !AISL_TYPES_H */

View File

@ -45,8 +45,15 @@ PROJECT_LIBRARIES = \
`$(PKG_CONFIG) --libs openssl` \
# compilation macro options:
# AISL_WITHOUT_SSL - exclude HTTPS support
# AISL_WITHOUT_STRINGIFIERS - exclude several *_to_string functions not
# flags
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L \
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
#PROJECT_CFLAGS += -DDEBUG
#PROJECT_CFLAGS += -DAISL_WITHOUT_SSL
#PROJECT_CFLAGS += -DAISL_WITHOUT_STRINGIFIERS
# PROJECT_LDFLAGS = -L

View File

@ -21,7 +21,7 @@
static int32_t
buffer_set_size(buffer_t buffer, int32_t new_size)
{
if ( new_size < buffer->size )
if ( new_size != buffer->size )
{
if (new_size)
{
@ -33,7 +33,7 @@ buffer_set_size(buffer_t buffer, int32_t new_size)
}
}
else
new_size = 4096;
new_size = 16*1024;
char * data = realloc(buffer->data, new_size);
@ -64,7 +64,10 @@ void
buffer_release( buffer_t buffer )
{
if (buffer->data)
{
free(buffer->data);
buffer->data = NULL;
}
buffer->used = 0;
buffer->size = 0;
@ -125,7 +128,7 @@ buffer_append_vprintf( buffer_t buffer, const char * format, va_list args )
result;
va_list cp_args;
va_copy(args, cp_args);
va_copy(cp_args, args);
result = vsnprintf( &buffer->data[buffer->used], space, format, args );
@ -143,7 +146,6 @@ buffer_append_vprintf( buffer_t buffer, const char * format, va_list args )
return result;
}
int32_t
buffer_append( buffer_t buffer, const char * data, int32_t size )
{

View File

@ -1,11 +0,0 @@
/*
* callback.c
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Distributed under terms of the MIT license.
*/
#include "callback.h"

View File

@ -1,42 +0,0 @@
/*
* src/callback.h
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_CALLBACK_H_43D3FC6B_22E5_481C_8EB2_00BF8C3D52CA
#define AISL_CALLBACK_H_43D3FC6B_22E5_481C_8EB2_00BF8C3D52CA
#include <aisl/types.h>
struct callback
{
void * source;
aisl_callback_t f_ptr;
aisl_event_t event;
};
typedef struct callback * callback_t;
static listener_t
listener_new( void * source, aisl_event_t e_id, aisl_callback_t cb)
{
listener_t self = malloc(sizeof(struct listener));
if (self)
{
self->source = source;
self->e_id = e_id;
self->cb = cb;
}
return self;
}
#endif /* !AISL_CALLBACK_H */

View File

@ -11,9 +11,11 @@
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "buffer.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
@ -23,72 +25,39 @@
#define BUFFER_SIZE (16*1024)
struct aisl_client
{
struct sockaddr_in address; /**< Client's address structure. */
aisl_server_t server; /**< Server instance. */
struct buffer in; /**< Client's input buffer. */
struct buffer out; /**< Client's output buffer. */
#ifndef AISL_WITHOUT_SSL
SSL * ssl; /**< SSL pointer for HTTPS. */
#endif
time_t timestamp; /**< Last communication timestamp. */
aisl_stream_t stream; /**< Pending client's stream. */
int next_id; /**< Stream id generator (even). */
int flags; /**< Client's flag bitmask. */
int fd; /**< Client's socket descriptor. */
aisl_http_version_t http_version; /**< Client's http_version version. */
};
static bool
aisl_client_raise(aisl_client_t client, aisl_event_t event, ...)
{
bool result;
va_list vl_args;
aisl_t instance = aisl_server_get_instance(client->server);
va_start(vl_args, event);
result = aisl_raise_vl(instance, client, event, vl_args);
va_end(vl_args);
return result;
}
static aisl_status_t
aisl_client_parse(aisl_client_t client, char * data, int32_t size)
{
aisl_stream_t s;
http_parser_t p = HTTP_PARSER_PENDING;
http_parser_t 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_PENDING )
while ( p == HTTP_PARSER_SUCCESS )
{
switch ( aisl_stream_get_state(s) )
{
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client);
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client);
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client);
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
@ -101,10 +70,16 @@ aisl_client_parse(aisl_client_t client, char * data, int32_t size)
size = bytes_left;
}
if (p == HTTP_PARSER_FINISHED)
if (p == HTTP_PARSER_READY)
{
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
aisl_raise(
client->server->instance,
client->stream,
AISL_EVENT_STREAM_REQUEST
);
}
break;
@ -117,6 +92,14 @@ aisl_client_parse(aisl_client_t client, char * data, int32_t size)
if (p == HTTP_PARSER_ERROR)
{
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(
client->server->instance,
client->stream,
AISL_EVENT_STREAM_ERROR
);
aisl_client_close(client);
return AISL_INPUT_ERROR;
@ -142,9 +125,12 @@ aisl_client_input(aisl_client_t client)
char * data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
DPRINTF("Buffer: %p, %d", data, size);
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
DPRINTF("SSL_read");
if ( !(client->flags & FLAG_HANDSHAKE) )
{
if ( (l = SSL_accept(client->ssl)) != 1 )
@ -154,15 +140,13 @@ aisl_client_input(aisl_client_t client)
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
fprintf( stderr
, "*AISL: SSL handshake fail: %s\n"
, ERR_error_string(l, NULL) );
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client);
return AISL_SYSCALL_ERROR;
}
client->flags |= FLAG_HANDSHAKE;
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size) ;
@ -173,6 +157,8 @@ aisl_client_input(aisl_client_t client)
if (l > 0)
{
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
@ -182,15 +168,19 @@ aisl_client_input(aisl_client_t client)
}
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));
}
}
@ -244,9 +234,13 @@ aisl_client_output(aisl_client_t client)
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)
{
@ -282,12 +276,14 @@ aisl_client_output(aisl_client_t client)
}
/* 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;
@ -299,24 +295,17 @@ aisl_client_output(aisl_client_t client)
}
#ifndef AISL_WITHOUT_SSL
aisl_client_t
aisl_client_new( aisl_server_t server,
int fd,
struct sockaddr_in * addr,
SSL_CTX * ssl_ctx )
#else
aisl_client_t
aisl_client_new( aisl_server_t server,
int fd,
struct sockaddr_in * addr )
#endif
{
aisl_client_t client;
aisl_stream_t 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;
@ -325,8 +314,9 @@ aisl_client_new( aisl_server_t server,
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) == 0)
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);
@ -335,15 +325,21 @@ aisl_client_new( aisl_server_t server,
{
client->stream = stream;
DPRINTF("client stream alocated");
#ifdef AISL_WITHOUT_SSL
return client;
#else
if ( !ssl_ctx )
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);
@ -436,7 +432,7 @@ aisl_client_is_timed_out(aisl_client_t client, uint32_t timeout)
if ( !(now - client->timestamp < timeout) )
{
aisl_client_raise(client, AISL_EVENT_CLIENT_TIMEOUT);
aisl_raise(client->server->instance, client, AISL_EVENT_CLIENT_TIMEOUT);
aisl_client_close(client);
return true;
}
@ -468,6 +464,8 @@ aisl_client_set_keepalive(aisl_client_t client, bool value)
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
aisl_server_t
@ -480,7 +478,11 @@ aisl_client_get_server(aisl_client_t client)
bool
aisl_client_is_secure(aisl_client_t client)
{
#ifdef AISL_WITHOUT_SSL
return false;
#else
return (client->ssl == NULL) ? false : true;
#endif
}
@ -496,7 +498,7 @@ aisl_client_close(aisl_client_t client)
{
if (client->fd != -1)
{
aisl_client_raise( client, AISL_EVENT_CLIENT_DISCONNECT );
aisl_raise(client->server->instance, client, AISL_EVENT_CLIENT_DISCONNECT);
close(client->fd);
shutdown(client->fd, SHUT_RDWR);

View File

@ -4,23 +4,30 @@
#include <arpa/inet.h>
#include <aisl/client.h>
#include "buffer.h"
#define AISL_CLIENT(x) ((aisl_client_t) x)
#ifndef AISL_WITHOUT_SSL
/**
* @brief Constructor for #aisl_client_t instance.
* @param server an #aisl_server_t instance pointer.
* @param fd a client socket descriptor.
* @param addr a pointer to client's address structure.
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
*/
aisl_client_t
aisl_client_new( aisl_server_t server,
int fd,
struct sockaddr_in * addr,
SSL_CTX * ssl_ctx);
#else
struct aisl_client
{
struct sockaddr_in address; /**< Client's address structure. */
aisl_server_t server; /**< Server instance. */
struct buffer in; /**< Client's input buffer. */
struct buffer out; /**< Client's output buffer. */
#ifndef AISL_WITHOUT_SSL
SSL * ssl; /**< SSL pointer for HTTPS. */
#endif
time_t timestamp; /**< Last communication timestamp. */
aisl_stream_t stream; /**< Pending client's stream. */
int next_id; /**< Stream id generator (even). */
int flags; /**< Client's flag bitmask. */
int fd; /**< Client's socket descriptor. */
aisl_http_version_t http_version; /**< Client's http_version version. */
};
/**
* @brief Constructor for #aisl_client_t instance.
@ -33,7 +40,6 @@ aisl_client_t
aisl_client_new( aisl_server_t server,
int fd,
struct sockaddr_in * addr );
#endif
/**
@ -90,4 +96,5 @@ aisl_client_set_keepalive(aisl_client_t client, bool value);
int
aisl_client_get_socket(aisl_client_t client);
#endif /* !AISL_CLIENT_H */

37
src/debug.h Normal file
View File

@ -0,0 +1,37 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file debug.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief AISL debug module
*
* @see https://lowenware.com/
*/
#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
#ifdef DEBUG
#include <stdio.h>
#define DPRINTF(...) do { \
fprintf(stderr, "* AISL: "); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while(0) \
#else
#define DPRINTF(...)
#endif
#endif /* !AISL_DEBUG_H */

View File

@ -1,28 +0,0 @@
#include <aisl/event.h>
__attribute__ ((visibility ("default") ))
const char *
aisl_event_get_text( aisl_event_t e_id )
{
switch(e_id)
{
case AISL_SERVER_OPEN: return "AISL_SERVER_OPEN";
case AISL_SERVER_ERROR: return "AISL_SERVER_ERROR";
case AISL_CLIENT_CONNECT: return "AISL_CLIENT_CONNECT";
case AISL_CLIENT_DISCONNECT: return "AISL_CLIENT_DISCONNECT";
case AISL_CLIENT_TIMEOUT: return "AISL_CLIENT_TIMEOUT";
case AISL_STREAM_OPEN: return "AISL_STREAM_OPEN";
case AISL_STREAM_INPUT: return "AISL_STREAM_INPUT";
case AISL_STREAM_REQUEST: return "AISL_STREAM_REQUEST";
case AISL_STREAM_OUTPUT: return "AISL_STREAM_OUTPUT";
case AISL_STREAM_CLOSE: return "AISL_STREAM_CLOSE";
case AISL_STREAM_ERROR: return "AISL_STREAM_ERROR";
default:
break;
}
return "AISL_CUSTOM_EVENT";
}

View File

@ -1,43 +0,0 @@
#ifndef _AISL_GLOBALS_H_
#define _AISL_GLOBALS_H_
//#pragma GCC diagnostic ignored "-Wuninitialized"
/* MACOS FIX AND OTHER OS */
#ifndef SOCK_NONBLOCK
#define SOCK_NONBLOCK 0
#endif
#ifndef AISL_MIN_SERVERS
#define AISL_MIN_SERVERS 1
#endif
#ifndef AISL_MIN_STREAMS
#define AISL_MIN_STREAMS 1
#endif
#ifndef AISL_MIN_CLIENTS
#define AISL_MIN_CLIENTS 32
#endif
#ifndef AISL_MIN_LISTENERS
#define AISL_MIN_LISTENERS 8
#endif
#ifndef AISL_MIN_DELAYS
#define AISL_MIN_DELAYS 8
#endif
#ifndef AISL_BUFFER_SIZE
#define AISL_BUFFER_SIZE 4096
#endif
#ifndef AISL_MIN_HEADERS
#define AISL_MIN_HEADERS 8
#endif
#ifndef AISL_MAX_CLIENT_SILENCE
#define AISL_MAX_CLIENT_SILENCE 10
#endif
#endif

View File

@ -1,807 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <openssl/err.h>
#include <aisl/handle.h>
#include "str-utils.h"
#include "list.h"
#include "stream.h"
#include "server.h"
#include "client.h"
#include "globals.h"
#include "handle.h"
/* -------------------------------------------------------------------------- */
struct listener
{
void *source;
aisl_callback_t cb;
aisl_event_t e_id;
};
typedef struct listener * listener_t;
/* -------------------------------------------------------------------------- */
struct delay
{
struct timespec next;
uint32_t delay;
void *u_data;
aisl_callback_t cb;
};
typedef struct delay * delay_t;
/* -------------------------------------------------------------------------- */
struct crypter
{
char * keyFile;
char * crtFile;
char * srvName;
SSL_CTX * sslCtx;
};
typedef struct crypter * crypter_t;
/* -------------------------------------------------------------------------- */
static int gHandles = 0;
/* -------------------------------------------------------------------------- */
static listener_t
listener_new( void * source, aisl_event_t e_id, aisl_callback_t cb)
{
listener_t self = malloc(sizeof(struct listener));
if (self)
{
self->source = source;
self->e_id = e_id;
self->cb = cb;
}
return self;
}
/* -------------------------------------------------------------------------- */
void
aisl_remove_listeners_for( aisl_handle_t self, void * source )
{
int i=self->listeners->count-1;
while ( !(i < 0) )
{
listener_t listener = list_index(self->listeners, i);
if ( listener->source == source )
{
free(listener);
list_remove_index(self->listeners, i);
}
i--;
}
}
/* -------------------------------------------------------------------------- */
static void
crypter_free( crypter_t self )
{
if (self->srvName)
free(self->srvName);
if (self->keyFile)
{
free(self->keyFile);
SSL_CTX_free(self->sslCtx);
}
if (self->crtFile)
free(self->crtFile);
free(self);
}
/* -------------------------------------------------------------------------- */
static crypter_t
crypter_new( const char * server_name,
const char * key_file,
const char * crt_file )
{
crypter_t self;
if ( (self=calloc(1, sizeof(struct crypter))) != NULL )
{
if (!(self->srvName = str_copy( server_name ? server_name : "*" )))
goto release;
if ( key_file && !(self->keyFile = str_copy(key_file)))
goto release;
if ( crt_file && !(self->crtFile = str_copy(crt_file)))
goto release;
}
goto finally;
release:
crypter_free(self);
self = NULL;
finally:
return self;
}
/* -------------------------------------------------------------------------- */
static bool
delay_is_expired(delay_t self)
{
if (!self->delay) return true;
struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
/*
printf("> %ld.%ld & %ld.%ld\n", self->next.tv_sec, self->next.tv_nsec,
tv.tv_sec, tv.tv_nsec);
*/
if (tv.tv_sec > self->next.tv_sec)
return true;
else if (tv.tv_sec == self->next.tv_sec && tv.tv_nsec >= self->next.tv_nsec)
return true;
return false;
}
/* -------------------------------------------------------------------------- */
static void
delay_reset(delay_t self)
{
clock_gettime(CLOCK_REALTIME, &self->next);
self->next.tv_sec += self->delay / 1000;
self->next.tv_nsec += (self->delay % 1000) * 1000000;
self->next.tv_sec += self->next.tv_nsec / 1000000000;
self->next.tv_nsec = self->next.tv_nsec % 1000000000;
}
/* -------------------------------------------------------------------------- */
static delay_t
delay_new(aisl_callback_t cb, uint32_t delay, void *u_data)
{
delay_t self = malloc(sizeof(struct delay));
if (self)
{
self->cb = cb;
self->u_data = u_data;
self->delay = delay;
delay_reset(self);
}
return self;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_handle_t
aisl_handle_new( size_t min_clients, size_t buffer_size )
{
aisl_handle_t self;
if ((gHandles++) == 0)
{
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
if ( !(self = calloc(1, sizeof(struct aisl_handle))) )
goto finally;
if ( !(self->servers = list_new(1)) )
goto release;
if ( !(self->clients = list_new(min_clients)) )
goto release;
if ( !(self->listeners = list_new(16)) )
goto release;
if ( !(self->buffer = buffer_new(buffer_size)) )
goto release;
if ( !(self->crypters = list_new(0)) )
goto release;
if ( !(self->delays = list_new(0)) )
goto release;
goto finally;
release:
aisl_handle_free(self);
self = NULL;
finally:
return self;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_handle_t
aisl_handle_from_stream( aisl_stream_t s )
{
return ((client_t)s->client)->server->owner;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
void
aisl_handle_free( aisl_handle_t self )
{
if ((--gHandles) == 0)
{
EVP_cleanup();
}
if (self->clients)
list_free(self->clients, (list_destructor_t) client_free );
if (self->servers)
list_free(self->servers, (list_destructor_t) server_free );
if (self->listeners)
list_free(self->listeners, free);
if (self->delays)
list_free(self->delays, free);
if (self->buffer)
buffer_free(self->buffer);
if (self->crypters)
list_free(self->crypters, (list_destructor_t) crypter_free );
if (self->lastError)
free(self->lastError);
free(self);
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
void
aisl_handle_set_error( aisl_handle_t self, const char * err_msg )
{
if (self->lastError)
free(self->lastError);
self->lastError = str_copy(err_msg);
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_bind( aisl_handle_t self, const char * address, int port, int flags )
{
server_t server;
if ( !(server = server_new(address, port)) )
goto finally;
server->owner = self;
server->flags |= (flags & AISL_FLAG_SSL);
if ( list_append(self->servers, server) == -1 )
goto release;
goto finally;
release:
server_free(server);
server = NULL;
finally:
return server ? AISL_SUCCESS : AISL_MALLOC_ERROR;
}
/* -------------------------------------------------------------------------- */
static int
get_ssl_context( SSL * ssl, int * ptr, void * handle )
{
const char * server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
SSL_CTX * ctx = aisl_get_ssl_ctx( (aisl_handle_t) handle, server_name );
if (ctx)
{
SSL_set_SSL_CTX(ssl, ctx);
}
return SSL_TLSEXT_ERR_OK;
}
/* -------------------------------------------------------------------------- */
static SSL_CTX *
create_ssl_context( aisl_handle_t self,
const char * key_file,
const char * crt_file )
{
const SSL_METHOD * method;
SSL_CTX * ctx;
method = SSLv23_server_method();
if ( !(ctx = SSL_CTX_new(method)) )
goto except;
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_set_tlsext_servername_callback( ctx, get_ssl_context );
SSL_CTX_set_tlsext_servername_arg( ctx, (void *) self );
if (!(SSL_CTX_use_certificate_file(ctx, crt_file, SSL_FILETYPE_PEM) > 0))
goto release;
if (!(SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) > 0))
goto release;
goto finally;
release:
SSL_CTX_free(ctx);
ctx = NULL;
except:
aisl_handle_set_error( self, ERR_error_string(ERR_get_error(),NULL) );
finally:
return ctx;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_set_ssl( aisl_handle_t self, const char * server_name,
const char * key_file,
const char * crt_file )
{
SSL_CTX * ssl_ctx = NULL;
int i;
crypter_t crypter;
/* lookup for existing contexts */
for (i=0; i<self->crypters->count; i++)
{
crypter = list_index(self->crypters, i);
if (crypter->keyFile && strcmp(crypter->keyFile, key_file)==0 &&
crypter->crtFile && strcmp(crypter->crtFile, crt_file)==0 )
{
ssl_ctx = crypter->sslCtx;
key_file = NULL;
crt_file = NULL;
break;
}
}
if (! (crypter = crypter_new(server_name, key_file, crt_file)) )
{
return AISL_MALLOC_ERROR;
}
if (! ssl_ctx)
{
if (!(ssl_ctx = create_ssl_context(self, key_file, crt_file)))
{
crypter_free(crypter);
return AISL_EXTCALL_ERROR;
}
}
crypter->sslCtx = ssl_ctx;
if (list_append(self->crypters, crypter)==-1)
{
crypter_free(crypter);
return AISL_MALLOC_ERROR;
}
return AISL_SUCCESS;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
SSL_CTX *
aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name )
{
int i;
crypter_t crypter;
for (i=0; i<self->crypters->count; i++)
{
crypter = list_index(self->crypters, i);
if (server_name)
{
if (strcmp(crypter->srvName, server_name)!=0)
continue;
}
return crypter->sslCtx;
}
return NULL;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_set_callback( aisl_handle_t self,
void * source,
aisl_event_t e_id,
aisl_callback_t cb )
{
listener_t listener;
if (! (listener = listener_new(source, e_id, cb)) )
return AISL_MALLOC_ERROR;
if (list_append(self->listeners, listener) == -1)
{
free(listener);
return AISL_MALLOC_ERROR;
}
if (e_id == AISL_STREAM_OUTPUT) /* subscribtion for chunked output */
{
if (source)
{
( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
}
}
else if (e_id == AISL_STREAM_OPEN)
{
self->flags |= AISL_HANDLE_HAS_STREAM_LISTENERS;
}
return AISL_SUCCESS;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_set_delay( aisl_handle_t self,
aisl_callback_t cb,
uint32_t usec,
void * u_data )
{
delay_t delay = delay_new(cb, usec, u_data);
if (!delay)
return AISL_MALLOC_ERROR;
if (list_append(self->delays, delay) == -1)
{
free(delay);
return AISL_MALLOC_ERROR;
}
return AISL_SUCCESS;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
bool
aisl_raise_event( aisl_handle_t self,
void * source,
aisl_event_t e_id,
... )
{
va_list vl;
bool result;
va_start(vl, e_id);
result = aisl_raise_event_vl(self, source, e_id, vl);
va_end(vl);
return result;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
bool
aisl_raise_event_vl( aisl_handle_t self,
void * source,
aisl_event_t e_id,
va_list vl )
{
int i,
i_val;
listener_t lst;
bool res = false;
char * c_ptr,
* c_ptr2;
for(i=self->listeners->count-1; i>=0; i--)
{
lst = list_index(self->listeners, i);
/*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/
if (lst->e_id == e_id && (source == lst->source || lst->source == NULL))
{
/*
if (e_id == AISL_STREAM_HEADER)
fprintf(stderr,"FOUND HANDLER %d\n", i);
*/
/*printf(" catch\n");*/
switch(e_id)
{
/* server events */
case AISL_SERVER_OPEN:
i_val = va_arg(vl, aisl_status_t);
res = ((aisl_server_open_t) lst->cb)( source, i_val );
break;
case AISL_SERVER_ERROR:
i_val = va_arg(vl, aisl_status_t);
c_ptr = va_arg(vl, char *);
res = ((aisl_server_error_t) lst->cb)( source, i_val, c_ptr );
break;
/* client events */
case AISL_CLIENT_CONNECT:
res = ((aisl_client_connect_t) lst->cb)(source, va_arg(vl, void *));
break;
case AISL_CLIENT_DISCONNECT:
res = ((aisl_client_disconnect_t) lst->cb)(source, va_arg(vl, void*));
aisl_remove_listeners_for( self, source );
break;
case AISL_CLIENT_TIMEOUT:
res = ((aisl_client_timeout_t) lst->cb)(source, va_arg(vl, void *));
break;
/* request events */
case AISL_STREAM_OPEN:
i_val = va_arg(vl, int);
c_ptr = va_arg(vl, char *);
c_ptr2 = va_arg(vl, char *);
res = ((aisl_stream_open_t) lst->cb)(source, i_val, c_ptr, c_ptr2);
break;
case AISL_STREAM_HEADER:
c_ptr = va_arg(vl, char *);
c_ptr2 = va_arg(vl, char *);
res = ((aisl_stream_header_t) lst->cb)(source, c_ptr, c_ptr2);
break;
case AISL_STREAM_INPUT:
/*printf("AISL> raise AISL_STREAM_INPUT\n");*/
c_ptr = va_arg(vl, char *);
i_val = va_arg(vl, int );
res = ((aisl_stream_input_t) lst->cb)(source, c_ptr, i_val);
break;
case AISL_STREAM_REQUEST:
/*printf("AISL> raise AISL_STREAM_REQUEST\n");*/
buffer_clear( STREAM(source)->buffer, 0);
res = ((aisl_stream_request_t) lst->cb)(source);
break;
case AISL_STREAM_ERROR:
res = ((aisl_stream_error_t) lst->cb)( source, va_arg(vl, char *));
break;
/* response events */
case AISL_STREAM_OUTPUT:
res = ((aisl_stream_output_t)lst->cb)(
source,
va_arg(vl, uint32_t)
);
break;
case AISL_STREAM_CLOSE:
res = ((aisl_stream_close_t)lst->cb)( source );
aisl_remove_listeners_for( self, source );
((aisl_stream_t) source)->u_ptr=NULL;
break;
default:
res = ((aisl_custom_event_t) lst->cb)(source, vl);
}
if (res) break;
}
}
return res;
}
/* -------------------------------------------------------------------------- */
/*
aisl_status_t
aisl_run( int * flags )
{
aisl_status_t exit_code = AISL_SUCCESS;
struct timeval timeout;
while( !(*flags & (1<<0)) )
{
exit_code = aisl_run_cycle( gHandle );
if (exit_code == AISL_IDLE)
{
timeout.tv_usec = 300;
timeout.tv_sec = 0;
select(0, NULL, NULL, NULL, &timeout);
}
}
return exit_code;
}
*/
/* -------------------------------------------------------------------------- */
#define STAGE_SERVER 0
#define STAGE_CLIENT 1
#define STAGE_DELAY 2
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_run_cycle( aisl_handle_t self )
{
int max = self->servers->count+self->clients->count+self->delays->count,
cnt = 0;
switch (self->stage)
{
case STAGE_SERVER:
while (self->iterator < self->servers->count )
{
server_t srv = (server_t)list_index(self->servers, self->iterator++);
if ( server_touch(srv) != AISL_IDLE )
return AISL_SUCCESS;
if ( ! (++cnt < max) ) return AISL_IDLE;
}
if ( ! (self->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) )
return AISL_IDLE;
self->iterator = 0;
self->stage++;
case STAGE_CLIENT:
while (self->iterator < self->clients->count )
{
int i = self->iterator++;
client_t cli = list_index(self->clients, i);
bool r = client_touch( cli );
if (client_is_timeout( cli ) )
aisl_raise_event( self, cli->server, AISL_CLIENT_TIMEOUT, cli );
if ( cli->fd == -1 )
{
client_free( cli );
list_remove_index(self->clients, i);
}
if (r) return AISL_SUCCESS;
if ( ! (++cnt < max) ) return AISL_IDLE;
}
self->iterator = 0;
self->stage++;
case STAGE_DELAY:
while (self->iterator < self->delays->count )
{
int i = self->iterator++;
delay_t dly = list_index(self->delays, i);
if( delay_is_expired(dly) )
{
if ( ((aisl_delay_timeout_t) dly->cb)(dly->u_data))
{
delay_reset(dly);
}
else
list_remove_index(self->delays, i);
return AISL_SUCCESS;
}
if ( ! (++cnt < max) ) return AISL_IDLE;
}
self->iterator = 0;
self->stage = 0;
}
return AISL_IDLE;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
const char *
aisl_handle_get_error( aisl_handle_t self )
{
return self->lastError;
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
int
aisl_sleep( aisl_handle_t self, unsigned long usec )
{
int maxfd=0;
size_t i;
struct timeval timeout = {0,usec};
memset(&timeout, 0, sizeof(struct timeval));
timeout.tv_usec = usec;
fd_set fs;
FD_ZERO (&fs);
for (i=0; i<self->servers->count; i++)
{
server_t s = list_index(self->servers, i);
if (s->fd != -1)
{
FD_SET(s->fd, &fs);
if (s->fd > maxfd) maxfd = s->fd;
}
}
for (i=0; i<self->clients->count; i++)
{
client_t c = list_index(self->clients, i);
if (c->fd != -1)
{
FD_SET(c->fd, &fs);
if (c->fd > maxfd) maxfd = c->fd;
}
}
return select(maxfd+1, &fs, NULL, NULL, &timeout);
}

View File

@ -1,47 +0,0 @@
#ifndef _AISL_HANDLE_H__
#define _AISL_HANDLE_H__
#include <stdbool.h>
#include <stdarg.h>
#include <aisl/handle.h>
#include "list.h"
#include <openssl/ssl.h>
#include "buffer.h"
#define AISL_HANDLE_HAS_STREAM_LISTENERS (1<<8)
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
aisl_status_t
aisl_set_delay( aisl_handle_t self,
aisl_callback_t cb,
uint32_t usec,
void * u_data );
/* -------------------------------------------------------------------------- */
bool
aisl_raise_event_vl( aisl_handle_t self,
void * source,
aisl_event_t e_id,
va_list vl );
/* -------------------------------------------------------------------------- */
SSL_CTX *
aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name );
/* -------------------------------------------------------------------------- */
void
aisl_remove_listeners_for( aisl_handle_t self, void * source );
/* -------------------------------------------------------------------------- */
#endif

View File

@ -1,6 +1,342 @@
#include <aisl/http.h>
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/* -------------------------------------------------------------------------- */
/**
* @file http.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief HTTP module source file
*
* @see https://lowenware.com/aisl/
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
#include "stream.h"
#include "debug.h"
#include "http.h"
struct http_request
{
char * method;
char * schema;
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;
static aisl_http_method_t
http_method_from_string( const char * method, int32_t length )
{
int i;
aisl_http_method_t methods[3] = {0, 0, 0};
switch(length)
{
case 3:
methods[0] = AISL_HTTP_GET;
methods[1] = AISL_HTTP_PUT;
methods[2] = AISL_HTTP_PRI;
break;
case 4:
methods[0] = AISL_HTTP_POST;
methods[1] = AISL_HTTP_HEAD;
break;
case 5:
methods[0] = AISL_HTTP_TRACE;
break;
case 6:
methods[0] = AISL_HTTP_DELETE;
break;
case 7:
methods[0] = AISL_HTTP_OPTIONS;
methods[1] = AISL_HTTP_CONNECT;
break;
}
for (i=0; i<sizeof(methods)/sizeof(aisl_http_method_t); i++)
{
if (!(methods[i]))
break;
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
return methods[i];
}
return AISL_HTTP_METHOD_UNKNOWN;
}
static aisl_http_version_t
http_version_from_string(const char * version_string)
{
if (strncmp(version_string, "HTTP/", 5)==0)
{
if (strncmp(&version_string[5], "0.9", 3)==0) return AISL_HTTP_0_9;
if (strncmp(&version_string[5], "1.0", 3)==0) return AISL_HTTP_1_0;
if (strncmp(&version_string[5], "1.1", 3)==0) return AISL_HTTP_1_1;
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
}
return 0;
}
/* Library Level */
http_parser_t
http_10_parse_request(char * data, int32_t * p_size, aisl_stream_t stream)
{
/* STEP 1. Split data according to HTTP request format
*
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
* ^ ^ ^ ^ ^ ^ ^ ^
* | | | | | | | |
* | | | | | | | +--- newline
* | | | | | | +------------- version
* | | | | | +------------------------- query
* | | | | +------------------------------------- path
* | | | +--------------------------------------- port
* | | +----------------------------------------------------- host
* | +------------------------------------------------------------ uri
* +---------------------------------------------------------------- method
*/
char * uri = NULL,
* uri_end = NULL,
* host = NULL,
* port = NULL,
* path = NULL,
* query = NULL,
* version = NULL,
* newline = NULL,
* method = data,
* method_end = NULL;
aisl_http_method_t http_method;
aisl_http_version_t http_version;
int32_t size = *p_size;
while(!newline && size--)
{
switch(*data)
{
case ' ':
if (!method_end)
method_end = data;
else if (path && !uri_end)
uri_end = data;
break;
case ':':
if (uri && !host)
host = data+3;
else if (host && !port)
port = data+1;
else if (version)
return HTTP_PARSER_ERROR;
break;
case '/':
if (!path && data > host)
{
path = data;
if (!uri)
uri = path;
}
else if (version && data-version != 4)
return HTTP_PARSER_ERROR;
break;
case '?':
if (!query)
query = data+1;
else if (version)
return HTTP_PARSER_ERROR;
break;
case '\n':
newline = data;
break;
case '\r':
if (!version)
return HTTP_PARSER_ERROR;
break;
default:
if (!uri && method_end)
uri = data;
else if (!version && uri_end)
version = data;
else if (version && data-version > 7)
return HTTP_PARSER_ERROR;
}
data++;
}
/* STEP 2. Verifly splitting was completed */
/* Was request sent? */
if (!newline)
return HTTP_PARSER_HUNGRY;
/* Check mandatory parts presence */
if (!method_end || !path || !uri_end || !version)
return HTTP_PARSER_ERROR;
*method_end = 0;
*newline = 0;
*uri_end = 0;
http_method = http_method_from_string(method, method_end - method);
if (http_method == AISL_HTTP_METHOD_UNKNOWN)
return HTTP_PARSER_ERROR;
if ((http_version = http_version_from_string(version))==0)
return HTTP_PARSER_ERROR;
if (query)
{
*(query-1)=0;
}
else
query = uri_end;
if (host)
{
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8))
return HTTP_PARSER_ERROR;
if (port)
*(port-1)=0;
}
stream->client->http_version = http_version;
aisl_stream_set_request(stream, http_method, path, query);
if (host)
aisl_stream_set_header(stream, "host", host);
/* how many characters has been read */
*(p_size)-=size;
return HTTP_PARSER_SUCCESS;
}
http_parser_t
http_10_parse_header(char * data, int32_t * p_size, aisl_stream_t stream)
{
int32_t size = *p_size;
char * key = data,
* colon = NULL,
* val = NULL,
* val_end = NULL,
* newline = NULL;
while(!newline && size-- )
{
switch(*data)
{
case ' ':
if (val && !val_end)
val_end = data;
break;
case ':':
if (!colon)
{
if (colon == key)
return HTTP_PARSER_ERROR;
colon = data;
}
break;
case '\n':
newline = data;
case '\r':
if (!val_end && val)
val_end = data;
break;
default:
if (!colon)
{
*data = tolower(*data);
}
else if (!val)
{
if (colon)
val = data;
}
if (val_end)
val_end = NULL;
}
data++;
}
if (!newline)
return HTTP_PARSER_HUNGRY;
if (colon && val && val_end)
{
*colon = 0;
*val_end = 0;
aisl_stream_set_header(stream, key, val);
*p_size -= size;
return HTTP_PARSER_SUCCESS;
}
else if (newline == key || (newline == key+1 && *key == '\r'))
{
return (aisl_stream_set_end_of_headers(stream) == 0) ? HTTP_PARSER_READY :
HTTP_PARSER_SUCCESS;
}
return HTTP_PARSER_ERROR;
}
http_parser_t
http_10_parse_body(char * data, int32_t * p_size, aisl_stream_t stream)
{
switch (aisl_stream_set_body(stream, data, *p_size))
{
case 0: return HTTP_PARSER_READY;
case -1: return HTTP_PARSER_ERROR;
default: return HTTP_PARSER_SUCCESS;
}
}
/* API Level */
__attribute__ ((visibility ("default") ))
const char *
@ -8,6 +344,7 @@ aisl_http_version_to_string(aisl_http_version_t version)
{
switch (version)
{
case AISL_HTTP_0_9: return "HTTP/0.9";
case AISL_HTTP_1_0: return "HTTP/1.0";
case AISL_HTTP_1_1: return "HTTP/1.1";
case AISL_HTTP_2_0: return "HTTP/2.0";
@ -15,7 +352,6 @@ aisl_http_version_to_string(aisl_http_version_t version)
return "";
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
const char *
@ -77,16 +413,6 @@ aisl_http_response_to_string(aisl_http_response_t code)
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
const char *
aisl_http_secure_to_string( int is_secure )
{
return (is_secure ? "HTTPS" : "HTTP");
}
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
const char *
@ -104,9 +430,12 @@ aisl_http_method_to_string( aisl_http_method_t method )
case AISL_HTTP_CONNECT: return "CONNECT";
case AISL_HTTP_PRI: return "PRI";
case AISL_HTTP_METHOD_UNKNOWN: break;
}
return "";
}
/* -------------------------------------------------------------------------- */

View File

@ -20,23 +20,24 @@
typedef enum
{
HTTP_PARSER_PENDING
HTTP_PARSER_SUCCESS
, HTTP_PARSER_READY
, HTTP_PARSER_HUNGRY
, HTTP_PARSER_ERROR
, HTTP_PARSER_FINISHED
} http_parser_t;
http_parser_t
http_10_parse_request(char * data, int32_t * size, aisl_client_t client);
http_10_parse_request(char * data, int32_t * size, aisl_stream_t stream);
http_parser_t
http_10_parse_header(char * data, int32_t * size, aisl_client_t client);
http_10_parse_header(char * data, int32_t * size, aisl_stream_t stream);
http_parser_t
http_10_parse_body(char * data, int32_t * size, aisl_client_t client);
http_10_parse_body(char * data, int32_t * size, aisl_stream_t stream);
#endif /* !AISL_HTTP_H */

View File

@ -19,7 +19,7 @@
#include <openssl/err.h>
#endif
#include "list.h"
#include "debug.h"
#include "str-utils.h"
#include "buffer.h"
@ -31,34 +31,6 @@
#include "instance.h"
struct callback
{
void * source;
aisl_callback_t f_ptr;
aisl_event_t event;
};
typedef struct callback * callback_t;
struct aisl
{
struct list server_spool;
struct list client_spool;
struct list callback_spool;
#ifndef AISL_WITHOUT_SSL
struct list ssl_spool;
#endif
char * last_error;
size_t iterator;
uint32_t accept_limit;
uint32_t silence_timeout;
};
#ifndef AISL_WITHOUT_SSL
static uint32_t m_instances = 0;
#endif
@ -89,9 +61,6 @@ aisl_new( aisl_config_t config )
if ( list_init(&instance->client_spool, config->client_spool_size) == -1 )
goto release;
if ( list_init(&instance->callback_spool, config->callback_spool_size) == -1 )
goto release;
#ifndef AISL_WITHOUT_SSL
if ( list_init(&instance->ssl_spool, config->ssl_spool_size) == -1 )
goto release;
@ -119,8 +88,6 @@ aisl_free( aisl_t instance )
list_release(&instance->server_spool, (list_destructor_t) aisl_server_free );
list_release(&instance->callback_spool, (list_destructor_t) free);
#ifndef AISL_WITHOUT_SSL
list_release(&instance->ssl_spool, (list_destructor_t) aisl_ssl_free );
#endif
@ -273,194 +240,148 @@ aisl_get_ssl_ctx( aisl_t instance, const char * host )
#endif
__attribute__ ((visibility ("default") ))
aisl_status_t
void
aisl_set_callback( aisl_t instance,
void * source,
aisl_event_t event,
aisl_callback_t callback )
{
callback_t cb;
if ( (cb = malloc(sizeof(struct callback))) != NULL )
switch( event )
{
cb->source = source;
cb->event = event;
cb->f_ptr = callback;
case AISL_EVENT_SERVER_OPEN:
instance->on_server_open = (aisl_on_server_open_t) callback;
break;
if ( list_append(&instance->callback_spool, cb) != -1 )
{
switch(event)
{
case AISL_EVENT_STREAM_OUTPUT:
if (source)
aisl_stream_set_chunked_output( (aisl_stream_t) source, true );
//( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
break;
case AISL_EVENT_SERVER_ERROR:
instance->on_server_error = (aisl_on_server_error_t) callback;
break;
/*
* case AISL_EVENT_STREAM_OPEN:
* instance->flags |= AISL_FLAG_HAS_STREAM_CALLBACKS;
*/
default:
break;
}
case AISL_EVENT_CLIENT_CONNECT:
instance->on_client_connect = (aisl_on_client_connect_t) callback;
break;
return AISL_SUCCESS;
}
free(cb);
case AISL_EVENT_CLIENT_DISCONNECT:
instance->on_client_disconnect = (aisl_on_client_disconnect_t) callback;
break;
case AISL_EVENT_CLIENT_TIMEOUT:
instance->on_client_timeout = (aisl_on_client_timeout_t) callback;
break;
case AISL_EVENT_STREAM_OPEN:
instance->on_stream_open = (aisl_on_stream_open_t) callback;
break;
case AISL_EVENT_STREAM_HEADER:
instance->on_stream_header = (aisl_on_stream_header_t) callback;
break;
case AISL_EVENT_STREAM_INPUT:
instance->on_stream_input = (aisl_on_stream_input_t) callback;
break;
case AISL_EVENT_STREAM_REQUEST:
instance->on_stream_request = (aisl_on_stream_request_t) callback;
break;
case AISL_EVENT_STREAM_OUTPUT:
instance->on_stream_output = (aisl_on_stream_output_t) callback;
break;
case AISL_EVENT_STREAM_CLOSE:
instance->on_stream_close = (aisl_on_stream_close_t) callback;
break;
case AISL_EVENT_STREAM_ERROR:
instance->on_stream_error = (aisl_on_stream_error_t) callback;
break;
}
return AISL_MALLOC_ERROR;
}
__attribute__ ((visibility ("default") ))
void
aisl_unset_callbacks_for( aisl_t instance, void * source )
{
size_t i = instance->callback_spool.count;
if (i)
{
for(i=i-1; i <= 0; i-- )
{
callback_t callback = LIST_INDEX(instance->callback_spool, i);
if ( callback->source == source )
{
free(callback);
list_remove_index(&instance->callback_spool, i);
}
}
}
}
__attribute__ ((visibility ("default") ))
bool
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... )
{
DPRINTF("event %s", aisl_event_to_string(event));
va_list vl;
bool result;
va_start(vl, event);
result = aisl_raise_vl(instance, source, event, vl);
va_end(vl);
return result;
}
static bool
aisl_on_source_event( void * source, aisl_callback_t cb, va_list vl_args )
{
aisl_on_source_event_t callback = (aisl_on_source_event_t) cb;
(void) vl_args;
return callback( (void *) source);
}
static bool
aisl_on_stream_open( void * source, aisl_callback_t cb, va_list vl_args )
{
aisl_on_stream_open_t callback = (aisl_on_stream_open_t) cb;
aisl_http_method_t method = va_arg(vl_args, aisl_http_method_t);
char * path = va_arg(vl_args, char *);
char * query = va_arg(vl_args, char *);
return callback( (aisl_stream_t) source, method, path, query);
}
static bool
aisl_on_stream_header( void * source, aisl_callback_t cb, va_list vl_args )
{
aisl_on_stream_header_t callback = (aisl_on_stream_header_t) cb;
char * key = va_arg(vl_args, char *);
char * val = va_arg(vl_args, char *);
return callback( (aisl_stream_t) source, key, val );
}
static bool
aisl_on_stream_input( void * source, aisl_callback_t cb, va_list vl_args )
{
aisl_on_stream_input_t callback = (aisl_on_stream_input_t) cb;
size_t len = va_arg(vl_args, size_t);
char * data = va_arg(vl_args, char *);
return callback( (aisl_stream_t) source, data, len );
}
__attribute__ ((visibility ("default") ))
bool
aisl_raise_vl( aisl_t instance,
void * source,
aisl_event_t event,
va_list vl_args )
{
va_list vl_copy;
size_t i;
callback_t cb;
bool result = false,
stop_propg = false;
i = instance->callback_spool.count;
while(i--)
switch( event )
{
cb = LIST_INDEX(instance->callback_spool, i);
case AISL_EVENT_SERVER_OPEN:
if (instance->on_server_open)
instance->on_server_open((aisl_server_t)source);
break;
if (cb->event == event && (source == cb->source || cb->source == NULL))
{
va_copy(vl_copy, vl_args);
switch(event)
case AISL_EVENT_SERVER_ERROR:
if (instance->on_server_error)
instance->on_server_error((aisl_server_t)source);
break;
case AISL_EVENT_CLIENT_CONNECT:
if (instance->on_client_connect)
instance->on_client_connect((aisl_client_t)source);
break;
case AISL_EVENT_CLIENT_DISCONNECT:
if (instance->on_client_disconnect)
instance->on_client_disconnect((aisl_client_t)source);
break;
case AISL_EVENT_CLIENT_TIMEOUT:
if (instance->on_client_timeout)
instance->on_client_timeout((aisl_client_t)source);
break;
case AISL_EVENT_STREAM_OPEN:
if (instance->on_stream_open)
{
/* server events */
case AISL_EVENT_SERVER_OPEN:
case AISL_EVENT_SERVER_ERROR:
case AISL_EVENT_CLIENT_CONNECT:
case AISL_EVENT_CLIENT_DISCONNECT:
case AISL_EVENT_CLIENT_TIMEOUT:
case AISL_EVENT_STREAM_REQUEST:
case AISL_EVENT_STREAM_CLOSE:
case AISL_EVENT_STREAM_ERROR:
case AISL_EVENT_STREAM_OUTPUT:
stop_propg = aisl_on_source_event(source, cb->f_ptr, vl_copy);
break;
case AISL_EVENT_STREAM_OPEN:
stop_propg = aisl_on_stream_open(source, cb->f_ptr, vl_copy);
break;
case AISL_EVENT_STREAM_HEADER:
stop_propg = aisl_on_stream_header(source, cb->f_ptr, vl_copy);
break;
case AISL_EVENT_STREAM_INPUT:
stop_propg = aisl_on_stream_input(source, cb->f_ptr, vl_copy);
break;
default:
stop_propg = ((aisl_on_custom_event_t) cb->f_ptr)(source, vl_copy);
aisl_http_method_t method = va_arg(vl, aisl_http_method_t);
const char * path = va_arg(vl, const char *);
const char * query = va_arg(vl, const char *);
instance->on_stream_open((aisl_stream_t)source, method, path, query);
}
break;
va_end(vl_copy);
case AISL_EVENT_STREAM_HEADER:
if (instance->on_stream_header)
{
const char * key = va_arg(vl, const char *);
const char * val = va_arg(vl, const char *);
instance->on_stream_header((aisl_stream_t)source, key, val);
}
break;
result = true;
case AISL_EVENT_STREAM_INPUT:
if (instance->on_stream_input)
{
const char * data = va_arg(vl, const char *);
int32_t size = va_arg(vl, int32_t);
instance->on_stream_input((aisl_stream_t)source, data, size);
}
break;
if (stop_propg)
break;
}
case AISL_EVENT_STREAM_REQUEST:
if (instance->on_stream_request)
instance->on_stream_request((aisl_stream_t)source);
break;
case AISL_EVENT_STREAM_OUTPUT:
if (instance->on_stream_output)
instance->on_stream_output((aisl_stream_t)source);
break;
case AISL_EVENT_STREAM_CLOSE:
if (instance->on_stream_close)
instance->on_stream_close((aisl_stream_t)source);
break;
case AISL_EVENT_STREAM_ERROR:
if (instance->on_stream_error)
instance->on_stream_error((aisl_stream_t)source);
break;
}
return result;
va_end(vl);
}
@ -470,30 +391,32 @@ aisl_run_cycle( aisl_t instance )
{
aisl_status_t result = AISL_IDLE;
size_t i = instance->iterator,
max = instance->server_spool.count + instance->client_spool.count;
int32_t i = instance->iterator,
max = instance->server_spool.count + instance->client_spool.count;
while ( result == AISL_IDLE && max != 0)
{
aisl_client_t cli;
aisl_client_t cli = NULL;
if (i < instance->server_spool.count)
{
if (instance->client_spool.count < instance->accept_limit)
{
aisl_server_t srv;
SSL_CTX * ssl_ctx;
/* SSL_CTX * ssl_ctx; */
srv = (aisl_server_t) LIST_INDEX(instance->server_spool, i);
/*
ssl_ctx = (aisl_server_get_ssl(srv)) ?
aisl_get_ssl_ctx(instance, NULL) :
NULL;
*/
result = aisl_server_touch(srv, &cli);
result = aisl_server_touch(srv, &cli, ssl_ctx);
if (result == AISL_SUCCESS)
if (cli)
{
DPRINTF("Accepted %p", (void*)cli);
if ( list_append(&instance->client_spool, cli) != -1 )
{
aisl_raise(instance, cli, AISL_EVENT_CLIENT_CONNECT);
@ -513,7 +436,7 @@ aisl_run_cycle( aisl_t instance )
}
else
{
size_t j = i - instance->server_spool.count;
int32_t j = i - instance->server_spool.count;
if (j < instance->client_spool.count)
{

View File

@ -15,6 +15,38 @@
#endif
#include <aisl/instance.h>
#include "list.h"
struct aisl
{
struct list server_spool;
struct list client_spool;
#ifndef AISL_WITHOUT_SSL
struct list ssl_spool;
#endif
char * last_error;
int32_t iterator;
uint32_t accept_limit;
uint32_t silence_timeout;
/* callbacks */
aisl_on_server_open_t on_server_open;
aisl_on_server_error_t on_server_error;
aisl_on_client_connect_t on_client_connect;
aisl_on_client_disconnect_t on_client_disconnect;
aisl_on_client_timeout_t on_client_timeout;
aisl_on_stream_open_t on_stream_open;
aisl_on_stream_header_t on_stream_header;
aisl_on_stream_input_t on_stream_input;
aisl_on_stream_request_t on_stream_request;
aisl_on_stream_output_t on_stream_output;
aisl_on_stream_close_t on_stream_close;
aisl_on_stream_error_t on_stream_error;
};
#ifndef AISL_WITHOUT_SSL
@ -37,4 +69,16 @@ aisl_get_ssl_ctx( aisl_t instance, const char * server_name );
void
aisl_set_error( aisl_t instance, const char * err_msg );
#endif /* !INSTANCE_H */
/**
* @brief Raises event from source.
* @param instance a pointer to #aisl_t instance.
* @param source a pointer to an event source.
* @param event a code of event.
* @param ... a list of arguments specific for event.
*/
void
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... );
#endif /* !AISL_INSTANCE_H */

View File

@ -14,6 +14,7 @@
*/
#include <stdlib.h>
#include "debug.h"
#include "list.h"
int32_t
@ -56,11 +57,15 @@ list_append(list_t list, void * entry)
{
int32_t pos = list->count;
DPRINTF("pos = %d", pos);
if ( !(pos < list->size) )
{
DPRINTF("extending, size = %d", list->size);
void ** new_list;
int32_t new_size = pos + 1;
DPRINTF("extending, ptr = %p", (void*)list->data);
if ( (new_list = realloc( list->data, new_size*sizeof(void*) )) == NULL )
return -1;

View File

@ -1,490 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "str-utils.h"
#include <aisl/http.h>
#include "parser.h"
#include "globals.h"
#include "stream.h"
/* length(multipart/form-data; boundary=) = 30 */
#define B_OFFSET 30
/* common HTTP headers */
static const char cCookie[] = "Cookie";
static const char cContentType[] = "Content-Type";
static const char cContentLength[] = "Content-Length";
/*
static const char cConnection[] = "Connection";
static const char cHost[] = "Host";
static const char cUserAgent[] = "User-Agent";
static const char cAccept[] = "Accept";
static const char cAcceptLanguage[] = "Accept-Language";
static const char cAcceptEncoding[] = "Accept-Encoding";
*/
#define CLI_STREAM(x) ( ((stream_t)list_index(x->streams,x->istream)) )
/*
static void
debug(const char * label, char * buffer, int len)
{
printf("<<< %s [", label);
fwrite(buffer, 1, len, stdout);
printf("]\n");
}
*/
static pair_t
pair_new(const char * key, int length)
{
pair_t p = calloc(1, sizeof(struct pair));
if (p)
{
p->key = str_ncopy(key, length);
if (!p->key)
{
free(p);
p = NULL;
}
}
return p;
}
/* HTTP METHOD -------------------------------------------------------------- */
parser_status_t
parse_request_method(client_t cli, char ** b_ptr, int *b_len)
{
char * cur = memchr(*b_ptr, ' ', *b_len);
if (!cur)
{
if (*b_len < 8)
return PARSER_HUNGRY;
else
return PARSER_FAILED;
}
int l = (int) (cur - *b_ptr);
stream_t s = list_index(cli->streams, cli->istream);
switch( l )
{
case 3:
if (strncmp(*b_ptr, "GET", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_GET;
else if (strncmp(*b_ptr, "PRI", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_PRI;
else if (strncmp(*b_ptr, "PUT", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_PUT;
else
return PARSER_FAILED;
break;
case 4:
if (strncmp(*b_ptr, "POST", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_POST;
else if (strncmp(*b_ptr, "HEAD", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_HEAD;
else
return PARSER_FAILED;
break;
case 5:
if (strncmp(*b_ptr, "TRACE", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_TRACE;
else
return PARSER_FAILED;
break;
case 6:
if (strncmp(*b_ptr, "DELETE", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_DELETE;
else
return PARSER_FAILED;
break;
case 7:
if (strncmp(*b_ptr, "OPTIONS", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_OPTIONS;
else if (strncmp(*b_ptr, "CONNECT", l)==0)
ASTREAM(s)->request_method = AISL_HTTP_CONNECT;
else
return PARSER_FAILED;
break;
default:
return PARSER_FAILED;
}
*b_ptr += ++l;
*b_len -= l; /* count method + space character */
s->state = STREAM_REQUEST_PATH;
return PARSER_PENDING;
}
/* HTTP REQUEST_URI and HOST ------------------------------------------------ */
static void
str_to_lower( char * src )
{
while (*src)
{
*src = tolower(*src);
src++;
}
}
parser_status_t
parse_request_path(client_t cli, char ** b_ptr, int *b_len)
{
parser_status_t result = PARSER_PENDING;
stream_t s = list_index(cli->streams, cli->istream);
int i;
char * host = NULL,
* path = NULL,
* query = NULL;
for ( i=0; i<*b_len; i++)
{
switch( (*b_ptr)[i] )
{
case ':':
if (host) /* if host is set, we parse host and it could not contain : */
{
result = PARSER_FAILED;
break;
}
else /* could be protocol separator */
{
if (i==5)
{
if (*b_len > 7 && strncmp(*b_ptr, "http://", 7)==0 ) /* protocol defined */
{
host = &(*b_ptr)[i+3];
i+=2;
continue;
}
result = PARSER_FAILED; /* something is wrong */
break;
}
}
continue;
case '?':
query = (*b_ptr) +i;
continue;
case ' ':
if (! path) path = *b_ptr;
if (query)
{
ASTREAM(s)->path = str_ncopy(path, (uint32_t) (query-path));
query++;
ASTREAM(s)->query = str_ncopy(query, (uint32_t)(&(*b_ptr)[i]-query));
}
else
ASTREAM(s)->path = str_ncopy(path, (uint32_t) (&(*b_ptr)[i]-path));
*b_len -= ++i;
*b_ptr += i;
s->state = STREAM_REQUEST_PROTOCOL;
return PARSER_PENDING;
break;
case '/':
if (host)
{
/* debug(" > host", host, (int) (&(*b_ptr)[i] - host)); */
pair_t p = malloc(sizeof(struct pair));
if (p)
{
p->key = str_copy("host");
p->value = str_ncopy(host, (uint32_t) (&(*b_ptr)[i] - host));
}
s->headers = list_new(AISL_MIN_HEADERS);
list_append(s->headers, p);
host = NULL;
path = &(*b_ptr)[i];
}
default:
continue;
}
break;
}
if (result == PARSER_PENDING) /* end space was not found */
result = PARSER_HUNGRY;
/*
if (result == PARSER_HUNGRY && *b_len == gBuffer->size)
result = PARSER_FAILED;*/ /* buffer is overloaded */
return result;
}
/* HTTP VERSION ------------------------------------------------------------- */
parser_status_t
parse_request_protocol(client_t cli, char ** b_ptr, int *b_len)
{
stream_t stream = CLI_STREAM(cli);
/* HTTP/X.X = 8 characters minimal */
if (*b_len < 8) return PARSER_HUNGRY;
char * ptr = memchr(*b_ptr, '\n', *b_len);
if (!ptr) return PARSER_HUNGRY;
int l = (int) (ptr - *b_ptr);
if (strncmp(*b_ptr, "HTTP/", 5)==0)
{
if (strncmp(&(*b_ptr)[5], "2.0", 3)==0) cli->protocol = AISL_HTTP_2_0; else
if (strncmp(&(*b_ptr)[5], "1.1", 3)==0) cli->protocol = AISL_HTTP_1_1; else
if (strncmp(&(*b_ptr)[5], "1.0", 3)==0) cli->protocol = AISL_HTTP_1_0; else
return PARSER_FAILED;
if ( (l==10 && *b_ptr[8]=='\r') || (l==9) )
{
/*r->version = str_ncopy(*b_ptr, 8); */
*b_ptr += ++l;
*b_len -=l;
stream->state=STREAM_REQUEST_HEADER_KEY;
aisl_raise_event(
cli->server->owner,
stream,
AISL_STREAM_OPEN,
ASTREAM(stream)->request_method,
ASTREAM(stream)->path,
ASTREAM(stream)->query
);
if (!stream->headers)
stream->headers = list_new(AISL_MIN_HEADERS);
else if (stream->headers->count == 1)
{
/* raise event for Host header */
pair_t p = list_index(stream->headers, 0);
aisl_raise_event(
cli->server->owner,
stream,
AISL_STREAM_HEADER,
p->key, p->value
);
}
return PARSER_PENDING;
}
}
return PARSER_FAILED;
}
/* HTTP HEADER KEY ---------------------------------------------------------- */
parser_status_t
parse_request_header_key(client_t cli, char ** b_ptr, int *b_len)
{
stream_t stream = CLI_STREAM(cli);
int l;
/* check end of headers */
switch (*b_ptr[0])
{
case '\r':
if (*b_len>1)
{
if( strncmp(*b_ptr, "\r\n", 2)==0 )
{
l=2;
}
else
return PARSER_FAILED;
}
else
return PARSER_HUNGRY;
break;
case '\n':
l=1;
break;
default:
l=0;
}
if (l)
{
/* end of headers */
*b_len -= l;
*b_ptr += l;
stream->state = STREAM_REQUEST_CONTENT;
/* aisl_raise_event(cli->server->owner, CLI_STREAM(cli), AISL_STREAM_OPEN);
* */
return PARSER_PENDING;
}
/* header key */
char * ptr = memchr(*b_ptr, ':', *b_len);
if (!ptr)
{
/*
if (*b_len == gBuffer->size)
return PARSER_FAILED;
*/
return PARSER_HUNGRY;
}
l = (int) (ptr-*b_ptr);
pair_t ppp = pair_new(*b_ptr, l);
str_to_lower(ppp->key);
if (ppp)
list_append(stream->headers, ppp);
*b_len -= ++l;
*b_ptr += l;
stream->state=STREAM_REQUEST_HEADER_VALUE;
return PARSER_PENDING;
}
/* HTTP HEADER VALUE -------------------------------------------------------- */
parser_status_t
parse_request_header_value(client_t cli, char ** b_ptr, int *b_len)
{
stream_t stream = CLI_STREAM(cli);
/* skip first space */
if (*b_len)
{
if ((*b_ptr)[0]==' ')
{
(*b_ptr)++;
(*b_len)--;
if (*b_len == 0)
return PARSER_HUNGRY;
}
}
else
return PARSER_HUNGRY;
char * ptr = memchr(*b_ptr, '\n', *b_len);
int l;
l = (ptr) ? (int) (ptr-*b_ptr) : *b_len;
uint32_t index = stream->headers->count -1;
pair_t p = list_index(stream->headers, index);
p->value = str_ncat(p->value, *b_ptr, (l && (*b_ptr)[l-1]=='\r') ? l-1 : l);
*b_len -= ++l;
*b_ptr += l;
stream->state=STREAM_REQUEST_HEADER_KEY;
/* todo: add limit for maximal header length */
if (ptr)
{
if (str_cmpi(p->key, cCookie )==0)
{
/* parse cookies */
}
else if (str_cmpi(p->key, cContentType )==0)
{
/* CLI(r)->c_type_index = index; */
}
else if (str_cmpi(p->key, cContentLength )==0)
{
stream->c_length = strtol(p->value, NULL, 0);
}
/* CLI(r)->c_length = strtol(p->value, NULL, 10); */
aisl_raise_event(
cli->server->owner,
stream,
AISL_STREAM_HEADER,
p->key, p->value
);
return PARSER_PENDING;
}
else
return PARSER_HUNGRY;
}
parser_status_t
parse_request_content(client_t cli, char ** b_ptr, int *b_len)
{
stream_t stream = CLI_STREAM(cli);
/*
fprintf(stdout, "AISL [%d]> ", CLI_STREAM(cli)->c_length);
fwrite(*b_ptr, 1, *b_len, stdout);
fprintf(stdout, "\n<");
*/
if (stream->c_length)
{
int l = *b_len;
aisl_raise_event(
cli->server->owner,
stream,
AISL_STREAM_INPUT,
*b_ptr,
l
);
*b_ptr += l;
*b_len = 0;
stream->c_length -= l;
}
else
goto request_ready;
if ( stream->c_length == 0 )
{
request_ready:
stream->state=STREAM_REQUEST_READY;
aisl_raise_event( cli->server->owner, stream, AISL_STREAM_REQUEST );
return PARSER_FINISHED;
}
else
return PARSER_HUNGRY;
}

View File

@ -1,52 +0,0 @@
#ifndef _AISL_PARSER_H_
#define _AISL_PARSER_H_
#include <aisl/aisl.h>
#include "client.h"
/* parser status ------------------------------------------------------------ */
typedef enum
{
PARSER_PENDING, /* process pending */
PARSER_FINISHED, /* successful finish */
PARSER_HUNGRY, /* not enough data in buffer */
PARSER_FAILED /* error happened */
} parser_status_t;
/* parse HTTP Request ------------------------------------------------------- */
parser_status_t
parse_request_method(client_t cli, char ** b_ptr, int *b_len);
/* parse HTTP Request URI --------------------------------------------------- */
parser_status_t
parse_request_path(client_t cli, char ** b_ptr, int *b_len);
/* parse HTTP Version ------------------------------------------------------- */
parser_status_t
parse_request_protocol(client_t cli, char ** b_ptr, int *b_len);
/* parse HTTP header key ---------------------------------------------------- */
parser_status_t
parse_request_header_key(client_t cli, char ** b_ptr, int *b_len);
/* parse HTTP header value -------------------------------------------------- */
parser_status_t
parse_request_header_value(client_t cli, char ** b_ptr, int *b_len);
/* parse HTTP data ---------------------------------------------------------- */
parser_status_t
parse_request_content(client_t cli, char ** b_ptr, int *b_len);
/* -------------------------------------------------------------------------- */
#endif

View File

@ -15,22 +15,14 @@
#include <openssl/ssl.h>
#include "debug.h"
#include "str-utils.h"
#include "instance.h"
#include "client.h"
#include "server.h"
/**
* @brief HTTP(s) server data structure represented by #aisl_server_t pointer.
*/
struct aisl_server
{
struct sockaddr_in address; /**< TCP server address to listen to. */
aisl_t instance; /**< Associated AISL instance pointer. */
int fd; /**< System socket descriptor. */
bool ssl; /**< SSL enabled/disabled flag. */
};
/**
@ -71,37 +63,36 @@ aisl_server_open(aisl_server_t server)
return AISL_SYSCALL_ERROR;
}
/**
* @brief Tries to accept a new client.
* @param server a pointer to #aisl_server_t instance.
* @param p_client a pointer to store #aisl_client_t instance pointer.
* @param ssl_ctx a pointer to SSL context or NULL if connection is unsecure.
* @return #aisl_status_t code.
*/
static aisl_status_t
aisl_server_accept( aisl_server_t server,
aisl_client_t * p_client,
SSL_CTX * ssl_ctx )
aisl_client_t * p_client )
{
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
*p_client = NULL;
fd = accept(server->fd, (struct sockaddr *) &addr, &len);
if (fd != -1)
{
int flags;
DPRINTF("accepted fd=%d", fd);
if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
{
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == 0)
{
return (!(*p_client = aisl_client_new(server, fd, &addr, ssl_ctx))) ?
return (!(*p_client = aisl_client_new(server, fd, &addr))) ?
AISL_MALLOC_ERROR : AISL_SUCCESS;
}
}
@ -119,8 +110,7 @@ aisl_server_accept( aisl_server_t server,
aisl_status_t
aisl_server_touch( aisl_server_t server,
aisl_client_t * p_client,
SSL_CTX * ssl_ctx )
aisl_client_t * p_client )
{
aisl_status_t result;
@ -135,7 +125,7 @@ aisl_server_touch( aisl_server_t server,
aisl_raise(server->instance, server, event);
}
else
result = aisl_server_accept(server, p_client, ssl_ctx);
result = aisl_server_accept(server, p_client);
return result;
}

View File

@ -14,6 +14,16 @@
#define AISL_SERVER(x) ((aisl_server_t) x)
/**
* @brief HTTP(s) server data structure represented by #aisl_server_t pointer.
*/
struct aisl_server
{
struct sockaddr_in address; /**< TCP server address to listen to. */
aisl_t instance; /**< Associated AISL instance pointer. */
int fd; /**< System socket descriptor. */
bool ssl; /**< SSL enabled/disabled flag. */
};
/**
* @brief Allocates and instance of #aisl_server_t.
@ -40,7 +50,6 @@ aisl_server_free(aisl_server_t server);
* new client connecting to the server.
* @param server a pointer to #aisl_server_t instance.
* @param p_client a pointer to store #aisl_client_t instance pointer.
* @param ssl_ctx a pointer to SSL context or NULL if connection is unsecure.
* @return #aisl_status_t code:
* - AISL_SUCCESS if client connected,
* - AISL_IDLE if there is no client to connect,
@ -48,8 +57,7 @@ aisl_server_free(aisl_server_t server);
*/
aisl_status_t
aisl_server_touch( aisl_server_t server,
aisl_client_t * p_client,
SSL_CTX * ssl_ctx );
aisl_client_t * p_client );
/**

View File

@ -1,21 +0,0 @@
#include <aisl/status.h>
/* -------------------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
const char *
aisl_status_to_string(aisl_status_t status)
{
switch( status )
{
case AISL_SUCCESS: return "success";
case AISL_IDLE: return "idle";
case AISL_MALLOC_ERROR: return "malloc error";
case AISL_SYSCALL_ERROR: return "system call error";
case AISL_EXTCALL_ERROR: return "external call error";
}
return "";
}
/* -------------------------------------------------------------------------- */

View File

@ -1,12 +1,13 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include "instance.h"
#include "client.h"
#include "server.h"
#include "buffer.h"
#include "str-utils.h"
#include "debug.h"
#include "stream.h"
#define FLAG_FLUSHED (1<<0)
@ -17,24 +18,6 @@
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
struct aisl_stream
{
struct buffer buffer;
aisl_client_t client;
void * u_ptr;
uint64_t content_length;
int32_t head_offset;
int32_t body_offset;
int id;
int flags;
aisl_http_response_t response;
aisl_stream_state_t state;
};
/* Library level */
static void
@ -47,24 +30,23 @@ aisl_stream_reset(aisl_stream_t stream, bool initial)
instance = aisl_stream_get_instance(stream);
aisl_raise(instance, stream, AISL_EVENT_STREAM_CLOSE);
aisl_unset_callbacks_for(instance, stream);
}
buffer_release(&stream->buffer);
stream->u_ptr = NULL;
stream->content_length = AISL_AUTO_LENGTH;
stream->head_offset = 0;
stream->head_offset = 0;
stream->flags = 0;
stream->state = AISL_STREAM_STATE_IDLE;
stream->response = AISL_HTTP_OK;
stream->http_response = AISL_HTTP_OK;
}
aisl_stream_t
aisl_stream_new(aisl_client_t client, int id)
{
aisl_stream_t stream = malloc(sizeof(struct aisl_stream));
aisl_stream_t stream = calloc(1, sizeof(struct aisl_stream));
if (stream)
{
@ -145,6 +127,112 @@ aisl_stream_get_state(aisl_stream_t stream)
return stream->state;
}
void
aisl_stream_set_request( aisl_stream_t stream,
aisl_http_method_t http_method,
const char * path,
const char * query )
{
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
DPRINTF(
"%s -> path: %s, query: %s"
, aisl_http_method_to_string(http_method)
, path
, query
);
aisl_raise(
aisl_stream_get_instance(stream),
stream,
AISL_EVENT_STREAM_OPEN,
http_method,
path,
query
);
}
void
aisl_stream_set_header( aisl_stream_t stream,
const char * key,
const char * value )
{
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
return;
if (strcmp(key, "content-length")==0)
stream->content_length = strtoll(value, NULL, 10);
else if (strcmp(key, "connection")==0)
aisl_client_set_keepalive(
stream->client
, (str_cmpi(value, "close")==0) ? false : true
);
DPRINTF("%s: %s", key, value);
aisl_raise(
aisl_stream_get_instance(stream),
stream,
AISL_EVENT_STREAM_HEADER,
key,
value
);
}
int
aisl_stream_set_end_of_headers( aisl_stream_t stream )
{
int result;
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER)
{
stream->state = AISL_STREAM_STATE_WAIT_BODY;
result = (stream->content_length == 0);
}
else
result = 2;
return result;
}
int
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size )
{
int result;
if (stream->state == AISL_STREAM_STATE_WAIT_BODY)
{
if ( !(stream->content_length < size) )
{
if (stream->content_length == 0)
{
stream->state = AISL_STREAM_STATE_READY;
result = 0;
}
else
result = 1;
aisl_raise(
aisl_stream_get_instance(stream),
stream,
AISL_EVENT_STREAM_OUTPUT,
data,
size
);
}
else
result = -1;
}
else
result = 2;
return result;
}
/* API Level */
/* Why it was here?
@ -296,17 +384,19 @@ aisl_response( aisl_stream_t stream
/* check if those headers were already sent */
if (stream->state > AISL_STREAM_STATE_READY) return AISL_IDLE;
stream->response = status_code;
stream->http_response = status_code;
stream->content_length = content_length;
buffer_init( &stream->buffer
, (content_length != AISL_AUTO_LENGTH) ? content_length : 0);
l = buffer_append_printf( &stream->buffer
, "%s %d %s\r\n"
, aisl_get_http_version(stream)
, status_code
, aisl_http_response_to_string(status_code) );
l = buffer_append_printf(
&stream->buffer
, "%s %d %s\r\n"
, aisl_http_version_to_string(stream->client->http_version)
, status_code
, aisl_http_response_to_string(status_code)
);
if ( l == -1 )
return AISL_MALLOC_ERROR;

View File

@ -3,6 +3,7 @@
#include <aisl/types.h>
#include <aisl/stream.h>
#include "buffer.h"
#define AISL_STREAM(x) ((aisl_stream_t) x)
@ -23,6 +24,24 @@ typedef enum
} aisl_stream_state_t;
struct aisl_stream
{
struct buffer buffer;
aisl_client_t client;
void * u_ptr;
uint64_t content_length;
int32_t head_offset;
int32_t body_offset;
int id;
int flags;
aisl_http_response_t http_response;
aisl_stream_state_t state;
};
aisl_stream_t
aisl_stream_new(aisl_client_t client, int id );
@ -62,4 +81,24 @@ aisl_stream_is_done(aisl_stream_t stream);
aisl_stream_state_t
aisl_stream_get_state(aisl_stream_t stream);
void
aisl_stream_set_request( aisl_stream_t stream,
aisl_http_method_t http_method,
const char * path,
const char * query );
void
aisl_stream_set_header( aisl_stream_t stream,
const char * key,
const char * value );
int
aisl_stream_set_end_of_headers( aisl_stream_t stream );
int
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size );
#endif /* !AISL_STREAM_H */

61
src/types.c Normal file
View File

@ -0,0 +1,61 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file types.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Types stringifiers module
*
* @see https://lowenware.com/aisl/
*/
#include <aisl/types.h>
#ifndef WITHOUT_STRINGIFIERS
__attribute__ ((visibility ("default") ))
const char *
aisl_status_to_string(aisl_status_t status)
{
switch(status)
{
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
case AISL_SUCCESS: return "AISL_SUCCESS";
case AISL_IDLE: return "AISL_IDLE";
}
return "UNKNOWN";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_event_to_string( aisl_event_t event )
{
switch(event)
{
case AISL_EVENT_SERVER_OPEN: return "SERVER_OPEN";
case AISL_EVENT_SERVER_ERROR: return "SERVER_ERROR";
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT_CONNECT";
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT_DISCONNECT";
case AISL_EVENT_CLIENT_TIMEOUT: return "CLIENT_TIMEOUT";
case AISL_EVENT_STREAM_OPEN: return "STREAM_OPEN";
case AISL_EVENT_STREAM_HEADER: return "STREAM_HEADER";
case AISL_EVENT_STREAM_INPUT: return "STREAM_INPUT";
case AISL_EVENT_STREAM_REQUEST: return "STREAM_REQUEST";
case AISL_EVENT_STREAM_OUTPUT: return "STREAM_OUTPUT";
case AISL_EVENT_STREAM_CLOSE: return "STREAM_CLOSE";
case AISL_EVENT_STREAM_ERROR: return "STREAM_ERROR";
}
return "UNKNOWN";
}
#endif