shttp

A small http server written in C

git clone git://git.janzachar.dev/shttp.git


0#include "super.h"
1
2#include <time.h>
3#include <stdio.h>
4#include <string.h>
5
6struct code {
7	int code;
8	char *message;
9} codes[] = {
10	{100, "Continue"},
11	{101, "Switching protocols"},
12
13	{200, "OK"},
14	{201, "Created"},
15	{202, "Accepted"},
16	{203, "Non-Authoritative Information"},
17	{204, "No Content"},
18	{205, "Reset Content"},
19	{206, "Partial Content"},
20
21	{300, "Multiple Choices"},
22	{301, "Moved Permanently"},
23	{302, "Found"},
24	{303, "See Other"},
25	{304, "Not Modified"},
26	{305, "Use Proxy"},
27	// 306 is outdated, according to rfc9110
28	{307, "Temporary Redirect"},
29	{308, "Permanent Redirect"},
30
31	{400, "Bad Request"},
32	{401, "Unauthorized"},
33	{402, "Payment Required"},
34	{403, "Forbidden"},
35	{404, "Not Found"},
36	{405, "Method Not Allowed"},
37	{406, "Not Acceptable"},
38	{407, "Proxy Authentication Required"},
39	{408, "Request Timeout"},
40	{409, "Conflict"},
41	{410, "Gone"},
42	{411, "Length Required"},
43	{412, "Precondition Failed"},
44	{413, "Content Too Large"},
45	{414, "URI Too Long"},
46	{415, "Unsupported Media Type"},
47	{416, "Range Not Satisfiable"},
48	{417, "Expectation Failed"},
49	{418, "I'm a teapot"},
50	{421, "Misdirected Request"},
51	{422, "Unprocessable Content"},
52	{426, "Upgrade Required"},
53
54
55	{500, "Internal Server Error"},
56	{501, "Not Implemented"},
57	{502, "Bad Gateway"},
58	{503, "Service Unavailable"},
59	{504, "Gateway Timeout"},
60	{505, "HTTP Version Not Supported"},
61
62	{0, NULL}
63};
64
65const char *get_date()
66{
67	static char date[32];
68	static time_t last = 0;
69
70	time_t t = time(NULL);
71	if (t != last) {
72		struct tm tm;
73		gmtime_r(&t, &tm);
74		strftime(date, sizeof(date), TIMESTAMP_FORMAT, &tm);
75		last = t;
76	}
77
78	return date;
79}
80
81int __build_reply(char buff[], int len, int code, struct header hd[])
82{
83	char *ptr = buff;
84	memset(buff, 0, len);
85
86	push(len, "HTTP/1.1 ");
87
88	int flag = 1;
89	for (struct code *p = codes; p->code; p++) {
90		if (code != p->code) continue;
91
92		push(len, "%d %s\r\n", p->code, p->message);
93
94		flag = 0;
95		break;
96	}
97	if (flag) return -1;
98
99	push(len, "Date: %s\r\n", get_date());
100	push(len, "Connection: close\r\n");
101	if (hd) while (hd->key) {
102		push(len, "%s: %s\r\n", hd->key, hd->value);
103		hd++;
104	}
105
106	return ptr-buff;
107}
108
109int reply(int fd, int code, struct header hd[])
110{
111	char buff[64 * 1024];
112	char *ptr = buff;
113
114	int r = __build_reply(buff, sizeof(buff), code, hd);
115	if (r < 0) return -1;
116	ptr += r;
117
118	push(sizeof(buff), "\r\n");
119	if (send(fd, buff, ptr-buff, 0) != ptr-buff) return 1;
120	return 0;
121}
122
123int freply(int fd, int code, struct header hd[], FILE *fp)
124{
125	char buff[64 * 1024];
126	char *ptr = buff;
127
128	int r = __build_reply(buff, sizeof(buff), code, hd);
129	if (r < 0) return -1;
130	ptr += r;
131
132	if (fp) {
133		long siz = getsize(fp);
134		if (siz == -1) return 1;
135		push(sizeof(buff), "Content-Length: %ld\r\n", siz);
136	} else push(sizeof(buff), "Content-Length: 0\r\n");
137	push(sizeof(buff), "\r\n");
138	
139	if (send(fd, buff, ptr-buff, 0) != ptr-buff) return 1;
140
141	if (fp) {
142		while (1) {
143			int c = fread(buff, 1, sizeof(buff), fp);
144			if (send(fd, buff, c, 0) != c) return 1;
145			if (c != sizeof(buff)) break;
146		}
147	}
148	return 0;
149}
150
151int sreply(int fd, int code, struct header hd[], char *out, int siz)
152{
153	char buff[64 * 1024];
154	char *ptr = buff;
155
156	int r = __build_reply(buff, sizeof(buff), code, hd);
157	if (r < 0) return -1;
158	ptr += r;
159
160	push(sizeof(buff), "Content-Length: %d\r\n", (out == NULL ? 0 : siz));
161	push(sizeof(buff), "\r\n");
162
163	if (send(fd, buff, ptr-buff, 0) != ptr-buff) return 1;
164
165	if (out != NULL && send(fd, out, siz, 0) != siz) return 1;
166	return 0;
167}
168