Skip to content

Instantly share code, notes, and snippets.

@eecheng87
Created April 13, 2020 16:12
Show Gist options
  • Save eecheng87/56834cc7cb021aa8a508e6aa14ff4f2b to your computer and use it in GitHub Desktop.
Save eecheng87/56834cc7cb021aa8a508e6aa14ff4f2b to your computer and use it in GitHub Desktop.
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/tcp.h>
#include <linux/workqueue.h>
#include "http_parser.h"
#include "http_server.h"
#define CRLF "\r\n"
#define HTTP_RESPONSE_200_DUMMY \
"" \
"HTTP/1.1 200 OK" CRLF "Server: " KBUILD_MODNAME CRLF \
"Content-Type: text/plain" CRLF "Content-Length: %d" CRLF \
"Connection: Close" CRLF CRLF "%s" CRLF
#define HTTP_RESPONSE_200_KEEPALIVE_DUMMY \
"" \
"HTTP/1.1 200 OK" CRLF "Server: " KBUILD_MODNAME CRLF \
"Content-Type: text/plain" CRLF "Content-Length: %d" CRLF \
"Connection: Keep-Alive" CRLF CRLF "%s" CRLF
#define HTTP_RESPONSE_501 \
"" \
"HTTP/1.1 501 Not Implemented" CRLF "Server: " KBUILD_MODNAME CRLF \
"Content-Type: text/plain" CRLF "Content-Length: 21" CRLF \
"Connection: Close" CRLF CRLF "501 Not Implemented" CRLF
#define HTTP_RESPONSE_501_KEEPALIVE \
"" \
"HTTP/1.1 501 Not Implemented" CRLF "Server: " KBUILD_MODNAME CRLF \
"Content-Type: text/plain" CRLF "Content-Length: 21" CRLF \
"Connection: KeepAlive" CRLF CRLF "501 Not Implemented" CRLF
#define RECV_BUFFER_SIZE 4096
struct http_request {
struct socket *socket;
enum http_method method;
char request_url[128];
int complete;
};
static int http_server_recv(struct socket *sock, char *buf, size_t size)
{
struct kvec iov = {.iov_base = (void *) buf, .iov_len = size};
struct msghdr msg = {.msg_name = 0,
.msg_namelen = 0,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0};
return kernel_recvmsg(sock, &msg, &iov, 1, size, msg.msg_flags);
}
static int http_server_send(struct socket *sock, const char *buf, size_t size)
{
struct msghdr msg = {.msg_name = NULL,
.msg_namelen = 0,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0};
int done = 0;
while (done < size) {
struct kvec iov = {
.iov_base = (void *) ((char *) buf + done),
.iov_len = size - done,
};
int length = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (length < 0) {
pr_err("write error: %d\n", length);
break;
}
done += length;
}
return done;
}
static int _log10(size_t N)
{
int i;
unsigned int vals[] = {
1UL, 10UL, 100UL, 1000UL, 10000UL,
100000UL, 1000000UL, 10000000UL, 100000000UL, 1000000000UL,
};
for (i = 0; i < 9; ++i) {
if (N >= vals[i] && N < vals[i + 1]) {
break;
}
}
return i + 1;
}
static char *fib(unsigned long long k)
{
char *newstr, *p;
int index = 0;
bignum a, b;
int i = 31 - __builtin_clz(k);
bignum big_two;
int_to_bignum(0, &a);
int_to_bignum(1, &b);
int_to_bignum(2, &big_two);
for (; i >= 0; i--) {
bignum t1, t2;
bignum tmp1, tmp2;
multiply_bignum(&b, &big_two, &tmp1);
(void) subtract_bignum(&tmp1, &a, &tmp2);
multiply_bignum(&a, &tmp2, &t1);
multiply_bignum(&a, &a, &tmp1);
multiply_bignum(&b, &b, &tmp2);
(void) add_bignum(&tmp1, &tmp2, &t2);
copy(&a, &t1);
copy(&b, &t2);
if ((k & (1 << i)) > 0) {
(void) add_bignum(&a, &b, &t1);
copy(&a, &b);
copy(&b, &t1);
}
}
newstr = kmalloc(a.lastdigit + 1, GFP_KERNEL);
if (!newstr)
return NULL;
p = newstr;
/* copy the string. */
while (index < a.lastdigit) {
*p++ = a.digits[index];
index++;
}
*p = '\0';
return newstr;
}
static char *parse_url(char *url, int keep_alive)
{
size_t size;
char const *del = "/";
char *token, *tmp = url, *fib_res = NULL, *res;
char *msg = keep_alive ? HTTP_RESPONSE_200_KEEPALIVE_DUMMY
: HTTP_RESPONSE_200_DUMMY;
tmp++;
token = strsep(&tmp, del);
if (strcmp(token, "fib") == 0) {
/* expected tmp is number now */
long k;
kstrtol(tmp, 10, &k);
fib_res = fib((unsigned long long) k);
}
if (!fib_res)
return NULL;
size = strlen(msg) + strlen(fib_res) + _log10(strlen(fib_res)) - 4;
res = kmalloc(size, GFP_KERNEL);
snprintf(res, size, msg, strlen(fib_res), fib_res);
return res;
}
static int http_server_response(struct http_request *request, int keep_alive)
{
char *response;
pr_info("requested_url = %s\n", request->request_url);
if (request->method != HTTP_GET)
response = keep_alive ? HTTP_RESPONSE_501_KEEPALIVE : HTTP_RESPONSE_501;
else
response = parse_url(request->request_url, keep_alive);
if (response)
http_server_send(request->socket, response, strlen(response));
return 0;
}
static int http_parser_callback_message_begin(http_parser *parser)
{
struct http_request *request = parser->data;
struct socket *socket = request->socket;
memset(request, 0x00, sizeof(struct http_request));
request->socket = socket;
return 0;
}
static int http_parser_callback_request_url(http_parser *parser,
const char *p,
size_t len)
{
struct http_request *request = parser->data;
strncat(request->request_url, p, len);
return 0;
}
static int http_parser_callback_header_field(http_parser *parser,
const char *p,
size_t len)
{
return 0;
}
static int http_parser_callback_header_value(http_parser *parser,
const char *p,
size_t len)
{
return 0;
}
static int http_parser_callback_headers_complete(http_parser *parser)
{
struct http_request *request = parser->data;
request->method = parser->method;
return 0;
}
static int http_parser_callback_body(http_parser *parser,
const char *p,
size_t len)
{
return 0;
}
static int http_parser_callback_message_complete(http_parser *parser)
{
struct http_request *request = parser->data;
http_server_response(request, http_should_keep_alive(parser));
request->complete = 1;
return 0;
}
static void http_server_worker(struct work_struct *arg)
{
char *buf;
struct http_parser parser;
struct http_parser_settings setting = {
.on_message_begin = http_parser_callback_message_begin,
.on_url = http_parser_callback_request_url,
.on_header_field = http_parser_callback_header_field,
.on_header_value = http_parser_callback_header_value,
.on_headers_complete = http_parser_callback_headers_complete,
.on_body = http_parser_callback_body,
.on_message_complete = http_parser_callback_message_complete};
struct http_request request;
struct http_server_param *worker = container_of(arg, struct http_server_param, _worker);
struct socket *socket = (struct socket *)worker->listen_socket;//(struct socket *) arg;
allow_signal(SIGKILL);
allow_signal(SIGTERM);
buf = kmalloc(RECV_BUFFER_SIZE, GFP_KERNEL);
if (!buf) {
pr_err("can't allocate memory!\n");
return;
//return -1;
}
request.socket = socket;
http_parser_init(&parser, HTTP_REQUEST);
parser.data = &request;
while (!kthread_should_stop()) {
int ret = http_server_recv(socket, buf, RECV_BUFFER_SIZE - 1);
if (ret <= 0) {
if (ret)
pr_err("recv error: %d\n", ret);
break;
}
http_parser_execute(&parser, &setting, buf, ret);
if (request.complete && !http_should_keep_alive(&parser))
break;
}
kernel_sock_shutdown(socket, SHUT_RDWR);
sock_release(socket);
kfree(buf);
kfree(worker);
//return 0;
}
int http_server_daemon(void *arg)
{
struct socket *socket;
//struct task_struct *worker;
struct http_server_param *param = (struct http_server_param *) arg;
struct workqueue_struct *queue = alloc_workqueue("cmwq", WQ_UNBOUND, 1);
if(!queue){
pr_err("Can't create working queue");
return -ENOMEM;
}
allow_signal(SIGKILL);
allow_signal(SIGTERM);
while (!kthread_should_stop()) {
//struct http_server_param *args = kmalloc (sizeof(struct http_server_param), GFP_KERNEL);
int err = kernel_accept(param->listen_socket, &socket, 0);
if (err < 0) {
if (signal_pending(current))
break;
pr_err("kernel_accept() error: %d\n", err);
continue;
}
INIT_WORK(&param->_worker, http_server_worker);
//worker = kthread_run(http_server_worker, socket, KBUILD_MODNAME);
queue_work(queue, &param->_worker);
/*if (IS_ERR(worker)) {
pr_err("can't create more worker process\n");
continue;
}*/
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment