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();