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
*.bz2
pkg/*
doc/html
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.
*
@ -12,4 +12,48 @@
#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 */

View File

@ -15,12 +15,13 @@
struct aisl_config
{
uint32_t servers_spool_size;
uint32_t clients_spool_size;
uint32_t server_spool_size;
uint32_t client_spool_size;
uint32_t ssl_spool_size;
uint32_t callbacks_spool_size;
uint32_t callback_spool_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;

View File

@ -1,5 +1,5 @@
/*
* <aisl/instance.h>
/**
* @file aisl/instance.h
*
* 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_new( aisl_config_t config );
/**
* @brief Frees previously allocated pointer of AISL instance.
* @param instance a pointer to #aisl_t instance.
*/
void
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_listen( aisl_t instance, const char * address, uint16_t port );
#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_set_ssl( aisl_t instance,
const char * hostname,
const char * domain,
const char * key_file,
const char * crt_file );
#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_set_callback( aisl_t instance,
void * source,
@ -50,33 +88,69 @@ aisl_set_callback( aisl_t instance,
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_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,
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_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_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_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 */

View File

@ -1,5 +1,5 @@
/*
* <aisl/server.h>
/**
* @file aisl/server.h
*
* Copyright (c) 2017-2019 by Löwenware Ltd.
*
@ -10,14 +10,46 @@
#ifndef AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#define AISL_SERVER_H_CC564608_7A05_4B31_9E7E_32750BC60768
#include <arpa/inet.h>
#include <aisl/types.h>
#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 );
/**
* @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 /* !AISL_SERVER_H */

View File

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

View File

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

View File

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

View File

@ -23,21 +23,15 @@
#include "server.h"
#include "ssl.h"
//#include "globals.h"
//#include "stream.h"
#include "stream.h"
#include "instance.h"
enum {
AISL_CYCLE_SERVER = 0
, AISL_CYCLE_CLIENT = 1
};
struct callback
{
void * source;
aisl_callback_t callback;
aisl_event_t eevent;
aisl_callback_t f_ptr;
aisl_event_t event;
};
typedef struct callback * callback_t;
@ -45,18 +39,20 @@ typedef struct callback * callback_t;
struct aisl
{
list_t servers;
list_t clients;
list_t callbacks;
list_t server_spool;
list_t client_spool;
list_t callback_spool;
#ifndef AISL_WITHOUT_SSL
list_t ssl;
list_t ssl_spool;
#endif
buffer_t buffer;
char * last_error;
int iterator;
int stage;
int flags;
size_t iterator;
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))) )
goto finally;
if ( !(instance->servers = list_new(config->servers_spool_size)) )
if ( !(instance->server_spool = list_new(config->server_spool_size)) )
goto release;
if ( !(instance->clients = list_new(config->clients_spool_size)) )
if ( !(instance->client_spool = list_new(config->client_spool_size)) )
goto release;
if ( !(instance->callbacks = list_new(config->callbacks_spool_size)) )
if ( !(instance->callback_spool = list_new(config->callback_spool_size)) )
goto release;
if ( !(instance->buffer = buffer_new(config->initial_buffer_size)) )
goto release;
#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;
#endif
instance->accept_limit = config->clients_accept_limit;
instance->accept_limit = config->client_accept_limit;
instance->silence_timeout = config->client_silence_timeout;
goto finally;
@ -118,21 +115,21 @@ __attribute__ ((visibility ("default") ))
void
aisl_free( aisl_t instance )
{
if (instance->clients)
list_free(instance->clients, (list_destructor_t) aisl_client_free );
if (instance->client_spool)
list_free(instance->client_spool, (list_destructor_t) aisl_client_free );
if (instance->servers)
list_free(instance->servers, (list_destructor_t) aisl_server_free );
if (instance->server_spool)
list_free(instance->server_spool, (list_destructor_t) aisl_server_free );
if (instance->callbacks)
list_free(instance->callbacks, free);
if (instance->callback_spool)
list_free(instance->callback_spool, (list_destructor_t) free);
if (instance->buffer)
buffer_free(instance->buffer);
#ifndef AISL_WITHOUT_SSL
if (instance->ssl)
list_free(instance->ssl, (list_destructor_t) ssl_free );
if (instance->ssl_spool)
list_free(instance->ssl_spool, (list_destructor_t) ssl_free );
#endif
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 (list_append(instance->servers, result) == LIST_NAN)
if (list_append(instance->server_spool, result) == LIST_NAN)
{
aisl_server_free(result);
}
@ -180,34 +177,21 @@ aisl_set_ssl( aisl_t instance, const char * domain,
size_t i;
/* 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 &&
ssl->crt_file && strcmp(ssl->crt_file, crt_file)==0 )
{
if ((ssl_ctx = crypter->ssl_ctx) != NULL)
{
key_file = NULL;
crt_file = NULL;
break;
}
ssl_ctx = ssl->ctx;
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)
{
return AISL_SUCCESS;
}
}
if (list_append(instance->ssl_spool, ssl) != LIST_NAN)
return AISL_SUCCESS;
ssl_free(ssl);
}
@ -224,9 +208,9 @@ aisl_get_ssl_ctx( aisl_t instance, const char * 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)
continue;
@ -251,22 +235,25 @@ aisl_set_callback( aisl_t instance,
if ( (cb = malloc(sizeof(struct callback))) != NULL )
{
cb->source = source;
cb->event = event;
cb->callback = callback;
cb->source = source;
cb->event = event;
cb->f_ptr = callback;
if ( list_append(instance->callbacks, cb) != LIST_NAN )
if ( list_append(instance->callback_spool, cb) != LIST_NAN )
{
switch(event)
{
case AISL_STREAM_OUTPUT:
case AISL_EVENT_STREAM_OUTPUT:
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;
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;
}
@ -280,19 +267,19 @@ aisl_set_callback( aisl_t instance,
__attribute__ ((visibility ("default") ))
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)
{
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 )
{
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") ))
bool
aisl_raise( aisl_t instance,
void * source,
aisl_event_t e_id,
... )
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... )
{
va_list vl;
bool result;
va_start(vl, e_id);
result = aisl_raise_vl(instance, source, e_id, vl);
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 );
}
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") ))
bool
aisl_raise_vl( aisl_t instance,
void * source,
aisl_event_t e_id,
va_list vl )
aisl_raise_vl( aisl_t instance,
void * source,
aisl_event_t event,
va_list vl_args )
{
int i,
i_val;
listener_t lst;
bool res = false;
va_list vl_copy;
size_t i;
char * c_ptr,
* c_ptr2;
callback_t cb;
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 (lst->e_id == e_id && (source == lst->source || lst->source == NULL))
if (cb->event == event && (source == cb->source || cb->source == NULL))
{
/*
if (e_id == AISL_STREAM_HEADER)
fprintf(stderr,"FOUND HANDLER %d\n", i);
*/
/*printf(" catch\n");*/
switch(e_id)
va_copy(vl_copy, vl_args);
switch(event)
{
/* server events */
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:
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:
res = ((aisl_on_client_connect_t) lst->cb)(source, va_arg(vl, void *));
break;
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:
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;
/* request events */
case AISL_EVENT_STREAM_OPEN:
i_val = va_arg(vl, int);
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);
stop_propg = aisl_on_stream_open(source, cb->f_ptr, vl_copy);
break;
case AISL_EVENT_STREAM_HEADER:
c_ptr = va_arg(vl, char *);
c_ptr2 = va_arg(vl, char *);
res = ((aisl_on_stream_header_t) lst->cb)(source, c_ptr, c_ptr2);
stop_propg = aisl_on_stream_header(source, cb->f_ptr, vl_copy);
break;
case AISL_EVENT_STREAM_INPUT:
/*printf("AISL> raise AISL_STREAM_INPUT\n");*/
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);
stop_propg = aisl_on_stream_input(source, cb->f_ptr, vl_copy);
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:
res = ((aisl_on_stream_output_t)lst->cb)(
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;
stop_propg = aisl_on_stream_output(source, cb->f_ptr, vl_copy);
break;
default:
res = ((aisl_on_custom_event_t) lst->cb)(source, vl);
}
if (res) break;
stop_propg = ((aisl_on_custom_event_t) cb->f_ptr)(source, vl_copy);
}
va_end(vl_copy);
result = true;
if (stop_propg)
break;
}
}
return res;
return result;
}
__attribute__ ((visibility ("default") ))
aisl_status_t
aisl_run_cycle( aisl_t instance )
{
int max = instance->servers->count+instance->clients->count+instance->delays->count,
cnt = 0;
aisl_status_t result = AISL_IDLE;
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:
while (instance->iterator < instance->servers->count )
aisl_client_t cli;
if (i < instance->server_spool->count)
{
if (instance->client_spool->count < instance->accept_limit)
{
aisl_server_t srv = (server_t)list_index(instance->servers, instance->iterator++);
if ( aisl_server_touch(srv) != AISL_IDLE )
return AISL_SUCCESS;
aisl_server_t srv;
SSL_CTX * ssl_ctx;
if ( ! (++cnt < max) ) return AISL_IDLE;
}
if ( ! (instance->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) )
return AISL_IDLE;
srv = (aisl_server_t) list_index(instance->server_spool, i);
instance->iterator = 0;
instance->stage++;
ssl_ctx = (aisl_server_get_ssl(srv)) ?
aisl_get_ssl_ctx(instance, NULL) :
NULL;
result = aisl_server_touch(srv, &cli, ssl_ctx);
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 )
if (result == AISL_SUCCESS)
{
aisl_client_free( cli );
list_remove_index(instance->clients, i);
if ( list_append(instance->client_spool, cli) != LIST_NAN )
{
aisl_raise(instance, cli, AISL_EVENT_CLIENT_CONNECT);
}
else
{
aisl_client_free(cli);
/* todo: save error message here */
}
}
if (r) return AISL_SUCCESS;
if ( ! (++cnt < max) ) return AISL_IDLE;
}
instance->iterator = 0;
instance->stage = 0;
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;
}
}
max--;
i++;
}
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") ))
int
aisl_sleep( aisl_t instance, unsigned long usec )
aisl_status_t
aisl_sleep( aisl_t instance, uint32_t usec )
{
int maxfd=0,
sd;
@ -514,9 +546,9 @@ aisl_sleep( aisl_t instance, unsigned long usec )
fd_set 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);
@ -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);
sd = aisl_client_get_socket(s);
aisl_client_t c = list_index(instance->client_spool, i);
sd = aisl_client_get_socket(c);
if (sd != -1)
{
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
#define INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#ifndef AISL_WITHOUT_SSL
#include <openssl/ssl.h>
#endif
#include <aisl/instance.h>
#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 *
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
aisl_set_error( aisl_t instance, const char * err_msg );
#endif /* !INSTANCE_H */

View File

@ -5,201 +5,204 @@
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include "server.h"
#include "instance.h"
#include "client.h"
#include "globals.h"
#include "str-utils.h"
#include <fcntl.h>
#ifdef __APPLE__
#include <fcntl.h>
#include <sys/ioctl.h>
#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 sockaddr_in address;
aisl_t instance;
char * host;
int fd;
int port;
int flags;
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. */
};
/* -------------------------------------------------------------------------- */
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
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 (!_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)
if (fd != -1)
{
setsockopt(
self->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int)
);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt, sizeof(int));
#ifdef __APPLE__
#ifdef __APPLE__
ioctl(fd, FIONBIO, (char *)&s_opt);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif
int on = 1;
s_opt = sizeof(struct sockaddr_in);
ioctl(self->fd, FIONBIO, (char *)&on);
fcntl(self->fd, F_SETFL, fcntl(self->fd, F_GETFL) | O_NONBLOCK);
#endif
if (bind( self->fd,
(struct sockaddr *) &self->address,
sizeof(struct sockaddr_in) )==0)
if (bind(fd, (struct sockaddr *) &server->address, s_opt)==0)
{
if (listen(self->fd, SOMAXCONN) == 0)
return result;
if (listen(fd, SOMAXCONN) == 0)
{
server->fd = fd;
return AISL_SUCCESS;
}
}
server_close(self);
close(fd);
}
result = AISL_SYSCALL_ERROR;
return result;
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 )
{
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
/* -------------------------------------------------------------------------- */
*p_client = NULL;
bool
server_touch(server_t self)
fd = accept(server->fd, (struct sockaddr *) &addr, &len);
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;
client_t cli;
if (self->fd == -1)
if (server->fd == -1)
{
result = server_open(self);
if (result == AISL_SUCCESS)
aisl_raise_event(
self->owner,
self,
AISL_SERVER_OPEN,
self->flags
);
else
aisl_raise_event(
self->owner,
self,
AISL_SERVER_ERROR,
self->flags,
strerror(errno)
);
return result;
}
result = client_accept(&cli, self);
if (result == AISL_SUCCESS)
{
if (list_append(self->owner->clients, cli) == -1)
{
client_free(cli);
result = AISL_MALLOC_ERROR;
}
else
aisl_raise_event(self->owner, self, AISL_CLIENT_CONNECT, cli);
aisl_event_t event;
result = aisl_server_open(server);
event = (result == AISL_SUCCESS) ? AISL_EVENT_SERVER_OPEN
: AISL_EVENT_SERVER_ERROR;
aisl_raise(server->instance, server, event);
}
else
result = aisl_server_accept(server, p_client, ssl_ctx);
return result;
}
/* -------------------------------------------------------------------------- */
/* API Level ---------------------------------------------------------------- */
server_t
server_new(const char * address, int port)
aisl_server_t
aisl_server_new(aisl_t instance, const char * host, uint16_t port)
{
server_t self;
if ( (self = calloc(1, sizeof(struct server))) != NULL )
aisl_server_t server;
if ( (server = calloc(1, sizeof(struct aisl_server))) != NULL )
{
self->fd = -1;
self->port = port;
if ( !(self->host = str_copy(address)) )
{
free(self);
self = NULL;
}
server->instance = instance;
server->fd = -1;
server->address.sin_family = AF_INET;
server->address.sin_addr.s_addr = inet_addr(host);
server->address.sin_port = htons(port);
}
return self;
return server;
}
/* -------------------------------------------------------------------------- */
void
server_free(server_t self)
aisl_server_free(aisl_server_t server)
{
if (self)
if (server)
{
if (self->fd > -1)
server_close(self);
if ( server->fd != -1)
{
close(server->fd);
server->fd=-1;
}
if (self->host)
free(self->host);
free(self);
free(server);
}
}
/* -------------------------------------------------------------------------- */
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
#define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#include <arpa/inet.h>
#include <aisl/types.h>
#include <aisl/server.h>
#define SERVER(x) ((aisl_server_t) x)
#define AISL_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_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
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 */

View File

@ -26,14 +26,11 @@ typedef struct ssl * 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
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 */

View File

@ -1,7 +1,7 @@
#ifndef 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 "list.h"
#include "buffer.h"
@ -49,15 +49,12 @@ typedef enum {
/* real wrapper for aisl_stream_t */
typedef struct aisl_stream * aisl_stream_t;
#define STREAM(x) ((stream_t) x)
#define ASTREAM(x) ((aisl_stream_t) x)
#define STREAM(x) ((aisl_stream_t) x)
/* -------------------------------------------------------------------------- */
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 */