From f81c91189876ed03ae60a824ed4b0119970a6ae3 Mon Sep 17 00:00:00 2001 From: Michael Thomson Date: Mon, 12 Feb 2024 16:57:40 -0500 Subject: [PATCH] big ol' update --- Makefile | 44 ++++++++++++++--------------- clicked.html | 2 +- header.c | 5 ++-- index.html | 28 +++++++++++++++---- main.c | 11 ++++++++ response.c | 23 +++++++++++----- response.h | 5 ++-- route.c | 12 ++++++++ route.h | 17 ++++++++++++ server | Bin 78184 -> 79840 bytes server.c | 76 ++++++++++++++++++++++++++++++--------------------- server.h | 10 ++++++- 12 files changed, 159 insertions(+), 74 deletions(-) create mode 100644 route.c create mode 100644 route.h diff --git a/Makefile b/Makefile index 34a2077..a4b8202 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,22 @@ -all: server +# ----- Variables for customization ----- +CC = gcc # Compiler (you can change to clang if desired) +CFLAGS = -g3 -Wall -Wextra -fsanitize=address,undefined # Standard flags + debugging & warnings +LDFLAGS = # Linker flags, if any +# ----- Automatic dependency tracking ----- +SRCS := $(wildcard *.c) # Find all .c source files +OBJS := $(SRCS:.c=.o) # Corresponding object files (.o) +HDRS := $(SRCS:.c=.h) + +# ----- Targets ----- +server: $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o server $(LDFLAGS) + +# Create object files from source files +%.o: %.c %.h + $(CC) $(CFLAGS) -c $< -o $@ + +# Phony target for cleaning +.PHONY: clean clean: - @rm -rf *.o - @rm -rf server - -server: main.o server.o request.o header.o utils.o response.o - gcc -g3 -fsanitize=address,undefined -o server $^ - -main.o: main.c - gcc -g3 -fsanitize=address,undefined -c -o main.o main.c - -server.o: server.c server.h - gcc -g3 -fsanitize=address,undefined -c -o server.o server.c - -request.o: request.c request.h - gcc -g3 -fsanitize=address,undefined -c -o request.o request.c - -response.o: response.c response.h - gcc -g3 -fsanitize=address,undefined -c -o response.o response.c - -header.o: header.c header.h - gcc -g3 -fsanitize=address,undefined -c -o header.o header.c - -utils.o: utils.c utils.h - gcc -g3 -fsanitize=address,undefined -c -o utils.o utils.c + rm -f $(OBJS) server diff --git a/clicked.html b/clicked.html index beb9256..3721df5 100644 --- a/clicked.html +++ b/clicked.html @@ -1 +1 @@ -

It works!

+

see?

diff --git a/header.c b/header.c index d5f3a79..deec1d2 100644 --- a/header.c +++ b/header.c @@ -1,8 +1,9 @@ #include "header.h" +#include struct Header header_constructor(char *key, char *value) { struct Header header; - header.key = key; - header.value = value; + header.key = strdup(key); + header.value = strdup(value); return header; } diff --git a/index.html b/index.html index a20480d..c9d14c9 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,23 @@ -
+ + + + + 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/main.c b/main.c index 6eb7587..d674725 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,19 @@ +#include "request.h" #include "server.h" #include +char *home(struct Request request) { + return read_file("index.html"); +} + +char *clicked(struct Request request) { + return read_file("clicked.html"); +} + int main(void) { struct Server server = server_constructor("3000"); + add_route(&server, "GET", "/", home); + add_route(&server, "POST", "/clicked", clicked); launch(&server); return EXIT_SUCCESS; } diff --git a/response.c b/response.c index f83ef51..148008e 100644 --- a/response.c +++ b/response.c @@ -1,23 +1,31 @@ #include "response.h" #include "header.h" #include +#include #include #define MAX_BUFFER_SIZE 2048 -struct Response response_constructor(char *version, char *status, char *body, - int num_headers, struct Header *headers) { +struct Response response_constructor(char *version, char *status, char *body) { struct Response response; - response.version = version; - response.status = status; - response.body = body; - response.num_headers = num_headers; - response.headers = headers; + response.version = strdup(version); + response.status = strdup(status); + response.body = strdup(body); + response.num_headers = 0; + response.headers = NULL; 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); @@ -26,6 +34,7 @@ void response_to_string(char *buf, struct Response *response) { 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 c021a0e..5c71aac 100644 --- a/response.h +++ b/response.h @@ -13,9 +13,10 @@ struct Response { struct Header *headers; }; -struct Response response_constructor(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); + #endif // !RESPONSE_H diff --git a/route.c b/route.c new file mode 100644 index 0000000..bf1af16 --- /dev/null +++ b/route.c @@ -0,0 +1,12 @@ +#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 new file mode 100644 index 0000000..ceba521 --- /dev/null +++ b/route.h @@ -0,0 +1,17 @@ +#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 index 1d595ce1323237853bdc1849dfbabd5aa03339a4..3258c4f77fdd33dbd49f5375685f07a800047f17 100755 GIT binary patch literal 79840 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 diff --git a/server.c b/server.c index 805c02e..891f6fc 100644 --- a/server.c +++ b/server.c @@ -8,9 +8,9 @@ #include #include -#include "header.h" #include "request.h" #include "response.h" +#include "route.h" #include "server.h" #define MAX_BUFFER_SIZE 2048 @@ -18,12 +18,32 @@ int sockfd; -struct Server server_constructor(char *PORT) { +struct Server server_constructor(const char *PORT) { struct Server server; server.PORT = PORT; + server.num_routes = 0; + server.routes = NULL; 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; @@ -90,7 +110,7 @@ static void start(const char *PORT) { } } -char *read_file(char *filename, size_t *file_size) { +char *read_file(char *filename) { // read in file FILE *fp = fopen(filename, "r"); if (fp == NULL) { @@ -100,11 +120,11 @@ char *read_file(char *filename, size_t *file_size) { // Calculate the content length fseek(fp, 0, SEEK_END); - *file_size = ftell(fp); + size_t file_size = ftell(fp); rewind(fp); // allocate memory for file contents - char *buffer = malloc(*file_size + 1); + char *buffer = malloc(file_size + 1); if (buffer == NULL) { fprintf(stderr, "read_file: memory allocation failed.\n"); fclose(fp); @@ -112,8 +132,8 @@ char *read_file(char *filename, size_t *file_size) { } // Read the file contents into buffer - size_t bytes_read = fread(buffer, 1, *file_size, fp); - if (bytes_read != *file_size) { + 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); @@ -121,41 +141,37 @@ char *read_file(char *filename, size_t *file_size) { } fclose(fp); - buffer[*file_size] = '\0'; + buffer[file_size] = '\0'; return buffer; } -void respond(int *client_fd) { - +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); - printf("%s %s %s\n", request.method, request.target, request.version); + // Match route and get body content + struct Route *matched_route = + match_route(server, request.method, request.target); - char *filename; - if (strcmp(request.target, "/") == 0) { - filename = "index.html"; - } else if (strcmp(request.target, "/clicked") == 0) { - filename = "clicked.html"; + struct Response response; + if (matched_route == NULL) { + response = response_constructor("HTTP/1.1", NOT_FOUND, "Not found"); } else { - filename = "404.html"; + char *body = matched_route->route_callback(request); + response = response_constructor("HTTP/1.1", OK, body); } - size_t body_length; - char *body = read_file(filename, &body_length); + // calculate content length + char body_length[12]; + snprintf(body_length, sizeof(body_length), "%lu", strlen(response.body)); + add_header(&response, "Content-Length", body_length); - // Create the Content-Length header - struct Header headers[MAX_HEADER_NUM]; - char contentLength[MAX_BUFFER_SIZE]; - snprintf(contentLength, sizeof(contentLength), "%zu", body_length); - headers[0] = header_constructor("Content-Length", contentLength); - - // construct response - struct Response response = - response_constructor("HTTP/1.1", OK, body, 1, headers); + printf("%s %s -> %s\n", request.method, request.target, response.status); // send response string char response_string[MAX_BUFFER_SIZE]; @@ -165,8 +181,6 @@ void respond(int *client_fd) { perror("send content"); exit(1); } - - free(body); } void launch(struct Server *server) { @@ -185,7 +199,7 @@ void launch(struct Server *server) { if (!fork()) { close(sockfd); - respond(&client_fd); + respond(server, &client_fd); close(client_fd); exit(0); } diff --git a/server.h b/server.h index c7ff4af..5851e4c 100644 --- a/server.h +++ b/server.h @@ -2,15 +2,23 @@ #define SERVER_H #include "header.h" +#include "route.h" #define BACKLOG 128 struct Server { const char *PORT; + struct Route *routes; + int num_routes; }; -struct Server server_constructor(char *PORT); +struct Server server_constructor(const char *PORT); + +void add_route(struct Server *server, char *method, char *target, + Route_Callback route_callback); void launch(struct Server *server); +char *read_file(char *filename); + #endif // !SERVER_H