Add cStuff and SDK submodules

This commit is contained in:
2019-07-13 14:08:18 +02:00
parent f7a9f255df
commit 06c99b1e4b
25 changed files with 8 additions and 0 deletions
+539
View File
@@ -0,0 +1,539 @@
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef AISL_WITHOUT_SSL
#include <openssl/err.h>
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
#define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
#define BUFFER_SIZE (16*1024)
static void
aisl_client_close(AislClient client, AislStatus status)
{
if (client->fd != -1)
{
aisl_raise(
client->server->instance
, (void *)client
, AISL_EVENT_CLIENT_DISCONNECT
, status
);
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
static AislStatus
aisl_client_parse(AislClient client, char * data, int32_t size)
{
AislStatus result = AISL_SUCCESS;
AislStream s = client->stream;
ParserStatus 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_SUCCESS )
{
switch ( aisl_stream_get_state(s) )
{
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
p = HTTP_PARSER_ERROR;
continue;
}
// size now has number of parsed bytes
data += size;
bytes_left -= size;
size = bytes_left;
}
break;
case AISL_HTTP_2_0:
break;
}
switch(p)
{
case HTTP_PARSER_READY:
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_REQUEST
, result
);
break;
case HTTP_PARSER_ERROR:
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(
client->server->instance
, (void *) s
, AISL_EVENT_STREAM_ERROR
, result
);
aisl_client_close(client, result);
return result;
default:
break;
}
if (size)
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
return result;
}
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
* parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0
*/
static AislStatus
aisl_client_input(AislClient client)
{
int l;
char * data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
{
DPRINTF("SSL_read");
if ( !(client->flags & FLAG_HANDSHAKE) )
{
if ( (l = SSL_accept(client->ssl)) != 1 )
{
l = SSL_get_error(client->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client, AISL_EXTCALL_ERROR);
return AISL_EXTCALL_ERROR;
}
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size) ;
}
else
#endif
l = recv( client->fd, data, size, 0);
if (l > 0)
{
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
}
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));
}
}
/* both: client disconnect + on read error */
/* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
static AislStatus
aisl_client_output(AislClient client)
{
int l;
char * data;
AislStream s = client->stream;
/* while stream is not flushed, we should raise event */
if( aisl_get_output_event(s) )
{
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* buffer->size will be used to carry amount of stored bytes
* */
l = aisl_stream_get_buffer_space(s);
/*
if (bsz < OUTPUT_BUFFER_SIZE)
{
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false;
s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE;
}
*/
if ( !(l < aisl_stream_get_buffer_size(s) / 2) )
{
aisl_raise(
client->server->instance
, (void*)s
, AISL_EVENT_STREAM_OUTPUT
, AISL_SUCCESS
);
}
}
data = aisl_stream_get_data(s, &l);
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)
{
aisl_stream_shift(s, l);
/*
if (s->state == STREAM_RESPONSE_READY && / * flushed * /
s->buffer->size == 0) / * all sent * /
*/
if ( aisl_stream_is_done(s) )
{
/* buffer_clear(s->buffer, 0); */
/* data has been sent */
if (client->flags & FLAG_KEEPALIVE)
{
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++);
if (client->stream != NULL )
return AISL_SUCCESS;
/* in case of malloc error it will not be error as long as request was
* handled and we just close the connection.
*/
}
aisl_client_close(client, AISL_SUCCESS);
}
return AISL_SUCCESS;
}
/* 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;
}
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
AislClient
aisl_client_new( AislServer server,
int fd,
struct sockaddr_in * addr )
{
AislClient client;
AislStream 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;
client->next_id = 2;
client->http_version = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1)
{
DPRINTF("client buffer alocated");
memcpy(&client->out, &client->in, sizeof (struct buffer));
stream = aisl_stream_new(client, 0);
if (stream != NULL)
{
client->stream = stream;
DPRINTF("client stream alocated");
#ifdef AISL_WITHOUT_SSL
return client;
#else
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);
return client;
}
#endif
}
}
aisl_client_free(client);
}
return NULL;
}
void
aisl_client_free(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
#ifndef AISL_WITHOUT_SSL
if (client->ssl)
SSL_free(client->ssl);
#endif
if (client->in.data)
free(client->in.data);
/* out buffer is a shared part of input buffer, so no need to free it */
if (client->stream)
aisl_stream_free(client->stream);
free(client);
}
AislStatus
AislClientouch(AislClient client, int32_t timeout)
{
AislStatus result = AISL_IDLE,
status = AISL_IDLE;
/* input */
if (client->flags & FLAG_CAN_READ)
{
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */
if (client->flags & FLAG_CAN_WRITE)
{
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
/*
if ((client->http_version==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) &&
(client_input(client)) ) result = true;
*/
/* output */
/*
s = list_index(client->streams, client->ostream);
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) )
result = client_output(client);
*/
/* update timestamp */
if (result == AISL_IDLE)
result = status;
if (result == AISL_SUCCESS)
client->timestamp = time(NULL);
else
{
time_t now;
time(&now);
if ( !(now - client->timestamp < timeout) )
{
aisl_client_close(client, result);
}
}
return result;
}
int
aisl_client_get_socket(AislClient client)
{
return client->fd;
}
bool
aisl_client_get_keepalive(AislClient client)
{
return (client->flags & FLAG_KEEPALIVE);
}
void
aisl_client_set_keepalive(AislClient client, bool value)
{
if (value)
client->flags |= FLAG_KEEPALIVE;
else
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
AislServer
aisl_client_get_server(AislClient client)
{
return client->server;
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_secure(AislClient client)
{
#ifdef AISL_WITHOUT_SSL
return false;
#else
return (client->ssl == NULL) ? false : true;
#endif
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_online(AislClient client)
{
return (client->fd == -1) ? false : true;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_disconnect(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_client_get_http_version(AislClient client)
{
return client->http_version;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_get_address( AislClient client, struct sockaddr_in * address)
{
memcpy(address, &client->address, sizeof (struct sockaddr_in));
}
+176
View File
@@ -0,0 +1,176 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file buffer.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Buffer module source file
*
* @see https://lowenware.com/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "buffer.h"
static int32_t
buffer_set_size(struct buffer *buffer, int32_t new_size)
{
if (!buffer->size || new_size != buffer->size) {
char *data;
if (new_size) {
if (new_size % 4096) {
new_size = (new_size / 4096 + 1) * 4096;
}
} else {
new_size = 16*1024;
}
if ((data = realloc(buffer->data, new_size)) != NULL) {
buffer->data = data;
buffer->size = new_size;
} else {
new_size = -1;
}
}
return new_size;
}
int32_t
buffer_init(struct buffer *buffer, int32_t size)
{
if ((size = buffer_set_size(buffer, size)) != -1)
buffer->used = 0;
return size;
}
void
buffer_release(struct buffer *buffer)
{
if (buffer->data) {
free(buffer->data);
buffer->data = NULL;
}
buffer->used = 0;
buffer->size = 0;
}
static int32_t
buffer_move_offset(struct buffer *buffer, int32_t offset, int32_t size)
{
int32_t to_move = buffer->used - offset;
if (to_move < 0) {
return -1;
} else if (to_move) {
memmove(&buffer->data[offset+size], &buffer->data[offset], to_move);
}
return size;
}
int32_t
buffer_insert(struct buffer *buffer, int32_t offset, const char *data,
int32_t size)
{
int32_t result;
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
if ( (result = buffer_set_size(buffer, buffer->size + size)) != -1) {
if ((result = buffer_move_offset(buffer, offset, size)) != -1) {
memcpy(&buffer->data[offset], data, size);
buffer->used += result;
}
}
return result;
}
int32_t
buffer_append_printf(struct buffer *buffer, const char *format, ...)
{
int32_t result;
va_list args;
va_start(args, format);
result = buffer_append_vprintf(buffer, format, args);
va_end(args);
return result;
}
int32_t
buffer_append_vprintf(struct buffer *buffer, const char *format, va_list args)
{
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
int32_t space, result;
va_list cp_args;
va_copy(cp_args, args);
space = buffer->size - buffer->used;
result = vsnprintf(&buffer->data[buffer->used], space, format, args);
if (result < space) { /* enough space */
buffer->used += result;
} else {
result = buffer_set_size(buffer, buffer->size + result - space + 1);
if (result != -1)
result = buffer_append_vprintf(buffer, format, cp_args);
}
va_end(cp_args);
return result;
}
int32_t
buffer_append(struct buffer *buffer, const char *data, int32_t size)
{
DPRINTF("Buffer: %d of %d", buffer->used, buffer->size);
int32_t used, space;
used = buffer->used,
space = buffer->size - used;
if (size > space) { /* enough space */
if (buffer_set_size(buffer, buffer->size + size - space) == -1)
return -1;
}
memcpy(&buffer->data[used], data, size);
buffer->used += size;
return size;
}
int32_t
buffer_shift(struct buffer *buffer, int32_t offset)
{
int32_t used = buffer->used - offset;
if (offset > 0) {
if (offset < used) {
memmove(buffer->data, &buffer->data[offset], used);
} else {
used = 0;
}
buffer->used = used;
}
return used;
}
+59
View File
@@ -0,0 +1,59 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/buffer.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of struct buffer and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9
#define AISL_BUFFER_H_D60EB5DF_B70B_4D8F_AA63_7FDB569D67E9
#include <stdint.h>
#include <stdarg.h>
struct buffer {
char *data;
int32_t size;
int32_t used;
};
int32_t
buffer_init(struct buffer *bs, int32_t size);
void
buffer_release(struct buffer *bs );
int32_t
buffer_insert(struct buffer *bs, int32_t offset, const char *data, int32_t size);
int32_t
buffer_append_printf(struct buffer *bs, const char *format, ...);
int32_t
buffer_append_vprintf(struct buffer *bs, const char *format, va_list args);
int32_t
buffer_append(struct buffer *bs, const char *data, int32_t size);
int32_t
buffer_shift(struct buffer *bs, int32_t size);
#endif /* !AISL_BUFFER_H */
+446
View File
@@ -0,0 +1,446 @@
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#if AISL_WITH_SSL == 1
#include <openssl/err.h>
#endif
#include <aisl/aisl.h>
#include "debug.h"
#include "stream.h"
#include "http.h"
#include "server.h"
#include "instance.h"
#include "client.h"
#define FLAG_KEEPALIVE (1<<0)
#define FLAG_HANDSHAKE (1<<1)
#define FLAG_CAN_READ (1<<2)
#define FLAG_CAN_WRITE (1<<3)
#define BUFFER_SIZE (16*1024)
static void
aisl_client_close(AislClient client, AislStatus status)
{
if (client->fd != -1) {
aisl_raise(client->server->instance, (void *)client,
AISL_EVENT_CLIENT_DISCONNECT, status );
close(client->fd);
shutdown(client->fd, SHUT_RDWR);
client->fd = -1;
}
}
static AislStatus
aisl_client_parse(AislClient client, char *data, int32_t size)
{
AislStatus result = AISL_SUCCESS;
AislStream s = client->stream;
ParserStatus p = HTTP_PARSER_SUCCESS;
int32_t data_size;
switch (client->http_version) {
case AISL_HTTP_0_9:
case AISL_HTTP_1_0:
case AISL_HTTP_1_1:
while (p == HTTP_PARSER_SUCCESS) {
data_size = size;
switch (aisl_stream_get_state(s)) {
case AISL_STREAM_STATE_IDLE:
p = http_10_parse_request(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_HEADER:
p = http_10_parse_header(data, &size, client->stream);
break;
case AISL_STREAM_STATE_WAIT_BODY:
p = http_10_parse_body(data, &size, client->stream);
break;
default: /* has input data, but request was already parsed */
DPRINTF("misleading request length");
p = HTTP_PARSER_ERROR;
continue;
}
// size now has number of parsed bytes
DPRINTF("%d bytes parsed, %d bytes left", (data_size - size), size);
data += (data_size - size);
}
break;
case AISL_HTTP_2_0:
break;
}
switch(p) {
case HTTP_PARSER_READY:
client->flags &= ~FLAG_CAN_READ;
client->flags |= FLAG_CAN_WRITE;
client->stream->content_length = AISL_AUTO_LENGTH;
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_REQUEST,
result);
break;
case HTTP_PARSER_ERROR:
/* reply Bad Request here */
client->stream->http_response = AISL_HTTP_BAD_REQUEST;
aisl_raise(client->server->instance, (void *)s, AISL_EVENT_STREAM_ERROR,
result);
aisl_client_close(client, result);
return result;
default:
break;
}
buffer_shift(&client->in, client->in.used - size); /* reset buffer */
return result;
}
/* In HTTP 2.0 client->stream will be NULL if stream related data was completely
* parsed. If it is not NULL, then stream expects additional data -> same like
* in mono stream HTTP 1.0
*/
static AislStatus
aisl_client_input(AislClient client)
{
int l;
char *data = &client->in.data[ client->in.used ];
int32_t size = client->in.size - client->in.used;
#if AISL_WITH_SSL == 1
if (client->ssl) {
DPRINTF("SSL_read");
if (!(client->flags & FLAG_HANDSHAKE)) {
if ( (l = SSL_accept(client->ssl)) != 1 ) {
l = SSL_get_error(client->ssl, l);
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
return AISL_IDLE;
DPRINTF("SSL handshake fail: %s\n", ERR_error_string(l, NULL) );
aisl_client_close(client, AISL_EXTCALL_ERROR);
return AISL_EXTCALL_ERROR;
}
client->flags &= ~FLAG_HANDSHAKE;
}
l = SSL_read(client->ssl, data, size);
} else
#endif
{
l = recv( client->fd, data, size, 0);
}
if (l > 0) {
DPRINTF("%d bytes received from client", l);
data = client->in.data;
size = client->in.used + l;
client->in.used = size;
return aisl_client_parse(client, data, size);
} else if (l<0) {
#if AISL_WITH_SSL == 1
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));
}
}
/* both: client disconnect + on read error */
/* todo: raise client error here */
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
static AislStatus
aisl_client_output(AislClient client)
{
int l;
char *data;
AislStream s = client->stream;
/* while stream is not flushed, we should raise event */
if(aisl_get_output_event(s)) {
/* in case of chunked output ( subscription for AISL_STREAM_OUTPUT event )
* stream buffer will be initialized with OUTPUT_BUFFER_SIZE size, but
* buffer->size will be used to carry amount of stored bytes
* */
l = aisl_stream_get_buffer_space(s);
/*
if (bsz < OUTPUT_BUFFER_SIZE)
{
if (buffer_clear(s->buffer, OUTPUT_BUFFER_SIZE) == 0)
return false;
s->buffer->size = bsz;
bsz = OUTPUT_BUFFER_SIZE;
}
*/
if (!(l < aisl_stream_get_buffer_size(s) / 2)) {
aisl_raise(client->server->instance, (void*)s, AISL_EVENT_STREAM_OUTPUT,
AISL_SUCCESS);
}
}
data = aisl_stream_get_data(s, &l);
if ( !l )
return AISL_IDLE;
#if AISL_WITH_SSL == 1
l = (client->ssl) ? SSL_write(client->ssl, data, l) :
send(client->fd, data, l, 0);
#else
l = send(client->fd, data, l, 0);
#endif
if (l > 0) {
aisl_stream_shift(s, l);
if (aisl_stream_is_done(s)) {
/* data has been sent */
if (client->flags & FLAG_KEEPALIVE) {
aisl_stream_free(s);
client->stream = aisl_stream_new(client, client->next_id++);
if (client->stream != NULL )
return AISL_SUCCESS;
/* in case of malloc error it will not be an error as long as the
* request was handled and we just close the connection.
*/
}
aisl_client_close(client, AISL_SUCCESS);
}
return AISL_SUCCESS;
}
/* l < 0 */
#if AISL_WITH_SSL == 1
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;
}
aisl_client_close(client, AISL_SYSCALL_ERROR);
return AISL_SYSCALL_ERROR;
}
AislClient
aisl_client_new(AislServer server, int fd, struct sockaddr_in *addr)
{
AislClient client;
AislStream 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;
client->next_id = 2;
client->http_version = AISL_HTTP_1_0;
client->timestamp = time(NULL);
client->flags = FLAG_KEEPALIVE | FLAG_HANDSHAKE | FLAG_CAN_READ;
if (buffer_init(&client->in, 2*BUFFER_SIZE) != -1) {
DPRINTF("client buffer alocated");
memcpy(&client->out, &client->in, sizeof (struct buffer));
stream = aisl_stream_new(client, 0);
if (stream != NULL) {
client->stream = stream;
DPRINTF("client stream alocated");
#if AISL_WITH_SSL == 1
if (server->ssl) {
SSL_CTX * ssl_ctx = aisl_get_ssl_ctx(server->instance, NULL);
if ((client->ssl = SSL_new(ssl_ctx)) != NULL ) {
SSL_set_fd(client->ssl, fd);
return client;
}
} else {
return client;
}
#else
return client;
#endif
}
}
aisl_client_free(client);
}
return NULL;
}
void
aisl_client_free(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
#if AISL_WITH_SSL == 1
if (client->ssl)
SSL_free(client->ssl);
#endif
if (client->in.data)
free(client->in.data);
if (client->stream)
aisl_stream_free(client->stream);
free(client);
}
AislStatus
aisl_client_touch(AislClient client, int32_t timeout)
{
AislStatus result, status;
result = AISL_IDLE;
status = AISL_IDLE;
/* input */
if (client->flags & FLAG_CAN_READ) {
if ( (result = aisl_client_input(client)) < 0 )
return result;
}
/* output */
if (client->flags & FLAG_CAN_WRITE) {
if ( (status = aisl_client_output(client)) < 0 )
return status;
}
if (result == AISL_IDLE)
result = status;
if (result != AISL_SUCCESS) {
time_t now;
time(&now);
if (!(now - client->timestamp < timeout)) {
aisl_client_close(client, result);
}
} else {
client->timestamp = time(NULL);
}
return result;
}
int
aisl_client_get_socket(AislClient client)
{
return client->fd;
}
bool
aisl_client_get_keepalive(AislClient client)
{
return (client->flags & FLAG_KEEPALIVE);
}
void
aisl_client_set_keepalive(AislClient client, bool value)
{
if (value)
client->flags |= FLAG_KEEPALIVE;
else
client->flags &= ~FLAG_KEEPALIVE;
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
AislServer
aisl_client_get_server(AislClient client)
{
return client->server;
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_secure(AislClient client)
{
#if AISL_WITH_SSL == 1
return (client->ssl == NULL) ? false : true;
#else
return false;
#endif
}
__attribute__ ((visibility ("default") ))
bool
aisl_client_is_online(AislClient client)
{
return (client->fd == -1) ? false : true;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_disconnect(AislClient client)
{
aisl_client_close(client, AISL_SUCCESS);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_client_get_http_version(AislClient client)
{
return client->http_version;
}
__attribute__ ((visibility ("default") ))
void
aisl_client_get_address(AislClient client, struct sockaddr_in *address)
{
memcpy(address, &client->address, sizeof (struct sockaddr_in));
}
+106
View File
@@ -0,0 +1,106 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file client.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of aisl_client structure and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
#define AISL_CLIENT_H_164FE6B2_E5D4_4968_B50F_823E30E8F777
#include <arpa/inet.h>
#if AISL_WITH_SSL == 1
#include <openssl/ssl.h>
#endif
#include <aisl/client.h>
#include "buffer.h"
#define AISL_CLIENT(x) ((AislClient) x)
struct aisl_client {
struct sockaddr_in address; /**< Client's address structure. */
struct buffer in; /**< Client's input buffer. */
struct buffer out; /**< Client's output buffer. */
AislServer server; /**< Server instance. */
AislStream stream; /**< Pending client's stream. */
#if AISL_WITH_SSL == 1
SSL *ssl; /**< SSL pointer for HTTPS. */
#endif
time_t timestamp; /**< Last communication timestamp. */
int next_id; /**< Stream id generator (even). */
int flags; /**< Client's flag bitmask. */
int fd; /**< Client's socket descriptor. */
AislHttpVersion http_version; /**< Client's http_version version. */
};
/**
* @brief Constructor for #AislClient instance.
* @param server an #AislServer 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
*/
AislClient
aisl_client_new(AislServer server,
int fd,
struct sockaddr_in *addr );
/**
* @brief Destructor for #AislClient instance.
* @param client an #AislClient instance pointer.
*/
void
aisl_client_free(AislClient client);
/**
* @brief Does all HTTP client routines.
* Reads and parses requests, writes responses.
* @param client an #AislClient instance pointer.
* @param timeout an allowed client silence time in seconds.
* @return #AislStatus code.
*/
AislStatus
aisl_client_touch(AislClient client, int32_t timeout);
/**
* @Brief Checks if client is about to keep connection alive.
* @param client an #AislClient instance pointer.
* @return true if keepalive mode is on, otherwise false.
*/
bool
aisl_client_get_keepalive(AislClient client);
/**
* @Brief Sets if connection with client must be kept alive.
* @param client an #AislClient instance pointer.
* @param value a true to enable keepalive mode, false to disable.
*/
void
aisl_client_set_keepalive(AislClient client, bool value);
/**
* @brief Gets socket descriptor associated with #AislClient instance.
* @param client an #AislClient instance pointer.
* @return a client socket descriptor.
*/
int
aisl_client_get_socket(AislClient client);
#endif /* !AISL_CLIENT_H */
+37
View File
@@ -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
#if AISL_WITH_DEBUG == 1
#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 */
+428
View File
@@ -0,0 +1,428 @@
/******************************************************************************
*
* 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"
static AislHttpMethod
http_method_from_string(const char *method, int32_t length)
{
int i;
AislHttpMethod 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 (AislHttpMethod); 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 AislHttpVersion
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 */
ParserStatus
http_10_parse_request(char *data, int32_t *p_size, AislStream 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;
AislHttpMethod http_method;
AislHttpVersion 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) {
DPRINTF("bad character in HTTP version (%c)", *data);
return HTTP_PARSER_ERROR;
}
break;
case '/':
if (!path && data > host) {
path = data;
if (!uri)
uri = path;
} else if (version && data-version != 4) {
DPRINTF("wrong HTTP version length");
return HTTP_PARSER_ERROR;
}
break;
case '?':
if (!query) {
query = data+1;
} else if (version) {
DPRINTF("bad character in HTTP version (%c)", *data);
return HTTP_PARSER_ERROR;
}
break;
case '\n':
newline = data;
break;
case '\r':
if (!version) {
DPRINTF("unexpected end of HTTP request");
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) {
DPRINTF("bad HTTP version length");
return HTTP_PARSER_ERROR;
}
}
data++;
}
/* STEP 2. Verify splitting was completed */
/* Was request sent? */
if (!newline)
return HTTP_PARSER_HUNGRY;
/* Check mandatory parts presence */
if (!method_end || !path || !uri_end || !version) {
DPRINTF("parser error: method=%d, path=%d, uri_end=%d, version=%d",
(method_end ? 1 : 0), (path ? 1 : 0), (uri_end ? 1 : 0),
(version ? 1: 0));
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) {
DPRINTF("invalid HTTP method");
return HTTP_PARSER_ERROR;
}
if ((http_version = http_version_from_string(version))==0) {
DPRINTF("invalid HTTP version");
return HTTP_PARSER_ERROR;
}
if (query) {
*(query - 1) = 0;
} else {
query = uri_end;
}
if (host) {
if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) {
DPRINTF("invalid HTTP uri");
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;
}
ParserStatus
http_10_parse_header(char *data, int32_t *p_size, AislStream stream)
{
int32_t size = *p_size;
char *key = data,
*colon = NULL,
*val = NULL,
*val_end = NULL,
*newline = NULL;
DPRINTF("parse header: %p, %d, %02X %02X", data, *p_size, *data & 0xFF, *(data+1) & 0xFF);
while(!newline && size-- ) {
switch(*data) {
case ' ':
if (val && !val_end)
val_end = data;
break;
case ':':
if (!colon) {
if (colon == key) {
DPRINTF("parser error: nameless HTTP header");
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;
/* DPRINTF("(%p == %p); *key == 0x%02x", newline, key, *key & 0xFF); */
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')) {
*p_size = size;
DPRINTF("end of headers received");
return (aisl_stream_set_end_of_headers(stream) == 0) ?
HTTP_PARSER_READY : HTTP_PARSER_SUCCESS;
}
DPRINTF("parser error: invalid HTTP header");
return HTTP_PARSER_ERROR;
}
ParserStatus
http_10_parse_body(char *data, int32_t *p_size, AislStream stream)
{
int32_t size = *p_size;
if (!size)
return HTTP_PARSER_HUNGRY;
*p_size = 0;
switch (aisl_stream_set_body(stream, data, size)) {
case 0:
return HTTP_PARSER_READY;
case -1:
DPRINTF("parser error: invalid HTTP body length");
return HTTP_PARSER_ERROR;
default:
return HTTP_PARSER_SUCCESS;
}
}
/* API Level */
__attribute__ ((visibility ("default") ))
const char *
aisl_http_version_to_string(AislHttpVersion 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";
}
return "";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_http_response_to_string(AislHttpResponse code)
{
switch (code) {
/* most common for faster behavior */
case AISL_HTTP_OK: return "OK";
case AISL_HTTP_MOVED_PERMANENTLY: return "Moved Permanently";
/* informational */
case AISL_HTTP_CONTINUE: return "Continue";
case AISL_HTTP_SWITCHING_PROTOCOLS: return "Switching Protocols";
/* Successful */
case AISL_HTTP_CREATED: return "Created";
case AISL_HTTP_ACCEPTED: return "Accepted";
case AISL_HTTP_NON_AUTHORITATIVE_INFORMATION: return "Non-Authoritative Information";
case AISL_HTTP_NO_CONTENT: return "No Content";
case AISL_HTTP_RESET_CONTENT: return "Reset Content";
case AISL_HTTP_PARTIAL_CONTENT: return "Partial Content";
/* redirection */
case AISL_HTTP_MULTIPLE_CHOICES: return "Multiple Choices";
case AISL_HTTP_FOUND: return "Found";
case AISL_HTTP_SEE_OTHER: return "See other";
case AISL_HTTP_NOT_MODIFIED: return "Not Modified";
case AISL_HTTP_USE_PROXY: return "Use Proxy";
case AISL_HTTP_UNUSED: return "(unused)";
case AISL_HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect";
/* client error */
case AISL_HTTP_BAD_REQUEST: return "Bad Request";
case AISL_HTTP_UNAUTHORIZED: return "Unauthorized";
case AISL_HTTP_PAYMENT_REQUIRED: return "Payment Required";
case AISL_HTTP_FORBIDDEN: return "Forbidden";
case AISL_HTTP_NOT_FOUND: return "Not Found";
case AISL_HTTP_METHOD_NOT_ALLOWED: return "Method Not Allowed";
case AISL_HTTP_NOT_ACCEPTABLE: return "Not Acceptable";
case AISL_HTTP_PROXY_AUTHENTICATION_REQUIRED: return "Proxy Authentication Required";
case AISL_HTTP_REQUEST_TIMEOUT: return "Request Timeout";
case AISL_HTTP_CONFLICT: return "Conflict";
case AISL_HTTP_GONE: return "Gone";
case AISL_HTTP_LENGTH_REQUIRED: return "Length Required";
case AISL_HTTP_PRECONDITION_FAILED: return "Precondition Failed";
case AISL_HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large";
case AISL_HTTP_REQUEST_URI_TOO_LONG: return "Request-URI Too Long";
case AISL_HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type";
case AISL_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable";
case AISL_HTTP_EXPECTATION_FAILED: return "Expectation Failed";
/* server error */
case AISL_HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
case AISL_HTTP_NOT_IMPLEMENTED: return "Not Implemented";
case AISL_HTTP_BAD_GATEWAY: return "Bad Gateway";
case AISL_HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
case AISL_HTTP_GATEWAY_TIMEOUT: return "Gateway Timeout";
case AISL_HTTP_VERSION_NOT_SUPPORTED: return "HTTP Version Not Supported";
}
return "";
}
__attribute__ ((visibility ("default") ))
const char *
aisl_http_method_to_string( AislHttpMethod method )
{
switch(method) {
case AISL_HTTP_GET: return "GET";
case AISL_HTTP_PUT: return "PUT";
case AISL_HTTP_POST: return "POST";
case AISL_HTTP_HEAD: return "HEAD";
case AISL_HTTP_TRACE: return "TRACE";
case AISL_HTTP_DELETE: return "DELETE";
case AISL_HTTP_OPTIONS: return "OPTIONS";
case AISL_HTTP_CONNECT: return "CONNECT";
case AISL_HTTP_PRI: return "PRI";
case AISL_HTTP_METHOD_UNKNOWN: break;
}
return "";
}
+41
View File
@@ -0,0 +1,41 @@
/******************************************************************************
*
* Copyright (c) 2017-%YEAR% by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file http.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of HTTP parser module
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01
#define AISL_HTTP_H_E2921FF1_6BD3_4E60_B599_ACA1A494CD01
#include <aisl/types.h>
typedef enum {
HTTP_PARSER_SUCCESS
, HTTP_PARSER_READY
, HTTP_PARSER_HUNGRY
, HTTP_PARSER_ERROR
} ParserStatus;
ParserStatus
http_10_parse_request(char *data, int32_t *size, AislStream stream);
ParserStatus
http_10_parse_header(char *data, int32_t *size, AislStream stream);
ParserStatus
http_10_parse_body(char *data, int32_t *size, AislStream stream);
#endif /* !AISL_HTTP_H */
+301
View File
@@ -0,0 +1,301 @@
/*
* src/instance.c
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/select.h>
#include <sys/time.h>
#if AISL_WITH_SSL == 1
#include <openssl/err.h>
#endif
#include "debug.h"
#include "str-utils.h"
#include "buffer.h"
#include "client.h"
#include "server.h"
#include "stream.h"
#include "instance.h"
#if AISL_WITH_SSL == 1
static uint32_t m_instances = 0;
static struct aisl_ssl *
aisl_new_ssl(AislInstance instance, const struct aisl_cfg_ssl *cfg_ssl)
{
SSL_CTX *ssl_ctx = NULL;
struct aisl_ssl **list, *ssl;
list = instance->ssl;
/* lookup for existing contexts */
while ((ssl = *list)) {
if (ssl->key_file && strcmp(ssl->key_file, cfg_ssl->key_file)==0 &&
ssl->crt_file && strcmp(ssl->crt_file, cfg_ssl->crt_file)==0
) {
ssl_ctx = ssl->ctx;
break;
}
list++;
}
ssl = aisl_ssl_new(cfg_ssl->host, cfg_ssl->key_file, cfg_ssl->crt_file,
ssl_ctx);
if (ssl) {
if (!ssl_ctx && !aisl_ssl_get_ctx(ssl, (void*) instance)) {
aisl_ssl_free(ssl);
ssl = NULL;
}
}
return ssl;
}
#endif
/* Initialization functions */
__attribute__ ((visibility ("default") ))
AislInstance
aisl_new(const struct aisl_cfg *cfg)
{
int i;
AislInstance instance;
/* allocate root structure */
if (!(instance = calloc(1, sizeof (struct aisl_instance))))
goto finally;
/* allocate servers */
if (!(instance->srv = calloc(cfg->srv_cnt+1, sizeof (AislServer))))
goto release;
for (i = 0; i < cfg->srv_cnt; i++) {
DPRINTF("new srv %d", i);
if (!(instance->srv[i] = aisl_server_new(&cfg->srv[i], instance)))
goto release;
}
#if AISL_WITH_SSL == 1
if ((m_instances++) == 0) {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
if (!(instance->ssl = calloc(cfg->ssl_cnt+1, sizeof (struct aisl_ssl))))
goto release;
for (i=0; i<cfg->ssl_cnt; i++) {
DPRINTF("new ssl %d", i);
if (!(instance->ssl[i] = aisl_new_ssl(instance, &cfg->ssl[i])))
goto release;
}
#endif
if (list_init(&instance->client_spool, cfg->client_spool_size) == -1)
goto release;
instance->accept_limit = cfg->client_accept_limit;
instance->silence_timeout = cfg->client_silence_timeout;
instance->callback = cfg->callback;
instance->p_ctx = cfg->p_ctx;
goto finally;
release:
aisl_free(instance);
instance = NULL;
finally:
return instance;
}
__attribute__ ((visibility ("default") ))
void
aisl_free(AislInstance instance)
{
if (instance->srv) {
AislServer * srv = instance->srv;
while (*srv) {
aisl_server_free(*(srv++));
}
free(instance->srv);
}
list_release(&instance->client_spool, (list_destructor_t)aisl_client_free);
#if AISL_WITH_SSL == 1
if (instance->ssl) {
struct aisl_ssl **ssl = instance->ssl;
while (*ssl) {
aisl_ssl_free(*(ssl++));
}
free(instance->ssl);
}
if ((--m_instances) == 0) {
EVP_cleanup();
}
#endif
free(instance);
}
#if AISL_WITH_SSL == 1
SSL_CTX *
aisl_get_ssl_ctx(AislInstance instance, const char * host)
{
struct aisl_ssl **list, *ssl;
list = instance->ssl;
if (host) {
while ((ssl = *list)) {
if (str_cmpi(ssl->host, host) == 0) {
return ssl->ctx;
}
list++;
}
}
return NULL;
}
#endif
void
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt)
{
#if AISL_WITH_STRINGIFIERS == 1
DPRINTF("! %s", aisl_event_to_string(evt->code));
#else
DPRINTF("! %d", evt->code);
#endif
if (instance->callback)
instance->callback(evt, instance->p_ctx);
}
void
aisl_raise(AislInstance instance,
void *source,
AislEvent code,
AislStatus status)
{
struct aisl_evt evt;
evt.source = source;
evt.code = code;
evt.status = status;
aisl_raise_evt(instance, &evt);
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_run_cycle(AislInstance instance)
{
AislStatus result = AISL_IDLE;
AislServer *list, srv;
AislClient cli;
int32_t i;
list = instance->srv;
while ((srv = *list)) {
cli = NULL;
if (aisl_server_touch(srv, &cli) != AISL_IDLE)
result = AISL_SUCCESS;
if (cli) {
DPRINTF("Accepted %p", (void*)cli);
if (list_append(&instance->client_spool, cli) == -1)
aisl_client_free(cli);
}
list++;
}
for (i=0; i < instance->client_spool.count; i++) {
cli = LIST_INDEX(instance->client_spool, i);
if (aisl_client_touch(cli, instance->silence_timeout) != AISL_IDLE)
result = AISL_SUCCESS;
if (!aisl_client_is_online(cli)) {
aisl_client_free( cli );
list_remove_index(&instance->client_spool, i);
}
}
return result;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_sleep(AislInstance instance, uint32_t usec)
{
AislServer *list, srv;
int sd, 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);
list = instance->srv;
while ((srv = *list)) {
sd = aisl_server_get_socket(srv);
if (sd != -1) {
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
list++;
}
for (i=0; i<instance->client_spool.count; i++) {
AislClient c = LIST_INDEX(instance->client_spool, i);
sd = aisl_client_get_socket(c);
if (sd != -1) {
FD_SET(sd, &fs);
if (sd > maxfd) maxfd = sd;
}
}
switch (select(maxfd+1, &fs, NULL, NULL, &timeout)) {
case -1:
return AISL_SYSCALL_ERROR;
case 0:
return AISL_IDLE;
default:
return AISL_SUCCESS;
}
}
+70
View File
@@ -0,0 +1,70 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/instance.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief Declarations of aisl_instance structure and functions
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#define AISL_INSTANCE_H_814CF474_A646_45B7_B6B2_3F4C7BEFA484
#if AISL_WITH_SSL == 1
#include <openssl/ssl.h>
#endif
#include <aisl/instance.h>
#include "ssl.h"
#include "list.h"
struct aisl_instance {
AislServer *srv;
#if AISL_WITH_SSL == 1
struct aisl_ssl * *ssl;
#endif
struct list client_spool;
AislCallback callback;
void *p_ctx;
uint32_t accept_limit;
uint32_t silence_timeout;
uint32_t buffer_size;
};
#if AISL_WITH_SSL == 1
/**
* @brief Gets SSL context for appropriate server name.
* @param instance a pointer to #AislInstance instance.
* @param server_name a null-terminated string with server name or NULL.
* @return a pointer to SSL context
*/
SSL_CTX *
aisl_get_ssl_ctx(AislInstance instance, const char *server_name);
#endif
/**
* @brief Raises event from source.
* @param instance a pointer to #AislInstance instance.
* @param evt a pointer to event structure.
*/
void
aisl_raise_evt(AislInstance instance, const struct aisl_evt *evt);
void
aisl_raise(AislInstance instance,
void *source,
AislEvent code,
AislStatus status);
#endif /* !AISL_INSTANCE_H */
+97
View File
@@ -0,0 +1,97 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file list.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief List module source file
*
* @see https://lowenware.com/aisl/
*/
#include <stdlib.h>
#include "debug.h"
#include "list.h"
int32_t
list_init(struct list *list, int32_t size)
{
if ((list->data = calloc(size, sizeof (void*))) != NULL) {
list->size = size;
list->count = 0;
return 0;
}
return -1;
}
void
list_release(struct list *list, list_destructor_t destructor)
{
if (list->data) {
if (destructor) {
int32_t i;
for (i=0; i<list->count; i++) {
void *ptr;
if ((ptr = list->data[i]) != NULL)
destructor(list->data[i]);
}
}
free(list->data);
}
}
int32_t
list_append(struct list *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;
list->data = new_list;
list->size = new_size;
}
list->data[pos]=entry;
list->count++;
return pos;
}
void *
list_remove_index(struct list *list, int32_t index)
{
void *result;
if (index < list->count) {
int32_t i, c = --list->count;
result = list->data[index];
for (i = index; i<c; i++) {
list->data[i]=list->data[i+1];
}
}
else
result = NULL;
return result;
}
+51
View File
@@ -0,0 +1,51 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file src/list.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief List module header file
*
* @see https://lowenware.com/aisl/
*/
#ifndef AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1
#define AISL_LIST_H_21495B65_111D_40F7_840F_CC50D9D324A1
#include <stdint.h>
#define LIST_INDEX(L, I) ( L.data[ I ] )
struct list {
void **data;
int32_t size;
int32_t count;
};
typedef void
(* list_destructor_t)(void *list_item);
int32_t
list_init(struct list *lst, int32_t size);
void
list_release(struct list *lst, list_destructor_t destructor);
int32_t
list_append(struct list *lst, void * entry);
void *
list_remove_index(struct list *lst, int32_t index);
#endif /* !AISL_LIST_H */
+169
View File
@@ -0,0 +1,169 @@
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <openssl/ssl.h>
#ifdef __APPLE__
#include <sys/ioctl.h>
#endif
#include "debug.h"
#include "str-utils.h"
#include "instance.h"
#include "client.h"
#include "server.h"
/**
* @brief Creates TCP server socket, binds to address and starts to listen.
* @param server a pointer to #AislServer instance.
* @return #AislStatus code.
*/
static AislStatus
aisl_server_open(AislServer server)
{
int fd, s_opt = 1;
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd != -1) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&s_opt,
sizeof (int));
#ifdef __APPLE__
ioctl(fd, FIONBIO, (char *)&s_opt);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#endif
s_opt = sizeof (struct sockaddr_in);
if (bind(fd, (struct sockaddr *) &server->address, s_opt) == 0) {
if (listen(fd, SOMAXCONN) == 0) {
server->fd = fd;
return AISL_SUCCESS;
}
}
close(fd);
}
return AISL_SYSCALL_ERROR;
}
/**
* @brief Tries to accept a new client.
* @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #AislClient instance pointer.
* @return #AislStatus code.
*/
static AislStatus
aisl_server_accept(AislServer server, AislClient *p_client )
{
int fd;
struct sockaddr_in addr;
socklen_t len = sizeof (struct sockaddr_in);
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))) ?
AISL_MALLOC_ERROR : AISL_SUCCESS;
}
}
close(fd);
} else if (errno == EWOULDBLOCK) {
return AISL_IDLE;
}
return AISL_SYSCALL_ERROR;
}
/* Library Level ------------------------------------------------------------ */
AislStatus
aisl_server_touch(AislServer server, AislClient *p_client)
{
AislStatus result;
if (server->fd == -1) {
if ((result = aisl_server_open(server)) != AISL_IDLE) {
aisl_raise(server->instance, server, ((result == AISL_SUCCESS) ?
AISL_EVENT_SERVER_READY : AISL_EVENT_SERVER_ERROR), result);
}
} else {
result = aisl_server_accept(server, p_client);
}
return result;
}
int
aisl_server_get_socket(AislServer server)
{
return server->fd;
}
AislServer
aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance)
{
AislServer server;
if ((server = calloc(1, sizeof (struct aisl_server))) != NULL) {
server->instance = instance;
server->fd = -1;
server->address.sin_family = AF_INET;
server->address.sin_addr.s_addr = inet_addr(cfg_srv->host);
server->address.sin_port = htons(cfg_srv->port);
server->ssl = cfg_srv->secure;
}
return server;
}
void
aisl_server_free(AislServer server)
{
if (server) {
if ( server->fd != -1) {
close(server->fd);
server->fd=-1;
}
free(server);
}
}
/* API Level ---------------------------------------------------------------- */
__attribute__ ((visibility ("default") ))
void
aisl_server_get_address(AislServer server, struct sockaddr_in *address)
{
memcpy(address, &server->address, sizeof (struct sockaddr_in));
}
#if AISL_WITH_SSL == 1
__attribute__ ((visibility ("default") ))
bool
aisl_server_get_ssl(AislServer server)
{
return server->ssl;
}
#endif
+83
View File
@@ -0,0 +1,83 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*//*
* @file src/server.h
*
* Copyright (c) 2017-2019 by Löwenware Ltd.
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#define AISL_SERVER_H_FACD3B4D_0D72_4345_9A30_811103DA9AE2
#include <aisl/config.h>
#include <aisl/server.h>
#define AISL_SERVER(x) ((AislServer) x)
/**
* @brief HTTP(s) server data structure represented by #AislServer pointer.
*/
struct aisl_server {
struct sockaddr_in address; /**< TCP server address to listen to. */
AislInstance instance; /**< Associated AISL instance pointer. */
int fd; /**< System socket descriptor. */
bool ssl; /**< SSL enabled/disabled flag. */
};
/**
* @brief Allocates and instance of #AislServer.
* @param cfg_srv a pointer to server configuration structure.
* @param instance a pointer to #AislInstance instance.
* @return a pointer to #AislServer instance.
*/
AislServer
aisl_server_new(const struct aisl_cfg_srv *cfg_srv, AislInstance instance);
/**
* @brief Frees memory allocated for #AislServer instance.
* @param server a pointer to #AislServer instance.
*/
void
aisl_server_free(AislServer server);
/**
* @brief Does server routines.
* Tries to open server if it was not opened yet, otherwise tries to accept a
* new client connecting to the server.
* @param server a pointer to #AislServer instance.
* @param p_client a pointer to store #AislClient instance pointer.
* @return #AislStatus code:
* - AISL_SUCCESS if client connected,
* - AISL_IDLE if there is no client to connect,
* - AISL_SYSCALL_ERROR if error occured.
*/
AislStatus
aisl_server_touch(AislServer server, AislClient *p_client);
/**
* @brief Gets a socket descriptor associated with HTTP client.
* @param server a pointer to #AislServer instance.
* @return a client socket descriptor.
*/
int
aisl_server_get_socket(AislServer server);
#endif /* !AISL_SERVER_H */
+102
View File
@@ -0,0 +1,102 @@
/*
* ssl.c
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Distributed under terms of the MIT license.
*/
#include <openssl/err.h>
#include "str-utils.h"
#include "instance.h"
#include "ssl.h"
#if AISL_WITH_SSL == 1
static int
aisl_ssl_on_get_ctx(SSL *ssl, int *ptr, void *instance )
{
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
SSL_CTX *ctx = aisl_get_ssl_ctx((AislInstance) instance, server_name);
if (ctx)
SSL_set_SSL_CTX(ssl, ctx);
(void)ptr;
return SSL_TLSEXT_ERR_OK;
}
SSL_CTX *
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance)
{
SSL_CTX * ctx;
if ((ctx = SSL_CTX_new(SSLv23_server_method())) != NULL) {
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_set_tlsext_servername_callback( ctx, aisl_ssl_on_get_ctx );
SSL_CTX_set_tlsext_servername_arg( ctx, p_instance );
if (!(SSL_CTX_use_certificate_file(ctx, ssl->crt_file, SSL_FILETYPE_PEM)>0))
goto except;
if (!(SSL_CTX_use_PrivateKey_file(ctx, ssl->key_file, SSL_FILETYPE_PEM)>0))
goto except;
ssl->ctx = ctx;
return ctx;
}
except:
SSL_CTX_free(ctx);
return NULL;
}
struct aisl_ssl *
aisl_ssl_new(const char *key_file,
const char *crt_file,
const char *host,
SSL_CTX *ctx)
{
struct aisl_ssl *ssl;
if ((ssl = calloc(1, sizeof (struct aisl_ssl))) != NULL) {
if ((ssl->host = str_copy( host ? host : "*" )) != NULL) {
if (ctx) {
ssl->ctx = ctx;
return ssl;
} else {
if ((ssl->key_file = str_copy(key_file)) != NULL) {
if ((ssl->crt_file = str_copy(crt_file)) != NULL) {
return ssl;
}
}
}
}
aisl_ssl_free(ssl);
}
return NULL;
}
void
aisl_ssl_free( struct aisl_ssl *ssl )
{
if (ssl->host)
free(ssl->host);
if (ssl->key_file) {
free(ssl->key_file);
SSL_CTX_free(ssl->ctx);
}
if (ssl->crt_file)
free(ssl->crt_file);
free(ssl);
}
#endif
+54
View File
@@ -0,0 +1,54 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*//*
* src/ssl.h
*
* Copyright (C) 2019 Ilja Kartašov <ik@lowenware.com>
*
* Project homepage: https://lowenware.com/aisl/
*
*/
#ifndef AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E
#define AISL_SSL_H_6F82B0BA_7C59_45BA_AF3B_C82A67C8585E
#include <aisl/config.h>
#include <aisl/types.h>
#include <openssl/ssl.h>
struct aisl_ssl {
char *key_file;
char *crt_file;
char *host;
SSL_CTX *ctx;
};
struct aisl_ssl *
aisl_ssl_new( const char *key_file,
const char *crt_file,
const char *host,
SSL_CTX *ctx );
SSL_CTX *
aisl_ssl_get_ctx(struct aisl_ssl *ssl, void *p_instance);
void
aisl_ssl_free(struct aisl_ssl *ssl);
#endif /* !AISL_SSL_H */
+52
View File
@@ -0,0 +1,52 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file str-utils.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief String utilities source file
*
* @see https://lowenware.com/aisl/
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "str-utils.h"
char *
str_copy(const char *source)
{
int l = strlen(source);
char *result = malloc(l+1);
if (result)
strcpy(result, source);
return result;
}
int
str_cmpi(const char *s1, const char *s2)
{
char c1, c2, r = 0;
do {
c1 = tolower(*(s1++));
c2 = tolower(*(s2++));
if ((r = c1-c2) != 0)
break;
} while (c1 != 0);
return r;
}
+26
View File
@@ -0,0 +1,26 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file str-utils.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief String utilities header file
*
* @see https://lowenware.com/aisl/
*/
#ifndef STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E
#define STR_UTILS_H_799148B5_F829_437C_8B96_55876A37C38E
char *
str_copy(const char *source);
int
str_cmpi(const char *anycase, const char *lowcase);
#endif /* !STR_UTILS_H */
+616
View File
@@ -0,0 +1,616 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include "instance.h"
#include "client.h"
#include "server.h"
#include "str-utils.h"
#include "debug.h"
#include "stream.h"
#define FLAG_FLUSHED (1<<0)
#define FLAG_OUTPUT_CHUNKED (1<<1)
#define FLAG_SERVER_HEADER_SENT (1<<2)
#define FLAG_CONTENT_TYPE_HEADER_SENT (1<<3)
#define FLAG_CONTENT_LENGTH_HEADER_SENT (1<<4)
#define FLAG_CONNECTION_HEADER_SENT (1<<5)
/* Library level */
static void
aisl_stream_reset(AislStream stream, bool initial)
{
if (!initial) {
aisl_raise(aisl_get_instance(stream), (void*) stream,
AISL_EVENT_STREAM_CLOSE, AISL_SUCCESS);
}
buffer_release(&stream->buffer);
stream->u_ptr = NULL;
stream->content_length = 0; //AISL_AUTO_LENGTH;
stream->head_offset = 0;
stream->flags = 0;
stream->state = AISL_STREAM_STATE_IDLE;
stream->http_response = AISL_HTTP_OK;
}
AislStream
aisl_stream_new(AislClient client, int id)
{
AislStream stream = calloc(1, sizeof (struct aisl_stream));
if (stream) {
stream->id = id;
stream->client = client;
aisl_stream_reset(stream, true);
}
return stream;
}
void
aisl_stream_free(AislStream stream)
{
aisl_stream_reset(stream, false);
free(stream);
}
int32_t
aisl_stream_get_buffer_space(AislStream stream)
{
return stream->buffer.size - stream->buffer.used;
}
int32_t
aisl_stream_get_buffer_size(AislStream stream)
{
return stream->buffer.size;
}
char *
aisl_stream_get_data(AislStream stream, int32_t *p_length)
{
*p_length = stream->buffer.used;
return stream->buffer.data;
}
void
aisl_stream_shift(AislStream stream, int32_t offset)
{
buffer_shift(&stream->buffer, offset);
}
bool
aisl_stream_is_done(AislStream stream)
{
return (!stream->buffer.used && stream->state == AISL_STREAM_STATE_DONE);
}
AislStreamState
aisl_stream_get_state(AislStream stream)
{
return stream->state;
}
void
aisl_stream_set_request(AislStream stream,
AislHttpMethod http_method,
const char *path,
const char *query)
{
struct aisl_evt_open on_open;
stream->state = AISL_STREAM_STATE_WAIT_HEADER;
DPRINTF("%s -> path: %s, query: %s", aisl_http_method_to_string(http_method),
path, query);
on_open.evt.code = AISL_EVENT_STREAM_OPEN;
on_open.evt.source = (void *) stream;
on_open.evt.status = AISL_SUCCESS;
on_open.http_method = http_method;
on_open.path = path;
on_open.query = query;
aisl_raise_evt(aisl_get_instance(stream), (struct aisl_evt *)&on_open);
}
void
aisl_stream_set_header(AislStream stream, const char *key, const char *value)
{
struct aisl_evt_header on_header;
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);
on_header.evt.code = AISL_EVENT_STREAM_HEADER;
on_header.evt.source = (void *) stream;
on_header.evt.status = AISL_SUCCESS;
on_header.key = key;
on_header.value = value;
aisl_raise_evt(aisl_get_instance(stream),
(struct aisl_evt *) &on_header);
}
int
aisl_stream_set_end_of_headers(AislStream stream)
{
int result;
DPRINTF("stream->content_length == %"PRIu64"", stream->content_length);
if (stream->state == AISL_STREAM_STATE_WAIT_HEADER) {
result = (stream->content_length != 0);
stream->state = (result) ? AISL_STREAM_STATE_WAIT_BODY :
AISL_STREAM_STATE_READY;
} else {
result = 2;
}
return result;
}
int
aisl_stream_set_body(AislStream stream, const char *data, int32_t size)
{
int result;
if (stream->state == AISL_STREAM_STATE_WAIT_BODY) {
if (!(stream->content_length < size)) {
struct aisl_evt_input on_input;
stream->content_length -= size;
if (stream->content_length == 0) {
stream->state = AISL_STREAM_STATE_READY;
result = 0;
} else {
result = 1;
}
on_input.evt.code = AISL_EVENT_STREAM_INPUT;
on_input.evt.source = (void *)stream;
on_input.evt.status = AISL_SUCCESS;
on_input.data = data;
on_input.size = size;
aisl_raise_evt(stream->client->server->instance,
(struct aisl_evt *) &on_input);
} else {
result = -1;
}
} else {
result = 2;
}
return result;
}
/* API Level */
/* Why it was here?
static int
aisl_stream_write(AislStream stream, const char * data, uint32_t d_len)
{
return buffer_add( &stream->buffer, data, d_len);
}
__attribute__ ((visibility ("default") ))
void
aisl_cancel(AislStream stream)
{
aisl_client_close( stream->client );
}
*/
__attribute__ ((visibility ("default") ))
void *
aisl_get_context(AislStream s)
{
return s->u_ptr;
}
__attribute__ ((visibility ("default") ))
void
aisl_set_context(AislStream s, void *u_ptr)
{
s->u_ptr = u_ptr;
}
__attribute__ ((visibility ("default") ))
bool
aisl_is_secure(AislStream stream)
{
return aisl_client_is_secure(stream->client);
}
__attribute__ ((visibility ("default") ))
AislClient
aisl_get_client(AislStream s)
{
return s->client;
}
__attribute__ ((visibility ("default") ))
AislServer
aisl_get_server(AislStream s)
{
return aisl_client_get_server(s->client);
}
__attribute__ ((visibility ("default") ))
AislHttpVersion
aisl_get_http_version(AislStream s)
{
return aisl_client_get_http_version(s->client);
}
__attribute__ ((visibility ("default") ))
void
aisl_reject(AislStream s)
{
aisl_client_disconnect( s->client );
}
static AislStatus
aisl_start_response(AislStream stream)
{
return aisl_response(stream, AISL_HTTP_OK, AISL_AUTO_LENGTH);
}
static AislStatus
aisl_stream_close_headers(AislStream stream)
{
int32_t l;
if (aisl_start_response(stream) == AISL_MALLOC_ERROR)
return AISL_MALLOC_ERROR;
if (!(stream->flags & FLAG_SERVER_HEADER_SENT)) {
l = buffer_append(&stream->buffer, "Server: AISL\r\n", 14);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_SERVER_HEADER_SENT;
}
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
l = buffer_append(&stream->buffer,
"Content-Type: text/html; encoding=utf-8\r\n", 41);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (stream->content_length != AISL_AUTO_LENGTH) {
l = buffer_append_printf(&stream->buffer, "Content-Length: %"PRIu64"\r\n",
stream->content_length);
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
}
}
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
l = buffer_append_printf(&stream->buffer, "Connection: %s\r\n",
(aisl_client_get_keepalive(stream->client) ? "keepalive" : "close"));
if (l == -1)
return AISL_MALLOC_ERROR;
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
}
if (buffer_append( &stream->buffer, "\r\n", 2 ) == -1)
return AISL_MALLOC_ERROR;
stream->body_offset = stream->buffer.used;
stream->state = AISL_STREAM_STATE_SEND_BODY;
return AISL_SUCCESS;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_response(AislStream stream, AislHttpResponse rs_code, uint64_t c_len)
{
int32_t l;
/* check if those headers were already sent */
if (stream->state > AISL_STREAM_STATE_READY)
return AISL_IDLE;
stream->http_response = rs_code;
stream->content_length = c_len;
buffer_init(&stream->buffer, (c_len != AISL_AUTO_LENGTH) ? c_len : 0);
l = buffer_append_printf(&stream->buffer, "%s %d %s\r\n",
aisl_http_version_to_string(stream->client->http_version), rs_code,
aisl_http_response_to_string(rs_code));
if (l == -1)
return AISL_MALLOC_ERROR;
stream->state = AISL_STREAM_STATE_SEND_HEADER;
return AISL_SUCCESS;
}
__attribute__ ((visibility ("default") ))
AislStatus
aisl_flush(AislStream s)
{
if (!(s->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
char hdr[ 40 ];
uint64_t c_len;
int32_t l;
if (s->body_offset) {
c_len = s->buffer.used - s->body_offset;
l = snprintf(hdr, sizeof (hdr), "Content-Length: %"PRIu64"\r\n", c_len);
l = buffer_insert(&s->buffer, s->body_offset - 2, hdr, l);
if (l == -1)
return AISL_MALLOC_ERROR;
} else {
aisl_stream_close_headers(s);
}
s->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
}
s->state = AISL_STREAM_STATE_DONE;
s->flags |= FLAG_FLUSHED;
return AISL_SUCCESS;
}
static int32_t
aisl_stream_verify_header(AislStream stream, const char *key, const char *value)
{
if (stream->state < AISL_STREAM_STATE_SEND_HEADER) {
if (aisl_start_response(stream) != AISL_SUCCESS)
return -1;
} else if (stream->state > AISL_STREAM_STATE_SEND_HEADER) {
return 0;
}
if (!(stream->flags & FLAG_CONNECTION_HEADER_SENT)) {
if (str_cmpi(key, "connection")==0) {
stream->flags |= FLAG_CONNECTION_HEADER_SENT;
if (value) {
aisl_client_set_keepalive(stream->client,
(str_cmpi(value, "keepalive") == 0));
}
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_TYPE_HEADER_SENT)) {
if (str_cmpi(key, "content-type") == 0) {
stream->flags |= FLAG_CONTENT_TYPE_HEADER_SENT;
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (str_cmpi(key, "content-length") == 0) {
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
return 1;
}
}
if (!(stream->flags & FLAG_CONTENT_LENGTH_HEADER_SENT)) {
if (str_cmpi(key, "content-length")==0) {
stream->flags |= FLAG_CONTENT_LENGTH_HEADER_SENT;
return 1;
}
}
return 1;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header(AislStream stream, const char *key, const char *value)
{
int32_t result;
if ( (result = aisl_stream_verify_header( stream, key, value )) != 1)
return result;
result = buffer_append_printf(&stream->buffer, "%s: %s\r\n", key, value);
return result;
/* For debug purposes
if ( (pch = str_printf("%s: %s\r\n", key, value)) != NULL )
{
ret = strlen(pch);
if ( buffer_insert(
&stream->buffer,
stream->end_of_headers,
pch,
ret
) == -1 )
{
ret = -1;
}
else
stream->end_of_headers += ret;
free(pch);
}
else
ret = -1;
return ret;
*/
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header_printf(AislStream stream, const char *key, const char *format, ...)
{
int32_t result;
va_list args;
va_start(args, format);
result = aisl_header_vprintf( stream, key, format, args );
va_end(args);
return result;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_header_vprintf(AislStream stream,
const char *key,
const char *format,
va_list args)
{
int32_t result, l;
if ( (result = aisl_stream_verify_header( stream, key, NULL )) != 1)
return result;
result = buffer_append_printf( &stream->buffer, "%s: ", key );
if (result != -1) {
l = buffer_append_vprintf( &stream->buffer, format, args );
if (l != -1) {
result += l;
if ((l = buffer_append(&stream->buffer, "\r\n", 2)) != -1) {
result += l;
return result;
}
}
}
return -1;
}
__attribute__ ((visibility ("default") ))
int
aisl_printf(AislStream stream, const char *format, ...)
{
int result;
va_list arg;
va_start(arg, format);
result = aisl_vprintf(stream, format, arg);
va_end(arg);
return result;
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_vprintf(AislStream stream, const char *format, va_list args)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
return buffer_append_vprintf(&stream->buffer, format, args);
}
__attribute__ ((visibility ("default") ))
int32_t
aisl_write(AislStream stream, const char *data, int32_t d_len)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
if (d_len == -1)
d_len = strlen(data);
return buffer_append(&stream->buffer, data, d_len);
}
__attribute__ ((visibility ("default") ))
int
aisl_puts(const char *str, AislStream stream)
{
if (stream->state < AISL_STREAM_STATE_SEND_BODY) {
if (aisl_stream_close_headers(stream) != AISL_SUCCESS)
return -1;
}
return aisl_write( stream, str, -1);
}
__attribute__ ((visibility ("default") ))
AislInstance
aisl_get_instance(AislStream stream)
{
return stream->client->server->instance;
}
__attribute__ ((visibility ("default") ))
void
aisl_set_output_event(AislStream stream, bool value)
{
if (value)
stream->flags |= FLAG_OUTPUT_CHUNKED;
else if (stream->flags & FLAG_OUTPUT_CHUNKED)
stream->flags &= ~FLAG_OUTPUT_CHUNKED;
}
__attribute__ ((visibility ("default") ))
bool
aisl_get_output_event(AislStream stream)
{
return (stream->flags & FLAG_OUTPUT_CHUNKED);
}
+106
View File
@@ -0,0 +1,106 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file dummy.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/#ifndef AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3
#define AISL_STREAM_H_A9EC6601_34B2_4F3E_B631_EEDA8B6EF0D3
#include <aisl/types.h>
#include <aisl/stream.h>
#include "buffer.h"
#define AISL_STREAM(x) ((AislStream) x)
typedef enum
{
AISL_STREAM_STATE_IDLE
, AISL_STREAM_STATE_WAIT_HEADER
, AISL_STREAM_STATE_WAIT_BODY
, AISL_STREAM_STATE_READY
, AISL_STREAM_STATE_SEND_HEADER
, AISL_STREAM_STATE_SEND_BODY
, AISL_STREAM_STATE_DONE
} AislStreamState;
struct aisl_stream {
struct buffer buffer;
AislClient client;
void *u_ptr;
uint64_t content_length;
int32_t head_offset;
int32_t body_offset;
int id;
int flags;
AislHttpResponse http_response;
AislStreamState state;
};
AislStream
aisl_stream_new(AislClient client, int id);
void
aisl_stream_free(AislStream stream);
int32_t
aisl_stream_get_buffer_space(AislStream stream);
int32_t
aisl_stream_get_buffer_size(AislStream stream);
char *
aisl_stream_get_data(AislStream stream, int32_t *p_length);
void
aisl_stream_shift(AislStream stream, int32_t offset);
bool
aisl_stream_is_done(AislStream stream);
AislStreamState
aisl_stream_get_state(AislStream stream);
void
aisl_stream_set_request(AislStream stream,
AislHttpMethod http_method,
const char *path,
const char *query );
void
aisl_stream_set_header(AislStream stream, const char *key, const char *value);
int
aisl_stream_set_end_of_headers(AislStream stream);
int
aisl_stream_set_body(AislStream stream, const char *data, int32_t size);
#endif /* !AISL_STREAM_H */
+58
View File
@@ -0,0 +1,58 @@
/******************************************************************************
*
* 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>
#if AISL_WITH_STRINGIFIERS == 1
__attribute__ ((visibility ("default") ))
const char *
aisl_status_to_string(AislStatus 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(AislEvent evt_code)
{
switch(evt_code) {
case AISL_EVENT_SERVER_READY: return "SERVER READY";
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_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