--- /dev/null
+struct http_request
+{
+ char token[ 128 ];
+ int token_len;
+
+ enum http_method
+ {
+ k_http_method_get,
+ k_http_method_post
+ }
+ method;
+
+ char resource[ 128 ];
+ int resource_len;
+
+ char host[ 64 ];
+ int host_len;
+
+ char user_agent[ 64 ];
+ int user_agent_len;
+
+ enum http_connection
+ {
+ k_http_connection_unknown,
+ k_http_connection_close,
+ k_http_connection_keepalive,
+ }
+ connection;
+
+ enum parse_state
+ {
+ k_parse_await,
+ k_parse_state_method,
+ k_parse_state_url,
+ k_parse_state_lf,
+ k_parse_state_lf_eom,
+ k_parse_state_csp,
+ k_parse_state_protocol,
+ k_parse_state_header_key,
+ 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
+ }
+ state;
+
+ enum header_key
+ {
+ k_header_key_unknown,
+ k_header_key_user_agent,
+ k_header_key_host,
+ k_header_key_connection,
+ }
+ header_key;
+};
+
+unsigned int http_strdjb2( const char *str, int len )
+{
+ unsigned int hash = 5381, c;
+
+ for( int i=0; i<len; i ++ )
+ {
+ hash = ((hash << 5) + hash) + str[i];
+ }
+
+ return hash;
+}
+
+static void http_request_admit_char( struct http_request *req,
+ char *buf, int buf_len,
+ int *buf_ptr, char c )
+{
+ int i = *buf_ptr;
+
+ if( i < buf_len )
+ {
+ buf[ i ++ ] = c;
+ }
+
+ *buf_ptr = i;
+}
+
+/* returns 0 if fail */
+static int http_request_parse_char( struct http_request *req, char c )
+{
+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;
+ return 0;
+ }
+
+ enum parse_state state = req->state;
+
+ if( state == k_parse_await )
+ {
+ if( c == '\r' || c == '\n' || c == ' ' )
+ {
+ /* ignore whitespace until we get a real character */
+ }
+ else if( c >= 'A' && c <= 'Z' )
+ {
+ req->state = k_parse_state_method;
+ goto 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;
+ }
+
+ return 1;
+ }
+
+ if( state == k_parse_state_method )
+ {
+ if( c >= 'A' && c <= 'Z' )
+ {
+ http_request_admit_char( req, req->token, sizeof(req->token),
+ &req->token_len, c );
+ }
+ else
+ {
+ if( c == ' ' )
+ {
+ req->state = k_parse_state_url;
+
+ if( !strncmp( req->token, "GET", req->token_len ) )
+ {
+ req->method = k_http_method_get;
+ }
+ else if( !strncmp( req->token, "POST", req->token_len ) )
+ {
+ req->method = k_http_method_post;
+ }
+ else
+ {
+ fprintf( stderr, "(parser:method) Unknown method (%.*s)\n",
+ req->token_len, req->token );
+ req->state = k_parse_state_error_method;
+ }
+ }
+ else
+ {
+ fprintf( stderr, "(parser:method) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
+ req->state = k_parse_state_error_syntax;
+ }
+ }
+ }
+ else if( state == k_parse_state_url )
+ {
+ if( c == ' ' )
+ {
+ req->state = k_parse_state_protocol;
+ }
+ else if( c < '!' )
+ {
+ fprintf( stderr, "(parser:url) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
+ req->state = k_parse_state_error_syntax;
+ }
+ else
+ {
+ http_request_admit_char( req, req->resource, sizeof(req->resource),
+ &req->resource_len, c );
+ }
+ }
+ else if( state == k_parse_state_protocol )
+ {
+ if( c == '\r' )
+ {
+ req->state = k_parse_state_lf;
+ req->token_len = 0;
+ }
+ else if( c >= '!' )
+ {
+ http_request_admit_char( req, req->token, sizeof(req->token),
+ &req->token_len, c );
+ }
+ else
+ {
+ fprintf( stderr, "(parser:protocol) Syntax, unexpected char '0x%x'\n", (unsigned int)c );
+ req->state = k_parse_state_error_syntax;
+ }
+ }
+ else if( state == k_parse_state_lf || state == k_parse_state_lf_eom )
+ {
+ if( c == '\n' )
+ {
+ 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;
+ }
+ }
+ else if( state == k_parse_state_header_key )
+ {
+ if( c == ':' )
+ {
+ req->state = k_parse_state_csp;
+
+ if( !strncmp( req->token, "Host", req->token_len ) )
+ {
+ req->header_key = k_header_key_host;
+ }
+ else if( !strncmp( req->token, "Connection", req->token_len ) )
+ {
+ req->header_key = k_header_key_connection;
+ }
+ else if( !strncmp( req->token, "User-Agent", req->token_len ) )
+ {
+ req->header_key = k_header_key_user_agent;
+ }
+ else
+ {
+ req->header_key = k_header_key_unknown;
+ }
+
+ req->token_len = 0;
+ }
+ else if( c == '\r' )
+ {
+ req->state = k_parse_state_lf_eom;
+ }
+ else
+ {
+ if( c > ' ' )
+ {
+ http_request_admit_char( req, req->token, sizeof(req->token),
+ &req->token_len, c );
+ }
+ else
+ {
+ fprintf( stderr, "(parser:header_key) Syntax, unexpected '%x'\n", (unsigned int)c );
+ req->state = k_parse_state_error_syntax;
+ }
+ }
+ }
+ else if( state == k_parse_state_header_value )
+ {
+ if( c == '\r' )
+ {
+ if( req->header_key == k_header_key_connection )
+ {
+ if( !strncmp( req->token, "close", req->token_len ) )
+ {
+ req->connection = k_http_connection_close;
+ }
+ else if( !strncmp( req->token, "keep-alive", req->token_len ) )
+ {
+ req->connection = k_http_connection_keepalive;
+ }
+
+ req->token_len = 0;
+ }
+
+ req->state = k_parse_state_lf;
+ }
+ else
+ {
+ if( c >= ' ' )
+ {
+ if( req->header_key == k_header_key_host )
+ {
+ http_request_admit_char( req, req->host, sizeof(req->host),
+ &req->host_len, c );
+ }
+ else if( req->header_key == k_header_key_connection )
+ {
+ http_request_admit_char( req, req->token, sizeof(req->token),
+ &req->token_len, c );
+ }
+ 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 );
+ }
+ else if( req->header_key == k_header_key_unknown )
+ {
+ /* into the void.. :) */
+ }
+ }
+ else
+ {
+ fprintf( stderr, "(parser:header_value) Syntax, unexpected '%x'\n", (unsigned int)c );
+ req->state = k_parse_state_error_syntax;
+ }
+ }
+ }
+ else if( state == k_parse_state_csp )
+ {
+ 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;
+ }
+ }
+ else
+ return 0;
+
+ if( req->state >= k_parse_state_errors ) return 0;
+ else return 1;
+}
+
+/* return value
+ * -1 if parse failure
+ * number of bytes parsed
+ */
+static int http_request_parse( struct http_request *req, char *buf, int len )
+{
+ for( int i=0; i<len; i ++ )
+ {
+ if( !http_request_parse_char( req, buf[i] ) )
+ return -1;
+
+ if( req->state == k_parse_state_complete )
+ return i;
+ }
+
+ return len;
+}
--- /dev/null
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <poll.h>
+#include <signal.h>
+
+#include "http.h"
+
+const char *WEBSITE =
+"HTTP/1.0 200 OK\r\n"
+"Server: u461\r\n"
+"Content-Type: text/html\r\n"
+"\r\n"
+"<html><body><h1>HELLO ITS A WEBSITE</h1></body></html>\r\n";
+
+#define SERVER_HEADER "Server: u461\r\n"
+#define HTTP_ERROR( X ) \
+ "HTTP/1.0 " X "\r\n" SERVER_HEADER "Connection: close\r\n\r\n"
+
+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" );
+
+#define MAX_CLIENTS 10
+#define REQUEST_MAX_SIZE 1024
+
+struct client
+{
+ int connfd, total_request_bytes;
+ struct http_request request;
+}
+_clients[ MAX_CLIENTS ];
+
+int _run_server = 1;
+
+static void int_handle( int sig )
+{
+ _run_server = 0;
+ printf( "Ok stopping..\n" );
+}
+
+int main(int argc, char *argv[])
+{
+ signal( SIGINT, int_handle );
+
+ int listenfd;
+ struct sockaddr_in serv_addr = {0};
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ _clients[i].connfd = -1;
+ }
+
+ listenfd = socket( AF_INET, SOCK_STREAM, 0 );
+
+ if( listenfd == -1 )
+ {
+ fprintf( stderr, "socket() error: %s\n", strerror(errno) );
+ return 1;
+ }
+
+ if( fcntl( listenfd, F_SETFL, O_NONBLOCK ) == -1 )
+ {
+ fprintf( stderr, "fcntl() error: %s\n", strerror(errno) );
+ return 2;
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
+ serv_addr.sin_port = htons( 1521 );
+
+ if( bind( listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) == -1 )
+ {
+ fprintf( stderr, "bind() error: %s\n", strerror(errno) );
+ return 3;
+ }
+
+ if( listen( listenfd, 10 ) == -1 )
+ {
+ fprintf( stderr, "listen() error: %s\n", strerror(errno) );
+ return 4;
+ }
+
+ while( _run_server )
+ {
+ /* accepting clients */
+ {
+ int fd = accept( listenfd, (struct sockaddr*)NULL, NULL );
+ if( fd == -1 )
+ {
+ if( !( errno == EAGAIN || errno == EWOULDBLOCK ) )
+ {
+ 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 )
+ {
+ 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 );
+ }
+ }
+ }
+
+ /* Handle incoming data */
+ {
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+
+ 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", i );
+ close( client->connfd );
+ client->connfd = -1;
+ }
+ else if( len == -1 )
+ {
+ if( errno == EAGAIN || errno == EWOULDBLOCK )
+ {
+ /* No incoming data read */
+ continue;
+ }
+ else
+ {
+ fprintf( stderr, "recv() error: %s\n", strerror(errno) );
+ close( client->connfd );
+ client->connfd = -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 )
+ {
+ 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;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ sleep(1);
+ }
+
+ for( int i=0; i<MAX_CLIENTS; i ++ )
+ {
+ struct client *client = &_clients[ i ];
+ if( client->connfd == -1 ) continue;
+ close( client->connfd );
+ }
+
+ close( listenfd );
+}