Add working Hello World example
This commit is contained in:
parent
8847f92528
commit
d28f942cf7
|
@ -58,9 +58,10 @@ hello_world(aisl_stream_t s)
|
|||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
aisl_t aisl; /**< AISL instance pointer */
|
||||
aisl_status_t status; /**< AISL status code */
|
||||
uint16_t port = 0;
|
||||
aisl_t aisl; /**< AISL instance pointer */
|
||||
aisl_status_t status; /**< AISL status code */
|
||||
struct aisl_config config = AISL_CONFIG_DEFAULT;
|
||||
uint16_t port = 0;
|
||||
|
||||
/* Try to use first argument as a port number */
|
||||
if (argc > 1)
|
||||
|
@ -71,32 +72,24 @@ main(int argc, char ** argv)
|
|||
port = DEFAULT_HTTP_PORT;
|
||||
|
||||
/* Initialize instance */
|
||||
if ( (aisl = aisl_new(NULL)) != NULL )
|
||||
if ( (aisl = aisl_new(&config)) != NULL )
|
||||
{
|
||||
if (aisl_listen( aisl, "0.0.0.0", port ) != NULL)
|
||||
{
|
||||
aisl_callback_t callback = AISL_CALLBACK(hello_world);
|
||||
|
||||
/* Set up request callback */
|
||||
status = aisl_set_callback( aisl
|
||||
, NULL
|
||||
, AISL_EVENT_STREAM_REQUEST
|
||||
, callback );
|
||||
aisl_set_callback( aisl
|
||||
, AISL_EVENT_STREAM_REQUEST
|
||||
, AISL_CALLBACK(hello_world) );
|
||||
|
||||
/* if callback was set, launch application loop */
|
||||
if ( status == AISL_SUCCESS )
|
||||
/* launch application loop */
|
||||
fprintf(stdout, "Entering main loop" );
|
||||
for(;;)
|
||||
{
|
||||
fprintf(stdout, "Entering main loop" );
|
||||
for(;;)
|
||||
{
|
||||
status = aisl_run_cycle(aisl);
|
||||
status = aisl_run_cycle(aisl);
|
||||
|
||||
if ( status != AISL_SUCCESS )
|
||||
aisl_sleep(aisl, 500);
|
||||
}
|
||||
if ( status != AISL_SUCCESS )
|
||||
aisl_sleep(aisl, 500);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "Failed to register callback" );
|
||||
|
||||
aisl_free(aisl);
|
||||
}
|
||||
|
|
|
@ -12,13 +12,22 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#define AISL_CONFIG_DEFAULT \
|
||||
{ \
|
||||
.server_spool_size = 1 \
|
||||
, .client_spool_size = 32 \
|
||||
, .ssl_spool_size = 1 \
|
||||
, .initial_buffer_size = 16536 \
|
||||
, .client_accept_limit = 1024 \
|
||||
, .client_silence_timeout = 30 \
|
||||
} \
|
||||
|
||||
|
||||
struct aisl_config
|
||||
{
|
||||
uint32_t server_spool_size;
|
||||
uint32_t client_spool_size;
|
||||
uint32_t ssl_spool_size;
|
||||
uint32_t callback_spool_size;
|
||||
uint32_t initial_buffer_size;
|
||||
uint32_t client_accept_limit;
|
||||
uint32_t client_silence_timeout;
|
||||
|
|
|
@ -76,52 +76,13 @@ aisl_set_ssl( aisl_t instance,
|
|||
* - #aisl_stream_t;
|
||||
*
|
||||
* @param instance a pointer to #aisl_t instance.
|
||||
* @param source a pointer to an event source.
|
||||
* @param event a code of event.
|
||||
* @param callback a pointer to function that will be triggered on event.
|
||||
* @return #aisl_status_t code.
|
||||
*/
|
||||
aisl_status_t
|
||||
aisl_set_callback( aisl_t instance,
|
||||
void * source,
|
||||
aisl_event_t event,
|
||||
aisl_callback_t callback );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Raises event from source.
|
||||
* @param instance a pointer to #aisl_t instance.
|
||||
* @param source a pointer to an event source.
|
||||
* @param event a code of event.
|
||||
* @param ... a list of arguments specific for event.
|
||||
* @return true if event was handled by at least one callback, false otherwise.
|
||||
*/
|
||||
bool
|
||||
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Raises event from source.
|
||||
* @param instance a pointer to #aisl_t instance.
|
||||
* @param source a pointer to an event source.
|
||||
* @param event a code of event.
|
||||
* @param args a list of arguments specific for event.
|
||||
* @return true if event was handled by at least one callback, false otherwise.
|
||||
*/
|
||||
bool
|
||||
aisl_raise_vl( aisl_t instance,
|
||||
void * source,
|
||||
aisl_event_t event,
|
||||
va_list args );
|
||||
|
||||
|
||||
/**
|
||||
* @brief Unsets callbacks for specified source.
|
||||
* @param instance a pointer to #aisl_t instance.
|
||||
* @param source a pointer to an event source.
|
||||
*/
|
||||
void
|
||||
aisl_unset_callbacks_for( aisl_t instance, void * source );
|
||||
aisl_set_callback( aisl_t instance,
|
||||
aisl_event_t event,
|
||||
aisl_callback_t callback );
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,23 +68,22 @@ aisl_status_to_string(aisl_status_t status);
|
|||
|
||||
typedef enum
|
||||
{
|
||||
AISL_HTTP_1_0
|
||||
, AISL_HTTP_1_1
|
||||
, AISL_HTTP_2_0
|
||||
AISL_HTTP_0_9 = 0x0009
|
||||
, AISL_HTTP_1_0 = 0x0100
|
||||
, AISL_HTTP_1_1 = 0x0101
|
||||
, AISL_HTTP_2_0 = 0x0200
|
||||
|
||||
} aisl_http_version_t;
|
||||
|
||||
#ifndef WITHOUT_STRINGIFIERS
|
||||
|
||||
const char *
|
||||
aisl_http_version_to_string( aisl_http_version_t version );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AISL_HTTP_GET
|
||||
AISL_HTTP_METHOD_UNKNOWN
|
||||
, AISL_HTTP_GET
|
||||
, AISL_HTTP_PUT
|
||||
, AISL_HTTP_POST
|
||||
, AISL_HTTP_HEAD
|
||||
|
@ -97,13 +96,10 @@ typedef enum
|
|||
|
||||
} aisl_http_method_t;
|
||||
|
||||
#ifndef WITHOUT_STRINGIFIERS
|
||||
|
||||
const char *
|
||||
aisl_http_method_to_string( aisl_http_method_t method );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
@ -160,14 +156,6 @@ const char *
|
|||
aisl_http_response_to_string( aisl_http_response_t code );
|
||||
|
||||
|
||||
#ifndef WITHOUT_STRINGIFIERS
|
||||
|
||||
const char *
|
||||
aisl_http_secure_to_string( bool is_secure );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Built-in Events IDs */
|
||||
|
||||
enum
|
||||
|
@ -187,8 +175,6 @@ enum
|
|||
, AISL_EVENT_STREAM_CLOSE = 350
|
||||
, AISL_EVENT_STREAM_ERROR = 390
|
||||
|
||||
, AISL_EVENTS_CUSTOM = 1000
|
||||
|
||||
};
|
||||
|
||||
#ifndef WITHOUT_STRINGIFIERS
|
||||
|
@ -231,7 +217,9 @@ typedef bool
|
|||
const char * val );
|
||||
|
||||
typedef bool
|
||||
(*aisl_on_stream_input_t)( aisl_stream_t stream, char * data, int32_t len );
|
||||
(*aisl_on_stream_input_t)( aisl_stream_t stream,
|
||||
const char * data,
|
||||
int32_t len );
|
||||
|
||||
typedef bool
|
||||
(*aisl_on_stream_request_t)( aisl_stream_t stream );
|
||||
|
@ -245,7 +233,4 @@ typedef bool
|
|||
typedef bool
|
||||
(*aisl_on_stream_error_t)( aisl_stream_t stream );
|
||||
|
||||
typedef bool
|
||||
(*aisl_on_custom_event_t)( void * source, va_list vl );
|
||||
|
||||
#endif /* !AISL_TYPES_H */
|
||||
|
|
|
@ -45,8 +45,15 @@ PROJECT_LIBRARIES = \
|
|||
`$(PKG_CONFIG) --libs openssl` \
|
||||
|
||||
|
||||
# compilation macro options:
|
||||
# AISL_WITHOUT_SSL - exclude HTTPS support
|
||||
# AISL_WITHOUT_STRINGIFIERS - exclude several *_to_string functions not
|
||||
|
||||
# flags
|
||||
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L \
|
||||
PROJECT_CFLAGS = -D_POSIX_C_SOURCE=200809L
|
||||
#PROJECT_CFLAGS += -DDEBUG
|
||||
#PROJECT_CFLAGS += -DAISL_WITHOUT_SSL
|
||||
#PROJECT_CFLAGS += -DAISL_WITHOUT_STRINGIFIERS
|
||||
|
||||
|
||||
# PROJECT_LDFLAGS = -L
|
||||
|
|
10
src/buffer.c
10
src/buffer.c
|
@ -21,7 +21,7 @@
|
|||
static int32_t
|
||||
buffer_set_size(buffer_t buffer, int32_t new_size)
|
||||
{
|
||||
if ( new_size < buffer->size )
|
||||
if ( new_size != buffer->size )
|
||||
{
|
||||
if (new_size)
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ buffer_set_size(buffer_t buffer, int32_t new_size)
|
|||
}
|
||||
}
|
||||
else
|
||||
new_size = 4096;
|
||||
new_size = 16*1024;
|
||||
|
||||
char * data = realloc(buffer->data, new_size);
|
||||
|
||||
|
@ -64,7 +64,10 @@ void
|
|||
buffer_release( buffer_t buffer )
|
||||
{
|
||||
if (buffer->data)
|
||||
{
|
||||
free(buffer->data);
|
||||
buffer->data = NULL;
|
||||
}
|
||||
|
||||
buffer->used = 0;
|
||||
buffer->size = 0;
|
||||
|
@ -125,7 +128,7 @@ buffer_append_vprintf( buffer_t buffer, const char * format, va_list args )
|
|||
result;
|
||||
|
||||
va_list cp_args;
|
||||
va_copy(args, cp_args);
|
||||
va_copy(cp_args, args);
|
||||
|
||||
result = vsnprintf( &buffer->data[buffer->used], space, format, args );
|
||||
|
||||
|
@ -143,7 +146,6 @@ buffer_append_vprintf( buffer_t buffer, const char * format, va_list args )
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
buffer_append( buffer_t buffer, const char * data, int32_t size )
|
||||
{
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* callback.c
|
||||
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include "callback.h"
|
||||
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* src/callback.h
|
||||
*
|
||||
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
|
||||
*
|
||||
* Project homepage: https://lowenware.com/aisl/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AISL_CALLBACK_H_43D3FC6B_22E5_481C_8EB2_00BF8C3D52CA
|
||||
#define AISL_CALLBACK_H_43D3FC6B_22E5_481C_8EB2_00BF8C3D52CA
|
||||
|
||||
#include <aisl/types.h>
|
||||
|
||||
|
||||
struct callback
|
||||
{
|
||||
void * source;
|
||||
aisl_callback_t f_ptr;
|
||||
aisl_event_t event;
|
||||
};
|
||||
|
||||
typedef struct callback * callback_t;
|
||||
|
||||
|
||||
static listener_t
|
||||
listener_new( void * source, aisl_event_t e_id, aisl_callback_t cb)
|
||||
{
|
||||
listener_t self = malloc(sizeof(struct listener));
|
||||
if (self)
|
||||
{
|
||||
self->source = source;
|
||||
self->e_id = e_id;
|
||||
self->cb = cb;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* !AISL_CALLBACK_H */
|
116
src/client.c
116
src/client.c
|
@ -11,9 +11,11 @@
|
|||
#endif
|
||||
|
||||
#include <aisl/aisl.h>
|
||||
#include "debug.h"
|
||||
#include "stream.h"
|
||||
#include "http.h"
|
||||
#include "buffer.h"
|
||||
#include "server.h"
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
|
||||
#define FLAG_KEEPALIVE (1<<0)
|
||||
|
@ -23,72 +25,39 @@
|
|||
|
||||
#define BUFFER_SIZE (16*1024)
|
||||
|
||||
struct aisl_client
|
||||
{
|
||||
struct sockaddr_in address; /**< Client's address structure. */
|
||||
aisl_server_t server; /**< Server instance. */
|
||||
struct buffer in; /**< Client's input buffer. */
|
||||
struct buffer out; /**< Client's output buffer. */
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
SSL * ssl; /**< SSL pointer for HTTPS. */
|
||||
#endif
|
||||
time_t timestamp; /**< Last communication timestamp. */
|
||||
|
||||
aisl_stream_t stream; /**< Pending client's stream. */
|
||||
int next_id; /**< Stream id generator (even). */
|
||||
int flags; /**< Client's flag bitmask. */
|
||||
int fd; /**< Client's socket descriptor. */
|
||||
|
||||
aisl_http_version_t http_version; /**< Client's http_version version. */
|
||||
};
|
||||
|
||||
|
||||
static bool
|
||||
aisl_client_raise(aisl_client_t client, aisl_event_t event, ...)
|
||||
{
|
||||
bool result;
|
||||
va_list vl_args;
|
||||
|
||||
aisl_t instance = aisl_server_get_instance(client->server);
|
||||
|
||||
va_start(vl_args, event);
|
||||
result = aisl_raise_vl(instance, client, event, vl_args);
|
||||
va_end(vl_args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static aisl_status_t
|
||||
aisl_client_parse(aisl_client_t client, char * data, int32_t size)
|
||||
{
|
||||
aisl_stream_t s;
|
||||
http_parser_t p = HTTP_PARSER_PENDING;
|
||||
http_parser_t p = HTTP_PARSER_SUCCESS;
|
||||
|
||||
int32_t bytes_left = size;
|
||||
|
||||
switch (client->http_version)
|
||||
{
|
||||
case AISL_HTTP_0_9:
|
||||
case AISL_HTTP_1_0:
|
||||
case AISL_HTTP_1_1:
|
||||
|
||||
s = client->stream;
|
||||
|
||||
while ( p == HTTP_PARSER_PENDING )
|
||||
while ( p == HTTP_PARSER_SUCCESS )
|
||||
{
|
||||
|
||||
switch ( aisl_stream_get_state(s) )
|
||||
{
|
||||
case AISL_STREAM_STATE_IDLE:
|
||||
p = http_10_parse_request(data, &size, client);
|
||||
p = http_10_parse_request(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
case AISL_STREAM_STATE_WAIT_HEADER:
|
||||
p = http_10_parse_header(data, &size, client);
|
||||
p = http_10_parse_header(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
case AISL_STREAM_STATE_WAIT_BODY:
|
||||
p = http_10_parse_body(data, &size, client);
|
||||
p = http_10_parse_body(data, &size, client->stream);
|
||||
break;
|
||||
|
||||
default: /* has input data, but request was already parsed */
|
||||
|
@ -101,10 +70,16 @@ aisl_client_parse(aisl_client_t client, char * data, int32_t size)
|
|||
size = bytes_left;
|
||||
}
|
||||
|
||||
if (p == HTTP_PARSER_FINISHED)
|
||||
if (p == HTTP_PARSER_READY)
|
||||
{
|
||||
client->flags &= ~FLAG_CAN_READ;
|
||||
client->flags |= FLAG_CAN_WRITE;
|
||||
|
||||
aisl_raise(
|
||||
client->server->instance,
|
||||
client->stream,
|
||||
AISL_EVENT_STREAM_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -117,6 +92,14 @@ aisl_client_parse(aisl_client_t client, char * data, int32_t size)
|
|||
if (p == HTTP_PARSER_ERROR)
|
||||
{
|
||||
/* reply Bad Request here */
|
||||
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
|
||||
|
||||
aisl_raise(
|
||||
client->server->instance,
|
||||
client->stream,
|
||||
AISL_EVENT_STREAM_ERROR
|
||||
);
|
||||
|
||||
aisl_client_close(client);
|
||||
|
||||
return AISL_INPUT_ERROR;
|
||||
|
@ -142,9 +125,12 @@ aisl_client_input(aisl_client_t client)
|
|||
char * data = &client->in.data[ client->in.used ];
|
||||
int32_t size = client->in.size - client->in.used;
|
||||
|
||||
DPRINTF("Buffer: %p, %d", data, size);
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
if (client->ssl)
|
||||
{
|
||||
DPRINTF("SSL_read");
|
||||
if ( !(client->flags & FLAG_HANDSHAKE) )
|
||||
{
|
||||
if ( (l = SSL_accept(client->ssl)) != 1 )
|
||||
|
@ -154,15 +140,13 @@ aisl_client_input(aisl_client_t client)
|
|||
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
|
||||
return AISL_IDLE;
|
||||
|
||||
fprintf( stderr
|
||||
, "*AISL: SSL handshake fail: %s\n"
|
||||
, ERR_error_string(l, NULL) );
|
||||
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
|
||||
|
||||
aisl_client_close(client);
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
client->flags |= FLAG_HANDSHAKE;
|
||||
client->flags &= ~FLAG_HANDSHAKE;
|
||||
}
|
||||
|
||||
l = SSL_read(client->ssl, data, size) ;
|
||||
|
@ -173,6 +157,8 @@ aisl_client_input(aisl_client_t client)
|
|||
|
||||
if (l > 0)
|
||||
{
|
||||
DPRINTF("%d bytes received from client", l);
|
||||
|
||||
data = client->in.data;
|
||||
size = client->in.used + l;
|
||||
|
||||
|
@ -182,15 +168,19 @@ aisl_client_input(aisl_client_t client)
|
|||
}
|
||||
else if (l<0)
|
||||
{
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
if (client->ssl)
|
||||
{
|
||||
if (SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_READ)
|
||||
return AISL_IDLE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(errno == EWOULDBLOCK)
|
||||
return AISL_IDLE;
|
||||
|
||||
DPRINTF("client - %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,9 +234,13 @@ aisl_client_output(aisl_client_t client)
|
|||
if ( !l )
|
||||
return AISL_IDLE;
|
||||
|
||||
#ifdef AISL_WITHOUT_SSL
|
||||
l = send( client->fd, data, l, 0);
|
||||
#else
|
||||
l = (client->ssl) ?
|
||||
SSL_write(client->ssl, data, l) :
|
||||
send( client->fd, data, l, 0);
|
||||
#endif
|
||||
|
||||
if (l > 0)
|
||||
{
|
||||
|
@ -282,12 +276,14 @@ aisl_client_output(aisl_client_t client)
|
|||
}
|
||||
|
||||
/* l < 0 */
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
if (client->ssl)
|
||||
{
|
||||
if ( SSL_get_error(client->ssl, l) == SSL_ERROR_WANT_WRITE )
|
||||
return AISL_IDLE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (errno == EWOULDBLOCK)
|
||||
return AISL_IDLE;
|
||||
|
@ -299,24 +295,17 @@ aisl_client_output(aisl_client_t client)
|
|||
}
|
||||
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
aisl_client_t
|
||||
aisl_client_new( aisl_server_t server,
|
||||
int fd,
|
||||
struct sockaddr_in * addr,
|
||||
SSL_CTX * ssl_ctx )
|
||||
#else
|
||||
aisl_client_t
|
||||
aisl_client_new( aisl_server_t server,
|
||||
int fd,
|
||||
struct sockaddr_in * addr )
|
||||
#endif
|
||||
{
|
||||
aisl_client_t client;
|
||||
aisl_stream_t stream;
|
||||
|
||||
if ( (client = calloc(1, sizeof(struct aisl_client))) != NULL )
|
||||
{
|
||||
DPRINTF("client alocated");
|
||||
memcpy(&client->address, addr, sizeof(struct sockaddr_in));
|
||||
client->server = server;
|
||||
client->fd = fd;
|
||||
|
@ -325,8 +314,9 @@ aisl_client_new( aisl_server_t server,
|
|||
client->timestamp = time(NULL);
|
||||
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
|
||||
|
||||
if (buffer_init(&client->in, 2*BUFFER_SIZE) == 0)
|
||||
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1)
|
||||
{
|
||||
DPRINTF("client buffer alocated");
|
||||
memcpy(&client->out, &client->in, sizeof(struct buffer));
|
||||
|
||||
stream = aisl_stream_new(client, 0);
|
||||
|
@ -335,15 +325,21 @@ aisl_client_new( aisl_server_t server,
|
|||
{
|
||||
client->stream = stream;
|
||||
|
||||
DPRINTF("client stream alocated");
|
||||
|
||||
#ifdef AISL_WITHOUT_SSL
|
||||
|
||||
return client;
|
||||
|
||||
#else
|
||||
|
||||
if ( !ssl_ctx )
|
||||
SSL_CTX * ssl_ctx;
|
||||
|
||||
if ( !server->ssl )
|
||||
return client;
|
||||
|
||||
ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
|
||||
|
||||
if ((client->ssl = SSL_new(ssl_ctx)) != NULL )
|
||||
{
|
||||
SSL_set_fd(client->ssl, fd);
|
||||
|
@ -436,7 +432,7 @@ aisl_client_is_timed_out(aisl_client_t client, uint32_t timeout)
|
|||
|
||||
if ( !(now - client->timestamp < timeout) )
|
||||
{
|
||||
aisl_client_raise(client, AISL_EVENT_CLIENT_TIMEOUT);
|
||||
aisl_raise(client->server->instance, client, AISL_EVENT_CLIENT_TIMEOUT);
|
||||
aisl_client_close(client);
|
||||
return true;
|
||||
}
|
||||
|
@ -468,6 +464,8 @@ aisl_client_set_keepalive(aisl_client_t client, bool value)
|
|||
client->flags &= ~FLAG_KEEPALIVE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* API Level ---------------------------------------------------------------- */
|
||||
|
||||
aisl_server_t
|
||||
|
@ -480,7 +478,11 @@ aisl_client_get_server(aisl_client_t client)
|
|||
bool
|
||||
aisl_client_is_secure(aisl_client_t client)
|
||||
{
|
||||
#ifdef AISL_WITHOUT_SSL
|
||||
return false;
|
||||
#else
|
||||
return (client->ssl == NULL) ? false : true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -496,7 +498,7 @@ aisl_client_close(aisl_client_t client)
|
|||
{
|
||||
if (client->fd != -1)
|
||||
{
|
||||
aisl_client_raise( client, AISL_EVENT_CLIENT_DISCONNECT );
|
||||
aisl_raise(client->server->instance, client, AISL_EVENT_CLIENT_DISCONNECT);
|
||||
|
||||
close(client->fd);
|
||||
shutdown(client->fd, SHUT_RDWR);
|
||||
|
|
37
src/client.h
37
src/client.h
|
@ -4,23 +4,30 @@
|
|||
#include <arpa/inet.h>
|
||||
|
||||
#include <aisl/client.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define AISL_CLIENT(x) ((aisl_client_t) x)
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
/**
|
||||
* @brief Constructor for #aisl_client_t instance.
|
||||
* @param server an #aisl_server_t instance pointer.
|
||||
* @param fd a client socket descriptor.
|
||||
* @param addr a pointer to client's address structure.
|
||||
* @param ssl_ctx a pointer to SSL context or NULL if encryption is disabled
|
||||
*/
|
||||
aisl_client_t
|
||||
aisl_client_new( aisl_server_t server,
|
||||
int fd,
|
||||
struct sockaddr_in * addr,
|
||||
SSL_CTX * ssl_ctx);
|
||||
#else
|
||||
|
||||
struct aisl_client
|
||||
{
|
||||
struct sockaddr_in address; /**< Client's address structure. */
|
||||
aisl_server_t server; /**< Server instance. */
|
||||
struct buffer in; /**< Client's input buffer. */
|
||||
struct buffer out; /**< Client's output buffer. */
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
SSL * ssl; /**< SSL pointer for HTTPS. */
|
||||
#endif
|
||||
time_t timestamp; /**< Last communication timestamp. */
|
||||
|
||||
aisl_stream_t stream; /**< Pending client's stream. */
|
||||
int next_id; /**< Stream id generator (even). */
|
||||
int flags; /**< Client's flag bitmask. */
|
||||
int fd; /**< Client's socket descriptor. */
|
||||
|
||||
aisl_http_version_t http_version; /**< Client's http_version version. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constructor for #aisl_client_t instance.
|
||||
|
@ -33,7 +40,6 @@ aisl_client_t
|
|||
aisl_client_new( aisl_server_t server,
|
||||
int fd,
|
||||
struct sockaddr_in * addr );
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
@ -90,4 +96,5 @@ aisl_client_set_keepalive(aisl_client_t client, bool value);
|
|||
int
|
||||
aisl_client_get_socket(aisl_client_t client);
|
||||
|
||||
|
||||
#endif /* !AISL_CLIENT_H */
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file debug.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL debug module
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||
#define AISL_DEBUG_H_703E22F9_4A6E_426F_B708_81BCA019049B
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define DPRINTF(...) do { \
|
||||
fprintf(stderr, "* AISL: "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} while(0) \
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define DPRINTF(...)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* !AISL_DEBUG_H */
|
28
src/event.c
28
src/event.c
|
@ -1,28 +0,0 @@
|
|||
#include <aisl/event.h>
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_event_get_text( aisl_event_t e_id )
|
||||
{
|
||||
switch(e_id)
|
||||
{
|
||||
case AISL_SERVER_OPEN: return "AISL_SERVER_OPEN";
|
||||
case AISL_SERVER_ERROR: return "AISL_SERVER_ERROR";
|
||||
|
||||
case AISL_CLIENT_CONNECT: return "AISL_CLIENT_CONNECT";
|
||||
case AISL_CLIENT_DISCONNECT: return "AISL_CLIENT_DISCONNECT";
|
||||
case AISL_CLIENT_TIMEOUT: return "AISL_CLIENT_TIMEOUT";
|
||||
|
||||
case AISL_STREAM_OPEN: return "AISL_STREAM_OPEN";
|
||||
case AISL_STREAM_INPUT: return "AISL_STREAM_INPUT";
|
||||
case AISL_STREAM_REQUEST: return "AISL_STREAM_REQUEST";
|
||||
case AISL_STREAM_OUTPUT: return "AISL_STREAM_OUTPUT";
|
||||
case AISL_STREAM_CLOSE: return "AISL_STREAM_CLOSE";
|
||||
case AISL_STREAM_ERROR: return "AISL_STREAM_ERROR";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "AISL_CUSTOM_EVENT";
|
||||
}
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#ifndef _AISL_GLOBALS_H_
|
||||
#define _AISL_GLOBALS_H_
|
||||
|
||||
//#pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
|
||||
/* MACOS FIX AND OTHER OS */
|
||||
#ifndef SOCK_NONBLOCK
|
||||
#define SOCK_NONBLOCK 0
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_SERVERS
|
||||
#define AISL_MIN_SERVERS 1
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_STREAMS
|
||||
#define AISL_MIN_STREAMS 1
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_CLIENTS
|
||||
#define AISL_MIN_CLIENTS 32
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_LISTENERS
|
||||
#define AISL_MIN_LISTENERS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_DELAYS
|
||||
#define AISL_MIN_DELAYS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_BUFFER_SIZE
|
||||
#define AISL_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MIN_HEADERS
|
||||
#define AISL_MIN_HEADERS 8
|
||||
#endif
|
||||
|
||||
#ifndef AISL_MAX_CLIENT_SILENCE
|
||||
#define AISL_MAX_CLIENT_SILENCE 10
|
||||
#endif
|
||||
|
||||
#endif
|
807
src/handle.c
807
src/handle.c
|
@ -1,807 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <aisl/handle.h>
|
||||
#include "str-utils.h"
|
||||
#include "list.h"
|
||||
#include "stream.h"
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "globals.h"
|
||||
#include "handle.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct listener
|
||||
{
|
||||
void *source;
|
||||
aisl_callback_t cb;
|
||||
aisl_event_t e_id;
|
||||
};
|
||||
|
||||
typedef struct listener * listener_t;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct delay
|
||||
{
|
||||
struct timespec next;
|
||||
uint32_t delay;
|
||||
void *u_data;
|
||||
aisl_callback_t cb;
|
||||
};
|
||||
|
||||
typedef struct delay * delay_t;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct crypter
|
||||
{
|
||||
char * keyFile;
|
||||
char * crtFile;
|
||||
char * srvName;
|
||||
SSL_CTX * sslCtx;
|
||||
};
|
||||
|
||||
typedef struct crypter * crypter_t;
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
static int gHandles = 0;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static listener_t
|
||||
listener_new( void * source, aisl_event_t e_id, aisl_callback_t cb)
|
||||
{
|
||||
listener_t self = malloc(sizeof(struct listener));
|
||||
if (self)
|
||||
{
|
||||
self->source = source;
|
||||
self->e_id = e_id;
|
||||
self->cb = cb;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
aisl_remove_listeners_for( aisl_handle_t self, void * source )
|
||||
{
|
||||
int i=self->listeners->count-1;
|
||||
while ( !(i < 0) )
|
||||
{
|
||||
listener_t listener = list_index(self->listeners, i);
|
||||
if ( listener->source == source )
|
||||
{
|
||||
free(listener);
|
||||
list_remove_index(self->listeners, i);
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
crypter_free( crypter_t self )
|
||||
{
|
||||
if (self->srvName)
|
||||
free(self->srvName);
|
||||
|
||||
if (self->keyFile)
|
||||
{
|
||||
free(self->keyFile);
|
||||
SSL_CTX_free(self->sslCtx);
|
||||
}
|
||||
|
||||
if (self->crtFile)
|
||||
free(self->crtFile);
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static crypter_t
|
||||
crypter_new( const char * server_name,
|
||||
const char * key_file,
|
||||
const char * crt_file )
|
||||
{
|
||||
crypter_t self;
|
||||
|
||||
if ( (self=calloc(1, sizeof(struct crypter))) != NULL )
|
||||
{
|
||||
if (!(self->srvName = str_copy( server_name ? server_name : "*" )))
|
||||
goto release;
|
||||
|
||||
if ( key_file && !(self->keyFile = str_copy(key_file)))
|
||||
goto release;
|
||||
|
||||
if ( crt_file && !(self->crtFile = str_copy(crt_file)))
|
||||
goto release;
|
||||
|
||||
}
|
||||
|
||||
goto finally;
|
||||
|
||||
|
||||
release:
|
||||
crypter_free(self);
|
||||
self = NULL;
|
||||
|
||||
finally:
|
||||
return self;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static bool
|
||||
delay_is_expired(delay_t self)
|
||||
{
|
||||
if (!self->delay) return true;
|
||||
|
||||
struct timespec tv;
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
|
||||
/*
|
||||
printf("> %ld.%ld & %ld.%ld\n", self->next.tv_sec, self->next.tv_nsec,
|
||||
tv.tv_sec, tv.tv_nsec);
|
||||
*/
|
||||
|
||||
if (tv.tv_sec > self->next.tv_sec)
|
||||
return true;
|
||||
else if (tv.tv_sec == self->next.tv_sec && tv.tv_nsec >= self->next.tv_nsec)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
delay_reset(delay_t self)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, &self->next);
|
||||
|
||||
self->next.tv_sec += self->delay / 1000;
|
||||
self->next.tv_nsec += (self->delay % 1000) * 1000000;
|
||||
|
||||
self->next.tv_sec += self->next.tv_nsec / 1000000000;
|
||||
self->next.tv_nsec = self->next.tv_nsec % 1000000000;
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static delay_t
|
||||
delay_new(aisl_callback_t cb, uint32_t delay, void *u_data)
|
||||
{
|
||||
delay_t self = malloc(sizeof(struct delay));
|
||||
|
||||
if (self)
|
||||
{
|
||||
self->cb = cb;
|
||||
self->u_data = u_data;
|
||||
self->delay = delay;
|
||||
|
||||
delay_reset(self);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_handle_t
|
||||
aisl_handle_new( size_t min_clients, size_t buffer_size )
|
||||
{
|
||||
aisl_handle_t self;
|
||||
|
||||
if ((gHandles++) == 0)
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
}
|
||||
|
||||
if ( !(self = calloc(1, sizeof(struct aisl_handle))) )
|
||||
goto finally;
|
||||
|
||||
if ( !(self->servers = list_new(1)) )
|
||||
goto release;
|
||||
|
||||
if ( !(self->clients = list_new(min_clients)) )
|
||||
goto release;
|
||||
|
||||
if ( !(self->listeners = list_new(16)) )
|
||||
goto release;
|
||||
|
||||
if ( !(self->buffer = buffer_new(buffer_size)) )
|
||||
goto release;
|
||||
|
||||
if ( !(self->crypters = list_new(0)) )
|
||||
goto release;
|
||||
|
||||
if ( !(self->delays = list_new(0)) )
|
||||
goto release;
|
||||
|
||||
goto finally;
|
||||
|
||||
release:
|
||||
aisl_handle_free(self);
|
||||
self = NULL;
|
||||
|
||||
finally:
|
||||
return self;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_handle_t
|
||||
aisl_handle_from_stream( aisl_stream_t s )
|
||||
{
|
||||
return ((client_t)s->client)->server->owner;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_handle_free( aisl_handle_t self )
|
||||
{
|
||||
if ((--gHandles) == 0)
|
||||
{
|
||||
EVP_cleanup();
|
||||
}
|
||||
|
||||
if (self->clients)
|
||||
list_free(self->clients, (list_destructor_t) client_free );
|
||||
|
||||
if (self->servers)
|
||||
list_free(self->servers, (list_destructor_t) server_free );
|
||||
|
||||
if (self->listeners)
|
||||
list_free(self->listeners, free);
|
||||
|
||||
if (self->delays)
|
||||
list_free(self->delays, free);
|
||||
|
||||
if (self->buffer)
|
||||
buffer_free(self->buffer);
|
||||
|
||||
if (self->crypters)
|
||||
list_free(self->crypters, (list_destructor_t) crypter_free );
|
||||
|
||||
if (self->lastError)
|
||||
free(self->lastError);
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_handle_set_error( aisl_handle_t self, const char * err_msg )
|
||||
{
|
||||
if (self->lastError)
|
||||
free(self->lastError);
|
||||
|
||||
self->lastError = str_copy(err_msg);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_bind( aisl_handle_t self, const char * address, int port, int flags )
|
||||
{
|
||||
server_t server;
|
||||
|
||||
if ( !(server = server_new(address, port)) )
|
||||
goto finally;
|
||||
|
||||
server->owner = self;
|
||||
server->flags |= (flags & AISL_FLAG_SSL);
|
||||
|
||||
if ( list_append(self->servers, server) == -1 )
|
||||
goto release;
|
||||
|
||||
goto finally;
|
||||
|
||||
release:
|
||||
server_free(server);
|
||||
server = NULL;
|
||||
|
||||
finally:
|
||||
return server ? AISL_SUCCESS : AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
get_ssl_context( SSL * ssl, int * ptr, void * handle )
|
||||
{
|
||||
const char * server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
|
||||
SSL_CTX * ctx = aisl_get_ssl_ctx( (aisl_handle_t) handle, server_name );
|
||||
|
||||
if (ctx)
|
||||
{
|
||||
SSL_set_SSL_CTX(ssl, ctx);
|
||||
}
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static SSL_CTX *
|
||||
create_ssl_context( aisl_handle_t self,
|
||||
const char * key_file,
|
||||
const char * crt_file )
|
||||
{
|
||||
const SSL_METHOD * method;
|
||||
SSL_CTX * ctx;
|
||||
|
||||
method = SSLv23_server_method();
|
||||
|
||||
if ( !(ctx = SSL_CTX_new(method)) )
|
||||
goto except;
|
||||
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
|
||||
SSL_CTX_set_tlsext_servername_callback( ctx, get_ssl_context );
|
||||
SSL_CTX_set_tlsext_servername_arg( ctx, (void *) self );
|
||||
|
||||
if (!(SSL_CTX_use_certificate_file(ctx, crt_file, SSL_FILETYPE_PEM) > 0))
|
||||
goto release;
|
||||
|
||||
if (!(SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) > 0))
|
||||
goto release;
|
||||
|
||||
goto finally;
|
||||
|
||||
release:
|
||||
SSL_CTX_free(ctx);
|
||||
ctx = NULL;
|
||||
|
||||
except:
|
||||
aisl_handle_set_error( self, ERR_error_string(ERR_get_error(),NULL) );
|
||||
|
||||
finally:
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_set_ssl( aisl_handle_t self, const char * server_name,
|
||||
const char * key_file,
|
||||
const char * crt_file )
|
||||
{
|
||||
SSL_CTX * ssl_ctx = NULL;
|
||||
int i;
|
||||
crypter_t crypter;
|
||||
|
||||
/* lookup for existing contexts */
|
||||
for (i=0; i<self->crypters->count; i++)
|
||||
{
|
||||
crypter = list_index(self->crypters, i);
|
||||
if (crypter->keyFile && strcmp(crypter->keyFile, key_file)==0 &&
|
||||
crypter->crtFile && strcmp(crypter->crtFile, crt_file)==0 )
|
||||
{
|
||||
ssl_ctx = crypter->sslCtx;
|
||||
key_file = NULL;
|
||||
crt_file = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! (crypter = crypter_new(server_name, key_file, crt_file)) )
|
||||
{
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
if (! ssl_ctx)
|
||||
{
|
||||
if (!(ssl_ctx = create_ssl_context(self, key_file, crt_file)))
|
||||
{
|
||||
crypter_free(crypter);
|
||||
return AISL_EXTCALL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
crypter->sslCtx = ssl_ctx;
|
||||
|
||||
if (list_append(self->crypters, crypter)==-1)
|
||||
{
|
||||
crypter_free(crypter);
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
SSL_CTX *
|
||||
aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name )
|
||||
{
|
||||
int i;
|
||||
crypter_t crypter;
|
||||
|
||||
for (i=0; i<self->crypters->count; i++)
|
||||
{
|
||||
crypter = list_index(self->crypters, i);
|
||||
if (server_name)
|
||||
{
|
||||
if (strcmp(crypter->srvName, server_name)!=0)
|
||||
continue;
|
||||
}
|
||||
|
||||
return crypter->sslCtx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_set_callback( aisl_handle_t self,
|
||||
void * source,
|
||||
aisl_event_t e_id,
|
||||
aisl_callback_t cb )
|
||||
{
|
||||
listener_t listener;
|
||||
|
||||
if (! (listener = listener_new(source, e_id, cb)) )
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if (list_append(self->listeners, listener) == -1)
|
||||
{
|
||||
free(listener);
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
if (e_id == AISL_STREAM_OUTPUT) /* subscribtion for chunked output */
|
||||
{
|
||||
if (source)
|
||||
{
|
||||
( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
|
||||
}
|
||||
}
|
||||
else if (e_id == AISL_STREAM_OPEN)
|
||||
{
|
||||
self->flags |= AISL_HANDLE_HAS_STREAM_LISTENERS;
|
||||
}
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_set_delay( aisl_handle_t self,
|
||||
aisl_callback_t cb,
|
||||
uint32_t usec,
|
||||
void * u_data )
|
||||
{
|
||||
delay_t delay = delay_new(cb, usec, u_data);
|
||||
if (!delay)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if (list_append(self->delays, delay) == -1)
|
||||
{
|
||||
free(delay);
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_raise_event( aisl_handle_t self,
|
||||
void * source,
|
||||
aisl_event_t e_id,
|
||||
... )
|
||||
{
|
||||
va_list vl;
|
||||
bool result;
|
||||
|
||||
va_start(vl, e_id);
|
||||
result = aisl_raise_event_vl(self, source, e_id, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_raise_event_vl( aisl_handle_t self,
|
||||
void * source,
|
||||
aisl_event_t e_id,
|
||||
va_list vl )
|
||||
{
|
||||
int i,
|
||||
i_val;
|
||||
listener_t lst;
|
||||
bool res = false;
|
||||
|
||||
char * c_ptr,
|
||||
* c_ptr2;
|
||||
|
||||
for(i=self->listeners->count-1; i>=0; i--)
|
||||
{
|
||||
lst = list_index(self->listeners, i);
|
||||
|
||||
/*printf("AISL> raise %s:%s, %p:%p\n", _event_text(e_id), _event_text(lst->e_id), source, lst->source);*/
|
||||
if (lst->e_id == e_id && (source == lst->source || lst->source == NULL))
|
||||
{
|
||||
/*
|
||||
if (e_id == AISL_STREAM_HEADER)
|
||||
fprintf(stderr,"FOUND HANDLER %d\n", i);
|
||||
*/
|
||||
|
||||
/*printf(" catch\n");*/
|
||||
switch(e_id)
|
||||
{
|
||||
/* server events */
|
||||
case AISL_SERVER_OPEN:
|
||||
i_val = va_arg(vl, aisl_status_t);
|
||||
res = ((aisl_server_open_t) lst->cb)( source, i_val );
|
||||
break;
|
||||
case AISL_SERVER_ERROR:
|
||||
i_val = va_arg(vl, aisl_status_t);
|
||||
c_ptr = va_arg(vl, char *);
|
||||
res = ((aisl_server_error_t) lst->cb)( source, i_val, c_ptr );
|
||||
break;
|
||||
|
||||
/* client events */
|
||||
case AISL_CLIENT_CONNECT:
|
||||
res = ((aisl_client_connect_t) lst->cb)(source, va_arg(vl, void *));
|
||||
break;
|
||||
case AISL_CLIENT_DISCONNECT:
|
||||
res = ((aisl_client_disconnect_t) lst->cb)(source, va_arg(vl, void*));
|
||||
aisl_remove_listeners_for( self, source );
|
||||
break;
|
||||
case AISL_CLIENT_TIMEOUT:
|
||||
res = ((aisl_client_timeout_t) lst->cb)(source, va_arg(vl, void *));
|
||||
break;
|
||||
|
||||
/* request events */
|
||||
case AISL_STREAM_OPEN:
|
||||
i_val = va_arg(vl, int);
|
||||
c_ptr = va_arg(vl, char *);
|
||||
c_ptr2 = va_arg(vl, char *);
|
||||
res = ((aisl_stream_open_t) lst->cb)(source, i_val, c_ptr, c_ptr2);
|
||||
break;
|
||||
|
||||
case AISL_STREAM_HEADER:
|
||||
c_ptr = va_arg(vl, char *);
|
||||
c_ptr2 = va_arg(vl, char *);
|
||||
res = ((aisl_stream_header_t) lst->cb)(source, c_ptr, c_ptr2);
|
||||
break;
|
||||
|
||||
|
||||
case AISL_STREAM_INPUT:
|
||||
/*printf("AISL> raise AISL_STREAM_INPUT\n");*/
|
||||
c_ptr = va_arg(vl, char *);
|
||||
i_val = va_arg(vl, int );
|
||||
res = ((aisl_stream_input_t) lst->cb)(source, c_ptr, i_val);
|
||||
break;
|
||||
case AISL_STREAM_REQUEST:
|
||||
/*printf("AISL> raise AISL_STREAM_REQUEST\n");*/
|
||||
buffer_clear( STREAM(source)->buffer, 0);
|
||||
res = ((aisl_stream_request_t) lst->cb)(source);
|
||||
break;
|
||||
|
||||
|
||||
case AISL_STREAM_ERROR:
|
||||
res = ((aisl_stream_error_t) lst->cb)( source, va_arg(vl, char *));
|
||||
break;
|
||||
|
||||
/* response events */
|
||||
case AISL_STREAM_OUTPUT:
|
||||
res = ((aisl_stream_output_t)lst->cb)(
|
||||
source,
|
||||
va_arg(vl, uint32_t)
|
||||
);
|
||||
break;
|
||||
case AISL_STREAM_CLOSE:
|
||||
res = ((aisl_stream_close_t)lst->cb)( source );
|
||||
aisl_remove_listeners_for( self, source );
|
||||
((aisl_stream_t) source)->u_ptr=NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
res = ((aisl_custom_event_t) lst->cb)(source, vl);
|
||||
}
|
||||
if (res) break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*
|
||||
aisl_status_t
|
||||
aisl_run( int * flags )
|
||||
{
|
||||
aisl_status_t exit_code = AISL_SUCCESS;
|
||||
struct timeval timeout;
|
||||
|
||||
while( !(*flags & (1<<0)) )
|
||||
{
|
||||
exit_code = aisl_run_cycle( gHandle );
|
||||
|
||||
if (exit_code == AISL_IDLE)
|
||||
{
|
||||
timeout.tv_usec = 300;
|
||||
timeout.tv_sec = 0;
|
||||
|
||||
select(0, NULL, NULL, NULL, &timeout);
|
||||
}
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define STAGE_SERVER 0
|
||||
#define STAGE_CLIENT 1
|
||||
#define STAGE_DELAY 2
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
aisl_run_cycle( aisl_handle_t self )
|
||||
{
|
||||
int max = self->servers->count+self->clients->count+self->delays->count,
|
||||
cnt = 0;
|
||||
|
||||
|
||||
switch (self->stage)
|
||||
{
|
||||
case STAGE_SERVER:
|
||||
while (self->iterator < self->servers->count )
|
||||
{
|
||||
server_t srv = (server_t)list_index(self->servers, self->iterator++);
|
||||
if ( server_touch(srv) != AISL_IDLE )
|
||||
return AISL_SUCCESS;
|
||||
|
||||
if ( ! (++cnt < max) ) return AISL_IDLE;
|
||||
}
|
||||
if ( ! (self->flags & AISL_HANDLE_HAS_STREAM_LISTENERS) )
|
||||
return AISL_IDLE;
|
||||
|
||||
self->iterator = 0;
|
||||
self->stage++;
|
||||
|
||||
|
||||
case STAGE_CLIENT:
|
||||
while (self->iterator < self->clients->count )
|
||||
{
|
||||
int i = self->iterator++;
|
||||
client_t cli = list_index(self->clients, i);
|
||||
bool r = client_touch( cli );
|
||||
|
||||
if (client_is_timeout( cli ) )
|
||||
aisl_raise_event( self, cli->server, AISL_CLIENT_TIMEOUT, cli );
|
||||
|
||||
if ( cli->fd == -1 )
|
||||
{
|
||||
client_free( cli );
|
||||
list_remove_index(self->clients, i);
|
||||
}
|
||||
|
||||
if (r) return AISL_SUCCESS;
|
||||
|
||||
if ( ! (++cnt < max) ) return AISL_IDLE;
|
||||
}
|
||||
self->iterator = 0;
|
||||
self->stage++;
|
||||
|
||||
case STAGE_DELAY:
|
||||
while (self->iterator < self->delays->count )
|
||||
{
|
||||
int i = self->iterator++;
|
||||
delay_t dly = list_index(self->delays, i);
|
||||
|
||||
if( delay_is_expired(dly) )
|
||||
{
|
||||
if ( ((aisl_delay_timeout_t) dly->cb)(dly->u_data))
|
||||
{
|
||||
delay_reset(dly);
|
||||
}
|
||||
else
|
||||
list_remove_index(self->delays, i);
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
if ( ! (++cnt < max) ) return AISL_IDLE;
|
||||
}
|
||||
self->iterator = 0;
|
||||
self->stage = 0;
|
||||
}
|
||||
|
||||
return AISL_IDLE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_handle_get_error( aisl_handle_t self )
|
||||
{
|
||||
return self->lastError;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
int
|
||||
aisl_sleep( aisl_handle_t self, unsigned long usec )
|
||||
{
|
||||
int maxfd=0;
|
||||
size_t i;
|
||||
struct timeval timeout = {0,usec};
|
||||
|
||||
memset(&timeout, 0, sizeof(struct timeval));
|
||||
timeout.tv_usec = usec;
|
||||
|
||||
fd_set fs;
|
||||
FD_ZERO (&fs);
|
||||
|
||||
for (i=0; i<self->servers->count; i++)
|
||||
{
|
||||
server_t s = list_index(self->servers, i);
|
||||
if (s->fd != -1)
|
||||
{
|
||||
FD_SET(s->fd, &fs);
|
||||
if (s->fd > maxfd) maxfd = s->fd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (i=0; i<self->clients->count; i++)
|
||||
{
|
||||
client_t c = list_index(self->clients, i);
|
||||
if (c->fd != -1)
|
||||
{
|
||||
FD_SET(c->fd, &fs);
|
||||
if (c->fd > maxfd) maxfd = c->fd;
|
||||
}
|
||||
}
|
||||
|
||||
return select(maxfd+1, &fs, NULL, NULL, &timeout);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
47
src/handle.h
47
src/handle.h
|
@ -1,47 +0,0 @@
|
|||
#ifndef _AISL_HANDLE_H__
|
||||
#define _AISL_HANDLE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <aisl/handle.h>
|
||||
|
||||
#include "list.h"
|
||||
#include <openssl/ssl.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define AISL_HANDLE_HAS_STREAM_LISTENERS (1<<8)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
aisl_status_t
|
||||
aisl_set_delay( aisl_handle_t self,
|
||||
aisl_callback_t cb,
|
||||
uint32_t usec,
|
||||
void * u_data );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool
|
||||
aisl_raise_event_vl( aisl_handle_t self,
|
||||
void * source,
|
||||
aisl_event_t e_id,
|
||||
va_list vl );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
SSL_CTX *
|
||||
aisl_get_ssl_ctx( aisl_handle_t self, const char * server_name );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void
|
||||
aisl_remove_listeners_for( aisl_handle_t self, void * source );
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
357
src/http.c
357
src/http.c
|
@ -1,6 +1,342 @@
|
|||
#include <aisl/http.h>
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/**
|
||||
* @file http.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief HTTP module source file
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "client.h"
|
||||
#include "stream.h"
|
||||
#include "debug.h"
|
||||
#include "http.h"
|
||||
|
||||
struct http_request
|
||||
{
|
||||
char * method;
|
||||
char * schema;
|
||||
char * host;
|
||||
char * port;
|
||||
char * path;
|
||||
char * version;
|
||||
char * newline;
|
||||
int32_t method_len;
|
||||
int32_t schema_len;
|
||||
int32_t host_len;
|
||||
int32_t port_len;
|
||||
int32_t path_len;
|
||||
int32_t version_len;
|
||||
};
|
||||
|
||||
typedef struct http_request * http_request_t;
|
||||
|
||||
static aisl_http_method_t
|
||||
http_method_from_string( const char * method, int32_t length )
|
||||
{
|
||||
int i;
|
||||
aisl_http_method_t methods[3] = {0, 0, 0};
|
||||
|
||||
switch(length)
|
||||
{
|
||||
case 3:
|
||||
methods[0] = AISL_HTTP_GET;
|
||||
methods[1] = AISL_HTTP_PUT;
|
||||
methods[2] = AISL_HTTP_PRI;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
methods[0] = AISL_HTTP_POST;
|
||||
methods[1] = AISL_HTTP_HEAD;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
methods[0] = AISL_HTTP_TRACE;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
methods[0] = AISL_HTTP_DELETE;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
methods[0] = AISL_HTTP_OPTIONS;
|
||||
methods[1] = AISL_HTTP_CONNECT;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i=0; i<sizeof(methods)/sizeof(aisl_http_method_t); i++)
|
||||
{
|
||||
if (!(methods[i]))
|
||||
break;
|
||||
|
||||
if (strcmp(method, aisl_http_method_to_string(methods[i]))==0)
|
||||
return methods[i];
|
||||
}
|
||||
|
||||
return AISL_HTTP_METHOD_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
static aisl_http_version_t
|
||||
http_version_from_string(const char * version_string)
|
||||
{
|
||||
if (strncmp(version_string, "HTTP/", 5)==0)
|
||||
{
|
||||
if (strncmp(&version_string[5], "0.9", 3)==0) return AISL_HTTP_0_9;
|
||||
if (strncmp(&version_string[5], "1.0", 3)==0) return AISL_HTTP_1_0;
|
||||
if (strncmp(&version_string[5], "1.1", 3)==0) return AISL_HTTP_1_1;
|
||||
if (strncmp(&version_string[5], "2.0", 3)==0) return AISL_HTTP_2_0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Library Level */
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_request(char * data, int32_t * p_size, aisl_stream_t stream)
|
||||
{
|
||||
/* STEP 1. Split data according to HTTP request format
|
||||
*
|
||||
* GET http://lowenware.com:80/index.html?param=value HTTP/1.1\r\n
|
||||
* ^ ^ ^ ^ ^ ^ ^ ^
|
||||
* | | | | | | | |
|
||||
* | | | | | | | +--- newline
|
||||
* | | | | | | +------------- version
|
||||
* | | | | | +------------------------- query
|
||||
* | | | | +------------------------------------- path
|
||||
* | | | +--------------------------------------- port
|
||||
* | | +----------------------------------------------------- host
|
||||
* | +------------------------------------------------------------ uri
|
||||
* +---------------------------------------------------------------- method
|
||||
*/
|
||||
char * uri = NULL,
|
||||
* uri_end = NULL,
|
||||
* host = NULL,
|
||||
* port = NULL,
|
||||
* path = NULL,
|
||||
* query = NULL,
|
||||
* version = NULL,
|
||||
* newline = NULL,
|
||||
* method = data,
|
||||
* method_end = NULL;
|
||||
|
||||
aisl_http_method_t http_method;
|
||||
aisl_http_version_t http_version;
|
||||
|
||||
int32_t size = *p_size;
|
||||
|
||||
while(!newline && size--)
|
||||
{
|
||||
switch(*data)
|
||||
{
|
||||
case ' ':
|
||||
if (!method_end)
|
||||
method_end = data;
|
||||
else if (path && !uri_end)
|
||||
uri_end = data;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (uri && !host)
|
||||
host = data+3;
|
||||
else if (host && !port)
|
||||
port = data+1;
|
||||
else if (version)
|
||||
return HTTP_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (!path && data > host)
|
||||
{
|
||||
path = data;
|
||||
if (!uri)
|
||||
uri = path;
|
||||
}
|
||||
else if (version && data-version != 4)
|
||||
return HTTP_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (!query)
|
||||
query = data+1;
|
||||
else if (version)
|
||||
return HTTP_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
newline = data;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
if (!version)
|
||||
return HTTP_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!uri && method_end)
|
||||
uri = data;
|
||||
else if (!version && uri_end)
|
||||
version = data;
|
||||
else if (version && data-version > 7)
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
|
||||
/* STEP 2. Verifly splitting was completed */
|
||||
|
||||
/* Was request sent? */
|
||||
if (!newline)
|
||||
return HTTP_PARSER_HUNGRY;
|
||||
|
||||
|
||||
/* Check mandatory parts presence */
|
||||
if (!method_end || !path || !uri_end || !version)
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
*method_end = 0;
|
||||
*newline = 0;
|
||||
*uri_end = 0;
|
||||
|
||||
http_method = http_method_from_string(method, method_end - method);
|
||||
if (http_method == AISL_HTTP_METHOD_UNKNOWN)
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
if ((http_version = http_version_from_string(version))==0)
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
if (query)
|
||||
{
|
||||
*(query-1)=0;
|
||||
}
|
||||
else
|
||||
query = uri_end;
|
||||
|
||||
if (host)
|
||||
{
|
||||
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8))
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
if (port)
|
||||
*(port-1)=0;
|
||||
}
|
||||
|
||||
stream->client->http_version = http_version;
|
||||
|
||||
aisl_stream_set_request(stream, http_method, path, query);
|
||||
|
||||
if (host)
|
||||
aisl_stream_set_header(stream, "host", host);
|
||||
|
||||
/* how many characters has been read */
|
||||
*(p_size)-=size;
|
||||
return HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_header(char * data, int32_t * p_size, aisl_stream_t stream)
|
||||
{
|
||||
int32_t size = *p_size;
|
||||
char * key = data,
|
||||
* colon = NULL,
|
||||
* val = NULL,
|
||||
* val_end = NULL,
|
||||
* newline = NULL;
|
||||
|
||||
while(!newline && size-- )
|
||||
{
|
||||
switch(*data)
|
||||
{
|
||||
case ' ':
|
||||
if (val && !val_end)
|
||||
val_end = data;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (!colon)
|
||||
{
|
||||
if (colon == key)
|
||||
return HTTP_PARSER_ERROR;
|
||||
|
||||
colon = data;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
newline = data;
|
||||
|
||||
case '\r':
|
||||
if (!val_end && val)
|
||||
val_end = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!colon)
|
||||
{
|
||||
*data = tolower(*data);
|
||||
}
|
||||
else if (!val)
|
||||
{
|
||||
if (colon)
|
||||
val = data;
|
||||
}
|
||||
|
||||
if (val_end)
|
||||
val_end = NULL;
|
||||
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
if (!newline)
|
||||
return HTTP_PARSER_HUNGRY;
|
||||
|
||||
if (colon && val && val_end)
|
||||
{
|
||||
*colon = 0;
|
||||
*val_end = 0;
|
||||
|
||||
aisl_stream_set_header(stream, key, val);
|
||||
|
||||
*p_size -= size;
|
||||
|
||||
return HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
else if (newline == key || (newline == key+1 && *key == '\r'))
|
||||
{
|
||||
return (aisl_stream_set_end_of_headers(stream) == 0) ? HTTP_PARSER_READY :
|
||||
HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
|
||||
return HTTP_PARSER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_body(char * data, int32_t * p_size, aisl_stream_t stream)
|
||||
{
|
||||
switch (aisl_stream_set_body(stream, data, *p_size))
|
||||
{
|
||||
case 0: return HTTP_PARSER_READY;
|
||||
case -1: return HTTP_PARSER_ERROR;
|
||||
default: return HTTP_PARSER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* API Level */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
|
@ -8,6 +344,7 @@ aisl_http_version_to_string(aisl_http_version_t version)
|
|||
{
|
||||
switch (version)
|
||||
{
|
||||
case AISL_HTTP_0_9: return "HTTP/0.9";
|
||||
case AISL_HTTP_1_0: return "HTTP/1.0";
|
||||
case AISL_HTTP_1_1: return "HTTP/1.1";
|
||||
case AISL_HTTP_2_0: return "HTTP/2.0";
|
||||
|
@ -15,7 +352,6 @@ aisl_http_version_to_string(aisl_http_version_t version)
|
|||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
|
@ -77,16 +413,6 @@ aisl_http_response_to_string(aisl_http_response_t code)
|
|||
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_http_secure_to_string( int is_secure )
|
||||
{
|
||||
return (is_secure ? "HTTPS" : "HTTP");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
|
@ -104,9 +430,12 @@ aisl_http_method_to_string( aisl_http_method_t method )
|
|||
case AISL_HTTP_CONNECT: return "CONNECT";
|
||||
|
||||
case AISL_HTTP_PRI: return "PRI";
|
||||
|
||||
case AISL_HTTP_METHOD_UNKNOWN: break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
|
|
11
src/http.h
11
src/http.h
|
@ -20,23 +20,24 @@
|
|||
|
||||
typedef enum
|
||||
{
|
||||
HTTP_PARSER_PENDING
|
||||
HTTP_PARSER_SUCCESS
|
||||
, HTTP_PARSER_READY
|
||||
, HTTP_PARSER_HUNGRY
|
||||
, HTTP_PARSER_ERROR
|
||||
, HTTP_PARSER_FINISHED
|
||||
|
||||
} http_parser_t;
|
||||
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_request(char * data, int32_t * size, aisl_client_t client);
|
||||
http_10_parse_request(char * data, int32_t * size, aisl_stream_t stream);
|
||||
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_header(char * data, int32_t * size, aisl_client_t client);
|
||||
http_10_parse_header(char * data, int32_t * size, aisl_stream_t stream);
|
||||
|
||||
|
||||
http_parser_t
|
||||
http_10_parse_body(char * data, int32_t * size, aisl_client_t client);
|
||||
http_10_parse_body(char * data, int32_t * size, aisl_stream_t stream);
|
||||
|
||||
|
||||
#endif /* !AISL_HTTP_H */
|
||||
|
|
331
src/instance.c
331
src/instance.c
|
@ -19,7 +19,7 @@
|
|||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#include "list.h"
|
||||
#include "debug.h"
|
||||
#include "str-utils.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
@ -31,34 +31,6 @@
|
|||
#include "instance.h"
|
||||
|
||||
|
||||
struct callback
|
||||
{
|
||||
void * source;
|
||||
aisl_callback_t f_ptr;
|
||||
aisl_event_t event;
|
||||
};
|
||||
|
||||
typedef struct callback * callback_t;
|
||||
|
||||
|
||||
struct aisl
|
||||
{
|
||||
struct list server_spool;
|
||||
struct list client_spool;
|
||||
struct list callback_spool;
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
struct list ssl_spool;
|
||||
#endif
|
||||
|
||||
char * last_error;
|
||||
|
||||
size_t iterator;
|
||||
|
||||
uint32_t accept_limit;
|
||||
uint32_t silence_timeout;
|
||||
};
|
||||
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
static uint32_t m_instances = 0;
|
||||
#endif
|
||||
|
@ -89,9 +61,6 @@ aisl_new( aisl_config_t config )
|
|||
if ( list_init(&instance->client_spool, config->client_spool_size) == -1 )
|
||||
goto release;
|
||||
|
||||
if ( list_init(&instance->callback_spool, config->callback_spool_size) == -1 )
|
||||
goto release;
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
if ( list_init(&instance->ssl_spool, config->ssl_spool_size) == -1 )
|
||||
goto release;
|
||||
|
@ -119,8 +88,6 @@ aisl_free( aisl_t instance )
|
|||
|
||||
list_release(&instance->server_spool, (list_destructor_t) aisl_server_free );
|
||||
|
||||
list_release(&instance->callback_spool, (list_destructor_t) free);
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
list_release(&instance->ssl_spool, (list_destructor_t) aisl_ssl_free );
|
||||
#endif
|
||||
|
@ -273,194 +240,148 @@ aisl_get_ssl_ctx( aisl_t instance, const char * host )
|
|||
#endif
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
aisl_status_t
|
||||
void
|
||||
aisl_set_callback( aisl_t instance,
|
||||
void * source,
|
||||
aisl_event_t event,
|
||||
aisl_callback_t callback )
|
||||
{
|
||||
callback_t cb;
|
||||
|
||||
if ( (cb = malloc(sizeof(struct callback))) != NULL )
|
||||
switch( event )
|
||||
{
|
||||
cb->source = source;
|
||||
cb->event = event;
|
||||
cb->f_ptr = callback;
|
||||
case AISL_EVENT_SERVER_OPEN:
|
||||
instance->on_server_open = (aisl_on_server_open_t) callback;
|
||||
break;
|
||||
|
||||
if ( list_append(&instance->callback_spool, cb) != -1 )
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case AISL_EVENT_STREAM_OUTPUT:
|
||||
if (source)
|
||||
aisl_stream_set_chunked_output( (aisl_stream_t) source, true );
|
||||
//( (stream_t) source )->flags |= STREAM_FLAG_OUTPUT_CHUNKED;
|
||||
break;
|
||||
case AISL_EVENT_SERVER_ERROR:
|
||||
instance->on_server_error = (aisl_on_server_error_t) callback;
|
||||
break;
|
||||
|
||||
/*
|
||||
* case AISL_EVENT_STREAM_OPEN:
|
||||
* instance->flags |= AISL_FLAG_HAS_STREAM_CALLBACKS;
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case AISL_EVENT_CLIENT_CONNECT:
|
||||
instance->on_client_connect = (aisl_on_client_connect_t) callback;
|
||||
break;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
free(cb);
|
||||
case AISL_EVENT_CLIENT_DISCONNECT:
|
||||
instance->on_client_disconnect = (aisl_on_client_disconnect_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_CLIENT_TIMEOUT:
|
||||
instance->on_client_timeout = (aisl_on_client_timeout_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_OPEN:
|
||||
instance->on_stream_open = (aisl_on_stream_open_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_HEADER:
|
||||
instance->on_stream_header = (aisl_on_stream_header_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_INPUT:
|
||||
instance->on_stream_input = (aisl_on_stream_input_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_REQUEST:
|
||||
instance->on_stream_request = (aisl_on_stream_request_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_OUTPUT:
|
||||
instance->on_stream_output = (aisl_on_stream_output_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_CLOSE:
|
||||
instance->on_stream_close = (aisl_on_stream_close_t) callback;
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_ERROR:
|
||||
instance->on_stream_error = (aisl_on_stream_error_t) callback;
|
||||
break;
|
||||
}
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
void
|
||||
aisl_unset_callbacks_for( aisl_t instance, void * source )
|
||||
{
|
||||
size_t i = instance->callback_spool.count;
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(i=i-1; i <= 0; i-- )
|
||||
{
|
||||
callback_t callback = LIST_INDEX(instance->callback_spool, i);
|
||||
if ( callback->source == source )
|
||||
{
|
||||
free(callback);
|
||||
list_remove_index(&instance->callback_spool, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... )
|
||||
{
|
||||
DPRINTF("event %s", aisl_event_to_string(event));
|
||||
|
||||
va_list vl;
|
||||
bool result;
|
||||
|
||||
va_start(vl, event);
|
||||
result = aisl_raise_vl(instance, source, event, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
aisl_on_source_event( void * source, aisl_callback_t cb, va_list vl_args )
|
||||
{
|
||||
aisl_on_source_event_t callback = (aisl_on_source_event_t) cb;
|
||||
|
||||
(void) vl_args;
|
||||
|
||||
return callback( (void *) source);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
aisl_on_stream_open( void * source, aisl_callback_t cb, va_list vl_args )
|
||||
{
|
||||
aisl_on_stream_open_t callback = (aisl_on_stream_open_t) cb;
|
||||
|
||||
aisl_http_method_t method = va_arg(vl_args, aisl_http_method_t);
|
||||
char * path = va_arg(vl_args, char *);
|
||||
char * query = va_arg(vl_args, char *);
|
||||
|
||||
return callback( (aisl_stream_t) source, method, path, query);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
aisl_on_stream_header( void * source, aisl_callback_t cb, va_list vl_args )
|
||||
{
|
||||
aisl_on_stream_header_t callback = (aisl_on_stream_header_t) cb;
|
||||
|
||||
char * key = va_arg(vl_args, char *);
|
||||
char * val = va_arg(vl_args, char *);
|
||||
|
||||
return callback( (aisl_stream_t) source, key, val );
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
aisl_on_stream_input( void * source, aisl_callback_t cb, va_list vl_args )
|
||||
{
|
||||
aisl_on_stream_input_t callback = (aisl_on_stream_input_t) cb;
|
||||
|
||||
size_t len = va_arg(vl_args, size_t);
|
||||
char * data = va_arg(vl_args, char *);
|
||||
|
||||
return callback( (aisl_stream_t) source, data, len );
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
bool
|
||||
aisl_raise_vl( aisl_t instance,
|
||||
void * source,
|
||||
aisl_event_t event,
|
||||
va_list vl_args )
|
||||
{
|
||||
va_list vl_copy;
|
||||
size_t i;
|
||||
|
||||
callback_t cb;
|
||||
bool result = false,
|
||||
stop_propg = false;
|
||||
|
||||
i = instance->callback_spool.count;
|
||||
|
||||
while(i--)
|
||||
switch( event )
|
||||
{
|
||||
cb = LIST_INDEX(instance->callback_spool, i);
|
||||
case AISL_EVENT_SERVER_OPEN:
|
||||
if (instance->on_server_open)
|
||||
instance->on_server_open((aisl_server_t)source);
|
||||
break;
|
||||
|
||||
if (cb->event == event && (source == cb->source || cb->source == NULL))
|
||||
{
|
||||
va_copy(vl_copy, vl_args);
|
||||
switch(event)
|
||||
case AISL_EVENT_SERVER_ERROR:
|
||||
if (instance->on_server_error)
|
||||
instance->on_server_error((aisl_server_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_CLIENT_CONNECT:
|
||||
if (instance->on_client_connect)
|
||||
instance->on_client_connect((aisl_client_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_CLIENT_DISCONNECT:
|
||||
if (instance->on_client_disconnect)
|
||||
instance->on_client_disconnect((aisl_client_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_CLIENT_TIMEOUT:
|
||||
if (instance->on_client_timeout)
|
||||
instance->on_client_timeout((aisl_client_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_OPEN:
|
||||
if (instance->on_stream_open)
|
||||
{
|
||||
/* server events */
|
||||
case AISL_EVENT_SERVER_OPEN:
|
||||
case AISL_EVENT_SERVER_ERROR:
|
||||
case AISL_EVENT_CLIENT_CONNECT:
|
||||
case AISL_EVENT_CLIENT_DISCONNECT:
|
||||
case AISL_EVENT_CLIENT_TIMEOUT:
|
||||
case AISL_EVENT_STREAM_REQUEST:
|
||||
case AISL_EVENT_STREAM_CLOSE:
|
||||
case AISL_EVENT_STREAM_ERROR:
|
||||
case AISL_EVENT_STREAM_OUTPUT:
|
||||
stop_propg = aisl_on_source_event(source, cb->f_ptr, vl_copy);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_OPEN:
|
||||
stop_propg = aisl_on_stream_open(source, cb->f_ptr, vl_copy);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_HEADER:
|
||||
stop_propg = aisl_on_stream_header(source, cb->f_ptr, vl_copy);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_INPUT:
|
||||
stop_propg = aisl_on_stream_input(source, cb->f_ptr, vl_copy);
|
||||
break;
|
||||
|
||||
default:
|
||||
stop_propg = ((aisl_on_custom_event_t) cb->f_ptr)(source, vl_copy);
|
||||
aisl_http_method_t method = va_arg(vl, aisl_http_method_t);
|
||||
const char * path = va_arg(vl, const char *);
|
||||
const char * query = va_arg(vl, const char *);
|
||||
instance->on_stream_open((aisl_stream_t)source, method, path, query);
|
||||
}
|
||||
break;
|
||||
|
||||
va_end(vl_copy);
|
||||
case AISL_EVENT_STREAM_HEADER:
|
||||
if (instance->on_stream_header)
|
||||
{
|
||||
const char * key = va_arg(vl, const char *);
|
||||
const char * val = va_arg(vl, const char *);
|
||||
instance->on_stream_header((aisl_stream_t)source, key, val);
|
||||
}
|
||||
break;
|
||||
|
||||
result = true;
|
||||
case AISL_EVENT_STREAM_INPUT:
|
||||
if (instance->on_stream_input)
|
||||
{
|
||||
const char * data = va_arg(vl, const char *);
|
||||
int32_t size = va_arg(vl, int32_t);
|
||||
instance->on_stream_input((aisl_stream_t)source, data, size);
|
||||
}
|
||||
break;
|
||||
|
||||
if (stop_propg)
|
||||
break;
|
||||
}
|
||||
case AISL_EVENT_STREAM_REQUEST:
|
||||
if (instance->on_stream_request)
|
||||
instance->on_stream_request((aisl_stream_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_OUTPUT:
|
||||
if (instance->on_stream_output)
|
||||
instance->on_stream_output((aisl_stream_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_CLOSE:
|
||||
if (instance->on_stream_close)
|
||||
instance->on_stream_close((aisl_stream_t)source);
|
||||
break;
|
||||
|
||||
case AISL_EVENT_STREAM_ERROR:
|
||||
if (instance->on_stream_error)
|
||||
instance->on_stream_error((aisl_stream_t)source);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
|
||||
|
@ -470,30 +391,32 @@ aisl_run_cycle( aisl_t instance )
|
|||
{
|
||||
aisl_status_t result = AISL_IDLE;
|
||||
|
||||
size_t i = instance->iterator,
|
||||
max = instance->server_spool.count + instance->client_spool.count;
|
||||
int32_t i = instance->iterator,
|
||||
max = instance->server_spool.count + instance->client_spool.count;
|
||||
|
||||
while ( result == AISL_IDLE && max != 0)
|
||||
{
|
||||
aisl_client_t cli;
|
||||
aisl_client_t cli = NULL;
|
||||
|
||||
if (i < instance->server_spool.count)
|
||||
{
|
||||
if (instance->client_spool.count < instance->accept_limit)
|
||||
{
|
||||
aisl_server_t srv;
|
||||
SSL_CTX * ssl_ctx;
|
||||
/* SSL_CTX * ssl_ctx; */
|
||||
|
||||
srv = (aisl_server_t) LIST_INDEX(instance->server_spool, i);
|
||||
|
||||
/*
|
||||
ssl_ctx = (aisl_server_get_ssl(srv)) ?
|
||||
aisl_get_ssl_ctx(instance, NULL) :
|
||||
NULL;
|
||||
*/
|
||||
result = aisl_server_touch(srv, &cli);
|
||||
|
||||
result = aisl_server_touch(srv, &cli, ssl_ctx);
|
||||
|
||||
if (result == AISL_SUCCESS)
|
||||
if (cli)
|
||||
{
|
||||
DPRINTF("Accepted %p", (void*)cli);
|
||||
if ( list_append(&instance->client_spool, cli) != -1 )
|
||||
{
|
||||
aisl_raise(instance, cli, AISL_EVENT_CLIENT_CONNECT);
|
||||
|
@ -513,7 +436,7 @@ aisl_run_cycle( aisl_t instance )
|
|||
}
|
||||
else
|
||||
{
|
||||
size_t j = i - instance->server_spool.count;
|
||||
int32_t j = i - instance->server_spool.count;
|
||||
|
||||
if (j < instance->client_spool.count)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,38 @@
|
|||
#endif
|
||||
|
||||
#include <aisl/instance.h>
|
||||
#include "list.h"
|
||||
|
||||
|
||||
struct aisl
|
||||
{
|
||||
struct list server_spool;
|
||||
struct list client_spool;
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
struct list ssl_spool;
|
||||
#endif
|
||||
|
||||
char * last_error;
|
||||
|
||||
int32_t iterator;
|
||||
|
||||
uint32_t accept_limit;
|
||||
uint32_t silence_timeout;
|
||||
|
||||
/* callbacks */
|
||||
aisl_on_server_open_t on_server_open;
|
||||
aisl_on_server_error_t on_server_error;
|
||||
aisl_on_client_connect_t on_client_connect;
|
||||
aisl_on_client_disconnect_t on_client_disconnect;
|
||||
aisl_on_client_timeout_t on_client_timeout;
|
||||
aisl_on_stream_open_t on_stream_open;
|
||||
aisl_on_stream_header_t on_stream_header;
|
||||
aisl_on_stream_input_t on_stream_input;
|
||||
aisl_on_stream_request_t on_stream_request;
|
||||
aisl_on_stream_output_t on_stream_output;
|
||||
aisl_on_stream_close_t on_stream_close;
|
||||
aisl_on_stream_error_t on_stream_error;
|
||||
};
|
||||
|
||||
|
||||
#ifndef AISL_WITHOUT_SSL
|
||||
|
@ -37,4 +69,16 @@ aisl_get_ssl_ctx( aisl_t instance, const char * server_name );
|
|||
void
|
||||
aisl_set_error( aisl_t instance, const char * err_msg );
|
||||
|
||||
#endif /* !INSTANCE_H */
|
||||
|
||||
/**
|
||||
* @brief Raises event from source.
|
||||
* @param instance a pointer to #aisl_t instance.
|
||||
* @param source a pointer to an event source.
|
||||
* @param event a code of event.
|
||||
* @param ... a list of arguments specific for event.
|
||||
*/
|
||||
void
|
||||
aisl_raise( aisl_t instance, void * source, aisl_event_t event, ... );
|
||||
|
||||
|
||||
#endif /* !AISL_INSTANCE_H */
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "debug.h"
|
||||
#include "list.h"
|
||||
|
||||
int32_t
|
||||
|
@ -56,11 +57,15 @@ list_append(list_t list, void * entry)
|
|||
{
|
||||
int32_t pos = list->count;
|
||||
|
||||
DPRINTF("pos = %d", pos);
|
||||
|
||||
if ( !(pos < list->size) )
|
||||
{
|
||||
DPRINTF("extending, size = %d", list->size);
|
||||
void ** new_list;
|
||||
int32_t new_size = pos + 1;
|
||||
|
||||
DPRINTF("extending, ptr = %p", (void*)list->data);
|
||||
if ( (new_list = realloc( list->data, new_size*sizeof(void*) )) == NULL )
|
||||
return -1;
|
||||
|
||||
|
|
490
src/parser.c
490
src/parser.c
|
@ -1,490 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "str-utils.h"
|
||||
#include <aisl/http.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "globals.h"
|
||||
#include "stream.h"
|
||||
|
||||
/* length(multipart/form-data; boundary=) = 30 */
|
||||
#define B_OFFSET 30
|
||||
|
||||
/* common HTTP headers */
|
||||
static const char cCookie[] = "Cookie";
|
||||
static const char cContentType[] = "Content-Type";
|
||||
static const char cContentLength[] = "Content-Length";
|
||||
/*
|
||||
static const char cConnection[] = "Connection";
|
||||
static const char cHost[] = "Host";
|
||||
static const char cUserAgent[] = "User-Agent";
|
||||
static const char cAccept[] = "Accept";
|
||||
static const char cAcceptLanguage[] = "Accept-Language";
|
||||
static const char cAcceptEncoding[] = "Accept-Encoding";
|
||||
*/
|
||||
|
||||
#define CLI_STREAM(x) ( ((stream_t)list_index(x->streams,x->istream)) )
|
||||
/*
|
||||
static void
|
||||
debug(const char * label, char * buffer, int len)
|
||||
{
|
||||
printf("<<< %s [", label);
|
||||
fwrite(buffer, 1, len, stdout);
|
||||
printf("]\n");
|
||||
}
|
||||
*/
|
||||
|
||||
static pair_t
|
||||
pair_new(const char * key, int length)
|
||||
{
|
||||
pair_t p = calloc(1, sizeof(struct pair));
|
||||
if (p)
|
||||
{
|
||||
p->key = str_ncopy(key, length);
|
||||
if (!p->key)
|
||||
{
|
||||
free(p);
|
||||
p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* HTTP METHOD -------------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_method(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
char * cur = memchr(*b_ptr, ' ', *b_len);
|
||||
|
||||
if (!cur)
|
||||
{
|
||||
if (*b_len < 8)
|
||||
return PARSER_HUNGRY;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
}
|
||||
|
||||
int l = (int) (cur - *b_ptr);
|
||||
|
||||
stream_t s = list_index(cli->streams, cli->istream);
|
||||
|
||||
switch( l )
|
||||
{
|
||||
case 3:
|
||||
if (strncmp(*b_ptr, "GET", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_GET;
|
||||
else if (strncmp(*b_ptr, "PRI", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_PRI;
|
||||
else if (strncmp(*b_ptr, "PUT", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_PUT;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
break;
|
||||
case 4:
|
||||
if (strncmp(*b_ptr, "POST", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_POST;
|
||||
else if (strncmp(*b_ptr, "HEAD", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_HEAD;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
break;
|
||||
case 5:
|
||||
if (strncmp(*b_ptr, "TRACE", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_TRACE;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
break;
|
||||
case 6:
|
||||
if (strncmp(*b_ptr, "DELETE", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_DELETE;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
break;
|
||||
case 7:
|
||||
if (strncmp(*b_ptr, "OPTIONS", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_OPTIONS;
|
||||
else if (strncmp(*b_ptr, "CONNECT", l)==0)
|
||||
ASTREAM(s)->request_method = AISL_HTTP_CONNECT;
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
break;
|
||||
default:
|
||||
return PARSER_FAILED;
|
||||
}
|
||||
|
||||
*b_ptr += ++l;
|
||||
*b_len -= l; /* count method + space character */
|
||||
|
||||
s->state = STREAM_REQUEST_PATH;
|
||||
|
||||
return PARSER_PENDING;
|
||||
}
|
||||
|
||||
/* HTTP REQUEST_URI and HOST ------------------------------------------------ */
|
||||
static void
|
||||
str_to_lower( char * src )
|
||||
{
|
||||
while (*src)
|
||||
{
|
||||
*src = tolower(*src);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
parser_status_t
|
||||
parse_request_path(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
parser_status_t result = PARSER_PENDING;
|
||||
stream_t s = list_index(cli->streams, cli->istream);
|
||||
|
||||
int i;
|
||||
char * host = NULL,
|
||||
* path = NULL,
|
||||
* query = NULL;
|
||||
|
||||
|
||||
for ( i=0; i<*b_len; i++)
|
||||
{
|
||||
switch( (*b_ptr)[i] )
|
||||
{
|
||||
case ':':
|
||||
if (host) /* if host is set, we parse host and it could not contain : */
|
||||
{
|
||||
result = PARSER_FAILED;
|
||||
break;
|
||||
}
|
||||
else /* could be protocol separator */
|
||||
{
|
||||
if (i==5)
|
||||
{
|
||||
if (*b_len > 7 && strncmp(*b_ptr, "http://", 7)==0 ) /* protocol defined */
|
||||
{
|
||||
host = &(*b_ptr)[i+3];
|
||||
i+=2;
|
||||
continue;
|
||||
}
|
||||
|
||||
result = PARSER_FAILED; /* something is wrong */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
case '?':
|
||||
query = (*b_ptr) +i;
|
||||
continue;
|
||||
|
||||
case ' ':
|
||||
if (! path) path = *b_ptr;
|
||||
if (query)
|
||||
{
|
||||
ASTREAM(s)->path = str_ncopy(path, (uint32_t) (query-path));
|
||||
query++;
|
||||
ASTREAM(s)->query = str_ncopy(query, (uint32_t)(&(*b_ptr)[i]-query));
|
||||
}
|
||||
else
|
||||
ASTREAM(s)->path = str_ncopy(path, (uint32_t) (&(*b_ptr)[i]-path));
|
||||
|
||||
*b_len -= ++i;
|
||||
*b_ptr += i;
|
||||
|
||||
s->state = STREAM_REQUEST_PROTOCOL;
|
||||
|
||||
return PARSER_PENDING;
|
||||
break;
|
||||
case '/':
|
||||
if (host)
|
||||
{
|
||||
/* debug(" > host", host, (int) (&(*b_ptr)[i] - host)); */
|
||||
pair_t p = malloc(sizeof(struct pair));
|
||||
if (p)
|
||||
{
|
||||
p->key = str_copy("host");
|
||||
p->value = str_ncopy(host, (uint32_t) (&(*b_ptr)[i] - host));
|
||||
}
|
||||
s->headers = list_new(AISL_MIN_HEADERS);
|
||||
|
||||
list_append(s->headers, p);
|
||||
host = NULL;
|
||||
|
||||
path = &(*b_ptr)[i];
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (result == PARSER_PENDING) /* end space was not found */
|
||||
result = PARSER_HUNGRY;
|
||||
|
||||
/*
|
||||
if (result == PARSER_HUNGRY && *b_len == gBuffer->size)
|
||||
result = PARSER_FAILED;*/ /* buffer is overloaded */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* HTTP VERSION ------------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_protocol(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
stream_t stream = CLI_STREAM(cli);
|
||||
/* HTTP/X.X = 8 characters minimal */
|
||||
|
||||
if (*b_len < 8) return PARSER_HUNGRY;
|
||||
|
||||
char * ptr = memchr(*b_ptr, '\n', *b_len);
|
||||
|
||||
if (!ptr) return PARSER_HUNGRY;
|
||||
|
||||
int l = (int) (ptr - *b_ptr);
|
||||
|
||||
if (strncmp(*b_ptr, "HTTP/", 5)==0)
|
||||
{
|
||||
if (strncmp(&(*b_ptr)[5], "2.0", 3)==0) cli->protocol = AISL_HTTP_2_0; else
|
||||
if (strncmp(&(*b_ptr)[5], "1.1", 3)==0) cli->protocol = AISL_HTTP_1_1; else
|
||||
if (strncmp(&(*b_ptr)[5], "1.0", 3)==0) cli->protocol = AISL_HTTP_1_0; else
|
||||
return PARSER_FAILED;
|
||||
|
||||
if ( (l==10 && *b_ptr[8]=='\r') || (l==9) )
|
||||
{
|
||||
/*r->version = str_ncopy(*b_ptr, 8); */
|
||||
|
||||
*b_ptr += ++l;
|
||||
*b_len -=l;
|
||||
|
||||
stream->state=STREAM_REQUEST_HEADER_KEY;
|
||||
|
||||
|
||||
|
||||
aisl_raise_event(
|
||||
cli->server->owner,
|
||||
stream,
|
||||
AISL_STREAM_OPEN,
|
||||
ASTREAM(stream)->request_method,
|
||||
ASTREAM(stream)->path,
|
||||
ASTREAM(stream)->query
|
||||
);
|
||||
|
||||
if (!stream->headers)
|
||||
stream->headers = list_new(AISL_MIN_HEADERS);
|
||||
else if (stream->headers->count == 1)
|
||||
{
|
||||
/* raise event for Host header */
|
||||
pair_t p = list_index(stream->headers, 0);
|
||||
|
||||
aisl_raise_event(
|
||||
cli->server->owner,
|
||||
stream,
|
||||
AISL_STREAM_HEADER,
|
||||
p->key, p->value
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return PARSER_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
return PARSER_FAILED;
|
||||
}
|
||||
|
||||
/* HTTP HEADER KEY ---------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_key(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
stream_t stream = CLI_STREAM(cli);
|
||||
int l;
|
||||
|
||||
/* check end of headers */
|
||||
switch (*b_ptr[0])
|
||||
{
|
||||
case '\r':
|
||||
if (*b_len>1)
|
||||
{
|
||||
if( strncmp(*b_ptr, "\r\n", 2)==0 )
|
||||
{
|
||||
l=2;
|
||||
}
|
||||
else
|
||||
return PARSER_FAILED;
|
||||
}
|
||||
else
|
||||
return PARSER_HUNGRY;
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
l=1;
|
||||
break;
|
||||
default:
|
||||
l=0;
|
||||
}
|
||||
|
||||
if (l)
|
||||
{
|
||||
/* end of headers */
|
||||
*b_len -= l;
|
||||
*b_ptr += l;
|
||||
|
||||
|
||||
stream->state = STREAM_REQUEST_CONTENT;
|
||||
|
||||
/* aisl_raise_event(cli->server->owner, CLI_STREAM(cli), AISL_STREAM_OPEN);
|
||||
* */
|
||||
|
||||
return PARSER_PENDING;
|
||||
}
|
||||
|
||||
/* header key */
|
||||
|
||||
char * ptr = memchr(*b_ptr, ':', *b_len);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
/*
|
||||
if (*b_len == gBuffer->size)
|
||||
return PARSER_FAILED;
|
||||
*/
|
||||
return PARSER_HUNGRY;
|
||||
}
|
||||
|
||||
l = (int) (ptr-*b_ptr);
|
||||
|
||||
|
||||
pair_t ppp = pair_new(*b_ptr, l);
|
||||
|
||||
str_to_lower(ppp->key);
|
||||
|
||||
if (ppp)
|
||||
list_append(stream->headers, ppp);
|
||||
|
||||
*b_len -= ++l;
|
||||
*b_ptr += l;
|
||||
|
||||
stream->state=STREAM_REQUEST_HEADER_VALUE;
|
||||
|
||||
return PARSER_PENDING;
|
||||
|
||||
}
|
||||
|
||||
/* HTTP HEADER VALUE -------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_value(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
stream_t stream = CLI_STREAM(cli);
|
||||
/* skip first space */
|
||||
|
||||
if (*b_len)
|
||||
{
|
||||
if ((*b_ptr)[0]==' ')
|
||||
{
|
||||
(*b_ptr)++;
|
||||
(*b_len)--;
|
||||
if (*b_len == 0)
|
||||
return PARSER_HUNGRY;
|
||||
}
|
||||
}
|
||||
else
|
||||
return PARSER_HUNGRY;
|
||||
|
||||
char * ptr = memchr(*b_ptr, '\n', *b_len);
|
||||
int l;
|
||||
|
||||
l = (ptr) ? (int) (ptr-*b_ptr) : *b_len;
|
||||
|
||||
uint32_t index = stream->headers->count -1;
|
||||
|
||||
pair_t p = list_index(stream->headers, index);
|
||||
|
||||
p->value = str_ncat(p->value, *b_ptr, (l && (*b_ptr)[l-1]=='\r') ? l-1 : l);
|
||||
|
||||
*b_len -= ++l;
|
||||
*b_ptr += l;
|
||||
|
||||
stream->state=STREAM_REQUEST_HEADER_KEY;
|
||||
|
||||
/* todo: add limit for maximal header length */
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
if (str_cmpi(p->key, cCookie )==0)
|
||||
{
|
||||
/* parse cookies */
|
||||
}
|
||||
else if (str_cmpi(p->key, cContentType )==0)
|
||||
{
|
||||
/* CLI(r)->c_type_index = index; */
|
||||
}
|
||||
else if (str_cmpi(p->key, cContentLength )==0)
|
||||
{
|
||||
stream->c_length = strtol(p->value, NULL, 0);
|
||||
}
|
||||
/* CLI(r)->c_length = strtol(p->value, NULL, 10); */
|
||||
|
||||
aisl_raise_event(
|
||||
cli->server->owner,
|
||||
stream,
|
||||
AISL_STREAM_HEADER,
|
||||
p->key, p->value
|
||||
);
|
||||
|
||||
return PARSER_PENDING;
|
||||
}
|
||||
else
|
||||
return PARSER_HUNGRY;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
parser_status_t
|
||||
parse_request_content(client_t cli, char ** b_ptr, int *b_len)
|
||||
{
|
||||
stream_t stream = CLI_STREAM(cli);
|
||||
/*
|
||||
fprintf(stdout, "AISL [%d]> ", CLI_STREAM(cli)->c_length);
|
||||
fwrite(*b_ptr, 1, *b_len, stdout);
|
||||
fprintf(stdout, "\n<");
|
||||
*/
|
||||
|
||||
if (stream->c_length)
|
||||
{
|
||||
int l = *b_len;
|
||||
|
||||
aisl_raise_event(
|
||||
cli->server->owner,
|
||||
stream,
|
||||
AISL_STREAM_INPUT,
|
||||
*b_ptr,
|
||||
l
|
||||
);
|
||||
|
||||
*b_ptr += l;
|
||||
*b_len = 0;
|
||||
stream->c_length -= l;
|
||||
}
|
||||
else
|
||||
goto request_ready;
|
||||
|
||||
if ( stream->c_length == 0 )
|
||||
{
|
||||
request_ready:
|
||||
stream->state=STREAM_REQUEST_READY;
|
||||
aisl_raise_event( cli->server->owner, stream, AISL_STREAM_REQUEST );
|
||||
return PARSER_FINISHED;
|
||||
}
|
||||
else
|
||||
return PARSER_HUNGRY;
|
||||
}
|
52
src/parser.h
52
src/parser.h
|
@ -1,52 +0,0 @@
|
|||
#ifndef _AISL_PARSER_H_
|
||||
#define _AISL_PARSER_H_
|
||||
|
||||
#include <aisl/aisl.h>
|
||||
#include "client.h"
|
||||
|
||||
|
||||
/* parser status ------------------------------------------------------------ */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PARSER_PENDING, /* process pending */
|
||||
PARSER_FINISHED, /* successful finish */
|
||||
PARSER_HUNGRY, /* not enough data in buffer */
|
||||
PARSER_FAILED /* error happened */
|
||||
|
||||
} parser_status_t;
|
||||
|
||||
|
||||
/* parse HTTP Request ------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_method(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP Request URI --------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_path(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP Version ------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_protocol(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP header key ---------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_key(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP header value -------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_header_value(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* parse HTTP data ---------------------------------------------------------- */
|
||||
|
||||
parser_status_t
|
||||
parse_request_content(client_t cli, char ** b_ptr, int *b_len);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
28
src/server.c
28
src/server.c
|
@ -15,22 +15,14 @@
|
|||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "str-utils.h"
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief HTTP(s) server data structure represented by #aisl_server_t pointer.
|
||||
*/
|
||||
struct aisl_server
|
||||
{
|
||||
struct sockaddr_in address; /**< TCP server address to listen to. */
|
||||
aisl_t instance; /**< Associated AISL instance pointer. */
|
||||
int fd; /**< System socket descriptor. */
|
||||
bool ssl; /**< SSL enabled/disabled flag. */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -71,37 +63,36 @@ aisl_server_open(aisl_server_t server)
|
|||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Tries to accept a new client.
|
||||
* @param server a pointer to #aisl_server_t instance.
|
||||
* @param p_client a pointer to store #aisl_client_t instance pointer.
|
||||
* @param ssl_ctx a pointer to SSL context or NULL if connection is unsecure.
|
||||
* @return #aisl_status_t code.
|
||||
*/
|
||||
static aisl_status_t
|
||||
aisl_server_accept( aisl_server_t server,
|
||||
aisl_client_t * p_client,
|
||||
SSL_CTX * ssl_ctx )
|
||||
aisl_client_t * p_client )
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(struct sockaddr_in);
|
||||
|
||||
*p_client = NULL;
|
||||
|
||||
fd = accept(server->fd, (struct sockaddr *) &addr, &len);
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
int flags;
|
||||
|
||||
DPRINTF("accepted fd=%d", fd);
|
||||
|
||||
if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
|
||||
{
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) == 0)
|
||||
{
|
||||
return (!(*p_client = aisl_client_new(server, fd, &addr, ssl_ctx))) ?
|
||||
return (!(*p_client = aisl_client_new(server, fd, &addr))) ?
|
||||
AISL_MALLOC_ERROR : AISL_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +110,7 @@ aisl_server_accept( aisl_server_t server,
|
|||
|
||||
aisl_status_t
|
||||
aisl_server_touch( aisl_server_t server,
|
||||
aisl_client_t * p_client,
|
||||
SSL_CTX * ssl_ctx )
|
||||
aisl_client_t * p_client )
|
||||
{
|
||||
aisl_status_t result;
|
||||
|
||||
|
@ -135,7 +125,7 @@ aisl_server_touch( aisl_server_t server,
|
|||
aisl_raise(server->instance, server, event);
|
||||
}
|
||||
else
|
||||
result = aisl_server_accept(server, p_client, ssl_ctx);
|
||||
result = aisl_server_accept(server, p_client);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
14
src/server.h
14
src/server.h
|
@ -14,6 +14,16 @@
|
|||
|
||||
#define AISL_SERVER(x) ((aisl_server_t) x)
|
||||
|
||||
/**
|
||||
* @brief HTTP(s) server data structure represented by #aisl_server_t pointer.
|
||||
*/
|
||||
struct aisl_server
|
||||
{
|
||||
struct sockaddr_in address; /**< TCP server address to listen to. */
|
||||
aisl_t instance; /**< Associated AISL instance pointer. */
|
||||
int fd; /**< System socket descriptor. */
|
||||
bool ssl; /**< SSL enabled/disabled flag. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocates and instance of #aisl_server_t.
|
||||
|
@ -40,7 +50,6 @@ aisl_server_free(aisl_server_t server);
|
|||
* new client connecting to the server.
|
||||
* @param server a pointer to #aisl_server_t instance.
|
||||
* @param p_client a pointer to store #aisl_client_t instance pointer.
|
||||
* @param ssl_ctx a pointer to SSL context or NULL if connection is unsecure.
|
||||
* @return #aisl_status_t code:
|
||||
* - AISL_SUCCESS if client connected,
|
||||
* - AISL_IDLE if there is no client to connect,
|
||||
|
@ -48,8 +57,7 @@ aisl_server_free(aisl_server_t server);
|
|||
*/
|
||||
aisl_status_t
|
||||
aisl_server_touch( aisl_server_t server,
|
||||
aisl_client_t * p_client,
|
||||
SSL_CTX * ssl_ctx );
|
||||
aisl_client_t * p_client );
|
||||
|
||||
|
||||
/**
|
||||
|
|
21
src/status.c
21
src/status.c
|
@ -1,21 +0,0 @@
|
|||
#include <aisl/status.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_status_to_string(aisl_status_t status)
|
||||
{
|
||||
switch( status )
|
||||
{
|
||||
case AISL_SUCCESS: return "success";
|
||||
case AISL_IDLE: return "idle";
|
||||
case AISL_MALLOC_ERROR: return "malloc error";
|
||||
case AISL_SYSCALL_ERROR: return "system call error";
|
||||
case AISL_EXTCALL_ERROR: return "external call error";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
148
src/stream.c
148
src/stream.c
|
@ -1,12 +1,13 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "instance.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "buffer.h"
|
||||
#include "str-utils.h"
|
||||
#include "debug.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define FLAG_FLUSHED (1<<0)
|
||||
|
@ -17,24 +18,6 @@
|
|||
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
|
||||
|
||||
|
||||
struct aisl_stream
|
||||
{
|
||||
struct buffer buffer;
|
||||
aisl_client_t client;
|
||||
|
||||
void * u_ptr;
|
||||
|
||||
uint64_t content_length;
|
||||
int32_t head_offset;
|
||||
int32_t body_offset;
|
||||
int id;
|
||||
int flags;
|
||||
|
||||
aisl_http_response_t response;
|
||||
aisl_stream_state_t state;
|
||||
};
|
||||
|
||||
|
||||
/* Library level */
|
||||
|
||||
static void
|
||||
|
@ -47,24 +30,23 @@ aisl_stream_reset(aisl_stream_t stream, bool initial)
|
|||
instance = aisl_stream_get_instance(stream);
|
||||
|
||||
aisl_raise(instance, stream, AISL_EVENT_STREAM_CLOSE);
|
||||
aisl_unset_callbacks_for(instance, stream);
|
||||
}
|
||||
|
||||
buffer_release(&stream->buffer);
|
||||
|
||||
stream->u_ptr = NULL;
|
||||
stream->content_length = AISL_AUTO_LENGTH;
|
||||
stream->head_offset = 0;
|
||||
stream->head_offset = 0;
|
||||
stream->flags = 0;
|
||||
stream->state = AISL_STREAM_STATE_IDLE;
|
||||
stream->response = AISL_HTTP_OK;
|
||||
stream->http_response = AISL_HTTP_OK;
|
||||
}
|
||||
|
||||
|
||||
aisl_stream_t
|
||||
aisl_stream_new(aisl_client_t client, int id)
|
||||
{
|
||||
aisl_stream_t stream = malloc(sizeof(struct aisl_stream));
|
||||
aisl_stream_t stream = calloc(1, sizeof(struct aisl_stream));
|
||||
|
||||
if (stream)
|
||||
{
|
||||
|
@ -145,6 +127,112 @@ aisl_stream_get_state(aisl_stream_t stream)
|
|||
return stream->state;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_set_request( aisl_stream_t stream,
|
||||
aisl_http_method_t http_method,
|
||||
const char * path,
|
||||
const char * query )
|
||||
{
|
||||
|
||||
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
|
||||
|
||||
DPRINTF(
|
||||
"%s -> path: %s, query: %s"
|
||||
, aisl_http_method_to_string(http_method)
|
||||
, path
|
||||
, query
|
||||
);
|
||||
|
||||
aisl_raise(
|
||||
aisl_stream_get_instance(stream),
|
||||
stream,
|
||||
AISL_EVENT_STREAM_OPEN,
|
||||
http_method,
|
||||
path,
|
||||
query
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_set_header( aisl_stream_t stream,
|
||||
const char * key,
|
||||
const char * value )
|
||||
{
|
||||
if (stream->state != AISL_STREAM_STATE_WAIT_HEADER)
|
||||
return;
|
||||
|
||||
if (strcmp(key, "content-length")==0)
|
||||
stream->content_length = strtoll(value, NULL, 10);
|
||||
else if (strcmp(key, "connection")==0)
|
||||
aisl_client_set_keepalive(
|
||||
stream->client
|
||||
, (str_cmpi(value, "close")==0) ? false : true
|
||||
);
|
||||
|
||||
DPRINTF("%s: %s", key, value);
|
||||
|
||||
aisl_raise(
|
||||
aisl_stream_get_instance(stream),
|
||||
stream,
|
||||
AISL_EVENT_STREAM_HEADER,
|
||||
key,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_end_of_headers( aisl_stream_t stream )
|
||||
{
|
||||
int result;
|
||||
|
||||
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER)
|
||||
{
|
||||
stream->state = AISL_STREAM_STATE_WAIT_BODY;
|
||||
result = (stream->content_length == 0);
|
||||
}
|
||||
else
|
||||
result = 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size )
|
||||
{
|
||||
int result;
|
||||
if (stream->state == AISL_STREAM_STATE_WAIT_BODY)
|
||||
{
|
||||
if ( !(stream->content_length < size) )
|
||||
{
|
||||
if (stream->content_length == 0)
|
||||
{
|
||||
stream->state = AISL_STREAM_STATE_READY;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = 1;
|
||||
|
||||
aisl_raise(
|
||||
aisl_stream_get_instance(stream),
|
||||
stream,
|
||||
AISL_EVENT_STREAM_OUTPUT,
|
||||
data,
|
||||
size
|
||||
);
|
||||
}
|
||||
else
|
||||
result = -1;
|
||||
}
|
||||
else
|
||||
result = 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* API Level */
|
||||
|
||||
/* Why it was here?
|
||||
|
@ -296,17 +384,19 @@ aisl_response( aisl_stream_t stream
|
|||
/* check if those headers were already sent */
|
||||
if (stream->state > AISL_STREAM_STATE_READY) return AISL_IDLE;
|
||||
|
||||
stream->response = status_code;
|
||||
stream->http_response = status_code;
|
||||
stream->content_length = content_length;
|
||||
|
||||
buffer_init( &stream->buffer
|
||||
, (content_length != AISL_AUTO_LENGTH) ? content_length : 0);
|
||||
|
||||
l = buffer_append_printf( &stream->buffer
|
||||
, "%s %d %s\r\n"
|
||||
, aisl_get_http_version(stream)
|
||||
, status_code
|
||||
, aisl_http_response_to_string(status_code) );
|
||||
l = buffer_append_printf(
|
||||
&stream->buffer
|
||||
, "%s %d %s\r\n"
|
||||
, aisl_http_version_to_string(stream->client->http_version)
|
||||
, status_code
|
||||
, aisl_http_response_to_string(status_code)
|
||||
);
|
||||
|
||||
if ( l == -1 )
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
|
39
src/stream.h
39
src/stream.h
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <aisl/types.h>
|
||||
#include <aisl/stream.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define AISL_STREAM(x) ((aisl_stream_t) x)
|
||||
|
||||
|
@ -23,6 +24,24 @@ typedef enum
|
|||
} aisl_stream_state_t;
|
||||
|
||||
|
||||
struct aisl_stream
|
||||
{
|
||||
struct buffer buffer;
|
||||
aisl_client_t client;
|
||||
|
||||
void * u_ptr;
|
||||
|
||||
uint64_t content_length;
|
||||
int32_t head_offset;
|
||||
int32_t body_offset;
|
||||
int id;
|
||||
int flags;
|
||||
|
||||
aisl_http_response_t http_response;
|
||||
aisl_stream_state_t state;
|
||||
};
|
||||
|
||||
|
||||
aisl_stream_t
|
||||
aisl_stream_new(aisl_client_t client, int id );
|
||||
|
||||
|
@ -62,4 +81,24 @@ aisl_stream_is_done(aisl_stream_t stream);
|
|||
aisl_stream_state_t
|
||||
aisl_stream_get_state(aisl_stream_t stream);
|
||||
|
||||
|
||||
void
|
||||
aisl_stream_set_request( aisl_stream_t stream,
|
||||
aisl_http_method_t http_method,
|
||||
const char * path,
|
||||
const char * query );
|
||||
|
||||
void
|
||||
aisl_stream_set_header( aisl_stream_t stream,
|
||||
const char * key,
|
||||
const char * value );
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_end_of_headers( aisl_stream_t stream );
|
||||
|
||||
|
||||
int
|
||||
aisl_stream_set_body( aisl_stream_t stream, const char * data, int32_t size );
|
||||
|
||||
#endif /* !AISL_STREAM_H */
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file types.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief Types stringifiers module
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#include <aisl/types.h>
|
||||
|
||||
|
||||
#ifndef WITHOUT_STRINGIFIERS
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_status_to_string(aisl_status_t status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case AISL_INPUT_ERROR: return "AISL_INPUT_ERROR";
|
||||
case AISL_EXTCALL_ERROR: return "AISL_EXTCALL_ERROR";
|
||||
case AISL_SYSCALL_ERROR: return "AISL_SYSCALL_ERROR";
|
||||
case AISL_MALLOC_ERROR: return "AISL_MALLOC_ERROR";
|
||||
case AISL_SUCCESS: return "AISL_SUCCESS";
|
||||
case AISL_IDLE: return "AISL_IDLE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
|
||||
__attribute__ ((visibility ("default") ))
|
||||
const char *
|
||||
aisl_event_to_string( aisl_event_t event )
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case AISL_EVENT_SERVER_OPEN: return "SERVER_OPEN";
|
||||
case AISL_EVENT_SERVER_ERROR: return "SERVER_ERROR";
|
||||
case AISL_EVENT_CLIENT_CONNECT: return "CLIENT_CONNECT";
|
||||
case AISL_EVENT_CLIENT_DISCONNECT: return "CLIENT_DISCONNECT";
|
||||
case AISL_EVENT_CLIENT_TIMEOUT: return "CLIENT_TIMEOUT";
|
||||
case AISL_EVENT_STREAM_OPEN: return "STREAM_OPEN";
|
||||
case AISL_EVENT_STREAM_HEADER: return "STREAM_HEADER";
|
||||
case AISL_EVENT_STREAM_INPUT: return "STREAM_INPUT";
|
||||
case AISL_EVENT_STREAM_REQUEST: return "STREAM_REQUEST";
|
||||
case AISL_EVENT_STREAM_OUTPUT: return "STREAM_OUTPUT";
|
||||
case AISL_EVENT_STREAM_CLOSE: return "STREAM_CLOSE";
|
||||
case AISL_EVENT_STREAM_ERROR: return "STREAM_ERROR";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue