diff --git a/examples/hello-world.c b/examples/hello-world.c index 0b3a002..ffacdd8 100644 --- a/examples/hello-world.c +++ b/examples/hello-world.c @@ -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); } diff --git a/include/aisl/config.h b/include/aisl/config.h index 4813dbc..0e8fe27 100644 --- a/include/aisl/config.h +++ b/include/aisl/config.h @@ -12,13 +12,22 @@ #include +#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; diff --git a/include/aisl/instance.h b/include/aisl/instance.h index af584bc..5f936c8 100644 --- a/include/aisl/instance.h +++ b/include/aisl/instance.h @@ -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 ); /** diff --git a/include/aisl/types.h b/include/aisl/types.h index 7c35a4e..a38f9eb 100644 --- a/include/aisl/types.h +++ b/include/aisl/types.h @@ -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 */ diff --git a/project.mk b/project.mk index 8327ffd..dbda41a 100644 --- a/project.mk +++ b/project.mk @@ -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 diff --git a/src/buffer.c b/src/buffer.c index 735aa09..2aef473 100644 --- a/src/buffer.c +++ b/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 ) { diff --git a/src/callback.c b/src/callback.c deleted file mode 100644 index d7c2b89..0000000 --- a/src/callback.c +++ /dev/null @@ -1,11 +0,0 @@ -/* - * callback.c - * Copyright (C) 2019 Ilja Kartašov - * - * Distributed under terms of the MIT license. - */ - -#include "callback.h" - - - diff --git a/src/callback.h b/src/callback.h deleted file mode 100644 index 246e345..0000000 --- a/src/callback.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * src/callback.h - * - * Copyright (C) 2019 Ilja Kartašov - * - * 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 - - -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 */ diff --git a/src/client.c b/src/client.c index 52fbb8f..c69815c 100644 --- a/src/client.c +++ b/src/client.c @@ -11,9 +11,11 @@ #endif #include +#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); diff --git a/src/client.h b/src/client.h index abcbe16..6586224 100644 --- a/src/client.h +++ b/src/client.h @@ -4,23 +4,30 @@ #include #include +#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 */ diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..ce0b65e --- /dev/null +++ b/src/debug.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 + * @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 + +#define DPRINTF(...) do { \ + fprintf(stderr, "* AISL: "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} while(0) \ + + +#else + +#define DPRINTF(...) + +#endif + + +#endif /* !AISL_DEBUG_H */ diff --git a/src/event.c b/src/event.c deleted file mode 100644 index 425852a..0000000 --- a/src/event.c +++ /dev/null @@ -1,28 +0,0 @@ -#include - -__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"; -} - - diff --git a/src/globals.h b/src/globals.h deleted file mode 100644 index b3e39fc..0000000 --- a/src/globals.h +++ /dev/null @@ -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 diff --git a/src/handle.c b/src/handle.c deleted file mode 100644 index 4d6707c..0000000 --- a/src/handle.c +++ /dev/null @@ -1,807 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#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; icrypters->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; icrypters->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; iservers->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; iclients->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); - -} - - - - diff --git a/src/handle.h b/src/handle.h deleted file mode 100644 index db66510..0000000 --- a/src/handle.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _AISL_HANDLE_H__ -#define _AISL_HANDLE_H__ - -#include -#include - -#include - -#include "list.h" -#include -#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 diff --git a/src/http.c b/src/http.c index 300ccaa..df57c19 100644 --- a/src/http.c +++ b/src/http.c @@ -1,6 +1,342 @@ -#include +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ -/* -------------------------------------------------------------------------- */ +/** + * @file http.c + * @author Ilja Kartašov + * @brief HTTP module source file + * + * @see https://lowenware.com/aisl/ + */ +#include +#include +#include +#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 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 ""; } -/* -------------------------------------------------------------------------- */ + + diff --git a/src/http.h b/src/http.h index 044a16d..c6069e5 100644 --- a/src/http.h +++ b/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 */ diff --git a/src/instance.c b/src/instance.c index 6c983cc..d47224c 100644 --- a/src/instance.c +++ b/src/instance.c @@ -19,7 +19,7 @@ #include #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) { diff --git a/src/instance.h b/src/instance.h index 9417b01..bff5fdb 100644 --- a/src/instance.h +++ b/src/instance.h @@ -15,6 +15,38 @@ #endif #include +#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 */ diff --git a/src/list.c b/src/list.c index 59fa517..980935c 100644 --- a/src/list.c +++ b/src/list.c @@ -14,6 +14,7 @@ */ #include +#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; diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index 348e4ad..0000000 --- a/src/parser.c +++ /dev/null @@ -1,490 +0,0 @@ -#include -#include -#include -#include - -#include "str-utils.h" -#include - -#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; -} diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index 835d0f3..0000000 --- a/src/parser.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _AISL_PARSER_H_ -#define _AISL_PARSER_H_ - -#include -#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 diff --git a/src/server.c b/src/server.c index b7e9375..6b93f36 100644 --- a/src/server.c +++ b/src/server.c @@ -15,22 +15,14 @@ #include +#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; } diff --git a/src/server.h b/src/server.h index 28ac2b3..ab4670c 100644 --- a/src/server.h +++ b/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 ); /** diff --git a/src/status.c b/src/status.c deleted file mode 100644 index 4a363ed..0000000 --- a/src/status.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -/* -------------------------------------------------------------------------- */ - -__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 ""; -} - -/* -------------------------------------------------------------------------- */ diff --git a/src/stream.c b/src/stream.c index 25b1cdd..90d2ce7 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1,12 +1,13 @@ #include #include #include +#include #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; diff --git a/src/stream.h b/src/stream.h index 1fb3e9e..7439b37 100644 --- a/src/stream.h +++ b/src/stream.h @@ -3,6 +3,7 @@ #include #include +#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 */ diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..fbcc284 --- /dev/null +++ b/src/types.c @@ -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 + * @brief Types stringifiers module + * + * @see https://lowenware.com/aisl/ + */ + +#include + + +#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