diff --git a/Makefile b/Makefile index 7fa9ed9..2d37d01 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # ----- Variables for customization ----- CC = gcc # Compiler (you can change to clang if desired) -CFLAGS = -g2 -Wall -Wextra #-fsanitize=address # Standard flags + debugging & warnings +CFLAGS = -g2 -Wall -Wextra #-fsanitize=address,undefined # Standard flags + debugging & warnings LDFLAGS = # Linker flags, if any # ----- Automatic dependency tracking ----- @@ -19,4 +19,4 @@ server: $(OBJS) # Phony target for cleaning .PHONY: clean clean: - rm -f $(OBJS) + rm -f $(OBJS) server diff --git a/content/post.md b/content/post.md new file mode 100644 index 0000000..c12ab36 --- /dev/null +++ b/content/post.md @@ -0,0 +1,20 @@ +--- +title: Hello, World! +author: Michael +date: 2024-09-13 +abstract: This is my first post +--- + +this is a test +- oh yeah + +this is a quote: + +> sdfdsfsdfdsfdsfdsfds +> sfdsfdsfsfdsfds +```c +int main() +{ + return 0; +} +``` diff --git a/flake.nix b/flake.nix index 7b99041..a5c06a1 100644 --- a/flake.nix +++ b/flake.nix @@ -30,7 +30,9 @@ vcpkg vcpkg-tool ccls + man-pages bear + pandoc ] ++ (if system == "aarch64-darwin" then [ ] else [ gdb ]); }; }); diff --git a/header.c b/header.c deleted file mode 100644 index deec1d2..0000000 --- a/header.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "header.h" -#include - -struct Header header_constructor(char *key, char *value) { - struct Header header; - header.key = strdup(key); - header.value = strdup(value); - return header; -} diff --git a/header.h b/header.h deleted file mode 100644 index 0353739..0000000 --- a/header.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef HEADER_H -#define HEADER_H - -struct Header { - char *key; - char *value; -}; - -struct Header header_constructor(char *key, char *value); - -#endif // !HEADER_H diff --git a/main.c b/main.c index c24017c..200a09b 100644 --- a/main.c +++ b/main.c @@ -2,18 +2,8 @@ #include "server.h" #include -char *home(struct Request request) { - return read_file("public/index.html"); -} - -char *clicked(struct Request request) { - return read_file("public/clicked.html"); -} - int main(void) { - struct Server server = server_constructor("3000"); - add_route(&server, "GET", "/", home); - add_route(&server, "POST", "/clicked", clicked); + struct Server server = server_constructor("3000", "public"); launch(&server); return EXIT_SUCCESS; } diff --git a/public/.DS_Store b/public/.DS_Store new file mode 100644 index 0000000..fab088f Binary files /dev/null and b/public/.DS_Store differ diff --git a/public/clicked.html b/public/clicked.html deleted file mode 100644 index 3721df5..0000000 --- a/public/clicked.html +++ /dev/null @@ -1 +0,0 @@ -

see?

diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..fb93d0b Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.css b/public/index.css new file mode 100644 index 0000000..44375b3 --- /dev/null +++ b/public/index.css @@ -0,0 +1,10 @@ +html { + background: #0D1017; + color: #BFBDB6; + font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, "Nimbus Sans L", Roboto, Noto, "Segoe UI", Arial, Helvetica, "Helvetica Neue", sans-serif; +} + +body { + max-width: 760px; + margin: auto; +} diff --git a/public/index.html b/public/index.html index c9d14c9..a27212b 100644 --- a/public/index.html +++ b/public/index.html @@ -5,19 +5,11 @@ Michael Thomson - +

Michael Thomson

-

Welcome to my site! doesn't it have so much content? I know, right?

-

It even has reactivity!

-
- - -
diff --git a/public/post.html b/public/post.html new file mode 100644 index 0000000..1fffc33 --- /dev/null +++ b/public/post.html @@ -0,0 +1,31 @@ + + + + + + Hello, World! + + + +

Michael Thomson

+ +

Hello, World!

+

Michael

+

2024-09-13

+ +

this is a test - oh yeah

+

this is a quote:

+
+

sdfdsfsdfdsfdsfdsfds sfdsfdsfsfdsfds

+
+
int main()
+{
+    return 0;
+}
+ + diff --git a/request.c b/request.c index 3918d1c..2b5f2a2 100644 --- a/request.c +++ b/request.c @@ -1,32 +1,13 @@ #include "request.h" -#include "header.h" -#include "utils.h" #include -#include - -#define MAX_HEADER_NUM 128 +#include struct Request request_constructor(char *raw_request) { struct Request request; - request.method = strstrtok(raw_request, " "); - request.target = strstrtok(NULL, " "); - request.version = strstrtok(NULL, "\r\n"); - char *raw_headers = strstrtok(NULL, "\r\n\r\n"); - request.body = strstrtok(NULL, "\0"); - - int num_headers = 0; - struct Header headers[MAX_HEADER_NUM]; - char *header_name = strstrtok(raw_headers, ": "); - if (header_name) { - char *header_value; - do { - header_value = strstrtok(NULL, "\r\n"); - headers[num_headers++] = header_constructor(header_name, header_value); - } while ((header_name = strstrtok(NULL, ": "))); - } - - request.num_headers = num_headers; - request.headers = headers; - + request.method = (char *)malloc(12 * sizeof(char)); + request.target = (char *)malloc(128 * sizeof(char)); + request.version = (char *)malloc(12 * sizeof(char)); + sscanf(raw_request, "%s %s %s", request.method, request.target, request.version); + // TODO: parse headers return request; } diff --git a/request.h b/request.h index b16b9b1..c3e6305 100644 --- a/request.h +++ b/request.h @@ -1,15 +1,10 @@ #ifndef REQUEST_H #define REQUEST_H -#include "header.h" - struct Request { char *method; // GET, POST, ... char *target; // /, /blog, /blog/post1, ... char *version; // HTTP/1.1 - char *body; - int num_headers; - struct Header *headers; }; struct Request request_constructor(char *raw_request); diff --git a/response.c b/response.c index 148008e..38f0ca5 100644 --- a/response.c +++ b/response.c @@ -1,40 +1,11 @@ #include "response.h" -#include "header.h" #include #include #include -#define MAX_BUFFER_SIZE 2048 - -struct Response response_constructor(char *version, char *status, char *body) { +struct Response response_constructor(char *version, char *status) { struct Response response; - response.version = strdup(version); - response.status = strdup(status); - response.body = strdup(body); - response.num_headers = 0; - response.headers = NULL; + response.version = version; + response.status = status; return response; } - -void add_header(struct Response *response, char *key, char *value) { - struct Header header = header_constructor(key, value); - response->headers = realloc( - response->headers, sizeof(struct Header) * (response->num_headers + 1)); - response->headers[response->num_headers++] = header; -} - -void response_to_string(char *buf, struct Response *response) { - char headers[MAX_BUFFER_SIZE]; - headers[0] = '\0'; - - for (int i = 0; i < response->num_headers; i++) { - strcat(headers, response->headers[i].key); - strcat(headers, ": "); - strcat(headers, response->headers[i].value); - strcat(headers, "\r\n"); - } - - - snprintf(buf, MAX_BUFFER_SIZE, "%s %s\r\n%s\r\n%s", response->version, - response->status, headers, response->body); -} diff --git a/response.h b/response.h index 5c71aac..bb6c1e3 100644 --- a/response.h +++ b/response.h @@ -1,22 +1,11 @@ #ifndef RESPONSE_H #define RESPONSE_H -#define OK "200 OK" -#define NOT_FOUND "404 Not Found" -#define INTERNAL_SERVER_ERROR "500 Internal Server Error" - struct Response { char *version; char *status; - char *body; - int num_headers; - struct Header *headers; }; -struct Response response_constructor(char *version, char *status, char *body); - -void response_to_string(char *buf, struct Response *response); - -void add_header(struct Response *response, char *key, char *value); +struct Response response_constructor(char *version, char *status); #endif // !RESPONSE_H diff --git a/route.c b/route.c deleted file mode 100644 index bf1af16..0000000 --- a/route.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "route.h" - -struct Route route_constructor(char *target, char *method, - Route_Callback route_callback) { - struct Route route; - route.target = strdup(target); - route.method = strdup(method); - route.route_callback = route_callback; - return route; -} diff --git a/route.h b/route.h deleted file mode 100644 index ceba521..0000000 --- a/route.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ROUTE_H -#define ROUTE_H - -#include "request.h" - -typedef char *(*Route_Callback)(struct Request request); - -struct Route { - char *target; - char *method; - Route_Callback route_callback; -}; - -struct Route route_constructor(char *target, char *method, - Route_Callback route_callback); - -#endif // !ROUTE_H diff --git a/server b/server deleted file mode 100755 index b9198f5..0000000 Binary files a/server and /dev/null differ diff --git a/server.c b/server.c index 891f6fc..af2e46b 100644 --- a/server.c +++ b/server.c @@ -10,42 +10,20 @@ #include "request.h" #include "response.h" -#include "route.h" #include "server.h" #define MAX_BUFFER_SIZE 2048 -#define MAX_HEADER_NUM 48 int sockfd; -struct Server server_constructor(const char *PORT) { +struct Server server_constructor(const char *port, const char *static_path) { struct Server server; - server.PORT = PORT; - server.num_routes = 0; - server.routes = NULL; + server.port = port; + server.static_path = static_path; return server; } -void add_route(struct Server *server, char *method, char *target, - Route_Callback route_callback) { - struct Route route = route_constructor(target, method, route_callback); - server->routes = - realloc(server->routes, sizeof(struct Route) * (server->num_routes + 1)); - server->routes[server->num_routes++] = route; -} - -struct Route *match_route(struct Server *server, char *method, char *target) { - for (int i = 0; i < server->num_routes; i++) { - if (strcmp(server->routes[i].target, target) == 0 && - strcmp(server->routes[i].method, method) == 0) { - return &server->routes[i]; - } - } - return NULL; -} - void sigchld_handler(int s) { - // waitpid() might overwrite errno, so we save and restore it: int saved_errno = errno; while (waitpid(-1, NULL, WNOHANG) > 0) @@ -54,7 +32,110 @@ void sigchld_handler(int s) { errno = saved_errno; } -static void start(const char *PORT) { +void path_from_target(struct Server *server, char *target, char *path) { + strcpy(path, server->static_path); + strcat(path, target); + + char *question = strchr(path, '?'); + if (question) + *question = '\0'; + + if (path[strlen(path) - 1] == '/') + strcat(path, "index.html"); + + char *dot = strchr(path, '.'); + if (!dot) + strcat(path, ".html"); +} + +void get_mime_type(char *path, char *mime) { + char *dot = strchr(path, '.'); + if (!dot) + strcpy(mime, "text/html"); + else if (strcmp(dot, ".html") == 0) + strcpy(mime, "text/html"); + else if (strcmp(dot, ".css") == 0) + strcpy(mime, "text/css"); + else if (strcmp(dot, ".js") == 0) + strcpy(mime, "application/js"); + else if (strcmp(dot, ".jpg") == 0) + strcpy(mime, "image/jpeg"); + else if (strcmp(dot, ".png") == 0) + strcpy(mime, "image/png"); + else if (strcmp(dot, ".gif") == 0) + strcpy(mime, "image/gif"); + else if (strcmp(dot, ".ico") == 0) + strcpy(mime, "image/x-icon"); + else + strcpy(mime, "text/html"); +} + +int sendall(int s, char *buf, int *len) +{ + int total = 0; // how many bytes we've sent + int bytesleft = *len; // how many we have left to send + int n; + + while(total < *len) { + n = send(s, buf+total, bytesleft, 0); + if (n == -1) { break; } + total += n; + bytesleft -= n; + } + + *len = total; // return number actually sent here + + return n==-1?-1:0; // return -1 on failure, 0 on success +} + +void handle(struct Server *server, int client_fd) { + char *buf = (char *)malloc(MAX_BUFFER_SIZE * sizeof(char)); + recv(client_fd, buf, MAX_BUFFER_SIZE, 0); + + struct Request request = request_constructor(buf); + + char *path = (char *)malloc(1024 * sizeof(char)); + path_from_target(server, request.target, path); + + FILE *fp = fopen(path, "rb"); + if (fp == NULL) { + const char response[] = "HTTP/1.1 404 Not Found\r\n\r\nNot Found"; + send(client_fd, response, sizeof(response), 0); + printf("[%s] %s -> %s %s\n", request.method, request.target, path, "404"); + } else { + fseek(fp, 0, SEEK_END); + size_t file_size = ftell(fp); + rewind(fp); + + char mime_type[32]; + get_mime_type(path, mime_type); + + char res_header[1024]; + sprintf(res_header, "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n\r\n", mime_type); + int header_size = strlen(res_header); + + char *res_buffer = (char *)malloc((file_size + header_size) * sizeof(char)); + strcpy(res_buffer, res_header); + + fread(res_buffer + header_size, 1, file_size, fp); + + int len = file_size + header_size; + if (sendall(client_fd, res_buffer, &len) == -1) { + perror("send content"); + exit(1); + } + + printf("[%s] %s -> %s %s\n", request.method, request.target, path, "200"); + + free(res_buffer); + }; + fclose(fp); +} + +void launch(struct Server *server) { + struct sockaddr_storage client_addr; + socklen_t client_addr_len = sizeof client_addr; + struct addrinfo hints, *res, *p; memset(&hints, 0, sizeof hints); @@ -63,7 +144,7 @@ static void start(const char *PORT) { hints.ai_flags = AI_PASSIVE; int gairv; - if ((gairv = getaddrinfo(NULL, PORT, &hints, &res)) != 0) { + if ((gairv = getaddrinfo(NULL, server->port, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gairv)); exit(1); } @@ -108,86 +189,6 @@ static void start(const char *PORT) { perror("sigaction"); exit(1); } -} - -char *read_file(char *filename) { - // read in file - FILE *fp = fopen(filename, "r"); - if (fp == NULL) { - fprintf(stderr, "read_file: error opening file: %s\n", filename); - return NULL; - } - - // Calculate the content length - fseek(fp, 0, SEEK_END); - size_t file_size = ftell(fp); - rewind(fp); - - // allocate memory for file contents - char *buffer = malloc(file_size + 1); - if (buffer == NULL) { - fprintf(stderr, "read_file: memory allocation failed.\n"); - fclose(fp); - return NULL; - } - - // Read the file contents into buffer - size_t bytes_read = fread(buffer, 1, file_size, fp); - if (bytes_read != file_size) { - fprintf(stderr, "read_file: error reading file.\n"); - free(buffer); - fclose(fp); - return NULL; - } - - fclose(fp); - buffer[file_size] = '\0'; - - return buffer; -} - -void respond(struct Server *server, int *client_fd) { - // Recieve request - char buf[MAX_BUFFER_SIZE]; - recv(*client_fd, buf, MAX_BUFFER_SIZE, 0); - - // Parse request - struct Request request = request_constructor(buf); - - // Match route and get body content - struct Route *matched_route = - match_route(server, request.method, request.target); - - struct Response response; - if (matched_route == NULL) { - response = response_constructor("HTTP/1.1", NOT_FOUND, "Not found"); - } else { - char *body = matched_route->route_callback(request); - response = response_constructor("HTTP/1.1", OK, body); - } - - // calculate content length - char body_length[12]; - snprintf(body_length, sizeof(body_length), "%lu", strlen(response.body)); - add_header(&response, "Content-Length", body_length); - - printf("%s %s -> %s\n", request.method, request.target, response.status); - - // send response string - char response_string[MAX_BUFFER_SIZE]; - response_to_string(response_string, &response); - - if (send(*client_fd, response_string, strlen(response_string), 0) == -1) { - perror("send content"); - exit(1); - } -} - -void launch(struct Server *server) { - struct sockaddr_storage client_addr; - socklen_t client_addr_len = sizeof client_addr; - - start(server->PORT); int client_fd; while (1) { @@ -199,7 +200,7 @@ void launch(struct Server *server) { if (!fork()) { close(sockfd); - respond(server, &client_fd); + handle(server, client_fd); close(client_fd); exit(0); } diff --git a/server.h b/server.h index 5851e4c..af7ac0a 100644 --- a/server.h +++ b/server.h @@ -1,21 +1,14 @@ #ifndef SERVER_H #define SERVER_H -#include "header.h" -#include "route.h" - -#define BACKLOG 128 +#define BACKLOG 20 struct Server { - const char *PORT; - struct Route *routes; - int num_routes; + const char *port; + const char *static_path; }; -struct Server server_constructor(const char *PORT); - -void add_route(struct Server *server, char *method, char *target, - Route_Callback route_callback); +struct Server server_constructor(const char *port, const char *static_path); void launch(struct Server *server); diff --git a/templates/post.html b/templates/post.html new file mode 100644 index 0000000..b2e0140 --- /dev/null +++ b/templates/post.html @@ -0,0 +1,38 @@ + + + +$for(author-meta)$ + +$endfor$ +$if(date-meta)$ + +$endif$ + $pagetitle$ +$for(css)$ + +$endfor$ + + +
+

Michael Thomson

+ +
+ $if(title)$ +

$title$

+ $endif$ + $if(subtitle)$ +

$subtitle$

+ $endif$ + $for(author)$ +

$author$

+ $endfor$ + $if(date)$ +

$date$

+ $endif$ + $body$ + + diff --git a/utils.c b/utils.c deleted file mode 100644 index 6a24f21..0000000 --- a/utils.c +++ /dev/null @@ -1,25 +0,0 @@ -#include - -char *strstrtok(char *input, char *delim) { - static char *p; - - if (input != NULL) { - p = input; - } - - if (p == NULL) { - return p; - } - - char *end = strstr(p, delim); - if (end == NULL) { - char *temp = p; - p = NULL; - return temp; - } - - char *temp = p; - *end = '\0'; - p = end + strlen(delim); - return temp; -} diff --git a/utils.h b/utils.h deleted file mode 100644 index c4b5991..0000000 --- a/utils.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef UTILS_H -#define UTILS_H - -char *strstrtok(char *input, char *delim); - -#endif // !UTILS_H -