big ol' update

This commit is contained in:
Michael Thomson 2024-02-12 16:57:40 -05:00
parent bdafb30b0a
commit f81c911898
No known key found for this signature in database
12 changed files with 159 additions and 74 deletions

View File

@ -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: clean:
@rm -rf *.o rm -f $(OBJS) server
@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

View File

@ -1 +1 @@
<p>It works!</p> <p>see?</p>

View File

@ -1,8 +1,9 @@
#include "header.h" #include "header.h"
#include <string.h>
struct Header header_constructor(char *key, char *value) { struct Header header_constructor(char *key, char *value) {
struct Header header; struct Header header;
header.key = key; header.key = strdup(key);
header.value = value; header.value = strdup(value);
return header; return header;
} }

View File

@ -1,7 +1,23 @@
<div> <!DOCTYPE html>
<html lang="en">
<head>
<title>Michael Thomson</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/htmx.org@1.9.10"></script> <script src="https://unpkg.com/htmx.org@1.9.10"></script>
<!-- have a button POST a click via AJAX --> </head>
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me <body>
</button> <h1>Michael Thomson</h1>
</div> <p>Welcome to my site! doesn't it have so much content? I know, right?</p>
<p>It even has reactivity!</p>
<div>
<!-- have a button POST a click via AJAX -->
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
</div>
</body>
</html>

11
main.c
View File

@ -1,8 +1,19 @@
#include "request.h"
#include "server.h" #include "server.h"
#include <stdlib.h> #include <stdlib.h>
char *home(struct Request request) {
return read_file("index.html");
}
char *clicked(struct Request request) {
return read_file("clicked.html");
}
int main(void) { int main(void) {
struct Server server = server_constructor("3000"); struct Server server = server_constructor("3000");
add_route(&server, "GET", "/", home);
add_route(&server, "POST", "/clicked", clicked);
launch(&server); launch(&server);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1,23 +1,31 @@
#include "response.h" #include "response.h"
#include "header.h" #include "header.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#define MAX_BUFFER_SIZE 2048 #define MAX_BUFFER_SIZE 2048
struct Response response_constructor(char *version, char *status, char *body, struct Response response_constructor(char *version, char *status, char *body) {
int num_headers, struct Header *headers) {
struct Response response; struct Response response;
response.version = version; response.version = strdup(version);
response.status = status; response.status = strdup(status);
response.body = body; response.body = strdup(body);
response.num_headers = num_headers; response.num_headers = 0;
response.headers = headers; response.headers = NULL;
return response; 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) { void response_to_string(char *buf, struct Response *response) {
char headers[MAX_BUFFER_SIZE]; char headers[MAX_BUFFER_SIZE];
headers[0] = '\0';
for (int i = 0; i < response->num_headers; i++) { for (int i = 0; i < response->num_headers; i++) {
strcat(headers, response->headers[i].key); strcat(headers, response->headers[i].key);
@ -26,6 +34,7 @@ void response_to_string(char *buf, struct Response *response) {
strcat(headers, "\r\n"); strcat(headers, "\r\n");
} }
snprintf(buf, MAX_BUFFER_SIZE, "%s %s\r\n%s\r\n%s", response->version, snprintf(buf, MAX_BUFFER_SIZE, "%s %s\r\n%s\r\n%s", response->version,
response->status, headers, response->body); response->status, headers, response->body);
} }

View File

@ -13,9 +13,10 @@ struct Response {
struct Header *headers; struct Header *headers;
}; };
struct Response response_constructor(char *version, char *status, char *body, struct Response response_constructor(char *version, char *status, char *body);
int num_headers, struct Header *headers);
void response_to_string(char *buf, struct Response *response); void response_to_string(char *buf, struct Response *response);
void add_header(struct Response *response, char *key, char *value);
#endif // !RESPONSE_H #endif // !RESPONSE_H

12
route.c Normal file
View File

@ -0,0 +1,12 @@
#include <string.h>
#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;
}

17
route.h Normal file
View File

@ -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

BIN
server

Binary file not shown.

View File

@ -8,9 +8,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "header.h"
#include "request.h" #include "request.h"
#include "response.h" #include "response.h"
#include "route.h"
#include "server.h" #include "server.h"
#define MAX_BUFFER_SIZE 2048 #define MAX_BUFFER_SIZE 2048
@ -18,12 +18,32 @@
int sockfd; int sockfd;
struct Server server_constructor(char *PORT) { struct Server server_constructor(const char *PORT) {
struct Server server; struct Server server;
server.PORT = PORT; server.PORT = PORT;
server.num_routes = 0;
server.routes = NULL;
return server; 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) { void sigchld_handler(int s) {
// waitpid() might overwrite errno, so we save and restore it: // waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno; 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 // read in file
FILE *fp = fopen(filename, "r"); FILE *fp = fopen(filename, "r");
if (fp == NULL) { if (fp == NULL) {
@ -100,11 +120,11 @@ char *read_file(char *filename, size_t *file_size) {
// Calculate the content length // Calculate the content length
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
*file_size = ftell(fp); size_t file_size = ftell(fp);
rewind(fp); rewind(fp);
// allocate memory for file contents // allocate memory for file contents
char *buffer = malloc(*file_size + 1); char *buffer = malloc(file_size + 1);
if (buffer == NULL) { if (buffer == NULL) {
fprintf(stderr, "read_file: memory allocation failed.\n"); fprintf(stderr, "read_file: memory allocation failed.\n");
fclose(fp); fclose(fp);
@ -112,8 +132,8 @@ char *read_file(char *filename, size_t *file_size) {
} }
// Read the file contents into buffer // Read the file contents into buffer
size_t bytes_read = fread(buffer, 1, *file_size, fp); size_t bytes_read = fread(buffer, 1, file_size, fp);
if (bytes_read != *file_size) { if (bytes_read != file_size) {
fprintf(stderr, "read_file: error reading file.\n"); fprintf(stderr, "read_file: error reading file.\n");
free(buffer); free(buffer);
fclose(fp); fclose(fp);
@ -121,41 +141,37 @@ char *read_file(char *filename, size_t *file_size) {
} }
fclose(fp); fclose(fp);
buffer[*file_size] = '\0'; buffer[file_size] = '\0';
return buffer; return buffer;
} }
void respond(int *client_fd) { void respond(struct Server *server, int *client_fd) {
// Recieve request
char buf[MAX_BUFFER_SIZE]; char buf[MAX_BUFFER_SIZE];
recv(*client_fd, buf, MAX_BUFFER_SIZE, 0); recv(*client_fd, buf, MAX_BUFFER_SIZE, 0);
// Parse request
struct Request request = request_constructor(buf); 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; struct Response response;
if (strcmp(request.target, "/") == 0) { if (matched_route == NULL) {
filename = "index.html"; response = response_constructor("HTTP/1.1", NOT_FOUND, "Not found");
} else if (strcmp(request.target, "/clicked") == 0) {
filename = "clicked.html";
} else { } else {
filename = "404.html"; char *body = matched_route->route_callback(request);
response = response_constructor("HTTP/1.1", OK, body);
} }
size_t body_length; // calculate content length
char *body = read_file(filename, &body_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 printf("%s %s -> %s\n", request.method, request.target, response.status);
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);
// send response string // send response string
char response_string[MAX_BUFFER_SIZE]; char response_string[MAX_BUFFER_SIZE];
@ -165,8 +181,6 @@ void respond(int *client_fd) {
perror("send content"); perror("send content");
exit(1); exit(1);
} }
free(body);
} }
void launch(struct Server *server) { void launch(struct Server *server) {
@ -185,7 +199,7 @@ void launch(struct Server *server) {
if (!fork()) { if (!fork()) {
close(sockfd); close(sockfd);
respond(&client_fd); respond(server, &client_fd);
close(client_fd); close(client_fd);
exit(0); exit(0);
} }

View File

@ -2,15 +2,23 @@
#define SERVER_H #define SERVER_H
#include "header.h" #include "header.h"
#include "route.h"
#define BACKLOG 128 #define BACKLOG 128
struct Server { struct Server {
const char *PORT; 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); void launch(struct Server *server);
char *read_file(char *filename);
#endif // !SERVER_H #endif // !SERVER_H