aboutsummaryrefslogtreecommitdiffstats
path: root/src/blogc-runserver
diff options
context:
space:
mode:
Diffstat (limited to 'src/blogc-runserver')
-rw-r--r--src/blogc-runserver/httpd-utils.c4
-rw-r--r--src/blogc-runserver/httpd-utils.h2
-rw-r--r--src/blogc-runserver/httpd.c72
-rw-r--r--src/blogc-runserver/httpd.h3
-rw-r--r--src/blogc-runserver/main.c24
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);