commit a063e39067588c7ec8d75a4528a8ad3a5e6b499b
parent 6832d4043024ccc6d9d796e7e5b1c6bfc3c57b19
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 19 Nov 2020 11:52:46 -0800
header parsing
Diffstat:
| M | src/wolfsocks.c |  |  | 146 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ | 
1 file changed, 125 insertions(+), 21 deletions(-)
diff --git a/src/wolfsocks.c b/src/wolfsocks.c
@@ -8,18 +8,14 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <ctype.h>
+#include <assert.h>
 
 #include "cursor.h"
 
 #define BUF_SIZE 4096
 #define ARENA_SIZE 8192
 
-struct http_req {
-	char *method;
-	char *path;
-	char *ver;
-};
-
 struct parser {
 	struct cursor cur;
 	struct cursor arena;
@@ -28,15 +24,24 @@ struct parser {
 struct http_header {
 	char *name;
 	char *value;
+	struct http_header *next;
 };
 
-static void error(char *msg) 
+struct http_req {
+	char *method;
+	char *path;
+	char *ver;
+	struct http_header *headers;
+};
+
+static void error(char *msg)
 {
 	perror(msg);
 	exit(1);
 }
 
-static int parse_segment(struct parser *p, char **seg)
+
+static int parse_segment_with(struct parser *p, char **seg, char delim)
 {
 	char c;
 
@@ -46,20 +51,55 @@ static int parse_segment(struct parser *p, char **seg)
 		if (!pull_byte(&p->cur, (unsigned char*)&c))
 			return 0;
 
-		if (c == ' ' || c == '\r' || c == '\n') {
+		if (c == delim) {
 			if (!push_byte(&p->arena, 0))
 				return 0;
-			break;
-		} else {
-			if (!push_byte(&p->arena, c))
-				return 0;
+			return 1;
 		}
+
+		if (!push_byte(&p->arena, c))
+			return 0;
 	}
 
 	return 1;
 }
 
-static int parse_header_segment(struct http_req *req, struct parser *p)
+static int consume_char(struct cursor *cur, char match)
+{
+	char c;
+	return pull_byte(cur, (unsigned char*)&c) && c == match;
+}
+
+static int parse_segment(struct parser *p, char **seg)
+{
+	return parse_segment_with(p, seg, ' ');
+}
+
+static int parse_last_segment(struct parser *p, char **seg)
+{
+	if (!parse_segment_with(p, seg, '\r'))
+		return 0;
+
+	/* don't consume crlf yet */
+	p->cur.p--;
+
+	return 1;
+}
+
+static int parse_header_name(struct parser *p, char **name)
+{
+	if (!parse_segment_with(p, name, ':'))
+		return 0;
+
+	return consume_char(&p->cur, ' ');
+}
+
+static int consume_crlf(struct cursor *cur)
+{
+	return consume_char(cur, '\r') && consume_char(cur, '\n');
+}
+
+static int parse_request_line(struct http_req *req, struct parser *p)
 {
 	if (!parse_segment(p, &req->method))
 		return 0;
@@ -67,23 +107,85 @@ static int parse_header_segment(struct http_req *req, struct parser *p)
 	if (!parse_segment(p, &req->path))
 		return 0;
 
-	if (!parse_segment(p, &req->ver))
+	if (!parse_last_segment(p, &req->ver))
 		return 0;
 
-	return 1;
+	return consume_crlf(&p->cur);
+}
+
+static int parse_header(struct http_header **prev, struct parser *p)
+{
+	struct http_header next;
+	struct http_header *next_p;
+	next.next = NULL;
+
+	assert(*prev == NULL || !(*prev)->next);
+
+	printf("parsing header name...\n");
+
+	if (!parse_header_name(p, &next.name))
+		return 0;
+
+	printf("got header name: %s\n", next.name);
+
+	if (!parse_last_segment(p, &next.value))
+		return 0;
+
+	printf("got header value: %s\n", next.value);
+
+	next_p = (struct http_header *)p->arena.p;
+
+	if (!push_data(&p->arena, (unsigned char*)&next, sizeof(next)))
+		return 0;
+
+	if (*prev == NULL)
+		*prev = next_p;
+	else
+		(*prev)->next = next_p;
+
+	printf("consuming crlf: %.*s\n", 2, p->cur.p);
+
+	return consume_crlf(&p->cur);
 }
 
+
 static int parse_http_request(struct http_req *req, struct parser *p)
 {
-	return parse_header_segment(req, p);
+	struct http_header **header;
+	struct cursor back;
+
+	if (!parse_request_line(req, p))
+		return 0;
+
+	printf("got request line\n");
+
+	header = &req->headers;
+
+	while (1) {
+		copy_cursor(&p->cur, &back);
+		if (!parse_header(header, p)) {
+			copy_cursor(&back, &p->cur);
+			break;
+		}
+		header = &(*header)->next;
+	}
+
+	printf("after headers: %.*s\n", 5, p->cur.p);
+
+	return consume_crlf(&p->cur);
 }
 
 static void print_http_request(struct http_req *req)
 {
+	struct http_header *header;
 	printf("%s %s %s\r\n", req->method, req->path, req->ver);
+	header = req->headers;
+	for (; header; header = header->next) {
+		printf("%s: %s\n", header->name, header->value);
+	}
 }
 
-void run_http_server() 
+void run_http_server()
 {
 	static unsigned char arena[ARENA_SIZE];
 	static unsigned char buffer[BUF_SIZE];
@@ -98,6 +200,7 @@ void run_http_server()
 	int optval;
 
 	struct http_req req;
+	memset(&req, 0, sizeof(req));
 
 	struct parser parser;
 
@@ -111,7 +214,7 @@ void run_http_server()
 	}
 
 	optval = 1;
-	setsockopt(parent, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, 
+	setsockopt(parent, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval,
 			sizeof(optval));
 
 	memset(&server_addr, 0, sizeof(server_addr));
@@ -158,8 +261,9 @@ void run_http_server()
 			if (len == 0) {
 				break;
 			}
-				
+
 			if (parse_http_request(&req, &parser)) {
+				printf("\nPARSED:\n\n");
 				print_http_request(&req);
 			}
 
@@ -179,7 +283,7 @@ void run_http_server()
 	}
 }
 
-int main(int argc, char *argv[]) 
+int main(int argc, char *argv[])
 {
 	run_http_server();