From 28d3eb7ec11b9724b1b1e9670939f2463281a3ad Mon Sep 17 00:00:00 2001 From: Aki Date: Tue, 12 May 2020 16:57:53 +0200 Subject: Implemented method, path and version parsing --- http.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ http.h | 33 ++++++++++++ 2 files changed, 221 insertions(+) diff --git a/http.c b/http.c index a966121..c61b47b 100644 --- a/http.c +++ b/http.c @@ -1,5 +1,6 @@ #include "http.h" +#include #include #include #include @@ -16,6 +17,42 @@ const char * status_str[] = { [STATUS_VERSION_NOT_SUPPORTED] = "505 Version Not Supported", }; +/// Allocates and initializes request structure. +/// \return Pointer to initialized request +/// \see /free_request +struct request * new_request(void) +{ + struct request * request = malloc(sizeof(struct request)); + + if (NULL == request) + { + return NULL; + } + + memset(request, 0, sizeof(struct request)); + request->step = parse_method; + + return request; +} + +/// Releases memory used by the request and the request itself. +/// \param request Request to free +/// \see /new_request +void free_request(struct request * request) +{ + if (NULL != request->data) + { + // TODO: free(request->data); data lifetime is now managed by plop, move it here + } + + if (NULL != request->headers) + { + free(request->headers); + } + + free(request); +} + /// Sends a simple response only with a status to the client. /// \param fd File descriptor of the client socket /// \param status HTTP response status code @@ -78,7 +115,158 @@ int collect_request(const int fd, char ** request) return -1; // TODO: Handle errors properly } + // TODO: Use struct request in plop + struct request * debug = new_request(); + debug->data = *request; + debug->length = length; + debug->step(debug); + + printf( + "(%.*s) [%.*s] %.*s\n", + debug->version.length, debug->data + debug->version.start, + debug->method.length, debug->data + debug->method.start, + debug->path.length, debug->data + debug->path.start); + + free_request(debug); + (*request)[length] = 0; return length; } + + +/// Progresses the readout of the request until delimiter is found. +/// \param request Request to process +/// \param delimiter Character that marks the end of the readout +/// \return Position of the delimiter or 0 if the deliminter could not be found +static int read_until_char(struct request * request, const char delimiter) +{ + char character; + while (request->position < request->length) + { + character = request->data[request->position]; + if (delimiter == character) + { + return request->position; + } + request->position++; + } + return 0; +} + +/// Progresses the readout of the request until non-whitespace character is found. +/// \param request Request to process +/// \return Position of the first non-whitespace character or 0 if it could not be found +/// \see isalpha(3) +static int read_until_word(struct request * request) +{ + char character; + while (request->position < request->length) + { + character = request->data[request->position]; + if (!isspace(character)) + { + return request->position; + } + request->position++; + } + return 0; +} + +/// Progresses the readout of the request until line separator is found. +/// \param request Request to process +/// \return Position of the first byte of the separator or 0 if line separator could not be found +static int read_rest_of_line(struct request * request) +{ + char buffer[2] = {0, 0}; + while (request->position < request->length - 1) + { + memcpy(buffer, &request->data[request->position], 2); + if (0 == strncmp(buffer, "\r\n", 2)) + { + return request->position; + } + if (buffer[1] == '\r') + { + request->position += 1; + continue; + } + request->position += 2; + } + return 0; +} + +/// Parses method field of the request. +/// \param request Request to process +/// \return -1 if an error has occured, 0 if too little data available or total number of bytes processed +int parse_method(struct request * request) +{ + request->method.length = read_until_char(request, ' '); + + if (0 == request->method.length) + { + return 0; + } + + request->step = parse_path; + + return parse_path(request); +} + +/// Parses path field of the request. +/// \param request Request to process +/// \return -1 if an error has occured, 0 if too little data available or total number of bytes processed +// TODO: Consider spliting path into an actual path and arguments in this stage +int parse_path(struct request * request) +{ + if (0 >= request->path.start) + { + request->path.start = read_until_word(request); + + if (0 == request->path.start) + { + return 0; + } + } + + const int result = read_until_char(request, ' '); + + if (0 == result) + { + return 0; + } + + request->path.length = request->position - request->path.start; + request->step = parse_version; + + return parse_version(request); +} + +/// Parses and verifies http version field of the request. +/// \param request Request to process +/// \return -1 if an error has occured, 0 if too little data available or total number of bytes processed +// TODO: Return -1 if version is unsupported, meaning other than HTTP/1.1 +int parse_version(struct request * request) +{ + if (0 >= request->version.start) + { + request->version.start = read_until_word(request); + + if (0 == request->version.start) + { + return 0; + } + } + + const int result = read_rest_of_line(request); + + if (0 == result) + { + return 0; + } + + request->version.length = request->position - request->version.start; + request->step = NULL; // TODO: Parse headers + + return request->position; +} diff --git a/http.h b/http.h index e0d7891..4629846 100644 --- a/http.h +++ b/http.h @@ -1,6 +1,31 @@ #ifndef HTTP_H #define HTTP_H +struct span +{ + int start; + int length; +}; + +struct header +{ + struct span name; + struct span value; +}; + +struct request +{ + int (* step)(struct request *); + char * data; + int length; + int position; + struct span method; + struct span path; + struct span version; + struct header * headers; + struct span body; +}; + enum status { STATUS_OK = 200, @@ -14,8 +39,16 @@ enum status extern const char * status_str[]; +struct request * new_request(void); +void free_request(struct request *); + int respond_only_status(int, enum status); int respond_with_body(int, enum status, const char *, int); + int collect_request(int, char **); +int parse_method(struct request *); +int parse_path(struct request *); +int parse_version(struct request *); + #endif // HTTP_H -- cgit v1.1