#include #include #include #include #include #include #include #ifndef AISL_WITHOUT_SSL #include #endif #include #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->statestreams, 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)); }