diff options
author | cflip <cflip@cflip.net> | 2023-08-07 09:44:56 -0600 |
---|---|---|
committer | cflip <cflip@cflip.net> | 2023-08-07 09:44:56 -0600 |
commit | 89c62ba0739421b44c2f6d54a89011d81bf9e57c (patch) | |
tree | a87cdf1dcacf248bad5d0b2d62c507ab943e5640 /http.c | |
parent | 555f21649b909fd37a6153843249d72ca80ac427 (diff) |
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.c | 139 |
1 files changed, 99 insertions, 40 deletions
@@ -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); } |