summaryrefslogtreecommitdiff
path: root/httpserve.c
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2024-12-07 19:10:58 +0000
committerIan C <ianc@noddybox.co.uk>2024-12-07 19:10:58 +0000
commita7458d16077dff12ca82f03c3d52d2f2de35c9f2 (patch)
tree8b1821fc5be1b812050a121d25fda4fa40cc36ef /httpserve.c
parent93cdb0f2f70df596baea0bb8c36a7a764753527d (diff)
Added POST support and rejigged reading of request.
Diffstat (limited to 'httpserve.c')
-rw-r--r--httpserve.c283
1 files changed, 235 insertions, 48 deletions
diff --git a/httpserve.c b/httpserve.c
index 946f4ae..98045ad 100644
--- a/httpserve.c
+++ b/httpserve.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <errno.h>
#include <stdarg.h>
+#include <ctype.h>
#include <magic.h>
@@ -45,9 +46,9 @@ typedef struct
{
int fd;
magic_t magic;
+ int allow_post;
} thread_arg_t;
-
/* ---------------------------------------------------------------------------
BASE ROUTINES
---------------------------------------------------------------------------
@@ -65,6 +66,42 @@ static char *Strdup(const char *p)
return ret;
}
+static void Chomp(char *p)
+{
+ int len = strlen(p);
+
+ len--;
+
+ while(len >= 0 && (p[len] == '\r' || p[len] == '\n'))
+ {
+ p[len--] = 0;
+ }
+}
+
+static const char *GetFilename(const char *path)
+{
+ const char *p = strrchr(path, '/');
+
+ if (p)
+ {
+ if (*(p + 1))
+ {
+ return p + 1;
+ }
+ else
+ {
+ return p;
+ }
+ }
+
+ return path;
+}
+
+static int StrMatch(const char *a, const char *b)
+{
+ return strcasecmp(a,b) == 0;
+}
+
static void *Malloc(size_t s)
{
void *ret = malloc(s);
@@ -120,6 +157,36 @@ static char *PrintStream(char *stream, const char *format, ...)
return stream;
}
+static int SplitHeader(char *buff, char **setting, char **value)
+{
+ char *p = buff;
+
+ *setting = NULL;
+ *value = NULL;
+
+ while(*p && !isspace((unsigned char)*p) && *p != ':')
+ {
+ p++;
+ }
+
+ if (*p)
+ {
+ *setting = buff;
+ }
+
+ while (*p && (isspace((unsigned char)*p) || *p == ':'))
+ {
+ *p++ = 0;
+ }
+
+ if (*p)
+ {
+ *value = p;
+ }
+
+ return *setting && *value;
+}
+
/* ---------------------------------------------------------------------------
SOCKET ROUTINES
@@ -128,6 +195,7 @@ static char *PrintStream(char *stream, const char *format, ...)
static int Listen(int port)
{
struct sockaddr_in addr;
+ int sockopt = 1;
int fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
@@ -136,6 +204,13 @@ static int Listen(int port)
return -1;
}
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ &sockopt, sizeof sockopt) == -1)
+ {
+ perror("setsockopt");
+ return -1;
+ }
+
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
@@ -155,35 +230,59 @@ static int Listen(int port)
return fd;
}
-static int Read(int fd, char *buff, size_t max)
+static int Read(int fd, char *buff, size_t len)
{
int rd;
size_t tot = 0;
- while(tot < max)
+ while(tot < len)
{
- rd = read(fd, buff + tot, max - tot);
+ rd = read(fd, buff + tot, len - tot);
if (rd < 1)
{
- return 0;
+ return tot > 0 ? tot : 0;
}
tot += rd;
+ }
- if (tot < max)
- {
- buff[tot] = 0;
+ return tot;
+}
- if (tot > 4 && buff[tot - 4] == '\r' && buff[tot - 3] == '\n' &&
- buff[tot - 2] == '\r' && buff[tot - 1] == '\n')
- {
- return 1;
- }
+static int ReadByte(int fd, unsigned char *byte)
+{
+ if (read(fd, byte, 1) == 1)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static int ReadLine(int fd, char *line, size_t max_size)
+{
+ unsigned char c;
+ unsigned char last = 0;
+ size_t len = 0;
+
+ while(ReadByte(fd, &c) && len < max_size - 1)
+ {
+ line[len] = c;
+ line[len + 1] = 0;
+
+ if (c == '\n' && last == '\r')
+ {
+ Chomp(line);
+ return 1;
}
+
+ len++;
+ last = c;
}
- fprintf(stderr, "HTTP request too big\n");
return 0;
}
@@ -297,12 +396,14 @@ static void DecodeURL(char *dest, const char *url)
*dest = 0;
}
-static int ParseRequest(char *request,
- char **method,
- char **path,
- char **protocol,
- int *close)
+static int GetRequest(int fd,
+ char **method,
+ char **path,
+ char **protocol,
+ size_t *content_length,
+ int *close)
{
+ char buff[0x4000];
char *tok[3] = {0};
char *last;
int f;
@@ -310,34 +411,69 @@ static int ParseRequest(char *request,
*method = NULL;
*path = NULL;
*protocol = NULL;
+ *content_length = 0;
*close = 0;
- if ((tok[0] = strtok_r(request, " ", &last)) &&
- (tok[1] = strtok_r(NULL, " ", &last)) &&
- (tok[2] = strtok_r(NULL, " \r\n", &last)))
+ if (ReadLine(fd, buff, sizeof buff))
{
- char *tmppath;
+ if ((tok[0] = strtok_r(buff, " ", &last)) &&
+ (tok[1] = strtok_r(NULL, " ", &last)) &&
+ (tok[2] = strtok_r(NULL, " \r\n", &last)))
+ {
+ char *tmppath;
- *method = tok[0];
- *path = tok[1];
- *protocol = tok[2];
+ *method = Strdup(tok[0]);
+ *path = Strdup(tok[1]);
+ *protocol = Strdup(tok[2]);
- /* This is slightly dodgy as hell, but should work
- */
- *close = (strstr(*protocol + strlen(*protocol) + 3,
- "Connection: close\r\n") ||
- strcmp(*protocol, "HTTP/1.0") == 0);
+ tmppath = Strdup(*path);
+ DecodeURL(*path, tmppath);
+ free(tmppath);
- tmppath = Strdup(*path);
- DecodeURL(*path, tmppath);
- free(tmppath);
+ if(strcmp(*protocol, "HTTP/1.0") == 0)
+ {
+ *close = 1;
+ }
+ }
+
+ while(ReadLine(fd, buff, sizeof buff) && buff[0])
+ {
+ if (SplitHeader(buff, &tok[0], &tok[1]))
+ {
+ if (StrMatch(tok[0], "connection") && StrMatch(tok[1], "close"))
+ {
+ *close = 1;
+ }
+ else if (StrMatch(tok[0], "content-length"))
+ {
+ *content_length = (size_t)atoll(tok[1]);
+ }
+ }
+ }
return 1;
}
+ *close = 1;
return 0;
}
+static char *ReadContents(int fd, size_t len)
+{
+ char *contents = Malloc(len);
+
+ if (contents)
+ {
+ if (Read(fd, contents, len) != len)
+ {
+ free(contents);
+ contents = NULL;
+ }
+ }
+
+ return contents;
+}
+
/* ---------------------------------------------------------------------------
THREAD CODE
@@ -345,33 +481,35 @@ static int ParseRequest(char *request,
*/
static void *ThreadCode(void *p)
{
- char buff[4096];
char *method = NULL;
char *path = NULL;
char *protocol = NULL;
+ size_t content_len = 0;
int close_connection = 0;
thread_arg_t *args = p;
- while (Read(args->fd, buff, sizeof buff) && !close_connection)
+ while (!close_connection)
{
- if (ParseRequest(buff, &method, &path,
- &protocol, &close_connection))
+ if (GetRequest(args->fd, &method, &path,
+ &protocol, &content_len, &close_connection))
{
- if (strcmp(method, "GET") == 0)
- {
- printf("%s %s %s\n", method, path, protocol);
+ const char *filename;
+
+ printf("%s %s %s\n", method, path, protocol);
- path = basename(path);
+ filename = GetFilename(path);
- if (strcmp(path, "/") == 0)
+ if (strcmp(method, "GET") == 0)
+ {
+ if (strcmp(filename, "/") == 0)
{
printf("Generate listing for /\n");
GenerateListing(args->fd, protocol);
}
- else if (access(path, R_OK) == 0)
+ else if (access(filename, R_OK) == 0)
{
- printf("Generating download for %s\n", path);
- GenerateFile(args->fd, args->magic, path, protocol);
+ printf("Generating download for %s\n", filename);
+ GenerateFile(args->fd, args->magic, filename, protocol);
}
else
{
@@ -379,15 +517,58 @@ static void *ThreadCode(void *p)
GenerateStatus(args->fd, 404, "Not found", protocol);
}
}
+ else if (strcmp(method, "POST") == 0 && args->allow_post)
+ {
+ char *contents;
+
+ if ((contents = ReadContents(args->fd, content_len)))
+ {
+ int file = -1;
+
+ printf("Got body of %lu bytes\n",
+ (unsigned long)content_len);
+
+ if (!StrMatch(filename, "/") &&
+ (file = open(filename,
+ O_WRONLY|O_CREAT|O_EXCL)) != -1 &&
+ write(file, contents, content_len) ==
+ content_len)
+ {
+ GenerateStatus(args->fd, 200, "OK", protocol);
+ }
+ else
+ {
+ GenerateStatus(args->fd, 500,
+ "Failed to write contents", protocol);
+ }
+
+ if (file != -1)
+ {
+ close(file);
+ }
+
+ free(contents);
+ }
+ else
+ {
+ GenerateStatus(args->fd, 500,
+ "Failed to read contents from request",
+ protocol);
+ }
+ }
else
{
printf("Method %s not supported\n", method);
GenerateStatus(args->fd, 501, "Not implemented", protocol);
}
+
+ free(method);
+ free(path);
+ free(protocol);
}
else
{
- printf("Bad request %s\n", method);
+ printf("Bad request\n");
}
}
@@ -409,8 +590,9 @@ int main(int argc, char *argv[])
int opt;
int sock;
const char *dir=".";
+ int allow_post = 0;
- while((opt = getopt(argc, argv, "p:d:")) != -1)
+ while((opt = getopt(argc, argv, "p:d:a")) != -1)
{
switch(opt)
{
@@ -422,6 +604,10 @@ int main(int argc, char *argv[])
dir = optarg;
break;
+ case 'a':
+ allow_post = 1;
+ break;
+
default:
fprintf(stderr, "%s: usage %s [-p port] [-d dir]\n",
argv[0], argv[0]);
@@ -465,6 +651,7 @@ int main(int argc, char *argv[])
thread_args = Malloc(sizeof *thread_args);
thread_args->fd = fd;
thread_args->magic = magic;
+ thread_args->allow_post = allow_post;
pthread_create(&thread, NULL, ThreadCode, thread_args);
pthread_detach(thread);