Compare commits

...

4 Commits
main ... static

Author SHA1 Message Date
471b72c23e gen 2024-09-13 11:01:18 -07:00
a42a63faac gen script 2024-09-13 10:51:15 -07:00
bdb302d05b update 2024-09-13 10:49:46 -07:00
291d55642d big update: simplifying + pandoc 2024-09-13 08:22:21 -07:00
27 changed files with 272 additions and 304 deletions

View File

@ -1,6 +1,6 @@
# ----- Variables for customization ----- # ----- Variables for customization -----
CC = gcc # Compiler (you can change to clang if desired) 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 LDFLAGS = # Linker flags, if any
# ----- Automatic dependency tracking ----- # ----- Automatic dependency tracking -----
@ -19,4 +19,4 @@ server: $(OBJS)
# Phony target for cleaning # Phony target for cleaning
.PHONY: clean .PHONY: clean
clean: clean:
rm -f $(OBJS) rm -f $(OBJS) server

View File

@ -0,0 +1,15 @@
---
title: First Post
---
This is my first post
Here's some code!
```c
int main()
{
printf("Hello, World!");
return 0;
}
```

8
content/index.md Normal file
View File

@ -0,0 +1,8 @@
---
title: Michael Thomson
---
Welcome to my site! not much here yet.
## Recent blog posts
- [First Blog Post](/blog/first-post)

View File

@ -30,7 +30,9 @@
vcpkg vcpkg
vcpkg-tool vcpkg-tool
ccls ccls
man-pages
bear bear
pandoc
] ++ (if system == "aarch64-darwin" then [ ] else [ gdb ]); ] ++ (if system == "aarch64-darwin" then [ ] else [ gdb ]);
}; };
}); });

4
gen.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
cd content
find . -type d -exec mkdir -p ../public/{} \;
find ./ -iname "*.md" -type f -exec sh -c 'pandoc "${0}" -f markdown -t html5 --template ../templates/page.html --css index.css -o "../public/${0%.md}.html"' {} \;

View File

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

View File

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

12
main.c
View File

@ -2,18 +2,8 @@
#include "server.h" #include "server.h"
#include <stdlib.h> #include <stdlib.h>
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) { int main(void) {
struct Server server = server_constructor("3000"); struct Server server = server_constructor("3000", "public");
add_route(&server, "GET", "/", home);
add_route(&server, "POST", "/clicked", clicked);
launch(&server); launch(&server);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

BIN
public/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1 +0,0 @@
Not found

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/hybrid.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<title>First Post</title>
<link rel="stylesheet" href="/index.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
</nav>
<h1 class="title">First Post</h1>
<p>This is my first post</p>
<p>Heres some code!</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> printf<span class="op">(</span><span class="st">&quot;Hello, World!&quot;</span><span class="op">);</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</body>
</html>

View File

@ -1 +0,0 @@
<p>see?</p>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

34
public/index.css Normal file
View File

@ -0,0 +1,34 @@
:root {
--bg: #0d1017;
--fg: #ffffff;
--link: #ff8f40;
}
html {
background: var(--bg);
color: var(--fg);
font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, "Nimbus Sans L", Roboto, Noto, "Segoe UI", Arial, Helvetica, "Helvetica Neue", sans-serif;
font-size: 16px;
}
body {
max-width: 760px;
margin: auto;
}
a {
text-decoration: none;
color: var(--link);
}
a:hover {
text-decoration: underline;
}
nav {
border-bottom: 1px dotted var(--link);
padding: 1rem 0 1rem 0;
}
nav a {
margin-right: 0.25rem;
}

View File

@ -1,23 +1,25 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/hybrid.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<title>Michael Thomson</title> <title>Michael Thomson</title>
<meta charset="UTF-8"> <link rel="stylesheet" href="/index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head> </head>
<body> <body>
<h1>Michael Thomson</h1> <nav>
<p>Welcome to my site! doesn't it have so much content? I know, right?</p> <a href="/">Home</a>
<p>It even has reactivity!</p> <a href="/about">About</a>
<div> <a href="/blog">Blog</a>
<!-- have a button POST a click via AJAX --> <a href="/contact">Contact</a>
<button hx-post="/clicked" hx-swap="outerHTML"> </nav>
Click Me <h1 class="title">Michael Thomson</h1>
</button> <p>Welcome to my site! not much here yet.</p>
</div> <h2 id="recent-blog-posts">Recent blog posts</h2>
<ul>
<li><a href="/blog/first-post">First Blog Post</a></li>
</ul>
</body> </body>
</html> </html>

View File

@ -1,32 +1,13 @@
#include "request.h" #include "request.h"
#include "header.h"
#include "utils.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <stdlib.h>
#define MAX_HEADER_NUM 128
struct Request request_constructor(char *raw_request) { struct Request request_constructor(char *raw_request) {
struct Request request; struct Request request;
request.method = strstrtok(raw_request, " "); request.method = (char *)malloc(12 * sizeof(char));
request.target = strstrtok(NULL, " "); request.target = (char *)malloc(128 * sizeof(char));
request.version = strstrtok(NULL, "\r\n"); request.version = (char *)malloc(12 * sizeof(char));
char *raw_headers = strstrtok(NULL, "\r\n\r\n"); sscanf(raw_request, "%s %s %s", request.method, request.target, request.version);
request.body = strstrtok(NULL, "\0"); // TODO: parse headers
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;
return request; return request;
} }

View File

@ -1,15 +1,10 @@
#ifndef REQUEST_H #ifndef REQUEST_H
#define REQUEST_H #define REQUEST_H
#include "header.h"
struct Request { struct Request {
char *method; // GET, POST, ... char *method; // GET, POST, ...
char *target; // /, /blog, /blog/post1, ... char *target; // /, /blog, /blog/post1, ...
char *version; // HTTP/1.1 char *version; // HTTP/1.1
char *body;
int num_headers;
struct Header *headers;
}; };
struct Request request_constructor(char *raw_request); struct Request request_constructor(char *raw_request);

View File

@ -1,40 +1,11 @@
#include "response.h" #include "response.h"
#include "header.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define MAX_BUFFER_SIZE 2048 struct Response response_constructor(char *version, char *status) {
struct Response response_constructor(char *version, char *status, char *body) {
struct Response response; struct Response response;
response.version = strdup(version); response.version = version;
response.status = strdup(status); response.status = status;
response.body = strdup(body);
response.num_headers = 0;
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) {
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);
}

View File

@ -1,22 +1,11 @@
#ifndef RESPONSE_H #ifndef RESPONSE_H
#define 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 { struct Response {
char *version; char *version;
char *status; char *status;
char *body;
int num_headers;
struct Header *headers;
}; };
struct Response response_constructor(char *version, char *status, char *body); struct Response response_constructor(char *version, char *status);
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
View File

@ -1,12 +0,0 @@
#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
View File

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

BIN
server

Binary file not shown.

217
server.c
View File

@ -10,42 +10,20 @@
#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
#define MAX_HEADER_NUM 48
int sockfd; int sockfd;
struct Server server_constructor(const char *PORT) { struct Server server_constructor(const char *port, const char *static_path) {
struct Server server; struct Server server;
server.PORT = PORT; server.port = port;
server.num_routes = 0; server.static_path = static_path;
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:
int saved_errno = errno; int saved_errno = errno;
while (waitpid(-1, NULL, WNOHANG) > 0) while (waitpid(-1, NULL, WNOHANG) > 0)
@ -54,7 +32,110 @@ void sigchld_handler(int s) {
errno = saved_errno; 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; struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof hints); memset(&hints, 0, sizeof hints);
@ -63,7 +144,7 @@ static void start(const char *PORT) {
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
int gairv; 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)); fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gairv));
exit(1); exit(1);
} }
@ -108,86 +189,6 @@ static void start(const char *PORT) {
perror("sigaction"); perror("sigaction");
exit(1); 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; int client_fd;
while (1) { while (1) {
@ -199,7 +200,7 @@ void launch(struct Server *server) {
if (!fork()) { if (!fork()) {
close(sockfd); close(sockfd);
respond(server, &client_fd); handle(server, client_fd);
close(client_fd); close(client_fd);
exit(0); exit(0);
} }

View File

@ -1,21 +1,14 @@
#ifndef SERVER_H #ifndef SERVER_H
#define SERVER_H #define SERVER_H
#include "header.h" #define BACKLOG 20
#include "route.h"
#define BACKLOG 128
struct Server { struct Server {
const char *PORT; const char *port;
struct Route *routes; const char *static_path;
int num_routes;
}; };
struct Server server_constructor(const char *PORT); struct Server server_constructor(const char *port, const char *static_path);
void add_route(struct Server *server, char *method, char *target,
Route_Callback route_callback);
void launch(struct Server *server); void launch(struct Server *server);

38
templates/page.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/hybrid.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
<meta name="dcterms.date" content="$date-meta$" />
$endif$
<title>$pagetitle$</title>
<link rel="stylesheet" href="/index.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
</nav>
$if(title)$
<h1 class="title">$title$</h1>
$endif$
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
$body$
</body>
</html>

25
utils.c
View File

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

View File

@ -1,7 +0,0 @@
#ifndef UTILS_H
#define UTILS_H
char *strstrtok(char *input, char *delim);
#endif // !UTILS_H