Parse HTTP requests and invoke the request handler.

This commit is contained in:
Jordan Bancino 2022-08-28 14:30:15 -04:00
parent d3e8886b69
commit d7d960b77d
3 changed files with 183 additions and 39 deletions

View file

@ -101,7 +101,7 @@ HttpRequestMethodFromString(const char *str)
return HTTP_PATCH; return HTTP_PATCH;
} }
return -1; return HTTP_METHOD_UNKNOWN;
} }
const char * const char *

View file

@ -34,6 +34,8 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <poll.h> #include <poll.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -72,50 +74,51 @@ struct HttpServerContext
static HttpServerContext * static HttpServerContext *
HttpServerContextCreate(HttpRequestMethod requestMethod, HttpServerContextCreate(HttpRequestMethod requestMethod,
char *requestPath, FILE *stream) char *requestPath, FILE * stream)
{ {
HttpServerContext *c; HttpServerContext *c;
c = malloc(sizeof(HttpServerContext)); c = malloc(sizeof(HttpServerContext));
if (!c) if (!c)
{ {
return NULL; return NULL;
} }
c->requestHeaders = HashMapCreate(); c->requestHeaders = HashMapCreate();
if (!c->requestHeaders) if (!c->requestHeaders)
{ {
free(c); free(c);
return NULL; return NULL;
} }
c->responseHeaders = HashMapCreate(); c->responseHeaders = HashMapCreate();
if (!c->responseHeaders) if (!c->responseHeaders)
{ {
free(c->requestHeaders); free(c->requestHeaders);
free(c); free(c);
return NULL; return NULL;
} }
c->requestMethod = requestMethod; c->requestMethod = requestMethod;
c->requestPath = requestPath; c->requestPath = requestPath;
c->stream = stream; c->stream = stream;
return c; return c;
} }
static void static void
HttpServerContextFree(HttpServerContext *c) HttpServerContextFree(HttpServerContext * c)
{ {
if (!c) if (!c)
{ {
return; return;
} }
HashMapFree(c->requestHeaders); HashMapFree(c->requestHeaders);
HashMapFree(c->responseHeaders); HashMapFree(c->responseHeaders);
free(c->requestPath); free(c->requestPath);
fclose(c->stream); fclose(c->stream);
}
static int static int
QueueConnection(HttpServer * server, int fd) QueueConnection(HttpServer * server, int fd)
@ -274,6 +277,19 @@ HttpServerWorkerThread(void *args)
while (!server->stop) while (!server->stop)
{ {
FILE *fp = DequeueConnection(server); FILE *fp = DequeueConnection(server);
HttpServerContext *context;
char *line = NULL;
size_t lineSize = 0;
ssize_t lineLen = 0;
char *requestMethodPtr;
char *pathPtr;
char *requestPath;
char *requestProtocol;
ssize_t i = 0;
HttpRequestMethod requestMethod;
if (!fp) if (!fp)
{ {
@ -283,12 +299,139 @@ HttpServerWorkerThread(void *args)
continue; continue;
} }
fprintf(fp, "HTTP/1.1 500 Internal Server Error\n"); /* Get the first line of the request */
fprintf(fp, "Server: Telodendria v" TELODENDRIA_VERSION "\n"); lineLen = getline(&line, &lineSize, fp);
fprintf(fp, "Content-Type: application/json\n"); if (lineLen == -1)
fprintf(fp, "\n"); {
fprintf(fp, "{}\n"); goto bad_request;
}
requestMethodPtr = line;
for (i = 0; i < lineLen; i++)
{
if (line[i] == ' ')
{
line[i] = '\0';
break;
}
}
if (i == lineLen)
{
goto bad_request;
}
requestMethod = HttpRequestMethodFromString(requestMethodPtr);
if (requestMethod == HTTP_METHOD_UNKNOWN)
{
goto bad_request;
}
pathPtr = line + i + 1;
for (i = 0; i < (line + lineLen) - pathPtr; i++)
{
if (pathPtr[i] == ' ')
{
pathPtr[i] = '\0';
break;
}
}
requestPath = malloc((i * sizeof(char)) + 1);
strcpy(requestPath, pathPtr);
requestProtocol = &pathPtr[i + 1];
line[lineLen - 2] = '\0'; /* Get rid of \r and \n */
if (strcmp(requestProtocol, "HTTP/1.1") != 0 && strcmp(requestProtocol, "HTTP/1.0") != 0)
{
printf("Bad protocol: [%s]\n", requestProtocol);
goto bad_request;
}
context = HttpServerContextCreate(requestMethod, requestPath, fp);
if (!context)
{
goto internal_error;
}
while ((lineLen = getline(&line, &lineSize, fp)) != -1)
{
char *headerKey;
char *headerValue;
char *headerPtr;
ssize_t i;
if (strcmp(line, "\r\n") == 0)
{
break;
}
for (i = 0; i < lineLen; i++)
{
if (line[i] == ':')
{
line[i] = '\0';
break;
}
line[i] = tolower(line[i]);
}
headerKey = malloc((i * sizeof(char)) + 1);
if (!headerKey)
{
goto internal_error;
}
strcpy(headerKey, line);
headerPtr = line + i + 1;
while (isspace(*headerPtr))
{
headerPtr++;
}
for (i = lineLen - 1; i > (line + lineLen) - headerPtr; i--)
{
if (!isspace(line[i]))
{
break;
}
line[i] = '\0';
}
headerValue = malloc(strlen(headerPtr) + 1);
if (!headerValue)
{
goto internal_error;
}
strcpy(headerValue, headerPtr);
HashMapSet(context->requestHeaders, headerKey, headerValue);
}
server->requestHandler(context, server->handlerArgs);
HttpServerContextFree(context);
goto finish;
internal_error:
fprintf(fp, "HTTP/1.0 500 Internal Server Error\n");
fprintf(fp, "Connection: close\n");
goto finish;
bad_request:
fprintf(fp, "HTTP/1.0 400 Bad Request\n");
fprintf(fp, "Connection: close\n");
goto finish;
finish:
free(line);
fclose(fp); fclose(fp);
} }

View file

@ -26,6 +26,7 @@
typedef enum HttpRequestMethod typedef enum HttpRequestMethod
{ {
HTTP_METHOD_UNKNOWN,
HTTP_GET, HTTP_GET,
HTTP_HEAD, HTTP_HEAD,
HTTP_POST, HTTP_POST,