summaryrefslogtreecommitdiff
path: root/http.c
diff options
context:
space:
mode:
authorcflip <cflip@cflip.net>2023-08-07 09:44:56 -0600
committercflip <cflip@cflip.net>2023-08-07 09:44:56 -0600
commit89c62ba0739421b44c2f6d54a89011d81bf9e57c (patch)
treea87cdf1dcacf248bad5d0b2d62c507ab943e5640 /http.c
parent555f21649b909fd37a6153843249d72ca80ac427 (diff)
Improve HTTP request parsing and pass cookies to CGI scriptsHEADmaster
The HTTP request parser has been mostly rewritten, and now it is able to parse header lines. This makes it possible to get the cookie string from the request and pass it to CGI scripts, which was needed for the cflip forum to retain login status. It is now possible to run the forum through cfws, log in and submit threads and posts!
Diffstat (limited to 'http.c')
-rw-r--r--http.c139
1 files changed, 99 insertions, 40 deletions
diff --git a/http.c b/http.c
index 3521e32..53d300f 100644
--- a/http.c
+++ b/http.c
@@ -1,69 +1,126 @@
#include "http.h"
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-struct http_request http_parse_request(const char *reqstr)
+ssize_t http_parse_request_line(const char *reqline, struct http_request *req_out)
{
char uribuf[CFWS_MAXURI];
size_t urilen = 0, query_len = 0;
- const char *counter;
- struct http_request req;
- req.method = HTTP_METHOD_UNKNOWN;
- req.uri = NULL;
- req.query_str = NULL;
- req.body = NULL;
+ const char *ch = reqline;
- if (strncmp(reqstr, "GET ", 4) == 0) {
- req.method = HTTP_METHOD_GET;
- counter = reqstr + 4;
- } else if (strncmp(reqstr, "POST ", 5) == 0) {
- req.method = HTTP_METHOD_POST;
- counter = reqstr + 5;
+ /* Quick 'n' dirty strncmp to determine the request method. */
+ if (strncmp(reqline, "GET ", 4) == 0) {
+ req_out->method = HTTP_METHOD_GET;
+ ch += 4;
+ } else if (strncmp(reqline, "POST ", 5) == 0) {
+ req_out->method = HTTP_METHOD_POST;
+ ch += 5;
} else {
- fprintf(stderr, "Unhandled request type: %s\n", reqstr);
- return req;
+ fprintf(stderr, "Unhandled request type: '%s'\n", reqline);
+ return -1;
+ }
+
+ /* The first char after the first space should be a slash, otherwise
+ * there's either an invalid URI or something we don't handle yet. */
+ if (*ch != '/') {
+ fprintf(stderr, "Invalid URI string: '%s'\n", ch);
+ return -1;
}
- while (*counter != ' ' && *counter != '?' && *counter != 0
- && urilen < CFWS_MAXURI) {
- uribuf[urilen++] = *counter;
- ++counter;
+ /* Copy URI path into a buffer, until we reach a space, query string, or
+ * one of the buffers reaches the end. */
+ while (*ch != ' ' && *ch != '?' && *ch != 0 && urilen < CFWS_MAXURI) {
+ uribuf[urilen++] = *ch;
+ ++ch;
}
- req.uri = malloc(urilen + 1);
- memcpy(req.uri, uribuf, urilen);
- req.uri[urilen] = '\0';
+ req_out->uri = malloc(urilen + 1);
+ memcpy(req_out->uri, uribuf, urilen);
+ req_out->uri[urilen] = '\0';
/* Parse the query string if one exists. */
- if (*counter == '?') {
- /* Skip the question mark at the beginning. */
- counter++;
-
- while (*counter != ' ' && *counter != 0
- && query_len < CFWS_MAXURI) {
- uribuf[query_len++] = *counter;
- ++counter;
+ if (*ch == '?') {
+ ch++; /* Skip the question mark at the beginning. */
+
+ while (*ch != ' ' && *ch != 0 && query_len < CFWS_MAXURI) {
+ uribuf[query_len++] = *ch;
+ ++ch;
}
- req.query_str = malloc(query_len + 1);
- memcpy(req.query_str, uribuf, query_len);
- req.query_str[query_len] = '\0';
+ req_out->query_str = malloc(query_len + 1);
+ memcpy(req_out->query_str, uribuf, query_len);
+ req_out->query_str[query_len] = '\0';
+ }
+
+ /* Lastly, read the HTTP version and for now make sure it's HTTP/1.1. */
+ if (strncmp(ch, " HTTP/1.1\r\n", 9) != 0) {
+ fprintf(stderr, "Invalid HTTP version: '%s'", ch);
+ return -1;
}
- /* TODO: Parse request headers. For now they are just ignored. */
- counter = strstr(reqstr, "\r\n\r\n");
- if (req.method == HTTP_METHOD_POST) {
- size_t body_len;
- counter += 4;
+ ch += 9;
+ return ch - reqline;
+}
+
+struct http_request http_parse_request(const char *reqstr)
+{
+ const char *current_line = reqstr;
+ bool has_parsed_header = false;
- body_len = strlen(counter);
+ struct http_request req;
+ req.method = HTTP_METHOD_UNKNOWN;
+ req.uri = NULL;
+ req.query_str = NULL;
+ req.cookie = NULL;
+ req.body = NULL;
+
+ while (current_line) {
+ char *next_line = strstr(current_line, "\r\n");
+ size_t line_length = 0;
+ if (next_line) {
+ line_length = next_line - current_line;
+ *next_line = '\0';
+ next_line++;
+ }
+
+ if (!has_parsed_header) {
+ http_parse_request_line(current_line, &req);
+ has_parsed_header = true;
+ } else {
+ if (line_length == 0) {
+ /* An empty line means we are at the end of the
+ * request headers. */
+ current_line = next_line ? (next_line + 1) : NULL;
+ break;
+ }
+
+ if (strncmp(current_line, "Cookie: ", 8) == 0) {
+ const char *cookie_str = current_line + 8;
+ size_t cookie_length = next_line - cookie_str;
+
+ req.cookie = malloc(cookie_length + 1);
+ memcpy(req.cookie, cookie_str, cookie_length);
+ req.cookie[cookie_length] = '\0';
+ }
+ }
+
+ if (next_line)
+ *next_line = '\n';
+ current_line = next_line ? (next_line + 1) : NULL;
+ }
+
+ /* Copy the request body if we have one */
+ if (current_line && *current_line != '\0') {
+ size_t body_len;
+ body_len = strlen(current_line);
req.body = malloc(body_len + 1);
- memcpy(req.body, counter, body_len);
+ memcpy(req.body, current_line, body_len);
req.body[body_len] = '\0';
}
@@ -75,6 +132,8 @@ void http_free_request(struct http_request *req)
free(req->uri);
if (req->query_str)
free(req->query_str);
+ if (req->cookie)
+ free(req->cookie);
if (req->body)
free(req->body);
}