summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--http.c188
-rw-r--r--http.h33
2 files changed, 221 insertions, 0 deletions
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 <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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