Summary Files Commits Branches @commit @diff @stats
path: root / src / http
diff options
context:
space:
mode:
Commit74630b4bfe1aa887ef57ee57b22241d030b38d76 (patch)
Author
 Syndamia <kamen@syndamia.com>
2025-11-15 21:37:37 +0200
Committer
 Syndamia <kamen@syndamia.com>
2025-11-15 21:37:37 +0200
Files3d39c585df1551d69fdb83980c010ec4b579f211 /src/http
tergiv-74630b4bfe1aa887ef57ee57b22241d030b38d76.tar
tergiv-74630b4bfe1aa887ef57ee57b22241d030b38d76.tar.gz
tergiv-74630b4bfe1aa887ef57ee57b22241d030b38d76.zip
Parent3bca23eaaa49fa01f60ecbc5e0a329d29f088506 (diff)
feat!: Initial web server base implementation
5 files changed, 239 insertions, 0 deletions
-rw-r--r--src/http/callback_http.c136
-rw-r--r--src/http/callback_http.h38
-rw-r--r--src/http/http_headers.h17
-rw-r--r--src/http/request_method.h30
-rw-r--r--src/http/response_code.h18
diff --git a/src/http/callback_http.c b/src/http/callback_http.c
new file mode 100644
index 0000000..5b2b944
--- /dev/null
+++ b/src/http/callback_http.c
@@ -0,0 +1,136 @@
+#include "callback_http.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../utils/strings.h"
+#include "http_headers.h"
+#include "request_method.h"
+#include "response_code.h"
+
+static const char* SEPS = " \n\r";
+#define return_bad_request() { \
+ args->output = "HTTP/1.1 400 Bad Request"; \
+ args->output_size = 24; \
+ return; \
+ }
+
+static void append_header(enum HttpHeader header, const char* value, const char** parts_array, size_t *ind);
+
+void callback_http(callback_args* args) {
+ if (args->extra == NULL) {
+ printf("No HTTP callbacks were set!");
+ return;
+ }
+
+ http_callbacks *callbacks = args->extra;
+
+ /* Parse input request */
+
+ client_message in_message = {
+ .method = RM_UNKNOWN,
+ .url = NULL,
+ .version = NULL,
+ .headers = { 0 },
+ };
+
+ /** Request method **/
+
+ char* word = strtok(args->input, SEPS);
+ if (word == NULL) return_bad_request()
+
+ for (enum RequestMethod rm = RM_GET; rm < RM_UNKNOWN; rm++) {
+ if (streq(REQUEST_METHOD_NAME[rm], word)) {
+ in_message.method = rm;
+ break;
+ }
+ }
+
+ if (in_message.method == RM_UNKNOWN)
+ return_bad_request()
+
+ /** URL **/
+
+ word = strtok(NULL, SEPS);
+ if (word == NULL) return_bad_request()
+
+ in_message.url = word;
+
+ /** HTTP version **/
+
+ word = strtok(NULL, SEPS);
+ if (word == NULL || !streq("HTTP/1.1", word))
+ return_bad_request()
+
+ in_message.version = word;
+
+ /** Headers **/
+
+ word = strtok(NULL, ":\n\r");
+
+ while(word != NULL) {
+ enum HttpHeader he = 0;
+
+ while (he < HE_LENGTH) {
+ if (streq(HTTP_HEADER_NAME[he], word))
+ break;
+ ++he;
+ }
+ word = strtok(NULL, "\n\r");
+
+ if (word == NULL) break;
+
+ if (he < HE_LENGTH) {
+ while (*word == ' ') ++word;
+ in_message.headers[he] = word;
+ }
+
+ word = strtok(NULL, ":\n\r");
+ }
+
+ /* Respond to request */
+
+ response_message out_message = {
+ .response_code = RC_500_INTERNAL_SERVER_ERRROR,
+
+ .headers = { 0 },
+
+ .body = NULL,
+ .body_size = 0,
+ .free_body = 0,
+ };
+
+ if (in_message.method < RM_UNKNOWN)
+ callbacks->all_callbacks[in_message.method](&in_message, &out_message);
+ else
+ printf("Unsupported method!");
+
+ const char* response[1024] = { 0 };
+ size_t rind = 0;
+
+ response[rind++] = "HTTP/1.1 ";
+ response[rind++] = RESPONSE_CODE_NAME[out_message.response_code];
+ response[rind++] = "\n";
+
+ for (enum HttpHeader he = 0; he < HE_LENGTH; he++) {
+ if (out_message.headers[he] != NULL)
+ append_header(he, out_message.headers[he], response, &rind);
+ }
+
+ response[rind++] = "\n";
+
+ if (out_message.body != NULL && out_message.body_size > 0)
+ response[rind++] = out_message.body;
+
+ args->output = concat_array(response, rind, &args->output_size);
+ args->free_output = 1;
+
+ if (out_message.free_body)
+ free(out_message.body);
+}
+
+static void append_header(enum HttpHeader header, const char* value, const char** parts_array, size_t *ind) {
+ parts_array[(*ind)++] = HTTP_HEADER_NAME[header];
+ parts_array[(*ind)++] = ": ";
+ parts_array[(*ind)++] = value;
+ parts_array[(*ind)++] = "\n";
+}
diff --git a/src/http/callback_http.h b/src/http/callback_http.h
new file mode 100644
index 0000000..5f38c86
--- /dev/null
+++ b/src/http/callback_http.h
@@ -0,0 +1,38 @@
+#ifndef __TERGIV_CALLBACK_HTTP
+#define __TERGIV_CALLBACK_HTTP
+
+#include "../sockets/socket_server.h"
+#include "request_method.h"
+#include "response_code.h"
+#include "http_headers.h"
+
+void callback_http(callback_args* args);
+
+typedef struct client_message {
+ enum RequestMethod method;
+ char* url;
+ char* version;
+ char* headers[HE_LENGTH];
+} client_message;
+
+typedef struct response_message {
+ enum ResponseCode response_code;
+
+ char* headers[HE_LENGTH];
+
+ char* body;
+ size_t body_size;
+ int free_body;
+} response_message;
+
+typedef struct http_callbacks {
+ union {
+ struct {
+ void (*get_callback)(client_message* in_message, response_message* out_message);
+ };
+ void (*all_callbacks[RM_UNKNOWN])(client_message* in_message, response_message* out_message);
+ };
+
+} http_callbacks;
+
+#endif /* __TERGIV_CALLBACK_HTTP */
diff --git a/src/http/http_headers.h b/src/http/http_headers.h
new file mode 100644
index 0000000..0e83dfd
--- /dev/null
+++ b/src/http/http_headers.h
@@ -0,0 +1,17 @@
+#ifndef __TERGIV_HTTP_HEADERS
+#define __TERGIV_HTTP_HEADERS
+
+enum HttpHeader {
+ HE_Host = 0,
+ HE_Content_Length,
+ HE_Content_Type,
+ HE_LENGTH,
+};
+
+static const char* HTTP_HEADER_NAME[] = {
+ "Host",
+ "Content-Length",
+ "Content-Type",
+};
+
+#endif /* __TERGIV_HTTP_HEADERS */
diff --git a/src/http/request_method.h b/src/http/request_method.h
new file mode 100644
index 0000000..32ec13e
--- /dev/null
+++ b/src/http/request_method.h
@@ -0,0 +1,30 @@
+#ifndef __TERGIV_REQUEST_METHOD
+#define __TERGIV_REQUEST_METHOD
+
+enum RequestMethod {
+ RM_GET = 0,
+ RM_HEAD,
+ RM_OPTIONS,
+ RM_TRACE,
+ RM_PUT,
+ RM_DELETE,
+ RM_POST,
+ RM_PATCH,
+ RM_CONNECT,
+ RM_UNKNOWN
+};
+
+static const char* REQUEST_METHOD_NAME[] = {
+ "GET",
+ "HEAD",
+ "OPTIONS",
+ "TRACE",
+ "PUT",
+ "DELETE",
+ "POST",
+ "PATCH",
+ "CONNECT",
+ 0
+};
+
+#endif /* __TERGIV_REQUEST_METHOD */
diff --git a/src/http/response_code.h b/src/http/response_code.h
new file mode 100644
index 0000000..f5cd191
--- /dev/null
+++ b/src/http/response_code.h
@@ -0,0 +1,18 @@
+#ifndef __TERGIV_RESPONSE_CODE
+#define __TERGIV_RESPONSE_CODE
+
+enum ResponseCode {
+ RC_200_OK,
+ RC_403_FORBIDDEN,
+ RC_404_NOT_FOUND,
+ RC_500_INTERNAL_SERVER_ERRROR,
+ RC_LENGTH
+};
+
+static const char* RESPONSE_CODE_NAME[] = {
+ "200 OK",
+ "403 FORBIDDEN",
+ "404 NOT_FOUND",
+};
+
+#endif /* __TERGIV_RESPONSE_CODE */