547 lines
12 KiB
C
547 lines
12 KiB
C
/*
|
|
* src/instance.c
|
|
*
|
|
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
|
|
*
|
|
* Project homepage: https://lowenware.com/aisl/
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "list.h"
|
|
#include "str-utils.h"
|
|
#include "buffer.h"
|
|
|
|
#include "client.h"
|
|
#include "server.h"
|
|
#include "ssl.h"
|
|
//#include "globals.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;
|
|
};
|
|
|
|
typedef struct callback * callback_t;
|
|
|
|
|
|
struct aisl
|
|
{
|
|
list_t servers;
|
|
list_t clients;
|
|
list_t callbacks;
|
|
#ifndef AISL_WITHOUT_SSL
|
|
list_t ssl;
|
|
#endif
|
|
buffer_t buffer;
|
|
char * last_error;
|
|
int iterator;
|
|
int stage;
|
|
int flags;
|
|
uint32_t accept_limit;
|
|
};
|
|
|
|
|
|
#ifndef AISL_WITHOUT_SSL
|
|
static uint32_t m_instances = 0;
|
|
#endif
|
|
|
|
|
|
/* Initialization functions */
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
aisl_t
|
|
aisl_new( aisl_config_t config )
|
|
{
|
|
aisl_t instance;
|
|
|
|
#ifndef AISL_WITHOUT_SSL
|
|
if ((m_instances++) == 0)
|
|
{
|
|
SSL_load_error_strings();
|
|
OpenSSL_add_ssl_algorithms();
|
|
}
|
|
#endif
|
|
|
|
if ( !(instance = calloc(1, sizeof(struct aisl))) )
|
|
goto finally;
|
|
|
|
if ( !(instance->servers = list_new(config->servers_spool_size)) )
|
|
goto release;
|
|
|
|
if ( !(instance->clients = list_new(config->clients_spool_size)) )
|
|
goto release;
|
|
|
|
if ( !(instance->callbacks = list_new(config->callbacks_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)) )
|
|
goto release;
|
|
#endif
|
|
|
|
instance->accept_limit = config->clients_accept_limit;
|
|
|
|
goto finally;
|
|
|
|
release:
|
|
aisl_free(instance);
|
|
instance = NULL;
|
|
|
|
finally:
|
|
return instance;
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
void
|
|
aisl_free( aisl_t instance )
|
|
{
|
|
if (instance->clients)
|
|
list_free(instance->clients, (list_destructor_t) aisl_client_free );
|
|
|
|
if (instance->servers)
|
|
list_free(instance->servers, (list_destructor_t) aisl_server_free );
|
|
|
|
if (instance->callbacks)
|
|
list_free(instance->callbacks, free);
|
|
|
|
if (instance->buffer)
|
|
buffer_free(instance->buffer);
|
|
|
|
#ifndef AISL_WITHOUT_SSL
|
|
if (instance->ssl)
|
|
list_free(instance->ssl, (list_destructor_t) ssl_free );
|
|
#endif
|
|
|
|
if (instance->last_error)
|
|
free(instance->last_error);
|
|
|
|
free(instance);
|
|
|
|
#ifndef AISL_WITHOUT_SSL
|
|
if ((--m_instances) == 0)
|
|
{
|
|
EVP_cleanup();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
aisl_server_t
|
|
aisl_listen( aisl_t instance, const char * address, uint16_t port )
|
|
{
|
|
aisl_server_t result;
|
|
|
|
if ( (result = aisl_server_new(instance, address, port)) != NULL )
|
|
{
|
|
if (list_append(instance->servers, result) == LIST_NAN)
|
|
{
|
|
aisl_server_free(result);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#ifndef AISL_WITHOUT_SSL
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
aisl_status_t
|
|
aisl_set_ssl( aisl_t instance, const char * domain,
|
|
const char * key_file,
|
|
const char * crt_file )
|
|
{
|
|
SSL_CTX * ssl_ctx = NULL;
|
|
ssl_t ssl;
|
|
size_t i;
|
|
|
|
/* lookup for existing contexts */
|
|
for (i=0; i<instance->ssl->count; i++)
|
|
{
|
|
ssl = list_index(instance->ssl, 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ssl = ssl_new(domain, key_file, crt_file)) != 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;
|
|
}
|
|
}
|
|
|
|
ssl_free(ssl);
|
|
}
|
|
|
|
return AISL_MALLOC_ERROR;
|
|
}
|
|
|
|
|
|
SSL_CTX *
|
|
aisl_get_ssl_ctx( aisl_t instance, const char * domain )
|
|
{
|
|
size_t i;
|
|
ssl_t ssl;
|
|
|
|
if (domain)
|
|
{
|
|
for (i=0; i<instance->ssl->count; i++)
|
|
{
|
|
ssl = list_index(instance->ssl, i);
|
|
if (strcmp(ssl->domain, domain) != 0)
|
|
continue;
|
|
|
|
return ssl->ctx;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
aisl_status_t
|
|
aisl_set_callback( aisl_t instance,
|
|
void * source,
|
|
aisl_event_t event,
|
|
aisl_callback_t callback )
|
|
{
|
|
callback_t cb;
|
|
|
|
if ( (cb = malloc(sizeof(struct callback))) != NULL )
|
|
{
|
|
cb->source = source;
|
|
cb->event = event;
|
|
cb->callback = callback;
|
|
|
|
if ( list_append(instance->callbacks, cb) != LIST_NAN )
|
|
{
|
|
switch(event)
|
|
{
|
|
case AISL_STREAM_OUTPUT:
|
|
if (source)
|
|
stream_set_chunked_output( (aisl_stream_t) source );
|
|
//( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
|
|
break;
|
|
|
|
case AISL_STREAM_OPEN:
|
|
instance->flags |= AISL_FLAG_HAS_STREAM_LISTENERS;
|
|
break;
|
|
}
|
|
|
|
return AISL_SUCCESS;
|
|
}
|
|
free(cb);
|
|
}
|
|
return AISL_MALLOC_ERROR;
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
void
|
|
aisl_unset_callback_for( aisl_t instance, void * source )
|
|
{
|
|
size_t i = instance->callbacks->count;
|
|
|
|
if (i)
|
|
{
|
|
for(i=i-1; i <= 0; i-- )
|
|
{
|
|
callback_t callback = list_index(instance->callbacks, i);
|
|
if ( callback->source == source )
|
|
{
|
|
free(callback);
|
|
list_remove_index(instance->callbacks, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
bool
|
|
aisl_raise( aisl_t instance,
|
|
void * source,
|
|
aisl_event_t e_id,
|
|
... )
|
|
{
|
|
va_list vl;
|
|
bool result;
|
|
|
|
va_start(vl, e_id);
|
|
result = aisl_raise_vl(instance, source, e_id, vl);
|
|
va_end(vl);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
bool
|
|
aisl_raise_vl( aisl_t instance,
|
|
void * source,
|
|
aisl_event_t e_id,
|
|
va_list vl )
|
|
{
|
|
int i,
|
|
i_val;
|
|
listener_t lst;
|
|
bool res = false;
|
|
|
|
char * c_ptr,
|
|
* c_ptr2;
|
|
|
|
for(i=instance->callbacks->count-1; i>=0; i--)
|
|
{
|
|
lst = list_index(instance->callbacks, i);
|
|
|
|
/*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/
|
|
if (lst->e_id == e_id && (source == lst->source || lst->source == NULL))
|
|
{
|
|
/*
|
|
if (e_id == AISL_STREAM_HEADER)
|
|
fprintf(stderr,"FOUND HANDLER %d\n", i);
|
|
*/
|
|
|
|
/*printf(" catch\n");*/
|
|
switch(e_id)
|
|
{
|
|
/* server events */
|
|
case AISL_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 *));
|
|
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);
|
|
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);
|
|
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);
|
|
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;
|
|
break;
|
|
|
|
default:
|
|
res = ((aisl_on_custom_event_t) lst->cb)(source, vl);
|
|
}
|
|
if (res) break;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
__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;
|
|
|
|
|
|
switch (instance->stage)
|
|
{
|
|
case STAGE_SERVER:
|
|
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 ( ! (instance->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) )
|
|
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 );
|
|
list_remove_index(instance->clients, i);
|
|
}
|
|
|
|
if (r) return AISL_SUCCESS;
|
|
|
|
if ( ! (++cnt < max) ) return AISL_IDLE;
|
|
}
|
|
instance->iterator = 0;
|
|
instance->stage = 0;
|
|
}
|
|
|
|
return AISL_IDLE;
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
const char *
|
|
aisl_get_error( aisl_t instance )
|
|
{
|
|
return instance->last_error;
|
|
}
|
|
|
|
|
|
void
|
|
aisl_set_error( aisl_t instance, const char * err_msg )
|
|
{
|
|
if (instance->last_error)
|
|
free(instance->last_error);
|
|
|
|
instance->last_error = str_copy(err_msg);
|
|
}
|
|
|
|
|
|
__attribute__ ((visibility ("default") ))
|
|
int
|
|
aisl_sleep( aisl_t instance, unsigned long usec )
|
|
{
|
|
int maxfd=0,
|
|
sd;
|
|
size_t i;
|
|
struct timeval timeout = {0,usec};
|
|
|
|
memset(&timeout, 0, sizeof(struct timeval));
|
|
timeout.tv_usec = usec;
|
|
|
|
fd_set fs;
|
|
FD_ZERO (&fs);
|
|
|
|
for (i=0; i<instance->servers->count; i++)
|
|
{
|
|
aisl_server_t s = list_index(instance->servers, i);
|
|
|
|
sd = aisl_server_get_socket(s);
|
|
|
|
if (sd != -1)
|
|
{
|
|
FD_SET(sd, &fs);
|
|
if (sd > maxfd) maxfd = sd;
|
|
}
|
|
}
|
|
|
|
|
|
for (i=0; i<instance->clients->count; i++)
|
|
{
|
|
aisl_client_t c = list_index(instance->clients, i);
|
|
sd = aisl_client_get_socket(s);
|
|
if (sd != -1)
|
|
{
|
|
FD_SET(sd, &fs);
|
|
if (sd > maxfd) maxfd = sd;
|
|
}
|
|
}
|
|
|
|
return select(maxfd+1, &fs, NULL, NULL, &timeout);
|
|
|
|
}
|
|
|
|
|