diff options
author | Ian C <ianc@noddybox.co.uk> | 2024-12-07 19:10:58 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2024-12-07 19:10:58 +0000 |
commit | a7458d16077dff12ca82f03c3d52d2f2de35c9f2 (patch) | |
tree | 8b1821fc5be1b812050a121d25fda4fa40cc36ef /httpserve.c | |
parent | 93cdb0f2f70df596baea0bb8c36a7a764753527d (diff) |
Added POST support and rejigged reading of request.
Diffstat (limited to 'httpserve.c')
-rw-r--r-- | httpserve.c | 283 |
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); |