cleaning
authorhgn <hgodden00@gmail.com>
Thu, 22 Aug 2024 23:45:40 +0000 (00:45 +0100)
committerhgn <hgodden00@gmail.com>
Thu, 22 Aug 2024 23:45:40 +0000 (00:45 +0100)
build.sh [new file with mode: 0755]
build3.sh [new file with mode: 0755]
http.h
server.c

diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
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 (executable)
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 0a205edb9b29a63bd49bd4e948d1c972dd17e252..c3684745f5faa88c9117aee57eb9b52d8f740033 100644 (file)
--- 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; i<len; i ++ )
    {
@@ -86,12 +140,13 @@ static void http_request_admit_char( struct http_request *req,
 /* 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;
    }
 
@@ -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" );
+}
index f1ddca6b674a5b9b96783cf4bb5d9e7337666848..13b0d60e144733747d622baaa7b135e6d2774aff 100644 (file)
--- 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; i<MAX_CLIENTS; i ++ )
@@ -64,181 +63,252 @@ int main(int argc, char *argv[])
    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 ++ )