@ -9,6 +9,7 @@
# include <sys/socket.h>
# include <unistd.h>
# include "auto_buf.h"
# include "debuglog.h"
# include "epoll_tag.h"
# include "http_parser.h"
@ -20,13 +21,6 @@
# include "route.h"
# include "tcp_session.h"
# include "tenant.h"
# include "vec.h"
# define HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE (PAGE_SIZE)
# define HTTP_SESSION_RESPONSE_HEADER_CAPACITY 256
# define u8 uint8_t
VEC ( u8 )
enum http_session_state
{
@ -52,12 +46,11 @@ struct http_session {
int socket ;
struct http_parser http_parser ;
struct http_request http_request ;
struct vec_u8 request_buffer ;
char response_header [ HTTP_SESSION_RESPONSE_HEADER_CAPACITY ] ;
size_t response_header_length ;
struct auto_buf request_buffer ;
struct auto_buf response_header ;
size_t response_header_written ;
struct vec_u8 response_buffer ;
size_t response_b uffer _written;
struct auto_buf response_body ;
size_t response_b ody _written;
struct tenant * tenant ; /* Backlink required when read blocks on listener core */
struct route * route ; /* Backlink required to handle http metrics */
uint64_t request_arrival_timestamp ;
@ -104,11 +97,11 @@ http_session_init(struct http_session *session, int socket_descriptor, const str
http_session_parser_init ( session ) ;
int rc = vec_u8 _init( & session - > request_buffer , HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE ) ;
int rc = auto_buf _init( & session - > request_buffer ) ;
if ( rc < 0 ) return - 1 ;
/* Defer allocating response until we've matched a route */
session - > response_buffer . buffer = NULL ;
/* Defer initializing response_body until we've matched a route */
auto_buf_init ( & session - > response_header ) ;
session - > state = HTTP_SESSION_INITIALIZED ;
@ -116,14 +109,16 @@ http_session_init(struct http_session *session, int socket_descriptor, const str
}
static inline int
http_session_init_response_b uffer ( struct http_session * session , size_t capacity )
http_session_init_response_b ody ( struct http_session * session )
{
assert ( session ! = NULL ) ;
assert ( session - > response_buffer . buffer = = NULL ) ;
assert ( session - > response_body . data = = NULL ) ;
assert ( session - > response_body . size = = 0 ) ;
assert ( session - > response_body_written = = 0 ) ;
int rc = vec_u8_init ( & session - > response_buffer , capacity ) ;
int rc = auto_buf_init( & session - > response_bod y) ;
if ( rc < 0 ) {
vec_u8 _deinit( & session - > request_buffer ) ;
auto_buf _deinit( & session - > request_buffer ) ;
return - 1 ;
}
@ -158,8 +153,9 @@ http_session_deinit(struct http_session *session)
{
assert ( session ) ;
vec_u8_deinit ( & session - > request_buffer ) ;
vec_u8_deinit ( & session - > response_buffer ) ;
auto_buf_deinit ( & session - > request_buffer ) ;
auto_buf_deinit ( & session - > response_header ) ;
auto_buf_deinit ( & session - > response_body ) ;
}
static inline void
@ -177,8 +173,7 @@ http_session_free(struct http_session *session)
* @ param status_code
*/
static inline void
http_session_set_response_header ( struct http_session * session , int status_code , const char * content_type ,
size_t content_length )
http_session_set_response_header ( struct http_session * session , int status_code )
{
assert ( session ! = NULL ) ;
assert ( status_code > = 200 & & status_code < = 599 ) ;
@ -187,20 +182,31 @@ http_session_set_response_header(struct http_session *session, int status_code,
/* We might not have actually matched a route */
if ( likely ( session - > route ! = NULL ) ) { http_route_total_increment ( & session - > route - > metrics , status_code ) ; }
int rc = fputs ( http_header_build ( status_code ) , session - > response_header . handle ) ;
assert ( rc ! = EOF ) ;
if ( status_code = = 200 ) {
session - > response_header_length = snprintf ( session - > response_header ,
HTTP_SESSION_RESPONSE_HEADER_CAPACITY ,
HTTP_RESPONSE_200_TEMPLATE , content_type , content_length ) ;
} else {
size_t header_len = http_header_len ( status_code ) ;
size_t to_copy = HTTP_SESSION_RESPONSE_HEADER_CAPACITY < header_len
? HTTP_SESSION_RESPONSE_HEADER_CAPACITY
: header_len ;
strncpy ( session - > response_header , http_header_build ( status_code ) , to_copy ) ;
session - > response_header_length = to_copy ;
/* Make sure the response_body is flushed */
int rc = auto_buf_flush ( & session - > response_body ) ;
if ( unlikely ( rc ! = 0 ) ) { panic ( " response_body auto_buf failed to flush: %s \n " , strerror ( errno ) ) ; } ;
/* Technically fprintf can truncate, but I assume this won't happen with a memstream */
rc = fprintf ( session - > response_header . handle , HTTP_RESPONSE_CONTENT_TYPE ,
session - > route - > response_content_type ) ;
assert ( rc > 0 ) ;
rc = fprintf ( session - > response_header . handle , HTTP_RESPONSE_CONTENT_LENGTH ,
session - > response_body . size ) ;
assert ( rc > 0 ) ;
rc = fputs ( HTTP_RESPONSE_200_OK , session - > response_header . handle ) ;
assert ( rc ! = EOF ) ;
}
rc = fputs ( HTTP_RESPONSE_TERMINATOR , session - > response_header . handle ) ;
assert ( rc ! = EOF ) ;
rc = auto_buf_flush ( & session - > response_header ) ;
if ( unlikely ( rc ! = 0 ) ) { panic ( " response_header auto_buf failed to flush: %s \n " , strerror ( errno ) ) ; } ;
session - > response_takeoff_timestamp = __getcycles ( ) ;
}
@ -226,11 +232,11 @@ http_session_send_response_header(struct http_session *session, void_star_cb on_
| | session - > state = = HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED ) ;
session - > state = HTTP_SESSION_SENDING_RESPONSE_HEADER ;
while ( session - > response_header _length > session - > response_header_written ) {
while ( session - > response_header . size > session - > response_header_written ) {
ssize_t sent =
tcp_session_send ( session - > socket ,
( const char * ) & session - > response_header [session - > response_header_written ] ,
session - > response_header _length - session - > response_header_written , on_eagain ,
( const char * ) & session - > response_header .data [session - > response_header_written ] ,
session - > response_header . size - session - > response_header_written , on_eagain ,
session ) ;
if ( sent < 0 ) {
return ( int ) sent ;
@ -259,16 +265,18 @@ http_session_send_response_body(struct http_session *session, void_star_cb on_ea
| | session - > state = = HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED ) ;
session - > state = HTTP_SESSION_SENDING_RESPONSE_BODY ;
while ( session - > response_buffer_written < session - > response_buffer . length ) {
/* Assumption: Already flushed in order to write content-length to header */
// TODO: Test if body is empty
while ( session - > response_body_written < session - > response_body . size ) {
ssize_t sent =
tcp_session_send ( session - > socket ,
( const char * ) & session - > response_buffer . buffer [ session - > response_buffer_written ] ,
session - > response_buffer . length - session - > response_buffer_written , on_eagain ,
session ) ;
( const char * ) & session - > response_body . data [ session - > response_body_written ] ,
session - > response_body . size - session - > response_body_written , on_eagain , session ) ;
if ( sent < 0 ) {
return ( int ) sent ;
} else {
session - > response_b uffer _written + = ( size_t ) sent ;
session - > response_b ody _written + = ( size_t ) sent ;
}
}
@ -276,45 +284,6 @@ http_session_send_response_body(struct http_session *session, void_star_cb on_ea
return 0 ;
}
static inline bool
http_session_request_buffer_is_full ( struct http_session * session )
{
return session - > request_buffer . length = = session - > request_buffer . capacity ;
}
static inline int
http_session_request_buffer_grow ( struct http_session * session )
{
/* We have not yet fully parsed the header, so we don't know content-length, so just grow
* ( double ) the buffer */
uint8_t * old_buffer = session - > request_buffer . buffer ;
if ( vec_u8_grow ( & session - > request_buffer ) ! = 0 ) {
debuglog ( " Failed to grow request buffer \n " ) ;
return - 1 ;
}
/* buffer moved, so invalidate to reparse */
if ( old_buffer ! = session - > request_buffer . buffer ) { http_session_parser_init ( session ) ; }
return 0 ;
}
static inline int
http_session_request_buffer_resize ( struct http_session * session , int required_size )
{
uint8_t * old_buffer = session - > request_buffer . buffer ;
if ( vec_u8_resize ( & session - > request_buffer , required_size ) ! = 0 ) {
debuglog ( " Failed to resize request vector to %d bytes \n " , required_size ) ;
return - 1 ;
}
/* buffer moved, so invalidate to reparse */
if ( old_buffer ! = session - > request_buffer . buffer ) { http_session_parser_init ( session ) ; }
return 0 ;
}
typedef void ( * http_session_cb ) ( struct http_session * ) ;
static inline ssize_t
@ -331,8 +300,8 @@ http_session_parse(struct http_session *session, ssize_t bytes_received)
# endif
size_t bytes_parsed =
http_parser_execute ( & session - > http_parser , settings ,
( const char * ) & session - > request_buffer . buffer [ session - > http_request . length_parsed ] ,
( size_t ) session - > request_buffer . length - session - > http_request . length_parsed ) ;
( const char * ) & session - > request_buffer . data [ session - > http_request . length_parsed ] ,
( size_t ) session - > request_buffer . size - session - > http_request . length_parsed ) ;
if ( session - > http_parser . http_errno ! = HPE_OK ) {
debuglog ( " Error: %s, Description: %s \n " ,
@ -382,52 +351,46 @@ static inline int
http_session_receive_request ( struct http_session * session , void_star_cb on_eagain )
{
assert ( session ! = NULL ) ;
assert ( session - > request_buffer . capacity > 0 ) ;
assert ( session - > request_buffer . length < = session - > request_buffer . capacity ) ;
assert ( session - > request_buffer . handle ! = NULL ) ;
assert ( session - > state = = HTTP_SESSION_INITIALIZED | | session - > state = = HTTP_SESSION_RECEIVE_REQUEST_BLOCKED ) ;
session - > state = HTTP_SESSION_RECEIVING_REQUEST ;
int rc = 0 ;
while ( ! session - > http_request . message_end ) {
/* If we know the header size and content-length, resize exactly. Otherwise double */
if ( session - > http_request . header_end & & session - > http_request . body ) {
int header_size = ( uint8_t * ) session - > http_request . body - session - > request_buffer . buffer ;
int required_size = header_size + session - > http_request . body_length ;
if ( required_size > session - > request_buffer . capacity ) {
rc = http_session_request_buffer_resize ( session , required_size ) ;
if ( rc ! = 0 ) goto err_nobufs ;
}
} else if ( http_session_request_buffer_is_full ( session ) ) {
rc = http_session_request_buffer_grow ( session ) ;
if ( rc ! = 0 ) goto err_nobufs ;
}
struct http_request * http_request = & session - > http_request ;
int rc = 0 ;
char temp [ BUFSIZ ] ;
ssize_t bytes_received =
tcp_session_recv ( session - > socket ,
( char * ) & session - > request_buffer . buffer [ session - > request_buffer . length ] ,
session - > request_buffer . capacity - session - > request_buffer . length , on_eagain ,
session ) ;
while ( ! http_request - > message_end ) {
ssize_t bytes_received = tcp_session_recv ( session - > socket , temp , BUFSIZ , on_eagain , session ) ;
if ( unlikely ( bytes_received = = - EAGAIN ) )
goto err_eagain ;
else if ( unlikely ( bytes_received < 0 ) )
goto err ;
/* If we received an EOF before we were able to parse a complete HTTP message, request is malformed */
else if ( unlikely ( bytes_received = = 0 & & ! session- > http_request . message_end ) )
else if ( unlikely ( bytes_received = = 0 & & ! http_request - > message_end ) )
goto err ;
assert ( bytes_received > 0 ) ;
assert ( session - > request_buffer . length < session - > request_buffer . capacity ) ;
session - > request_buffer . length + = bytes_received ;
const char * old_buffer = session - > request_buffer . data ;
const ssize_t header_length = session - > request_buffer . size - http_request - > body_length_read ;
assert ( ! http_request - > header_end | | header_length > 0 ) ;
/* Write temp buffer to memstream */
fwrite ( temp , 1 , bytes_received , session - > request_buffer . handle ) ;
/* fflush memstream managed buffer */
fflush ( session - > request_buffer . handle ) ;
ssize_t bytes_parsed = http_session_parse ( session , bytes_received ) ;
if ( bytes_parsed = = - 1 ) goto err ;
/* Update parser structure if buffer moved */
if ( old_buffer ! = session - > request_buffer . data ) {
http_request - > body = header_length ? session - > request_buffer . data + header_length : NULL ;
}
if ( http_session_parse ( session , bytes_received ) = = - 1 ) goto err ;
}
assert ( session - > http_request . message_end = = true ) ;
assert ( http_request- > message_end = = true ) ;
session - > state = HTTP_SESSION_RECEIVED_REQUEST ;
http_session_log_query_params ( session ) ;
@ -456,26 +419,10 @@ static inline int
http_session_write_response ( struct http_session * session , const uint8_t * source , size_t n )
{
assert ( session ) ;
assert ( session - > response_b uffer. buffer ! = NULL ) ;
assert ( session - > response_b ody. handle ! = NULL ) ;
assert ( source ) ;
int rc = 0 ;
size_t buffer_remaining = session - > response_buffer . capacity - session - > response_buffer . length ;
if ( buffer_remaining < n ) {
rc = vec_u8_resize ( & session - > response_buffer , session - > response_buffer . capacity + n - buffer_remaining ) ;
if ( rc ! = 0 ) goto DONE ;
}
assert ( session - > response_buffer . capacity - session - > response_buffer . length > = n ) ;
memcpy ( & session - > response_buffer . buffer [ session - > response_buffer . length ] , source , n ) ;
session - > response_buffer . length + = n ;
rc = n ;
DONE :
return rc ;
return fwrite ( source , 1 , n , session - > response_body . handle ) ;
}
static inline void