Documentation and code base update

This commit is contained in:
Ilja Kartašov 2019-03-10 21:53:40 +01:00
parent 3efc86a341
commit 0b05984f85
19 changed files with 5116 additions and 701 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ tmp/*
*.gz *.gz
*.bz2 *.bz2
pkg/* pkg/*
doc/html
include/webstuff_bak include/webstuff_bak

2482
doc/api-reference.conf Normal file

File diff suppressed because it is too large Load Diff

21
doc/footer.html Normal file
View File

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

56
doc/header.html Normal file
View File

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

1596
doc/stylesheet.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /**
* <aisl/server.h> * @file aisl/client.h
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd.
* *
@ -12,4 +12,48 @@
#include <aisl/types.h> #include <aisl/types.h>
/**
* @brief Gets #aisl_server_t instance associated with client.
* @param client an #aisl_client_t instance pointer.
* @return an associated #aisl_server_t pointer.
*/
aisl_server_t
aisl_client_get_server(aisl_client_t client);
/**
* @brief Gets security connection status.
* @param client an #aisl_client_t instance pointer.
* @return true if SSL is enabled and false if disabled.
*/
bool
aisl_client_is_secure(aisl_client_t client);
/**
* @brief Gets client's connection state.
* @param client an #aisl_client_t instance pointer.
* @return true if client is online and false if is offline.
*/
bool
aisl_client_is_online(aisl_client_t client);
/**
* @brief Forcefully closes client's connection.
* @param client an #aisl_client_t instance pointer.
*/
void
aisl_client_close(aisl_client_t client);
/**
* @brief Gets HTTP protocol version.
* @param client an #aisl_client_t instance pointer.
* @return HTTP protocol version
*/
aisl_http_version_t
aisl_client_get_http_version(aisl_client_t client);
#endif /* !AISL_CLIENT_H */ #endif /* !AISL_CLIENT_H */

View File

@ -15,12 +15,13 @@
struct aisl_config struct aisl_config
{ {
uint32_t servers_spool_size; uint32_t server_spool_size;
uint32_t clients_spool_size; uint32_t client_spool_size;
uint32_t ssl_spool_size; uint32_t ssl_spool_size;
uint32_t callbacks_spool_size; uint32_t callback_spool_size;
uint32_t initial_buffer_size; uint32_t initial_buffer_size;
uint32_t clients_accept_limit; uint32_t client_accept_limit;
uint32_t client_silence_timeout;
}; };
typedef struct aisl_config * aisl_config_t; typedef struct aisl_config * aisl_config_t;

View File

@ -1,5 +1,5 @@
/* /**
* <aisl/instance.h> * @file aisl/instance.h
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd.
* *
@ -17,32 +17,70 @@
/* Initialization functions */ /**
* @brief Allocates new AISL instance.
*
* @param config a pointer to #aisl_config structure.
* @return an #aisl_t instance pointer.
*/
aisl_t aisl_t
aisl_new( aisl_config_t config ); aisl_new( aisl_config_t config );
/**
* @brief Frees previously allocated pointer of AISL instance.
* @param instance a pointer to #aisl_t instance.
*/
void void
aisl_free( aisl_t instance ); aisl_free( aisl_t instance );
/**
* @brief Allocates and registers HTTP server instance.
* @param instance a pointer to #aisl_t instance.
* @param address a null-terminated ip or hostname string.
* @param port a number of port to listen.
* @return a pointer representing HTTP server.
*/
aisl_server_t aisl_server_t
aisl_listen( aisl_t instance, const char * address, uint16_t port ); aisl_listen( aisl_t instance, const char * address, uint16_t port );
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
/**
* @brief Sets pair of SSL certificate and key for domain name.
* @param instance a pointer to #aisl_t instance.
* @param domain a null-terminated string with domain name.
* @param key_file a null-terminated string with path to private SSL key file.
* @param crt_file a null-terminated string with path to SSL certificate file.
* @return #aisl_status_t code.
*/
aisl_status_t aisl_status_t
aisl_set_ssl( aisl_t instance, aisl_set_ssl( aisl_t instance,
const char * hostname, const char * domain,
const char * key_file, const char * key_file,
const char * crt_file ); const char * crt_file );
#endif #endif
/* Callbacks and Events functions */
/**
* @brief Registers a callback for event and its source.
* If source is NULL, then callback will be executed for all events of specified
* type.
*
* Typical sources are:
* - #aisl_server_t,
* - #aisl_client_t,
* - #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_status_t
aisl_set_callback( aisl_t instance, aisl_set_callback( aisl_t instance,
void * source, void * source,
@ -50,33 +88,69 @@ aisl_set_callback( aisl_t instance,
aisl_callback_t callback ); aisl_callback_t callback );
aisl_status_t /**
* @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, ... ); aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... );
aisl_status_t /**
* @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, aisl_raise_vl( aisl_t instance,
void * source, void * source,
aisl_event_t event, aisl_event_t event,
va_list args ); 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 void
aisl_unset_callback_for( aisl_t instance, void * source ); aisl_unset_callbacks_for( aisl_t instance, void * source );
/* Control functions */ /**
* @brief A core function doing all the library routines.
* Designed to be called inside application main loop
* @param instance a pointer to #aisl_t instance.
* @return #aisl_status_t code.
*/
aisl_status_t aisl_status_t
aisl_run_cycle( aisl_t instance ); aisl_run_cycle( aisl_t instance );
const char * /**
aisl_get_error( aisl_t instance ); * @brief Function to sleep CPU if nothing to do.
* Calls select on all the opened sockets inside.
* @param instance a pointer to #aisl_t instance.
* @param usec a number of miliseconds to wait for any data on sockets.
* @return #aisl_status_t code.
*/
aisl_status_t aisl_status_t
aisl_sleep( aisl_t instance, uint32_t usec ); aisl_sleep( aisl_t instance, uint32_t usec );
/**
* @brief Get last error message.
* @param instance a pointer to #aisl_t instance.
* @return a null-terminated string with error message.
*/
const char *
aisl_get_error( aisl_t instance );
#endif /* !AISL_INSTANCE_H */ #endif /* !AISL_INSTANCE_H */

View File

@ -1,5 +1,5 @@
/* /**
* <aisl/server.h> * @file aisl/server.h
* *
* Copyright (c) 2017-2019 by Löwenware Ltd. * Copyright (c) 2017-2019 by Löwenware Ltd.
* *
@ -10,14 +10,46 @@
#ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768 #ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#define AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768 #define AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#include <arpa/inet.h>
#include <aisl/types.h> #include <aisl/types.h>
#ifdef AISL_WITH_SSL
aisl_status_t /**
* @brief Function to get appropriate AISL instance pointer from server pointer.
* @param server an #aisl_server_t pointer.
* @return an #aisl_t instance pointer.
*/
aisl_t
aisl_server_get_instance( aisl_server_t server );
/**
* @brief Copies server listen address information to sockaddr_in structure.
* @param server an #aisl_server_t pointer.
* @param address a pointer to sockaddr_in structure.
*/
void
aisl_server_get_address( aisl_server_t server, struct sockaddr_in * address);
#ifndef AISL_WITHOUT_SSL
/**
* @brief Function to switch on and off SSL for the #aisl_server_t.
* @param server an #aisl_server_t pointer.
* @param value a boolean value representing SSL enabled/disabled state.
*/
void
aisl_server_set_ssl( aisl_server_t server, bool value ); aisl_server_set_ssl( aisl_server_t server, bool value );
/**
* @brief Function to get on and off status of SSL for the #aisl_server_t.
* @param server an #aisl_server_t pointer.
* @return a boolean value representing SSL enabled/disabled state.
*/
bool
aisl_server_get_ssl( aisl_server_t server );
#endif #endif
#endif /* !AISL_SERVER_H */ #endif /* !AISL_SERVER_H */

View File

@ -196,6 +196,9 @@ aisl_event_to_string( aisl_event_t event );
/* real type event callbacks */ /* real type event callbacks */
typedef bool
(*aisl_on_source_event_t)( void * source );
typedef bool typedef bool
(*aisl_on_server_open_t)( aisl_server_t server ); (*aisl_on_server_open_t)( aisl_server_t server );

View File

@ -16,8 +16,8 @@
struct callback struct callback
{ {
void * source; void * source;
aisl_callback_t callback; aisl_callback_t f_ptr;
aisl_event_t eevent; aisl_event_t event;
}; };
typedef struct callback * callback_t; typedef struct callback * callback_t;

View File

@ -1,3 +1,4 @@
#include <time.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
@ -6,117 +7,82 @@
#include <fcntl.h> #include <fcntl.h>
#include <aisl/aisl.h> #include <aisl/aisl.h>
#include "client.h" #include "list.h"
#include "stream.h" #include "stream.h"
#include "parser.h" #include "client.h"
#include "globals.h"
#include "handle.h"
#ifndef OUTPUT_BUFFER_SIZE #define FLAG_KEEPALIVE (1<<0)
#define OUTPUT_BUFFER_SIZE 4096 #define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
struct aisl_client
{
struct sockaddr_in address; /**< Client's address structure */
aisl_server_t server; /**< Server instance associated with client */
list_t streams; /**< list of client streams */
struct buffer in; /**< Client's input buffer */
struct buffer out; /**< Client's output buffer */
#ifndef AISL_WITHOUT_SSL
SSL * ssl; /**< SSL pointer for encrypted connections */
#endif #endif
struct client time_t timestamp; /**< Last communication timestamp */
{
struct sockaddr_in address;
server_t server;
int fd;
int next_id; /* server id generator (even, starts from 2) */ aisl_stream_t stream; /**< Pending client's stream */
int istream; /* input stream id */ /* int next_id; / **< stream id generator (even)*/
int ostream; /* output stream id */ /* int istream; / **< input stream id */
list_t streams; /* int ostream; / **< output stream id */
SSL * ssl; int flags; /**< Client's flag bitmask */
int fd; /**< Client's socket descriptor */
time_t timestamp; aisl_http_version_t protocol; /**< Client's protocol version */
aisl_http_version_t protocol;
int flags;
}; };
/* -------------------------------------------------------------------------- */
void
client_close(client_t self)
{
close(self->fd);
/* will provide double free
if (self->ssl)
SSL_free(self->ssl);
*/
shutdown(self->fd, SHUT_RDWR);
self->fd=-1;
}
/* -------------------------------------------------------------------------- */
static bool static bool
client_input(client_t self) aisl_client_raise_event(aisl_client_t client, aisl_event_t event, ...)
{ {
int l; bool result;
parser_status_t p_status = PARSER_PENDING; va_list vl_args;
char *ptr;
buffer_t buffer = self->server->owner->buffer;
stream_t s = list_index(self->streams, self->istream); 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);
if (self->ssl) return result;
{
if (self->flags & CLIENT_FLAG_HANDSHAKE)
{
if ( (l = SSL_accept(self->ssl)) != 1 )
{
l = SSL_get_error(self->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return false;
client_close(self);
fprintf(stderr, "SSL handshake fail: %s\n", ERR_error_string(l, NULL));
return true;
} }
self->flags ^= CLIENT_FLAG_HANDSHAKE;
}
l = SSL_read(self->ssl, buffer->data, buffer->size) ; static aisl_status_t
} aisl_client_parse(aisl_client_t client, char * data, size_t size)
else
l = recv( self->fd, buffer->data, buffer->size, 0);
if (l>0)
{ {
if( buffer_add(s->buffer, buffer->data, l) == BUFFER_EOB )
{
client_close(self);
return true;
}
ptr = s->buffer->data;
l = s->buffer->size;
/* parse next data chunk */ /* parse next data chunk */
while ( p_status == PARSER_PENDING ) while ( p_status == PARSER_PENDING )
{ {
switch(s->state) switch(s->state)
{ {
case STREAM_REQUEST_METHOD: case STREAM_REQUEST_METHOD:
p_status = parse_request_method(self, &ptr, &l); p_status = parse_request_method(client, &ptr, &l);
break; break;
case STREAM_REQUEST_PATH: case STREAM_REQUEST_PATH:
p_status = parse_request_path(self, &ptr, &l); p_status = parse_request_path(client, &ptr, &l);
break; break;
case STREAM_REQUEST_PROTOCOL: case STREAM_REQUEST_PROTOCOL:
p_status = parse_request_protocol(self, &ptr, &l); p_status = parse_request_protocol(client, &ptr, &l);
break; break;
case STREAM_REQUEST_HEADER_KEY: case STREAM_REQUEST_HEADER_KEY:
p_status = parse_request_header_key(self, &ptr, &l); p_status = parse_request_header_key(client, &ptr, &l);
break; break;
case STREAM_REQUEST_HEADER_VALUE: case STREAM_REQUEST_HEADER_VALUE:
p_status = parse_request_header_value(self, &ptr, &l); p_status = parse_request_header_value(client, &ptr, &l);
break; break;
case STREAM_REQUEST_CONTENT: case STREAM_REQUEST_CONTENT:
p_status = parse_request_content(self, &ptr, &l); p_status = parse_request_content(client, &ptr, &l);
break; break;
default: default:
@ -129,43 +95,95 @@ client_input(client_t self)
if (p_status == PARSER_FAILED) if (p_status == PARSER_FAILED)
{ {
/* reply Bad Request here */ /* reply Bad Request here */
client_close(self); client_close(client);
} }
else if (l) else if (l)
{ {
buffer_shift(s->buffer, s->buffer->size-l); /* reset buffer */ buffer_shift(s->buffer, s->buffer->size-l); /* reset buffer */
} }
/*
else
buffer_clear(s->buffer, 0);*/
return true;
}
/* 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 aisl_status_t
aisl_client_input(aisl_client_t client)
{
int l;
parser_status_t p_status = PARSER_PENDING;
aisl_stream_t s = client->stream;
char * data = &client->in.data[ client->in.length ];
size_t size = client->in.size - client.in.length;
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
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;
fprintf( stderr
, "*AISL: SSL handshake fail: %s\n"
, ERR_error_string(l, NULL) );
aisl_client_close(client);
return AISL_SYSCALL_ERROR;
}
client->flags |= FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size) ;
}
else
#endif
l = recv( client->fd, data, size, 0);
if (l > 0)
{
data = client->in.data;
size = client->in.length + l;
client->in.length = size;
return aisl_client_parse(client, data, size);
} }
else if (l<0) else if (l<0)
{ {
if (self->ssl) if (client->ssl)
{ {
if (SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_READ) if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
return false; return AISL_IDLE;
} }
else else
{ {
if(errno == EWOULDBLOCK) if(errno == EWOULDBLOCK)
return false; return AISL_IDLE;
} }
} }
/* both: client disconnect + on read error */ /* both: client disconnect + on read error */
/* todo: raise client error here */ /* todo: raise client error here */
client_close(self); client_close(client);
return true; return AISL_SYSCALL_ERROR;
} }
static bool static bool
client_output(client_t self) aisl_client_output(aisl_client_t client)
{ {
if (self->fd < 0) if (client->fd < 0)
{ {
fprintf(stderr, "[aisl] assertion !(client->fd<0) failed\n"); fprintf(stderr, "[aisl] assertion !(client->fd<0) failed\n");
return true; return true;
@ -174,7 +192,7 @@ client_output(client_t self)
stream_t s; stream_t s;
int l; int l;
s = list_index(self->streams, self->ostream); s = list_index(client->streams, client->ostream);
/* /*
if (!s->c_length_unknown && s->buffer && s->buffer->len) if (!s->c_length_unknown && s->buffer && s->buffer->len)
@ -200,15 +218,15 @@ client_output(client_t self)
} }
if ( (l = bsz - s->buffer->size) > OUTPUT_BUFFER_SIZE / 2 ) if ( (l = bsz - s->buffer->size) > OUTPUT_BUFFER_SIZE / 2 )
aisl_raise_event( self->server->owner, s, AISL_STREAM_OUTPUT, l); aisl_raise_event( client->server->owner, s, AISL_STREAM_OUTPUT, l);
} }
if (s->buffer->size == 0) if (s->buffer->size == 0)
return false; return false;
l = (self->ssl) ? l = (client->ssl) ?
SSL_write(self->ssl, s->buffer->data, s->buffer->size) : SSL_write(client->ssl, s->buffer->data, s->buffer->size) :
send( self->fd, s->buffer->data, s->buffer->size, 0); send( client->fd, s->buffer->data, s->buffer->size, 0);
if (l > 0) if (l > 0)
{ {
@ -220,31 +238,31 @@ client_output(client_t self)
/* data has been sent */ /* data has been sent */
/* /*
if (self->protocol == AISL_HTTP_2_0) if (client->protocol == AISL_HTTP_2_0)
{ {
} }
else*/ else*/
if (self->flags & CLIENT_FLAG_KEEPALIVE) if (client->flags & CLIENT_FLAG_KEEPALIVE)
{ {
list_remove(self->streams, s); list_remove(client->streams, s);
stream_free(s); stream_free(s);
s = stream_new((struct sockaddr_in *) self, self->next_id++, STREAM_REQUEST_METHOD ); s = stream_new((struct sockaddr_in *) client, client->next_id++, STREAM_REQUEST_METHOD );
list_append(self->streams, s); list_append(client->streams, s);
} }
else else
{ {
client_close(self); client_close(client);
} }
} }
return true; return true;
} }
/* l < 0 */ /* l < 0 */
if (self->ssl) if (client->ssl)
{ {
if ( SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_WRITE ) if ( SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE )
return false; return false;
} }
else else
@ -253,223 +271,195 @@ client_output(client_t self)
return false; return false;
} }
client_close(self); client_close(client);
return true; return true;
} }
bool
client_touch(client_t self) aisl_client_t
aisl_client_new( aisl_server_t server,
int fd,
struct sockaddr_in * addr,
SSL_CTX * ssl_ctx )
{ {
bool result = false; aisl_client_t client;
stream_t s; aisl_stream_t stream;
/* input */ if ( (client = calloc(1, sizeof(struct client))) != NULL )
s = list_index(self->streams, self->istream); {
memcpy(&client->address, addr, sizeof(struct sockaddr_in));
client->fd = fd;
client->next_id = 2;
client->protocol = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if ((self->protocol==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) && if (buffer_init(client->in, 2*BUFFER_SIZE) == 0)
(client_input(self)) ) result = true; {
memcpy(&client->out, client->in, sizeof(struct buffer));
/* output */ stream = aisl_stream_new(client, 0, STREAM_REQUEST_METHOD);
s = list_index(self->streams, self->ostream);
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) ) if (stream != NULL)
result = client_output(self); {
client->stream = stream;
/* update timestamp */ #ifdef AISL_WITHOUT_SSL
if (result)
self->timestamp = time(NULL);
return result; return client;
#else
if ( !ssl_ctx )
return client;
if ((client->ssl = SSL_new(ssl_ctx)) != NULL )
{
SSL_set_fd(client->ssl, fd);
return client;
} }
#endif
}
}
aisl_client_free(client);
/* constructor -------------------------------------------------------------- */
static client_t
client_new( int fd, struct sockaddr_in * addr )
{
client_t self;
stream_t stream;
if ( !(self = calloc(1, sizeof(struct client))) )
goto finally;
memcpy(&self->address, addr, sizeof(struct sockaddr_in));
self->fd = fd;
self->next_id = 2;
/*
self->istream = 0;
self->ostream = 0;
* UTPUT
*/
self->protocol = AISL_HTTP_1_0;
self->timestamp = time(NULL);
self->flags = CLIENT_FLAG_KEEPALIVE | CLIENT_FLAG_HANDSHAKE;
if ( !(self->streams = list_new(AISL_MIN_STREAMS)) )
goto except;
if ( !(stream = stream_new((struct sockaddr_in *)self, 0, STREAM_REQUEST_METHOD)) )
goto e_stream;
if (list_append(self->streams, stream) == -1)
goto e_append;
goto finally;
e_append:
stream_free(stream);
e_stream:
list_free(self->streams, NULL);
except:
free(self);
self=NULL;
finally:
return self;
} }
aisl_status_t return NULL;
client_accept(client_t * p_self, server_t server)
{
aisl_status_t result;
const char * e_detail = NULL;
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
SSL * ssl = NULL;
SSL_CTX * ssl_ctx;
*p_self = NULL;
if ( (fd = accept(server->fd, (struct sockaddr *) &addr, &len)) < 0 )
{
if (errno != EWOULDBLOCK)
{
result = AISL_SYSCALL_ERROR;
e_detail = strerror(errno);
goto raise;
} }
result = AISL_IDLE;
goto finally;
}
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
{
result = AISL_SYSCALL_ERROR;
e_detail = strerror(errno);
goto raise;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) != 0)
{
result = AISL_SYSCALL_ERROR;
e_detail = strerror(errno);
goto raise;
}
if (server->flags & AISL_FLAG_SSL)
{
if ( !(ssl_ctx = aisl_get_ssl_ctx( server->owner, NULL )) )
goto except;
if ( !(ssl = SSL_new(ssl_ctx)) )
{
e_detail = "SSL_new";
result = AISL_EXTCALL_ERROR;
goto except;
}
SSL_set_fd(ssl, fd);
}
else
ssl = NULL;
if ( !(*p_self = client_new(fd, &addr)) )
{
result = AISL_MALLOC_ERROR;
e_detail = "client_t";
goto raise;
}
(*p_self)->server = server;
(*p_self)->ssl = ssl;
result = AISL_SUCCESS;
goto finally;
except:
close(fd);
if (ssl)
SSL_free(ssl);
raise:
aisl_raise_event(
server->owner,
server,
AISL_SERVER_ERROR,
server->flags,
e_detail
);
finally:
return result;
}
/* destructor --------------------------------------------------------------- */
void void
client_free(client_t self) aisl_client_free(aisl_client_t client)
{ {
if (self->fd > -1) aisl_client_close(client);
close(self->fd);
if (self->ssl) #ifndef AISL_WITHOUT_SSL
SSL_free(self->ssl); if (client->ssl)
SSL_free(client->ssl);
#endif
list_free(self->streams, (list_destructor_t)stream_free); if (client->in.data)
free(client->in.data);
aisl_raise_event( /* out buffer is a shared part of input buffer, so no need to free it */
self->server->owner,
self->server,
AISL_CLIENT_DISCONNECT,
self
);
free(self); list_free(client->streams, (list_destructor_t)aisl_stream_free);
free(client);
} }
/* check if communication time with client is expired ----------------------- */
bool aisl_status_t
client_is_timeout(client_t self) aisl_client_touch(aisl_client_t client)
{
bool result = false;
stream_t s;
if (self->protocol == AISL_HTTP_2_0)
{ {
aisl_status_t result, status;
aisl_stream_t s = client->stream;
} /* input */
else if (client->flags & FLAG_CAN_READ)
{ {
s = list_index(self->streams, self->istream); if ( (result = client_input(client)) < 0 )
if ( (s->state < STREAM_REQUEST_READY) && /* still waiting for data */ return result;
(time(NULL)-self->timestamp > AISL_MAX_CLIENT_SILENCE) ) result=true;
} }
if (result) /* output */
client_close(self); if (client->flags & FLAG_CAN_WRITE)
{
if ( (status = client_output(client)) < 0 )
return status;
}
/*
if ((client->protocol==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);
return result; return result;
} }
/* -------------------------------------------------------------------------- */
bool
aisl_client_is_timed_out(aisl_client_t client, uint32_t timeout)
{
time_t now;
time(&now);
if ( !(now - client->timestamp < timeout) )
{
aisl_client_raise_event( client, AISL_CLIENT_TIMEOUT );
aisl_client_close(client);
return true;
}
else
return false;
}
int
aisl_client_get_socket(aisl_client_t client)
{
return client->fd;
}
/* API Level ---------------------------------------------------------------- */
aisl_server_t
aisl_client_get_server(aisl_client_t client)
{
return client->server;
}
bool
aisl_client_is_secure(aisl_client_t client)
{
return (client->ssl == NULL) ? false : true;
}
bool
aisl_client_is_online(aisl_client_t client)
{
return (client->fd == -1) ? false : true;
}
void
aisl_client_close(aisl_client_t client)
{
if (client->fd != -1)
{
aisl_client_raise_event( client, AISL_CLIENT_DISCONNECT );
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
aisl_http_version_t
aisl_client_get_http_version(aisl_client_t client)
{
return client->http_version;
}

View File

@ -1,47 +1,61 @@
#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
#include <time.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <aisl/client.h> #include <aisl/client.h>
#include "server.h"
#include "list.h"
#define CLIENT_FLAG_KEEPALIVE (1<<0) #define AISL_CLIENT(x) ((aisl_client_t) x)
#define CLIENT_FLAG_HANDSHAKE (1<<1)
#define CLIENT(x) ((aisl_client_t)x)
/* constructor -------------------------------------------------------------- */ /**
* @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);
/**
* @brief Destructor for #aisl_client_t instance.
* @param client an #aisl_client_t instance pointer.
*/
void
aisl_client_free(aisl_client_t client);
/**
* @brief Does all HTTP client routines.
* Reads and parses requests, writes responses.
* @param client an #aisl_client_t instance pointer.
* @return #aisl_status_t code.
*/
aisl_status_t aisl_status_t
aisl_client_accept(aisl_client_t * self, aisl_server_t server); aisl_client_touch(aisl_client_t client);
/* destructor --------------------------------------------------------------- */
void
aisl_client_free(aisl_client_t self);
/* all regular client routines. return true if something happened ----------- */
/**
* @brief Controls if communication time is not timed out.
* @param client an #aisl_client_t instance pointer.
* @param timeout an allowed client silence time in seconds.
* @return true if client communication is timed out, otherwise false.
*/
bool bool
aisl_client_touch(aisl_client_t self); aisl_client_is_timed_out(aisl_client_t client, uint32_t timeout);
/* check if communication time with client is expired ----------------------- */
bool /**
aisl_client_is_timeout(aisl_client_t self); * @brief Gets socket descriptor associated with #aisl_client_t instance.
* @param client an #aisl_client_t instance pointer.
/* -------------------------------------------------------------------------- */ * @return a client socket descriptor.
*/
void int
aisl_client_close(aisl_client_t self); aisl_client_get_socket(aisl_client_t client);
/* -------------------------------------------------------------------------- */
#endif /* !AISL_CLIENT_H */ #endif /* !AISL_CLIENT_H */

View File

@ -23,21 +23,15 @@
#include "server.h" #include "server.h"
#include "ssl.h" #include "ssl.h"
//#include "globals.h" //#include "globals.h"
//#include "stream.h" #include "stream.h"
#include "instance.h" #include "instance.h"
enum {
AISL_CYCLE_SERVER = 0
, AISL_CYCLE_CLIENT = 1
};
struct callback struct callback
{ {
void * source; void * source;
aisl_callback_t callback; aisl_callback_t f_ptr;
aisl_event_t eevent; aisl_event_t event;
}; };
typedef struct callback * callback_t; typedef struct callback * callback_t;
@ -45,18 +39,20 @@ typedef struct callback * callback_t;
struct aisl struct aisl
{ {
list_t servers; list_t server_spool;
list_t clients; list_t client_spool;
list_t callbacks; list_t callback_spool;
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
list_t ssl; list_t ssl_spool;
#endif #endif
buffer_t buffer; buffer_t buffer;
char * last_error; char * last_error;
int iterator;
int stage; size_t iterator;
int flags;
uint32_t accept_limit; uint32_t accept_limit;
uint32_t silence_timeout;
}; };
@ -84,24 +80,25 @@ aisl_new( aisl_config_t config )
if ( !(instance = calloc(1, sizeof(struct aisl))) ) if ( !(instance = calloc(1, sizeof(struct aisl))) )
goto finally; goto finally;
if ( !(instance->servers = list_new(config->servers_spool_size)) ) if ( !(instance->server_spool = list_new(config->server_spool_size)) )
goto release; goto release;
if ( !(instance->clients = list_new(config->clients_spool_size)) ) if ( !(instance->client_spool = list_new(config->client_spool_size)) )
goto release; goto release;
if ( !(instance->callbacks = list_new(config->callbacks_spool_size)) ) if ( !(instance->callback_spool = list_new(config->callback_spool_size)) )
goto release; goto release;
if ( !(instance->buffer = buffer_new(config->initial_buffer_size)) ) if ( !(instance->buffer = buffer_new(config->initial_buffer_size)) )
goto release; goto release;
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
if ( !(instance->ssl = list_new(config->ssl_spool_size)) ) if ( !(instance->ssl_spool = list_new(config->ssl_spool_size)) )
goto release; goto release;
#endif #endif
instance->accept_limit = config->clients_accept_limit; instance->accept_limit = config->client_accept_limit;
instance->silence_timeout = config->client_silence_timeout;
goto finally; goto finally;
@ -118,21 +115,21 @@ __attribute__ ((visibility ("default") ))
void void
aisl_free( aisl_t instance ) aisl_free( aisl_t instance )
{ {
if (instance->clients) if (instance->client_spool)
list_free(instance->clients, (list_destructor_t) aisl_client_free ); list_free(instance->client_spool, (list_destructor_t) aisl_client_free );
if (instance->servers) if (instance->server_spool)
list_free(instance->servers, (list_destructor_t) aisl_server_free ); list_free(instance->server_spool, (list_destructor_t) aisl_server_free );
if (instance->callbacks) if (instance->callback_spool)
list_free(instance->callbacks, free); list_free(instance->callback_spool, (list_destructor_t) free);
if (instance->buffer) if (instance->buffer)
buffer_free(instance->buffer); buffer_free(instance->buffer);
#ifndef AISL_WITHOUT_SSL #ifndef AISL_WITHOUT_SSL
if (instance->ssl) if (instance->ssl_spool)
list_free(instance->ssl, (list_destructor_t) ssl_free ); list_free(instance->ssl_spool, (list_destructor_t) ssl_free );
#endif #endif
if (instance->last_error) if (instance->last_error)
@ -157,7 +154,7 @@ aisl_listen( aisl_t instance, const char * address, uint16_t port )
if ( (result = aisl_server_new(instance, address, port)) != NULL ) if ( (result = aisl_server_new(instance, address, port)) != NULL )
{ {
if (list_append(instance->servers, result) == LIST_NAN) if (list_append(instance->server_spool, result) == LIST_NAN)
{ {
aisl_server_free(result); aisl_server_free(result);
} }
@ -180,34 +177,21 @@ aisl_set_ssl( aisl_t instance, const char * domain,
size_t i; size_t i;
/* lookup for existing contexts */ /* lookup for existing contexts */
for (i=0; i<instance->ssl->count; i++) for (i=0; i<instance->ssl_spool->count; i++)
{ {
ssl = list_index(instance->ssl, i); ssl = list_index(instance->ssl_spool, i);
if (ssl->key_file && strcmp(ssl->key_file, key_file)==0 && if (ssl->key_file && strcmp(ssl->key_file, key_file)==0 &&
ssl->crt_file && strcmp(ssl->crt_file, crt_file)==0 ) ssl->crt_file && strcmp(ssl->crt_file, crt_file)==0 )
{ {
if ((ssl_ctx = crypter->ssl_ctx) != NULL) ssl_ctx = ssl->ctx;
{
key_file = NULL;
crt_file = NULL;
break; break;
} }
} }
}
if ((ssl = ssl_new(domain, key_file, crt_file)) != NULL) if ((ssl = ssl_new(domain, key_file, crt_file, ssl_ctx)) != NULL)
{
if (! ssl_ctx)
ssl_ctx = ssl__new_context(key_file, crt_file, (void*)instance);
if (ssl_ctx)
{
ssl->ctx = ssl_ctx;
if (list_append(instance->ssl, ssl) != LIST_NAN)
{ {
if (list_append(instance->ssl_spool, ssl) != LIST_NAN)
return AISL_SUCCESS; return AISL_SUCCESS;
}
}
ssl_free(ssl); ssl_free(ssl);
} }
@ -224,9 +208,9 @@ aisl_get_ssl_ctx( aisl_t instance, const char * domain )
if (domain) if (domain)
{ {
for (i=0; i<instance->ssl->count; i++) for (i=0; i<instance->ssl_spool->count; i++)
{ {
ssl = list_index(instance->ssl, i); ssl = list_index(instance->ssl_spool, i);
if (strcmp(ssl->domain, domain) != 0) if (strcmp(ssl->domain, domain) != 0)
continue; continue;
@ -253,20 +237,23 @@ aisl_set_callback( aisl_t instance,
{ {
cb->source = source; cb->source = source;
cb->event = event; cb->event = event;
cb->callback = callback; cb->f_ptr = callback;
if ( list_append(instance->callbacks, cb) != LIST_NAN ) if ( list_append(instance->callback_spool, cb) != LIST_NAN )
{ {
switch(event) switch(event)
{ {
case AISL_STREAM_OUTPUT: case AISL_EVENT_STREAM_OUTPUT:
if (source) if (source)
stream_set_chunked_output( (aisl_stream_t) source ); aisl_stream_set_chunked_output( (aisl_stream_t) source, true );
//( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED; //( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
break; break;
case AISL_STREAM_OPEN: /*
instance->flags |= AISL_FLAG_HAS_STREAM_LISTENERS; * case AISL_EVENT_STREAM_OPEN:
* instance->flags |= AISL_FLAG_HAS_STREAM_CALLBACKS;
*/
default:
break; break;
} }
@ -280,19 +267,19 @@ aisl_set_callback( aisl_t instance,
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
void void
aisl_unset_callback_for( aisl_t instance, void * source ) aisl_unset_callbacks_for( aisl_t instance, void * source )
{ {
size_t i = instance->callbacks->count; size_t i = instance->callback_spool->count;
if (i) if (i)
{ {
for(i=i-1; i <= 0; i-- ) for(i=i-1; i <= 0; i-- )
{ {
callback_t callback = list_index(instance->callbacks, i); callback_t callback = list_index(instance->callback_spool, i);
if ( callback->source == source ) if ( callback->source == source )
{ {
free(callback); free(callback);
list_remove_index(instance->callbacks, i); list_remove_index(instance->callback_spool, i);
} }
} }
} }
@ -301,183 +288,228 @@ aisl_unset_callback_for( aisl_t instance, void * source )
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_raise( aisl_t instance, aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... )
void * source,
aisl_event_t e_id,
... )
{ {
va_list vl; va_list vl;
bool result; bool result;
va_start(vl, e_id); va_start(vl, event);
result = aisl_raise_vl(instance, source, e_id, vl); result = aisl_raise_vl(instance, source, event, vl);
va_end(vl); va_end(vl);
return result; 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 );
}
static bool
aisl_on_stream_output( void * source, aisl_callback_t cb, va_list vl_args )
{
aisl_on_stream_output_t callback = (aisl_on_stream_output_t) cb;
size_t buffer_size = va_arg(vl_args, size_t);
return callback( (aisl_stream_t) source, buffer_size );
}
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
bool bool
aisl_raise_vl( aisl_t instance, aisl_raise_vl( aisl_t instance,
void * source, void * source,
aisl_event_t e_id, aisl_event_t event,
va_list vl ) va_list vl_args )
{ {
int i, va_list vl_copy;
i_val; size_t i;
listener_t lst;
bool res = false;
char * c_ptr, callback_t cb;
* c_ptr2; bool result = false,
stop_propg = false;
for(i=instance->callbacks->count-1; i>=0; i--) i = instance->callback_spool->count;
while(i--)
{ {
lst = list_index(instance->callbacks, i); cb = list_index(instance->callback_spool, i);
/*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/ if (cb->event == event && (source == cb->source || cb->source == NULL))
if (lst->e_id == e_id && (source == lst->source || lst->source == NULL))
{ {
/* va_copy(vl_copy, vl_args);
if (e_id == AISL_STREAM_HEADER) switch(event)
fprintf(stderr,"FOUND HANDLER %d\n", i);
*/
/*printf(" catch\n");*/
switch(e_id)
{ {
/* server events */ /* server events */
case AISL_EVENT_SERVER_OPEN: case AISL_EVENT_SERVER_OPEN:
i_val = va_arg(vl, aisl_status_t);
res = ((aisl_on_server_open_t) lst->cb)( source, i_val );
break;
case AISL_EVENT_SERVER_ERROR: case AISL_EVENT_SERVER_ERROR:
i_val = va_arg(vl, aisl_status_t);
c_ptr = va_arg(vl, char *);
res = ((aisl_on_server_error_t) lst->cb)( source, i_val, c_ptr );
break;
/* client events */
case AISL_EVENT_CLIENT_CONNECT: case AISL_EVENT_CLIENT_CONNECT:
res = ((aisl_on_client_connect_t) lst->cb)(source, va_arg(vl, void *));
break;
case AISL_EVENT_CLIENT_DISCONNECT: case AISL_EVENT_CLIENT_DISCONNECT:
res = ((aisl_on_client_disconnect_t) lst->cb)(source, va_arg(vl, void*));
aisl_unset_callback_for( instance, source );
break;
case AISL_EVENT_CLIENT_TIMEOUT: case AISL_EVENT_CLIENT_TIMEOUT:
res = ((aisl_client_timeout_t) lst->cb)(source, va_arg(vl, void *)); case AISL_EVENT_STREAM_REQUEST:
case AISL_EVENT_STREAM_CLOSE:
case AISL_EVENT_STREAM_ERROR:
stop_propg = aisl_on_source_event(source, cb->f_ptr, vl_copy);
break; break;
/* request events */
case AISL_EVENT_STREAM_OPEN: case AISL_EVENT_STREAM_OPEN:
i_val = va_arg(vl, int); stop_propg = aisl_on_stream_open(source, cb->f_ptr, vl_copy);
c_ptr = va_arg(vl, char *);
c_ptr2 = va_arg(vl, char *);
res = ((aisl_on_stream_open_t) lst->cb)(source, i_val, c_ptr, c_ptr2);
break; break;
case AISL_EVENT_STREAM_HEADER: case AISL_EVENT_STREAM_HEADER:
c_ptr = va_arg(vl, char *); stop_propg = aisl_on_stream_header(source, cb->f_ptr, vl_copy);
c_ptr2 = va_arg(vl, char *);
res = ((aisl_on_stream_header_t) lst->cb)(source, c_ptr, c_ptr2);
break; break;
case AISL_EVENT_STREAM_INPUT: case AISL_EVENT_STREAM_INPUT:
/*printf("AISL> raise AISL_STREAM_INPUT\n");*/ stop_propg = aisl_on_stream_input(source, cb->f_ptr, vl_copy);
c_ptr = va_arg(vl, char *);
i_val = va_arg(vl, int );
res = ((aisl_on_stream_input_t) lst->cb)(source, c_ptr, i_val);
break;
case AISL_EVENT_STREAM_REQUEST:
/*printf("AISL> raise AISL_STREAM_REQUEST\n");*/
buffer_clear( STREAM(source)->buffer, 0);
res = ((aisl_on_stream_request_t) lst->cb)(source);
break; break;
case AISL_EVENT_STREAM_ERROR:
res = ((aisl_on_stream_error_t) lst->cb)( source, va_arg(vl, char *));
break;
/* response events */
case AISL_EVENT_STREAM_OUTPUT: case AISL_EVENT_STREAM_OUTPUT:
res = ((aisl_on_stream_output_t)lst->cb)( stop_propg = aisl_on_stream_output(source, cb->f_ptr, vl_copy);
source,
va_arg(vl, uint32_t)
);
break;
case AISL_EVENT_STREAM_CLOSE:
res = ((aisl_on_stream_close_t)lst->cb)( source );
aisl_unset_callback_for( instance, source );
((aisl_stream_t) source)->u_ptr=NULL;
break; break;
default: default:
res = ((aisl_on_custom_event_t) lst->cb)(source, vl); stop_propg = ((aisl_on_custom_event_t) cb->f_ptr)(source, vl_copy);
} }
if (res) break;
va_end(vl_copy);
result = true;
if (stop_propg)
break;
} }
} }
return res; return result;
} }
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
aisl_status_t aisl_status_t
aisl_run_cycle( aisl_t instance ) aisl_run_cycle( aisl_t instance )
{ {
int max = instance->servers->count+instance->clients->count+instance->delays->count, aisl_status_t result = AISL_IDLE;
cnt = 0;
size_t i = instance->iterator,
max = instance->server_spool->count + instance->client_spool->count;
switch (instance->stage) while ( result == AISL_IDLE && max != 0)
{ {
case STAGE_SERVER: aisl_client_t cli;
while (instance->iterator < instance->servers->count )
{
aisl_server_t srv = (server_t)list_index(instance->servers, instance->iterator++);
if ( aisl_server_touch(srv) != AISL_IDLE )
return AISL_SUCCESS;
if ( ! (++cnt < max) ) return AISL_IDLE; if (i < instance->server_spool->count)
{
if (instance->client_spool->count < instance->accept_limit)
{
aisl_server_t srv;
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, ssl_ctx);
if (result == AISL_SUCCESS)
{
if ( list_append(instance->client_spool, cli) != LIST_NAN )
{
aisl_raise(instance, cli, AISL_EVENT_CLIENT_CONNECT);
} }
if ( ! (instance->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) ) else
return AISL_IDLE;
instance->iterator = 0;
instance->stage++;
case STAGE_CLIENT:
while (instance->iterator < instance->clients->count )
{
int i = instance->iterator++;
aisl_client_t cli = list_index(instance->clients, i);
bool r = aisl_client_touch( cli );
if (aisl_client_is_timeout( cli ) )
aisl_raise( instance, cli->server, AISL_CLIENT_TIMEOUT, cli );
if ( cli->fd == -1 )
{ {
aisl_client_free(cli); aisl_client_free(cli);
list_remove_index(instance->clients, i); /* todo: save error message here */
}
}
}
else
{
i = instance->server_spool->count;
continue;
}
}
else
{
size_t j = i - instance->server_spool->count;
if (j < instance->client_spool->count)
{
aisl_client_t c = list_index(instance->client_spool, j);
result = aisl_client_touch(c);
if (aisl_client_is_timed_out( c, instance->silence_timeout ) )
aisl_raise( instance, c, AISL_EVENT_CLIENT_TIMEOUT );
if ( !aisl_client_is_online(c) )
{
aisl_client_free( c );
list_remove_index(instance->client_spool, j);
}
}
else
{
i = 0;
continue;
}
} }
if (r) return AISL_SUCCESS; max--;
i++;
if ( ! (++cnt < max) ) return AISL_IDLE;
}
instance->iterator = 0;
instance->stage = 0;
} }
return AISL_IDLE; instance->iterator = i;
return result;
} }
@ -500,8 +532,8 @@ aisl_set_error( aisl_t instance, const char * err_msg )
__attribute__ ((visibility ("default") )) __attribute__ ((visibility ("default") ))
int aisl_status_t
aisl_sleep( aisl_t instance, unsigned long usec ) aisl_sleep( aisl_t instance, uint32_t usec )
{ {
int maxfd=0, int maxfd=0,
sd; sd;
@ -514,9 +546,9 @@ aisl_sleep( aisl_t instance, unsigned long usec )
fd_set fs; fd_set fs;
FD_ZERO (&fs); FD_ZERO (&fs);
for (i=0; i<instance->servers->count; i++) for (i=0; i<instance->server_spool->count; i++)
{ {
aisl_server_t s = list_index(instance->servers, i); aisl_server_t s = list_index(instance->server_spool, i);
sd = aisl_server_get_socket(s); sd = aisl_server_get_socket(s);
@ -528,10 +560,10 @@ aisl_sleep( aisl_t instance, unsigned long usec )
} }
for (i=0; i<instance->clients->count; i++) for (i=0; i<instance->client_spool->count; i++)
{ {
aisl_client_t c = list_index(instance->clients, i); aisl_client_t c = list_index(instance->client_spool, i);
sd = aisl_client_get_socket(s); sd = aisl_client_get_socket(c);
if (sd != -1) if (sd != -1)
{ {
FD_SET(sd, &fs); FD_SET(sd, &fs);
@ -539,8 +571,16 @@ aisl_sleep( aisl_t instance, unsigned long usec )
} }
} }
return select(maxfd+1, &fs, NULL, NULL, &timeout); switch ( select(maxfd+1, &fs, NULL, NULL, &timeout) )
{
case -1:
return AISL_SYSCALL_ERROR;
case 0:
return AISL_IDLE;
default:
return AISL_SUCCESS;
}
} }

View File

@ -7,15 +7,34 @@
* *
*/ */
#ifndef INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484 #ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#define INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484 #define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#ifndef AISL_WITHOUT_SSL
#include <openssl/ssl.h> #include <openssl/ssl.h>
#endif
#include <aisl/instance.h> #include <aisl/instance.h>
#ifndef AISL_WITHOUT_SSL
/**
* @brief Gets SSL context for appropriate server name.
* @param instance a pointer to #aisl_t instance.
* @param server_name a null-terminated string with server name or NULL.
* @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( aisl_t instance, const char * server_name );
#endif
/**
* @brief Sets error message for AISL instace.
* @param instance a pointer to #aisl_t instance.
* @param err_msge a null-terminated string with error message.
*/
void void
aisl_set_error( aisl_t instance, const char * err_msg ); aisl_set_error( aisl_t instance, const char * err_msg );
#endif /* !INSTANCE_H */ #endif /* !INSTANCE_H */

View File

@ -5,201 +5,204 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <netdb.h> #include <netdb.h>
#include <fcntl.h>
#include "server.h"
#include "instance.h"
#include "client.h"
#include "globals.h"
#include "str-utils.h"
#ifdef __APPLE__ #ifdef __APPLE__
#include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#endif #endif
#include <openssl/ssl.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 aisl_server
{ {
struct sockaddr_in address; struct sockaddr_in address; /**< TCP server address to listen to. */
aisl_t instance; aisl_t instance; /**< Associated AISL instance pointer. */
char * host; int fd; /**< System socket descriptor. */
int fd; bool ssl; /**< SSL enabled/disabled flag. */
int port;
int flags;
}; };
/* -------------------------------------------------------------------------- */
static bool
_set_addres_from_host_and_port( struct sockaddr_in * sa,
const char * host,
int port )
{
int rs;
struct addrinfo * ai;
memset(sa, 0, sizeof( struct sockaddr_in ));
sa->sin_family = AF_INET;
rs = getaddrinfo(host, NULL, NULL, &ai);
if(rs != 0)
{
return false;
}
sa->sin_addr.s_addr=((struct sockaddr_in*)(ai->ai_addr))->sin_addr.s_addr;
sa->sin_port =htons(port);
freeaddrinfo(ai);
return true;
}
/* -------------------------------------------------------------------------- */
static void
server_close(server_t self)
{
close(self->fd);
self->fd=-1;
}
/* -------------------------------------------------------------------------- */
/**
* @brief Creates TCP server socket, binds to address and starts to listen.
* @param server a pointer to #aisl_server_t instance.
* @return #aisl_status_t code.
*/
static aisl_status_t static aisl_status_t
server_open(server_t self) aisl_server_open(aisl_server_t server)
{ {
int fd, s_opt = 1;
aisl_status_t result = AISL_SUCCESS; fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
int s_opt = 1; if (fd != -1)
if (!_set_addres_from_host_and_port(&self->address, self->host, self->port))
return AISL_EXTCALL_ERROR;
self->fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (self->fd != -1)
{ {
setsockopt( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int));
self->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int)
);
#ifdef __APPLE__ #ifdef __APPLE__
ioctl(fd, FIONBIO, (char *)&s_opt);
int on = 1; fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
ioctl(self->fd, FIONBIO, (char *)&on);
fcntl(self->fd, F_SETFL, fcntl(self->fd, F_GETFL) | O_NONBLOCK);
#endif #endif
if (bind( self->fd, s_opt = sizeof(struct sockaddr_in);
(struct sockaddr *) &self->address,
sizeof(struct sockaddr_in) )==0) if (bind(fd, (struct sockaddr *) &server->address, s_opt)==0)
{ {
if (listen(self->fd, SOMAXCONN) == 0) if (listen(fd, SOMAXCONN) == 0)
return result; {
server->fd = fd;
return AISL_SUCCESS;
}
} }
server_close(self); close(fd);
} }
result = AISL_SYSCALL_ERROR; return AISL_SYSCALL_ERROR;
return result;
} }
/**
* @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 )
{
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
/* -------------------------------------------------------------------------- */ *p_client = NULL;
bool fd = accept(server->fd, (struct sockaddr *) &addr, &len);
server_touch(server_t self)
if (fd != -1)
{
int flags;
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))) ?
AISL_MALLOC_ERROR : AISL_SUCCESS;
}
}
close(fd);
}
else if (errno == EWOULDBLOCK)
return AISL_IDLE;
aisl_raise( server->instance, server, AISL_EVENT_SERVER_ERROR );
return AISL_SYSCALL_ERROR;
}
/* Library Level ------------------------------------------------------------ */
aisl_status_t
aisl_server_touch( aisl_server_t server,
aisl_client_t * p_client,
SSL_CTX * ssl_ctx )
{ {
aisl_status_t result; aisl_status_t result;
client_t cli;
if (self->fd == -1) if (server->fd == -1)
{ {
result = server_open(self); aisl_event_t event;
if (result == AISL_SUCCESS)
aisl_raise_event( result = aisl_server_open(server);
self->owner, event = (result == AISL_SUCCESS) ? AISL_EVENT_SERVER_OPEN
self, : AISL_EVENT_SERVER_ERROR;
AISL_SERVER_OPEN,
self->flags aisl_raise(server->instance, server, event);
); }
else else
aisl_raise_event( result = aisl_server_accept(server, p_client, ssl_ctx);
self->owner,
self,
AISL_SERVER_ERROR,
self->flags,
strerror(errno)
);
return result; return result;
} }
result = client_accept(&cli, self); /* API Level ---------------------------------------------------------------- */
if (result == AISL_SUCCESS) aisl_server_t
aisl_server_new(aisl_t instance, const char * host, uint16_t port)
{ {
if (list_append(self->owner->clients, cli) == -1) aisl_server_t server;
if ( (server = calloc(1, sizeof(struct aisl_server))) != NULL )
{ {
client_free(cli); server->instance = instance;
result = AISL_MALLOC_ERROR; server->fd = -1;
} server->address.sin_family = AF_INET;
else server->address.sin_addr.s_addr = inet_addr(host);
aisl_raise_event(self->owner, self, AISL_CLIENT_CONNECT, cli); server->address.sin_port = htons(port);
} }
return result; return server;
} }
/* -------------------------------------------------------------------------- */
server_t
server_new(const char * address, int port)
{
server_t self;
if ( (self = calloc(1, sizeof(struct server))) != NULL )
{
self->fd = -1;
self->port = port;
if ( !(self->host = str_copy(address)) )
{
free(self);
self = NULL;
}
}
return self;
}
/* -------------------------------------------------------------------------- */
void void
server_free(server_t self) aisl_server_free(aisl_server_t server)
{ {
if (self) if (server)
{ {
if (self->fd > -1) if ( server->fd != -1)
server_close(self); {
close(server->fd);
server->fd=-1;
}
if (self->host) free(server);
free(self->host);
free(self);
} }
} }
/* -------------------------------------------------------------------------- */ aisl_t
aisl_server_get_instance( aisl_server_t server )
{
return server->instance;
}
void
aisl_server_get_address( aisl_server_t server, struct sockaddr_in * address)
{
memcpy(address, &server->address, sizeof(struct sockaddr_in));
}
#ifndef AISL_WITHOUT_SSL
void
aisl_server_set_ssl( aisl_server_t server, bool value )
{
server->ssl = value;
}
bool
aisl_server_get_ssl( aisl_server_t server )
{
return server->ssl;
}
#endif

View File

@ -1,24 +1,64 @@
/*
* @file src/server.h
*
* Copyright (c) 2017-2019 by Löwenware Ltd.
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2 #ifndef AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2 #define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#include <arpa/inet.h>
#include <aisl/types.h>
#include <aisl/server.h> #include <aisl/server.h>
#define AISL_SERVER(x) ((aisl_server_t) x)
#define SERVER(x) ((aisl_server_t) x)
/**
* @brief Allocates and instance of #aisl_server_t.
* @param instance a pointer to #aisl_t instance.
* @param host a null-terminated string with address to listen to.
* @param port a TCP server port number to listen to.
* @return a pointer to #aisl_server_t instance.
*/
aisl_server_t aisl_server_t
aisl_server_new(aisl_t instance, const char * address, int port); aisl_server_new(aisl_t instance, const char * host, uint16_t port);
/**
* @brief Frees memory allocated for #aisl_server_t instance.
* @param server a pointer to #aisl_server_t instance.
*/
void void
aisl_server_free(aisl_server_t self); aisl_server_free(aisl_server_t server);
bool /**
aisl_server_touch(aisl_server_t self); * @brief Does server routines.
* Tries to open server if it was not opened yet, otherwise tries to accept a
* new client connecting to the server.
* @param server a pointer to #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,
* - AISL_SYSCALL_ERROR if error occured.
*/
aisl_status_t
aisl_server_touch( aisl_server_t server,
aisl_client_t * p_client,
SSL_CTX * ssl_ctx );
/**
* @brief Gets a socket descriptor associated with HTTP client.
* @param server a pointer to #aisl_server_t instance.
* @return a client socket descriptor.
*/
int
aisl_server_get_socket(aisl_server_t server);
#endif /* !AISL_SERVER_H */ #endif /* !AISL_SERVER_H */

View File

@ -26,14 +26,11 @@ typedef struct ssl * ssl_t;
ssl_t ssl_t
ssl_new( const char * key_file, const char * crt_file, const char * domain ); ssl_new( const char * key_file, const char * crt_file, const char * domain, SSL_CTX * ctx );
void void
ssl_free( ssl_t self ); ssl_free( ssl_t self );
SSL_CTX *
ssl__new_context( const char * key_file, const char * crt_file, void * u_ptr );
#endif /* !AISL_SSL_H */ #endif /* !AISL_SSL_H */

View File

@ -1,7 +1,7 @@
#ifndef AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3 #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 <stdbool.h> #include <aisl/types.h>
#include <aisl/stream.h> #include <aisl/stream.h>
#include "list.h" #include "list.h"
#include "buffer.h" #include "buffer.h"
@ -49,15 +49,12 @@ typedef enum {
/* real wrapper for aisl_stream_t */ /* real wrapper for aisl_stream_t */
typedef struct aisl_stream * aisl_stream_t; #define STREAM(x) ((aisl_stream_t) x)
#define STREAM(x) ((stream_t) x)
#define ASTREAM(x) ((aisl_stream_t) x)
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
aisl_stream_t aisl_stream_t
aisl_stream_new(struct sockaddr_in *client, int id, stream_state_t state); aisl_stream_new(struct sockaddr_in *client, int id, aisl_stream_state_t state);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
@ -71,4 +68,9 @@ aisl_stream_reset(aisl_stream_t self);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
void
aisl_stream_set_chunked_output(aisl_stream_t self, bool value);
/* -------------------------------------------------------------------------- */
#endif /* !AISL_STREAM_H */ #endif /* !AISL_STREAM_H */