Skip to content

Commit a3a321f

Browse files
committed
Initial import from internal tree
0 parents  commit a3a321f

15 files changed

+480
-0
lines changed

.clang-format

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
BasedOnStyle: Chromium
2+
Language: Cpp
3+
MaxEmptyLinesToKeep: 3
4+
IndentCaseLabels: false
5+
AllowShortIfStatementsOnASingleLine: false
6+
AllowShortCaseLabelsOnASingleLine: false
7+
AllowShortLoopsOnASingleLine: false
8+
DerivePointerAlignment: false
9+
PointerAlignment: Right
10+
SpaceAfterCStyleCast: true
11+
TabWidth: 4
12+
UseTab: Never
13+
IndentWidth: 4
14+
BreakBeforeBraces: Linux
15+
AccessModifierOffset: -4
16+
ForEachMacros:
17+
- foreach
18+
- Q_FOREACH
19+
- BOOST_FOREACH
20+
- list_for_each
21+
- list_for_each_safe
22+
- list_for_each_entry
23+
- list_for_each_entry_safe
24+
- hlist_for_each_entry
25+
- rb_list_foreach
26+
- rb_list_foreach_safe
27+
- vec_foreach

.gitignore

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# kernel module compile output
2+
*.ko.cmd
3+
*.o.cmd
4+
.tmp_versions
5+
*.symvers
6+
*.mod.c
7+
*.mod.o
8+
*.order
9+
*.o
10+
*.ko
11+
12+
# Other
13+
http_parser.c
14+
http_parser.h

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (C) 2020 National Cheng Kung University, Taiwan.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
CFILES = main.c http_server.c http_parser.c
2+
3+
obj-m += khttpd.o
4+
khttpd-objs := $(CFILES:.c=.o)
5+
6+
all: http_parser.c
7+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
8+
9+
clean:
10+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
11+
12+
http_parser.c:
13+
wget https://raw.githubusercontent.com/nodejs/http-parser/master/http_parser.c
14+
sed -i 's/#include <assert.h>/#include "compat\/assert.h"/' $@
15+
sed -i 's/#include <stddef.h>/#include "compat\/stddef.h"/' $@
16+
sed -i 's/#include <ctype.h>/#include "compat\/ctype.h"/' $@
17+
sed -i 's/#include <string.h>/#include "compat\/string.h"/' $@
18+
sed -i 's/#include <limits.h>/#include "compat\/limits.h"/' $@
19+
wget https://raw.githubusercontent.com/nodejs/http-parser/master/http_parser.h
20+
sed -i 's/#include <stddef.h>/#include "compat\/stddef.h"/' http_parser.h
21+
sed -i 's/#include <stdint.h>/#include "compat\/stdint.h"/' http_parser.h
22+
23+
distclean: clean
24+
$(RM) http_parser.c http_parser.h

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# khttpd
2+
3+
`khttpd` is an experimental HTTP server implemented as Linux kernel module.
4+
The server defaults to port 8081 but this can be easily configured using command line arguments to the LKM.
5+
6+
## TODO
7+
* Request queue and/or cache
8+
* Slab cache
9+
* Kthread pool
10+
* Dynamic framework
11+
* Reverse proxy
12+
13+
## License
14+
15+
`khttpdc`is released under the MIT License. Use of this source code is governed by
16+
a MIT License that can be found in the LICENSE file.
17+

common.h

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef KHTTPD_COMMON_H
2+
#define KHTTPD_COMMON_H
3+
4+
#include "http_parser.h"
5+
#include "http_server.h"
6+
7+
#define MODULE_NAME "khttpd"
8+
9+
#endif

compat/assert.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef COMPAT_ASSERT_H
2+
#define COMPAT_ASSERT_H
3+
4+
static inline void assert(int x) {};
5+
6+
#endif

compat/ctype.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* Dummy */

compat/limits.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* Dummy */

compat/stddef.h

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include <linux/types.h>
2+
#include <linux/kernel.h>

compat/stdint.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* Dummy */

compat/string.h

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include <linux/string.h>
2+
#include <linux/memory.h>

http_server.c

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include <linux/kthread.h>
2+
#include <linux/sched/signal.h>
3+
#include <linux/tcp.h>
4+
5+
#include "common.h"
6+
7+
#define CRLF "\r\n"
8+
9+
#define HTTP_RESPONSE_200_DUMMY \
10+
"" \
11+
"HTTP/1.1 200 OK" CRLF "Server: " MODULE_NAME CRLF \
12+
"Content-Type: text/plain" CRLF "Content-Length: 8" CRLF \
13+
"Connection: Close" CRLF CRLF "200 OK" CRLF
14+
#define HTTP_RESPONSE_200_KEEPALIVE_DUMMY \
15+
"" \
16+
"HTTP/1.1 200 OK" CRLF "Server: " MODULE_NAME CRLF \
17+
"Content-Type: text/plain" CRLF "Content-Length: 8" CRLF \
18+
"Connection: Keep-Alive" CRLF CRLF "200 OK" CRLF
19+
#define HTTP_RESPONSE_501 \
20+
"" \
21+
"HTTP/1.1 501 Not Implemented" CRLF "Server: " MODULE_NAME CRLF \
22+
"Content-Type: text/plain" CRLF "Content-Length: 21" CRLF \
23+
"Connection: Close" CRLF CRLF "501 Not Implemented" CRLF
24+
#define HTTP_RESPONSE_501_KEEPALIVE \
25+
"" \
26+
"HTTP/1.1 501 Not Implemented" CRLF "Server: " MODULE_NAME CRLF \
27+
"Content-Type: text/plain" CRLF "Content-Length: 21" CRLF \
28+
"Connection: KeepAlive" CRLF CRLF "501 Not Implemented" CRLF
29+
30+
#define RECV_BUFFER_SIZE 4096
31+
32+
struct http_request {
33+
struct socket *socket;
34+
enum http_method method;
35+
char request_url[128];
36+
int complete;
37+
};
38+
39+
static int http_server_recv(struct socket *sock, char *buf, size_t size)
40+
{
41+
struct kvec iov = {.iov_base = (void *) buf, .iov_len = size};
42+
struct msghdr msg = {.msg_name = 0,
43+
.msg_namelen = 0,
44+
.msg_control = NULL,
45+
.msg_controllen = 0,
46+
.msg_flags = 0};
47+
return kernel_recvmsg(sock, &msg, &iov, 1, size, msg.msg_flags);
48+
;
49+
}
50+
51+
static int http_server_send(struct socket *sock, const char *buf, size_t size)
52+
{
53+
struct msghdr msg = {.msg_name = NULL,
54+
.msg_namelen = 0,
55+
.msg_control = NULL,
56+
.msg_controllen = 0,
57+
.msg_flags = 0};
58+
int done = 0;
59+
while (done < size) {
60+
struct kvec iov = {
61+
.iov_base = (void *) ((char *) buf + done),
62+
.iov_len = size - done,
63+
};
64+
int length = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
65+
if (length < 0) {
66+
printk(KERN_ERR MODULE_NAME ": write error: %d\n", length);
67+
break;
68+
}
69+
done += length;
70+
}
71+
return done;
72+
}
73+
74+
static int http_server_response(struct http_request *request, int keep_alive)
75+
{
76+
char *response;
77+
78+
printk(KERN_INFO MODULE_NAME ": requested_url = %s\n",
79+
request->request_url);
80+
if (request->method != HTTP_GET)
81+
response = keep_alive ? HTTP_RESPONSE_501_KEEPALIVE : HTTP_RESPONSE_501;
82+
else
83+
response = keep_alive ? HTTP_RESPONSE_200_KEEPALIVE_DUMMY
84+
: HTTP_RESPONSE_200_DUMMY;
85+
http_server_send(request->socket, response, strlen(response));
86+
return 0;
87+
}
88+
89+
static int http_parser_callback_message_begin(http_parser *parser)
90+
{
91+
struct http_request *request = parser->data;
92+
struct socket *socket = request->socket;
93+
memset(request, 0x00, sizeof(struct http_request));
94+
request->socket = socket;
95+
return 0;
96+
}
97+
98+
static int http_parser_callback_request_url(http_parser *parser,
99+
const char *p,
100+
size_t len)
101+
{
102+
struct http_request *request = parser->data;
103+
strncat(request->request_url, p, len);
104+
return 0;
105+
}
106+
107+
static int http_parser_callback_header_field(http_parser *parser,
108+
const char *p,
109+
size_t len)
110+
{
111+
return 0;
112+
}
113+
114+
static int http_parser_callback_header_value(http_parser *parser,
115+
const char *p,
116+
size_t len)
117+
{
118+
return 0;
119+
}
120+
121+
static int http_parser_callback_headers_complete(http_parser *parser)
122+
{
123+
struct http_request *request = parser->data;
124+
request->method = parser->method;
125+
return 0;
126+
}
127+
128+
static int http_parser_callback_body(http_parser *parser,
129+
const char *p,
130+
size_t len)
131+
{
132+
return 0;
133+
}
134+
135+
static int http_parser_callback_message_complete(http_parser *parser)
136+
{
137+
struct http_request *request = parser->data;
138+
http_server_response(request, http_should_keep_alive(parser));
139+
request->complete = 1;
140+
return 0;
141+
}
142+
143+
static int http_server_worker(void *arg)
144+
{
145+
char *buf;
146+
struct http_parser parser;
147+
struct http_parser_settings setting = {
148+
.on_message_begin = http_parser_callback_message_begin,
149+
.on_url = http_parser_callback_request_url,
150+
.on_header_field = http_parser_callback_header_field,
151+
.on_header_value = http_parser_callback_header_value,
152+
.on_headers_complete = http_parser_callback_headers_complete,
153+
.on_body = http_parser_callback_body,
154+
.on_message_complete = http_parser_callback_message_complete};
155+
struct http_request request;
156+
struct socket *socket = (struct socket *) arg;
157+
158+
allow_signal(SIGKILL);
159+
allow_signal(SIGTERM);
160+
buf = kmalloc(RECV_BUFFER_SIZE, GFP_KERNEL);
161+
if (!buf) {
162+
printk(KERN_ERR MODULE_NAME ": can't allocate memory!\n");
163+
return -1;
164+
}
165+
request.socket = socket;
166+
http_parser_init(&parser, HTTP_REQUEST);
167+
parser.data = &request;
168+
while (!kthread_should_stop()) {
169+
int ret = http_server_recv(socket, buf, RECV_BUFFER_SIZE - 1);
170+
if (ret <= 0) {
171+
if (ret)
172+
printk(KERN_ERR MODULE_NAME ": recv error: %d\n", ret);
173+
break;
174+
}
175+
http_parser_execute(&parser, &setting, buf, ret);
176+
if (request.complete && !http_should_keep_alive(&parser))
177+
break;
178+
}
179+
kernel_sock_shutdown(socket, SHUT_RDWR);
180+
sock_release(socket);
181+
kfree(buf);
182+
return 0;
183+
}
184+
185+
int http_server_daemon(void *arg)
186+
{
187+
struct socket *socket;
188+
struct task_struct *worker;
189+
struct http_server_param *param = (struct http_server_param *) arg;
190+
191+
allow_signal(SIGKILL);
192+
allow_signal(SIGTERM);
193+
194+
while (!kthread_should_stop()) {
195+
int err = kernel_accept(param->listen_socket, &socket, 0);
196+
if (err < 0) {
197+
if (signal_pending(current))
198+
break;
199+
printk(KERN_ERR MODULE_NAME ": kernel_accept() error: %d\n", err);
200+
continue;
201+
}
202+
worker = kthread_run(http_server_worker, socket, MODULE_NAME);
203+
if (IS_ERR(worker)) {
204+
printk(KERN_ERR MODULE_NAME ": can't create more worker process\n");
205+
continue;
206+
}
207+
}
208+
return 0;
209+
}

http_server.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef KHTTPD_HTTP_SERVER_H
2+
#define KHTTPD_HTTP_SERVER_H
3+
4+
#include <linux/types.h>
5+
#include <net/sock.h>
6+
7+
struct http_server_param {
8+
struct socket *listen_socket;
9+
};
10+
11+
extern int http_server_daemon(void *arg);
12+
13+
#endif

0 commit comments

Comments
 (0)