461 lines
9.2 KiB
C
461 lines
9.2 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
|
||
|
#include <aisl/aisl.h>
|
||
|
#include "client.h"
|
||
|
#include "stream.h"
|
||
|
#include "parser.h"
|
||
|
#include "globals.h"
|
||
|
#include "handle.h"
|
||
|
|
||
|
#ifndef OUTPUT_BUFFER_SIZE
|
||
|
#define OUTPUT_BUFFER_SIZE 4096
|
||
|
#endif
|
||
|
|
||
|
/* -------------------------------------------------------------------------- */
|
||
|
|
||
|
void
|
||
|
client_close(client_t self)
|
||
|
{
|
||
|
close(self->fd);
|
||
|
/* will provide double free
|
||
|
if (self->ssl)
|
||
|
SSL_free(self->ssl);
|
||
|
*/
|
||
|
shutdown(self->fd, SHUT_RDWR);
|
||
|
self->fd=-1;
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------- */
|
||
|
|
||
|
static bool
|
||
|
client_input(client_t self)
|
||
|
{
|
||
|
int l;
|
||
|
parser_status_t p_status = PARSER_PENDING;
|
||
|
char *ptr;
|
||
|
buffer_t buffer = self->server->owner->buffer;
|
||
|
|
||
|
stream_t s = list_index(self->streams, self->istream);
|
||
|
|
||
|
|
||
|
if (self->ssl)
|
||
|
{
|
||
|
if (self->flags & CLIENT_FLAG_HANDSHAKE)
|
||
|
{
|
||
|
if ( (l = SSL_accept(self->ssl)) != 1 )
|
||
|
{
|
||
|
l = SSL_get_error(self->ssl, l);
|
||
|
|
||
|
if (l == SSL_ERROR_WANT_READ || l == SSL_ERROR_WANT_WRITE)
|
||
|
return false;
|
||
|
|
||
|
client_close(self);
|
||
|
fprintf(stderr, "SSL handshake fail: %s\n", ERR_error_string(l, NULL));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
self->flags ^= CLIENT_FLAG_HANDSHAKE;
|
||
|
}
|
||
|
|
||
|
l = SSL_read(self->ssl, buffer->data, buffer->size) ;
|
||
|
}
|
||
|
else
|
||
|
l = recv( self->fd, buffer->data, buffer->size, 0);
|
||
|
|
||
|
if (l>0)
|
||
|
{
|
||
|
if( buffer_add(s->buffer, buffer->data, l) == BUFFER_EOB )
|
||
|
{
|
||
|
client_close(self);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ptr = s->buffer->data;
|
||
|
l = s->buffer->size;
|
||
|
|
||
|
/* parse next data chunk */
|
||
|
while ( p_status == PARSER_PENDING )
|
||
|
{
|
||
|
switch(s->state)
|
||
|
{
|
||
|
case STREAM_REQUEST_METHOD:
|
||
|
p_status = parse_request_method(self, &ptr, &l);
|
||
|
break;
|
||
|
case STREAM_REQUEST_PATH:
|
||
|
p_status = parse_request_path(self, &ptr, &l);
|
||
|
break;
|
||
|
case STREAM_REQUEST_PROTOCOL:
|
||
|
p_status = parse_request_protocol(self, &ptr, &l);
|
||
|
break;
|
||
|
|
||
|
case STREAM_REQUEST_HEADER_KEY:
|
||
|
p_status = parse_request_header_key(self, &ptr, &l);
|
||
|
break;
|
||
|
case STREAM_REQUEST_HEADER_VALUE:
|
||
|
p_status = parse_request_header_value(self, &ptr, &l);
|
||
|
break;
|
||
|
|
||
|
case STREAM_REQUEST_CONTENT:
|
||
|
p_status = parse_request_content(self, &ptr, &l);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
p_status = PARSER_FINISHED;
|
||
|
/* this is error actually */
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (p_status == PARSER_FAILED)
|
||
|
{
|
||
|
/* reply Bad Request here */
|
||
|
client_close(self);
|
||
|
}
|
||
|
else if (l)
|
||
|
{
|
||
|
buffer_shift(s->buffer, s->buffer->size-l); /* reset buffer */
|
||
|
}
|
||
|
/*
|
||
|
else
|
||
|
buffer_clear(s->buffer, 0);*/
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else if (l<0)
|
||
|
{
|
||
|
if (self->ssl)
|
||
|
{
|
||
|
if (SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_READ)
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(errno == EWOULDBLOCK)
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* both: client disconnect + on read error */
|
||
|
/* todo: raise client error here */
|
||
|
client_close(self);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
client_output(client_t self)
|
||
|
{
|
||
|
if (self->fd < 0)
|
||
|
{
|
||
|
fprintf(stderr, "[aisl] assertion !(client->fd<0) failed\n");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
stream_t s;
|
||
|
int l;
|
||
|
|
||
|
s = list_index(self->streams, self->ostream);
|
||
|
|
||
|
/*
|
||
|
if (!s->c_length_unknown && s->buffer && s->buffer->len)
|
||
|
buffer_move(gBuffer, s->buffer);
|
||
|
*/
|
||
|
|
||
|
/* while stream is not flushed, we should raise event */
|
||
|
if(s->flags & STREAM_FLAG_OUTPUT_CHUNKED)
|
||
|
{
|
||
|
/* 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
|
||
|
* */
|
||
|
size_t bsz = s->buffer->size;
|
||
|
|
||
|
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 = bsz - s->buffer->size) > OUTPUT_BUFFER_SIZE / 2 )
|
||
|
aisl_raise_event( self->server->owner, s, AISL_STREAM_OUTPUT, l);
|
||
|
}
|
||
|
|
||
|
if (s->buffer->size == 0)
|
||
|
return false;
|
||
|
|
||
|
l = (self->ssl) ?
|
||
|
SSL_write(self->ssl, s->buffer->data, s->buffer->size) :
|
||
|
send( self->fd, s->buffer->data, s->buffer->size, 0);
|
||
|
|
||
|
if (l > 0)
|
||
|
{
|
||
|
buffer_shift(s->buffer, l);
|
||
|
if (s->state == STREAM_RESPONSE_READY && /* flushed */
|
||
|
s->buffer->size == 0) /* all sent */
|
||
|
{
|
||
|
buffer_clear(s->buffer, 0);
|
||
|
|
||
|
/* data has been sent */
|
||
|
/*
|
||
|
if (self->protocol == AISL_HTTP_2_0)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
else*/
|
||
|
if (self->flags & CLIENT_FLAG_KEEPALIVE)
|
||
|
{
|
||
|
list_remove(self->streams, s);
|
||
|
stream_free(s);
|
||
|
|
||
|
s = stream_new((struct sockaddr_in *) self, self->next_id++, STREAM_REQUEST_METHOD );
|
||
|
list_append(self->streams, s);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
client_close(self);
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/* l < 0 */
|
||
|
if (self->ssl)
|
||
|
{
|
||
|
if ( SSL_get_error(self->ssl, l) == SSL_ERROR_WANT_WRITE )
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (errno == EWOULDBLOCK)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
client_close(self);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
client_touch(client_t self)
|
||
|
{
|
||
|
bool result = false;
|
||
|
stream_t s;
|
||
|
|
||
|
/* input */
|
||
|
s = list_index(self->streams, self->istream);
|
||
|
|
||
|
if ((self->protocol==AISL_HTTP_2_0 || s->state<STREAM_REQUEST_READY) &&
|
||
|
(client_input(self)) ) result = true;
|
||
|
|
||
|
/* output */
|
||
|
s = list_index(self->streams, self->ostream);
|
||
|
|
||
|
if ( s->flags & (STREAM_FLAG_OUTPUT_READY | STREAM_FLAG_OUTPUT_CHUNKED) )
|
||
|
result = client_output(self);
|
||
|
|
||
|
/* update timestamp */
|
||
|
if (result)
|
||
|
self->timestamp = time(NULL);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* constructor -------------------------------------------------------------- */
|
||
|
|
||
|
static client_t
|
||
|
client_new( int fd, struct sockaddr_in * addr )
|
||
|
{
|
||
|
client_t self;
|
||
|
stream_t stream;
|
||
|
|
||
|
if ( !(self = calloc(1, sizeof(struct client))) )
|
||
|
goto finally;
|
||
|
|
||
|
memcpy(&self->address, addr, sizeof(struct sockaddr_in));
|
||
|
|
||
|
self->fd = fd;
|
||
|
self->next_id = 2;
|
||
|
/*
|
||
|
self->istream = 0;
|
||
|
self->ostream = 0;
|
||
|
* UTPUT
|
||
|
*/
|
||
|
self->protocol = AISL_HTTP_1_0;
|
||
|
self->timestamp = time(NULL);
|
||
|
self->flags = CLIENT_FLAG_KEEPALIVE | CLIENT_FLAG_HANDSHAKE;
|
||
|
|
||
|
if ( !(self->streams = list_new(AISL_MIN_STREAMS)) )
|
||
|
goto except;
|
||
|
|
||
|
if ( !(stream = stream_new((struct sockaddr_in *)self, 0, STREAM_REQUEST_METHOD)) )
|
||
|
goto e_stream;
|
||
|
|
||
|
if (list_append(self->streams, stream) == -1)
|
||
|
goto e_append;
|
||
|
|
||
|
goto finally;
|
||
|
|
||
|
e_append:
|
||
|
stream_free(stream);
|
||
|
|
||
|
e_stream:
|
||
|
list_free(self->streams, NULL);
|
||
|
|
||
|
except:
|
||
|
free(self);
|
||
|
self=NULL;
|
||
|
|
||
|
finally:
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
aisl_status_t
|
||
|
client_accept(client_t * p_self, server_t server)
|
||
|
{
|
||
|
aisl_status_t result;
|
||
|
const char * e_detail = NULL;
|
||
|
int fd;
|
||
|
struct sockaddr_in addr;
|
||
|
socklen_t len = sizeof(struct sockaddr_in);
|
||
|
SSL * ssl = NULL;
|
||
|
SSL_CTX * ssl_ctx;
|
||
|
|
||
|
*p_self = NULL;
|
||
|
|
||
|
if ( (fd = accept(server->fd, (struct sockaddr *) &addr, &len)) < 0 )
|
||
|
{
|
||
|
if (errno != EWOULDBLOCK)
|
||
|
{
|
||
|
result = AISL_SYSCALL_ERROR;
|
||
|
e_detail = strerror(errno);
|
||
|
goto raise;
|
||
|
}
|
||
|
|
||
|
result = AISL_IDLE;
|
||
|
goto finally;
|
||
|
}
|
||
|
|
||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||
|
if (flags == -1)
|
||
|
{
|
||
|
result = AISL_SYSCALL_ERROR;
|
||
|
e_detail = strerror(errno);
|
||
|
goto raise;
|
||
|
}
|
||
|
|
||
|
flags |= O_NONBLOCK;
|
||
|
|
||
|
if (fcntl(fd, F_SETFL, flags) != 0)
|
||
|
{
|
||
|
result = AISL_SYSCALL_ERROR;
|
||
|
e_detail = strerror(errno);
|
||
|
goto raise;
|
||
|
}
|
||
|
|
||
|
if (server->flags & AISL_FLAG_SSL)
|
||
|
{
|
||
|
if ( !(ssl_ctx = aisl_get_ssl_ctx( server->owner, NULL )) )
|
||
|
goto except;
|
||
|
|
||
|
if ( !(ssl = SSL_new(ssl_ctx)) )
|
||
|
{
|
||
|
e_detail = "SSL_new";
|
||
|
result = AISL_EXTCALL_ERROR;
|
||
|
goto except;
|
||
|
}
|
||
|
|
||
|
SSL_set_fd(ssl, fd);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
ssl = NULL;
|
||
|
|
||
|
if ( !(*p_self = client_new(fd, &addr)) )
|
||
|
{
|
||
|
result = AISL_MALLOC_ERROR;
|
||
|
e_detail = "client_t";
|
||
|
goto raise;
|
||
|
}
|
||
|
|
||
|
(*p_self)->server = server;
|
||
|
(*p_self)->ssl = ssl;
|
||
|
result = AISL_SUCCESS;
|
||
|
|
||
|
goto finally;
|
||
|
|
||
|
except:
|
||
|
close(fd);
|
||
|
if (ssl)
|
||
|
SSL_free(ssl);
|
||
|
|
||
|
raise:
|
||
|
aisl_raise_event(
|
||
|
server->owner,
|
||
|
server,
|
||
|
AISL_SERVER_ERROR,
|
||
|
server->flags,
|
||
|
e_detail
|
||
|
);
|
||
|
|
||
|
finally:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* destructor --------------------------------------------------------------- */
|
||
|
|
||
|
void
|
||
|
client_free(client_t self)
|
||
|
{
|
||
|
if (self->fd > -1)
|
||
|
close(self->fd);
|
||
|
|
||
|
if (self->ssl)
|
||
|
SSL_free(self->ssl);
|
||
|
|
||
|
list_free(self->streams, (list_destructor_t)stream_free);
|
||
|
|
||
|
aisl_raise_event(
|
||
|
self->server->owner,
|
||
|
self->server,
|
||
|
AISL_CLIENT_DISCONNECT,
|
||
|
self
|
||
|
);
|
||
|
|
||
|
free(self);
|
||
|
}
|
||
|
|
||
|
/* check if communication time with client is expired ----------------------- */
|
||
|
|
||
|
bool
|
||
|
client_is_timeout(client_t self)
|
||
|
{
|
||
|
bool result = false;
|
||
|
stream_t s;
|
||
|
if (self->protocol == AISL_HTTP_2_0)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s = list_index(self->streams, self->istream);
|
||
|
if ( (s->state < STREAM_REQUEST_READY) && /* still waiting for data */
|
||
|
(time(NULL)-self->timestamp > AISL_MAX_CLIENT_SILENCE) ) result=true;
|
||
|
}
|
||
|
|
||
|
if (result)
|
||
|
client_close(self);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------- */
|