diff options
-rw-r--r-- | file.c | 4 | ||||
-rw-r--r-- | http.c | 139 | ||||
-rw-r--r-- | http.h | 1 |
3 files changed, 104 insertions, 40 deletions
@@ -126,6 +126,9 @@ static void cgi_setup_env(const char *filepath, const struct http_request *req) if (req->query_str) setenv("QUERY_STRING", req->query_str, 1); + if (req->cookie) + setenv("HTTP_COOKIE", req->cookie, 1); + if (req->body) { static char intbuf[20]; static char *content_type = "application/x-www-form-urlencoded"; @@ -196,6 +199,7 @@ int file_read_cgi(const char *filepath, const char *program, const struct http_r write(sockfd, buffer, bytes_read); unsetenv("QUERY_STRING"); + unsetenv("HTTP_COOKIE"); unsetenv("CONTENT"); unsetenv("CONTENT_LENGTH"); @@ -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); } @@ -23,6 +23,7 @@ struct http_request { int method; char *uri; char *query_str; + char *cookie; char *body; }; |