diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/blogc-runserver/httpd-utils.c | 4 | ||||
| -rw-r--r-- | src/blogc-runserver/httpd-utils.h | 2 | ||||
| -rw-r--r-- | src/blogc-runserver/httpd.c | 72 | ||||
| -rw-r--r-- | src/blogc-runserver/httpd.h | 3 | ||||
| -rw-r--r-- | src/blogc-runserver/main.c | 24 | 
5 files changed, 85 insertions, 20 deletions
diff --git a/src/blogc-runserver/httpd-utils.c b/src/blogc-runserver/httpd-utils.c index e935668..8d9c024 100644 --- a/src/blogc-runserver/httpd-utils.c +++ b/src/blogc-runserver/httpd-utils.c @@ -24,9 +24,9 @@ br_readline(int socket)      char buffer[READLINE_BUFFER_SIZE];      ssize_t len; -    while ((len = read(socket, buffer, READLINE_BUFFER_SIZE)) != -1) { +    while ((len = read(socket, buffer, READLINE_BUFFER_SIZE)) > 0) {          for (ssize_t i = 0; i < len; i++) { -            if (buffer[i] == '\r' || buffer[i] == '\n') +            if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\0')                  goto end;              bc_string_append_c(rv, buffer[i]);          } diff --git a/src/blogc-runserver/httpd-utils.h b/src/blogc-runserver/httpd-utils.h index b9af852..af4c57c 100644 --- a/src/blogc-runserver/httpd-utils.h +++ b/src/blogc-runserver/httpd-utils.h @@ -9,7 +9,7 @@  #ifndef _HTTPD_UTILS_H  #define _HTTPD_UTILS_H -#define READLINE_BUFFER_SIZE 1024 +#define READLINE_BUFFER_SIZE 256  char* br_readline(int socket);  int br_hextoi(const char c); diff --git a/src/blogc-runserver/httpd.c b/src/blogc-runserver/httpd.c index c725c1f..c10a365 100644 --- a/src/blogc-runserver/httpd.c +++ b/src/blogc-runserver/httpd.c @@ -31,6 +31,18 @@  #define LISTEN_BACKLOG 100 +typedef struct { +    pthread_t thread; +    bool initialized; +} thread_data_t; + +typedef struct { +    size_t thread_id; +    int socket; +    char *ip; +    const char *docroot; +} request_data_t; +  static void  error(int socket, int status_code, const char *error) @@ -48,28 +60,31 @@ error(int socket, int status_code, const char *error)  } -typedef struct { -    int socket; -    const char *docroot; -} request_data_t; - -  static void*  handle_request(void *arg)  {      request_data_t *req = arg; +    size_t thread_id = req->thread_id;      int client_socket = req->socket; +    char *ip = req->ip;      const char *docroot = req->docroot;      free(arg);      char *conn_line = br_readline(client_socket); +    if (conn_line == NULL || conn_line[0] == '\0') +        goto point0; + +    unsigned short status_code = 200; +      char **pieces = bc_str_split(conn_line, ' ', 3);      if (bc_strv_length(pieces) != 3) { +        status_code = 400;          error(client_socket, 400, "Bad Request");          goto point1;      }      if (strcmp(pieces[0], "GET") != 0) { +        status_code = 405;          error(client_socket, 405, "Method Not Allowed");          goto point1;      } @@ -79,6 +94,7 @@ handle_request(void *arg)      bc_strv_free(pieces2);      if (path == NULL) { +        status_code = 400;          error(client_socket, 400, "Bad Request");          goto point1;      } @@ -88,23 +104,27 @@ handle_request(void *arg)      free(abs_path);      if (real_path == NULL) { +        status_code = 404;          error(client_socket, 404, "Not Found");          goto point1;      }      char *real_root = realpath(docroot, NULL);      if (real_root == NULL) { +        status_code = 500;          error(client_socket, 500, "Internal Server Error");          goto point2;      }      if (0 != strncmp(real_root, real_path, strlen(real_root))) { +        status_code = 404;          error(client_socket, 404, "Not Found");          goto point3;      }      struct stat st;      if (0 > stat(real_path, &st)) { +        status_code = 404;          error(client_socket, 404, "Not Found");          goto point3;      } @@ -115,6 +135,7 @@ handle_request(void *arg)          char *found = br_mime_guess_index(real_path);          if (found == NULL) { +            status_code = 403;              error(client_socket, 403, "Forbidden");              goto point3;          } @@ -128,6 +149,7 @@ handle_request(void *arg)      }      if (0 != access(real_path, F_OK)) { +        status_code = 500;          error(client_socket, 500, "Internal Server Error");          goto point3;      } @@ -140,6 +162,7 @@ handle_request(void *arg)              "Location: %s/\r\n"              "Connection: close\r\n"              "\r\n", path); +        status_code = 302;          if (write(client_socket, tmp, strlen(tmp)) == -1) {              // do nothing, just avoid warnig          } @@ -151,6 +174,7 @@ handle_request(void *arg)      bc_error_t *err = NULL;      char* contents = bc_file_get_contents(real_path, false, &len, &err);      if (err != NULL) { +        status_code = 500;          error(client_socket, 500, "Internal Server Error");          bc_error_free(err);          goto point3; @@ -176,15 +200,24 @@ point3:  point2:      free(real_path);  point1: +    fprintf(stderr, "[Thread-%zu] %s - - \"%s\" %d\n", thread_id + 1, +        ip, conn_line, status_code);      bc_strv_free(pieces); +point0: +    free(ip);      close(client_socket);      return NULL;  }  int -br_httpd_run(const char *host, unsigned short port, const char *docroot) +br_httpd_run(const char *host, unsigned short port, const char *docroot, +    size_t max_threads)  { +    thread_data_t threads[max_threads]; +    for (size_t i = 0; i < max_threads; i++) +        threads[i].initialized = false; +      int server_socket = socket(AF_INET, SOCK_STREAM, 0);      if (server_socket == -1) {          fprintf(stderr, "Failed to open server socket: %s\n", strerror(errno)); @@ -226,13 +259,15 @@ br_httpd_run(const char *host, unsigned short port, const char *docroot)      }      fprintf(stderr, -        " * Running on http://%s:%d/\n" +        " * Running on http://%s:%d/ (max threads: %zu)\n"          "\n"          "WARNING!!! This is a development server, DO NOT RUN IT IN PRODUCTION!\n" -        "\n", host, port); +        "\n", host, port, max_threads);      socklen_t len = sizeof(struct sockaddr_in); +    size_t current_thread = 0; +      while (1) {          struct sockaddr_in client_addr;          int client_socket = accept(server_socket, @@ -244,16 +279,29 @@ br_httpd_run(const char *host, unsigned short port, const char *docroot)          }          request_data_t *arg = malloc(sizeof(request_data_t)); +        arg->thread_id = current_thread;          arg->socket = client_socket; +        arg->ip = bc_strdup(inet_ntoa(client_addr.sin_addr));          arg->docroot = docroot; -        // this isn't really safe. the server can be easily DDoS'ed. -        pthread_t thread; -        if (pthread_create(&thread, NULL, handle_request, arg) != 0) { +        if (threads[current_thread].initialized) { +            if (pthread_join(threads[current_thread].thread, NULL) != 0) { +                fprintf(stderr, "Failed to join thread\n"); +                rv = 1; +                goto cleanup; +            } +        } + +        if (pthread_create(&(threads[current_thread].thread), NULL, handle_request, arg) != 0) {              fprintf(stderr, "Failed to create thread\n");              rv = 1;              goto cleanup;          } + +        threads[current_thread++].initialized = true; + +        if (current_thread >= max_threads) +            current_thread = 0;      }  cleanup: diff --git a/src/blogc-runserver/httpd.h b/src/blogc-runserver/httpd.h index a59a467..7a948ca 100644 --- a/src/blogc-runserver/httpd.h +++ b/src/blogc-runserver/httpd.h @@ -9,6 +9,7 @@  #ifndef _HTTPD_H  #define _HTTPD_H -int br_httpd_run(const char *host, unsigned short port, const char *docroot); +int br_httpd_run(const char *host, unsigned short port, const char *docroot, +    size_t max_threads);  #endif /* _HTTPD_H */ diff --git a/src/blogc-runserver/main.c b/src/blogc-runserver/main.c index d12fc99..afc9db6 100644 --- a/src/blogc-runserver/main.c +++ b/src/blogc-runserver/main.c @@ -23,7 +23,7 @@ print_help(void)  {      printf(          "usage:\n" -        "    blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n" +        "    blogc-runserver [-h] [-v] [-t HOST] [-p PORT] [-m THREADS] DOCROOT\n"          "                    - A simple HTTP server to test blogc websites.\n"          "\n"          "positional arguments:\n" @@ -33,14 +33,15 @@ print_help(void)          "    -h            show this help message and exit\n"          "    -v            show version and exit\n"          "    -t HOST       set server listen address (default: 127.0.0.1)\n" -        "    -p PORT       set server listen port (default: 8080)\n"); +        "    -p PORT       set server listen port (default: 8080)\n" +        "    -m THREADS    set maximum number of threads to spawn (default: 20)\n");  }  static void  print_usage(void)  { -    printf("usage: blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n"); +    printf("usage: blogc-runserver [-h] [-v] [-t HOST] [-p PORT] [-m THREADS] DOCROOT\n");  } @@ -53,6 +54,7 @@ main(int argc, char **argv)      char *host = NULL;      char *docroot = NULL;      unsigned short port = 8080; +    size_t max_threads = 20;      unsigned int args = 0; @@ -77,6 +79,12 @@ main(int argc, char **argv)                      else                          port = strtoul(argv[++i], NULL, 10);                      break; +                case 'm': +                    if (argv[i][2] != '\0') +                        max_threads = strtoul(argv[i] + 2, NULL, 10); +                    else +                        max_threads = strtoul(argv[++i], NULL, 10); +                    break;                  default:                      print_usage();                      fprintf(stderr, "blogc-runserver: error: invalid " @@ -106,10 +114,18 @@ main(int argc, char **argv)          goto cleanup;      } +    if (max_threads <= 0 || max_threads > 1000) { +        print_usage(); +        fprintf(stderr, "blogc-runserver: error: invalid value for -m. " +            "Must be integer > 0 and <= 1000\n"); +        rv = 2; +        goto cleanup; +    } +      if (host == NULL)          host = bc_strdup("127.0.0.1"); -    rv = br_httpd_run(host, port, docroot); +    rv = br_httpd_run(host, port, docroot, max_threads);  cleanup:      free(host);  | 
