{
char token[ 128 ];
int token_len;
+ int line, col;
enum http_method
{
k_http_method_get,
- k_http_method_post
+ k_http_method_post,
+ k_http_method_max,
}
method;
k_http_connection_unknown,
k_http_connection_close,
k_http_connection_keepalive,
+ k_http_connection_max
}
connection;
k_parse_state_header_value,
k_parse_state_body,
k_parse_state_complete,
- k_parse_state_errors,
- k_parse_state_error_out_of_range,
- k_parse_state_error_syntax,
- k_parse_state_error_method
+ k_parse_state_max
}
state;
+ enum parse_error
+ {
+ k_parse_error_none,
+ k_parse_error_out_of_range,
+ k_parse_error_syntax,
+ k_parse_error_method,
+ k_parse_error_max,
+ }
+ error;
+
enum header_key
{
k_header_key_unknown,
k_header_key_user_agent,
k_header_key_host,
k_header_key_connection,
+ k_header_key_max,
}
header_key;
};
+const char *ehttp_method_str[ k_http_method_max ] =
+{
+ [ k_http_method_get ] = "GET",
+ [ k_http_method_post ] = "POST",
+};
+
+const char *ehttp_connection_str[ k_http_connection_max ] =
+{
+ [ k_http_connection_unknown ] = "?",
+ [ k_http_connection_close ] = "close",
+ [ k_http_connection_keepalive ] = "keep-alive"
+};
+
+#define CON_BOLD "\033[1m"
+#define CON_NORM "\033[0m"
+
+#define BOLD( X ) CON_BOLD X CON_NORM
+
+const char *ehttp_parse_error[ k_parse_error_max ] =
+{
+ /* While parsing the request, there was .. */
+ [ k_parse_error_none ] = BOLD( "no error" ),
+ [ k_parse_error_out_of_range ] = "a " BOLD("character out of range"),
+ [ k_parse_error_syntax ] = "a " BOLD("syntax error"),
+ [ k_parse_error_method ] = "an " BOLD("invalid/unsupported method"),
+};
+
+const char *ehttp_parse_state[ k_parse_state_max ] =
+{
+ /* while ... */
+ [ k_parse_await ] = BOLD("waiting") " for a request",
+ [ k_parse_state_method ] = "parsing the " BOLD("method name"),
+ [ k_parse_state_url ] = "parsing the " BOLD("url/resource"),
+ [ k_parse_state_lf ] = "parsing a " BOLD("CRLF"),
+ [ k_parse_state_lf_eom ] = "parsing the " BOLD("trailing CRLF"),
+ [ k_parse_state_csp ] = "expecting a " BOLD("header key seperator"),
+ [ k_parse_state_protocol ] = "reading the " BOLD("HTTP protocol version"),
+ [ k_parse_state_header_key ] = "reading a " BOLD("header key"),
+ [ k_parse_state_header_value ] = "reading a " BOLD("header value"),
+ [ k_parse_state_body ] = "reading the " BOLD("HTTP body"),
+ [ k_parse_state_complete ] = BOLD("complete")
+};
+
unsigned int http_strdjb2( const char *str, int len )
{
- unsigned int hash = 5381, c;
+ unsigned int hash = 5381;
for( int i=0; i<len; i ++ )
{
/* returns 0 if fail */
static int http_request_parse_char( struct http_request *req, char c )
{
+ req->col ++;
+
try_again:;
if( c > 127 || c == 0 )
{
- fprintf( stderr, "(parser) Generic out of range error\n" );
- req->state = k_parse_state_error_out_of_range;
+ req->error = k_parse_error_out_of_range;
return 0;
}
{
if( c == '\r' || c == '\n' || c == ' ' )
{
+ if( c == '\n' )
+ {
+ req->line ++;
+ req->col = 0;
+ }
/* ignore whitespace until we get a real character */
+ return 1;
}
else if( c >= 'A' && c <= 'Z' )
{
}
else
{
- fprintf( stderr, "(parser:await) While waiting we got an unwanted character 'x%x'\n", c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
+ return 0;
}
-
- return 1;
}
if( state == k_parse_state_method )
}
else
{
- fprintf( stderr, "(parser:method) Unknown method (%.*s)\n",
- req->token_len, req->token );
- req->state = k_parse_state_error_method;
+ req->error = k_parse_error_method;
}
}
else
{
- fprintf( stderr, "(parser:method) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
}
}
else if( c < '!' )
{
- fprintf( stderr, "(parser:url) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
else
{
}
else
{
- fprintf( stderr, "(parser:protocol) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
else if( state == k_parse_state_lf || state == k_parse_state_lf_eom )
{
if( c == '\n' )
{
+ req->line ++;
+ req->col = 0;
+
if( state == k_parse_state_lf_eom ) req->state = k_parse_state_complete;
else req->state = k_parse_state_header_key;
}
else
{
- fprintf( stderr, "(parser:lf|lf_eom) Syntax, expected \\n, got '%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
else if( state == k_parse_state_header_key )
}
else
{
- fprintf( stderr, "(parser:header_key) Syntax, unexpected '%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
}
}
else if( req->header_key == k_header_key_user_agent )
{
- http_request_admit_char( req, req->user_agent, sizeof(req->user_agent),
- &req->user_agent_len, c );
+ http_request_admit_char( req, req->user_agent,
+ sizeof(req->user_agent),
+ &req->user_agent_len, c );
}
else if( req->header_key == k_header_key_unknown )
{
}
else
{
- fprintf( stderr, "(parser:header_value) Syntax, unexpected '%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
}
else if( state == k_parse_state_csp )
{
- if( c == ' ' ) req->state = k_parse_state_header_value;
+ if( c == ' ' )
+ {
+ req->state = k_parse_state_header_value;
+ }
else
{
- fprintf( stderr, "(parser:header_key.csp) Syntax, expected ' ', got '%x'\n", (unsigned int)c );
- req->state = k_parse_state_error_syntax;
+ req->error = k_parse_error_syntax;
}
}
else
return 0;
- if( req->state >= k_parse_state_errors ) return 0;
- else return 1;
+ return !(req->error);
}
/* return value
return len;
}
+
+static void http_fprintf_maybe_trunc( FILE *fp, const char *buf,
+ int buf_len, int len )
+{
+ printf( "%.*s %s", len, buf, buf_len == len? BOLD("[TRUNC]"): "" );
+}
+
+static void http_fprintf_request_info( FILE *fp, struct http_request *req )
+{
+ fprintf( fp, "------------------------------\n" );
+ fprintf( fp, "HTTP Request info ( %s )", ehttp_method_str[ req->method ] );
+
+ fprintf( fp, "\n Host:" );
+ http_fprintf_maybe_trunc( fp, req->host, sizeof(req->host), req->host_len );
+
+ fprintf( fp, "\n User-agent:" );
+ http_fprintf_maybe_trunc( fp, req->user_agent, sizeof(req->user_agent),
+ req->user_agent_len );
+
+ fprintf( fp, "\n Resource:" );
+ http_fprintf_maybe_trunc( fp, req->resource, sizeof(req->resource),
+ req->resource_len );
+ fprintf( fp, "\n------------------------------\n" );
+}
const char *k_response_parsefail = HTTP_ERROR( "500 Internal Server Error" );
const char *k_response_toobig = HTTP_ERROR( "413 Payload Too Large" );
const char *k_response_temp = HTTP_ERROR( "501 Not Implemented" );
+const char *k_response_timeout = HTTP_ERROR( "408 Request Timeout" );
#define MAX_CLIENTS 10
#define REQUEST_MAX_SIZE 1024
struct client
{
- int connfd, total_request_bytes;
+ int connfd, total_request_bytes, ticks_used;
struct http_request request;
}
_clients[ MAX_CLIENTS ];
-
+int _ticks_without_action = 0;
int _run_server = 1;
static void int_handle( int sig )
printf( "Ok stopping..\n" );
}
-int main(int argc, char *argv[])
+int open_listen_socket( int port )
{
- signal( SIGINT, int_handle );
-
int listenfd;
struct sockaddr_in serv_addr = {0};
for( int i=0; i<MAX_CLIENTS; i ++ )
if( listenfd == -1 )
{
fprintf( stderr, "socket() error: %s\n", strerror(errno) );
- return 1;
+ return -1;
}
if( fcntl( listenfd, F_SETFL, O_NONBLOCK ) == -1 )
{
fprintf( stderr, "fcntl() error: %s\n", strerror(errno) );
- return 2;
+ return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
- serv_addr.sin_port = htons( 1521 );
+ serv_addr.sin_port = htons( port );
if( bind( listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) == -1 )
{
fprintf( stderr, "bind() error: %s\n", strerror(errno) );
- return 3;
+ return -1;
}
if( listen( listenfd, 10 ) == -1 )
{
fprintf( stderr, "listen() error: %s\n", strerror(errno) );
- return 4;
+ return -1;
}
- while( _run_server )
- {
- /* accepting clients */
+ return listenfd;
+}
+
+int _accept_any_new_client( int listenfd )
+{
+ int fd = accept( listenfd, (struct sockaddr*)NULL, NULL );
+ if( fd == -1 )
+ {
+ if( !( errno == EAGAIN || errno == EWOULDBLOCK ) )
{
- int fd = accept( listenfd, (struct sockaddr*)NULL, NULL );
- if( fd == -1 )
+ fprintf( stderr, "accept() warning: %s\n", strerror(errno) );
+ }
+ }
+ else
+ {
+ struct client *client = NULL;
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *c = &_clients[ i ];
+ if( c->connfd == -1 )
{
- if( !( errno == EAGAIN || errno == EWOULDBLOCK ) )
- {
- fprintf( stderr, "accept() warning: %s\n", strerror(errno) );
- }
+ client = c;
+ break;
}
- else
- {
- struct client *client = NULL;
- for( int i=0; i<MAX_CLIENTS; i ++ )
- {
- struct client *c = &_clients[ i ];
- if( c->connfd == -1 )
- {
- client = c;
- break;
- }
- }
+ }
- if( client )
- {
- memset( client, 0, sizeof(struct client) );
- client->connfd = fd;
- client->total_request_bytes = 0;
- printf( "Accepted new client\n" );
- }
- else
- {
- fprintf( stderr, "Server saturated, cannot accept new client.\n" );
- write( fd, k_response_saturated, strlen(k_response_saturated) );
- close( fd );
- }
- }
+ if( client )
+ {
+ memset( client, 0, sizeof(struct client) );
+ client->connfd = fd;
+ client->total_request_bytes = 0;
+ client->ticks_used = 0;
+
+ printf( "Accepted new client (%d)\n", fd );
+ return 1;
+ }
+ else
+ {
+ fprintf( stderr, "Server saturated, cannot accept new client.\n" );
+ write( fd, k_response_saturated, strlen(k_response_saturated) );
+ close( fd );
}
+ }
- /* Handle incoming data */
+ return 0;
+}
+
+/* returns 1 if timeout is passed. */
+int client_timeout_step( struct client *client )
+{
+ client->ticks_used ++;
+ if( client->ticks_used > 80 )
+ {
+ /* Future is now old man */
+ write( client->connfd, k_response_timeout, strlen(k_response_timeout) );
+ close( client->connfd );
+ client->connfd = -1;
+ return 1;
+ }
+ return 0;
+}
+
+/* returns -1 if error, 0 if no action/disconnect, 1 if recieved data/sent */
+int client_handle_requests( struct client *client )
+{
+ char recv_buf[ 512 ];
+ int len = recv( client->connfd, recv_buf, sizeof(recv_buf), 0 );
+
+ if( len == 0 )
+ {
+ /* Client did an orderly shutdown */
+ printf( "Client #%d disconnected\n", client->connfd );
+ close( client->connfd );
+ client->connfd = -1;
+ return -1;
+ }
+ else if( len == -1 )
+ {
+ if( errno == EAGAIN || errno == EWOULDBLOCK )
+ {
+ /* No incoming data read */
+ return 0;
+ }
+ else
{
- for( int i=0; i<MAX_CLIENTS; i ++ )
+ fprintf( stderr, "recv() error: %s\n", strerror(errno) );
+ close( client->connfd );
+ client->connfd = -1;
+ return -1;
+ }
+ }
+ else
+ {
+ //printf( "RAW BUF ------\n%.*s\n-----------\n", len, recv_buf );
+
+ int remaining = len;
+ while( remaining )
+ {
+ /* New data has been read off the socket */
+ int amt = http_request_parse( &client->request,
+ recv_buf + (len-remaining),
+ remaining );
+ if( amt == -1 )
{
- struct client *client = &_clients[ i ];
- if( client->connfd == -1 ) continue;
+ fprintf( stderr, "[#%d][http parser] There was %s while %s.\n"
+ " line: %d, column: %d\n",
+ client->connfd,
+ ehttp_parse_error[ client->request.error ],
+ ehttp_parse_state[ client->request.state ],
+ client->request.line, client->request.col );
- char recv_buf[ 512 ];
- int len = recv( client->connfd, recv_buf, sizeof(recv_buf), 0 );
+ write( client->connfd,
+ k_response_parsefail,
+ strlen(k_response_parsefail) );
+ close( client->connfd );
+ client->connfd = -1;
+ return -1;
+ }
+ else
+ {
+ remaining -= amt;
+ client->total_request_bytes += amt;
- if( len == 0 )
+ if( client->request.state == k_parse_state_complete )
{
- /* Client did an orderly shutdown */
- printf( "Client #%d disconnected\n", i );
- close( client->connfd );
- client->connfd = -1;
- }
- else if( len == -1 )
- {
- if( errno == EAGAIN || errno == EWOULDBLOCK )
+ http_fprintf_request_info( stdout, &client->request );
+
+ if( !strncmp( client->request.resource, "/",
+ client->request.resource_len ) )
{
- /* No incoming data read */
- continue;
+ printf( "Gave website :D\n" );
+ write( client->connfd, WEBSITE, strlen(WEBSITE) );
}
else
{
- fprintf( stderr, "recv() error: %s\n", strerror(errno) );
- close( client->connfd );
- client->connfd = -1;
+ fprintf( stderr, "Responding #%d with 501\n", client->connfd );
+ write( client->connfd,
+ k_response_temp, strlen(k_response_temp) );
}
+
+ client->total_request_bytes = 0;
+
+ /* reset parser */
+ memset( &client->request, 0, sizeof(struct http_request) );
+
+ close( client->connfd );
+ client->connfd = -1;
+ return 1;
}
else
{
- //printf( "RAW BUF ------\n%.*s\n-----------\n", len, recv_buf );
-
- int remaining = len;
-
- while( remaining )
+ if( client->total_request_bytes > 1024 )
{
- /* New data has been read off the socket */
- int amt = http_request_parse( &client->request,
- recv_buf + (len-remaining),
- remaining );
- if( amt == -1 )
- {
- fprintf( stderr, "Failed to parse request from client#%d (reason: %d)\n", i, client->request.state );
- write( client->connfd,
- k_response_parsefail,
- strlen(k_response_parsefail) );
- close( client->connfd );
- client->connfd = -1;
- break;
- }
- else
- {
- remaining -= amt;
- client->total_request_bytes += amt;
-
- if( client->request.state == k_parse_state_complete )
- {
- /* handle request... */
- printf( "Request info ( " );
- printf( "%s )\n", client->request.method == k_http_method_get? "GET": "POST" );
- printf( " Host: %.*s %s\n", client->request.host_len, client->request.host, client->request.host_len == sizeof(client->request.host)? "[TRUNC]": "" );
- printf( " User-agent: %.*s %s\n", client->request.user_agent_len, client->request.user_agent, client->request.user_agent_len == sizeof(client->request.user_agent)? "[TRUNC]": "" );
- printf( " Resource: %.*s %s\n", client->request.resource_len, client->request.resource, client->request.resource_len == sizeof(client->request.resource)? "[TRUNC]": "" );
-
-
- if( !strncmp( client->request.resource, "/",
- client->request.resource_len ) )
- {
- printf( "Gave website :D\n" );
- write( client->connfd, WEBSITE, strlen(WEBSITE) );
- }
- else
- {
- fprintf( stderr, "Responding #%d with 501\n", i );
- write( client->connfd,
- k_response_temp, strlen(k_response_temp) );
- }
-
- client->total_request_bytes = 0;
-
- /* reset parser */
- memset( &client->request, 0, sizeof(struct http_request) );
-
- close( client->connfd );
- client->connfd = -1;
- break;
- }
- else
- {
- if( client->total_request_bytes > 1024 )
- {
- fprintf( stderr, "Client#%d sent us too much\n", i );
- write( client->connfd,
- k_response_toobig, strlen(k_response_toobig) );
- close( client->connfd );
- client->connfd = -1;
- break;
- }
- }
- }
+ fprintf( stderr, "Client#%d sent too much\n", client->connfd );
+ write( client->connfd,
+ k_response_toobig, strlen(k_response_toobig) );
+ close( client->connfd );
+ client->connfd = -1;
+ return -1;
}
}
}
}
- sleep(1);
+ return 1;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ signal( SIGINT, int_handle );
+
+ int listenfd = open_listen_socket( 1521 );
+
+ if( listenfd == -1 )
+ return 1;
+
+ int ticks_without_action = 0;
+
+ while( _run_server )
+ {
+ ticks_without_action ++;
+
+ if( _accept_any_new_client( listenfd ) )
+ ticks_without_action = 0;
+
+ /* Handle incoming data */
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+ if( client_timeout_step( client ) ) continue;
+
+ int handled = client_handle_requests( client );
+
+ if( handled == 1 )
+ ticks_without_action = 0;
+ else if( handled == -1 )
+ continue;
+ }
+
+ /* frequency control */
+ if( ticks_without_action > 40 )
+ {
+ if( ticks_without_action == 41 )
+ {
+ printf( "Entering low frequency mode... (2 tick/s)\n" );
+ }
+
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 500000000; /* 0.5 s (2 tick) */
+ nanosleep( &spec, NULL );
+ }
+ else
+ {
+ struct timespec spec;
+ spec.tv_sec = 0;
+ spec.tv_nsec = 50000000; /* 0.05s (20 tick)*/
+ nanosleep( &spec, NULL );
+ }
}
for( int i=0; i<MAX_CLIENTS; i ++ )