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