Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
471b72c23e | |||
a42a63faac | |||
bdb302d05b | |||
291d55642d |
4
Makefile
4
Makefile
@ -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
|
||||||
|
15
content/blog/first-post.md
Normal file
15
content/blog/first-post.md
Normal 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
8
content/index.md
Normal 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)
|
@ -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
4
gen.sh
Executable 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"' {} \;
|
9
header.c
9
header.c
@ -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;
|
|
||||||
}
|
|
11
header.h
11
header.h
@ -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
12
main.c
@ -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
BIN
public/.DS_Store
vendored
Normal file
Binary file not shown.
@ -1 +0,0 @@
|
|||||||
Not found
|
|
28
public/blog/first-post.html
Normal file
28
public/blog/first-post.html
Normal 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>Here’s 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">"Hello, World!"</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>
|
@ -1 +0,0 @@
|
|||||||
<p>see?</p>
|
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
34
public/index.css
Normal file
34
public/index.css
Normal 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;
|
||||||
|
}
|
@ -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>
|
||||||
|
31
request.c
31
request.c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
35
response.c
35
response.c
@ -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);
|
|
||||||
}
|
|
||||||
|
13
response.h
13
response.h
@ -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
12
route.c
@ -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
17
route.h
@ -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
|
|
217
server.c
217
server.c
@ -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);
|
||||||
}
|
}
|
||||||
|
15
server.h
15
server.h
@ -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
38
templates/page.html
Normal 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
25
utils.c
@ -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;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user