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 1d595ce..3258c4f 100755 Binary files a/server and b/server differ 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