From 20eb5052be1b7ebdf246bdf4d1e9d1bea229438a Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 23 Aug 2024 00:45:40 +0100 Subject: [PATCH] cleaning --- build.sh | 1 + build3.sh | 1 + http.h | 148 ++++++++++++++++++------ server.c | 340 ++++++++++++++++++++++++++++++++---------------------- 4 files changed, 321 insertions(+), 169 deletions(-) create mode 100755 build.sh create mode 100755 build3.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..46ef361 --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +clang -Wall -g -O0 -fsanitize=address server.c -o bin/server && bin/server diff --git a/build3.sh b/build3.sh new file mode 100755 index 0000000..9f36423 --- /dev/null +++ b/build3.sh @@ -0,0 +1 @@ +clang -Wall -O3 server.c -o bin/server && bin/server diff --git a/http.h b/http.h index 0a205ed..c368474 100644 --- a/http.h +++ b/http.h @@ -2,11 +2,13 @@ struct http_request { 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; @@ -24,6 +26,7 @@ struct http_request k_http_connection_unknown, k_http_connection_close, k_http_connection_keepalive, + k_http_connection_max } connection; @@ -40,26 +43,77 @@ struct http_request 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; icol ++; + 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; } @@ -101,7 +156,13 @@ try_again:; { 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' ) { @@ -110,11 +171,9 @@ try_again:; } 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 ) @@ -140,15 +199,12 @@ try_again:; } 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; } } } @@ -160,8 +216,7 @@ try_again:; } 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 { @@ -183,21 +238,22 @@ try_again:; } 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 ) @@ -238,8 +294,7 @@ try_again:; } 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; } } } @@ -279,8 +334,9 @@ try_again:; } 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 ) { @@ -289,25 +345,25 @@ try_again:; } 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 @@ -327,3 +383,27 @@ static int http_request_parse( struct http_request *req, char *buf, int len ) 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" ); +} diff --git a/server.c b/server.c index f1ddca6..13b0d60 100644 --- a/server.c +++ b/server.c @@ -29,17 +29,18 @@ const char *k_response_saturated = HTTP_ERROR( "503 Service Unavailable" ); 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 ) @@ -48,10 +49,8 @@ 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; iconnfd == -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; iconnfd == -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; iconnfd ); + 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; iconnfd == -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