commit 2421ea92eba33682a20571fc7817b96341294546 Author: Ruthenic Date: Sun May 1 18:24:06 2022 -0400 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd46336 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC ?= gcc +target ?= $(shell ${CC} -dumpmachine) +CC_FLAGS ?= +CC_FLAGS := ${CC_FLAGS} -Ilib +DESTDIR ?= / + +.PHONY: all debug clean build-release install +all: + @$(shell mkdir -p bin) +all: main + +main: + @${CC} -o bin/httpsrv src/main.c ${CC_FLAGS} + +debug: CC_FLAGS:=-g -O0 -v ${CC_FLAGS} +debug: all + +clean: + @rm -rf bin + +release: CC_FLAGS:=-O3 ${CC_FLAGS} +release: all +release: + @strip bin/* + tar -czf release.tar.gz bin/* + +install: + @install -m 777 bin/httpsrv ${DESTDIR}/usr/local/bin diff --git a/bin/httpsrv b/bin/httpsrv new file mode 100755 index 0000000..bd95929 Binary files /dev/null and b/bin/httpsrv differ diff --git a/bin/main b/bin/main new file mode 100755 index 0000000..5840d7c Binary files /dev/null and b/bin/main differ diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..b0910f9 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1 @@ +-Ilib diff --git a/lib/file.h b/lib/file.h new file mode 100644 index 0000000..30c0599 --- /dev/null +++ b/lib/file.h @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +char* readFile(char* path) { + path++; + if (strlen(path) <= 1) { + printf("/ converted to /index.html\n"); + path = (char*)malloc(11); + path = "index.html"; + } + + FILE* fp = fopen(path, "rb"); + if (!(access(path, F_OK) == 0)) { + errno = -1; + return ""; + } + char* buf = NULL; + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + rewind(fp); + + buf = (char*)malloc(sizeof(char) * (length + 1)); + fread(buf, sizeof(char), length, fp); + buf[length] = '\0'; + + char* tmp = buf; + return tmp; +} diff --git a/lib/reqHeader.h b/lib/reqHeader.h new file mode 100644 index 0000000..cef15f7 --- /dev/null +++ b/lib/reqHeader.h @@ -0,0 +1,22 @@ +#include +#include +#include + +typedef struct { + char method[16]; + char path[256]; + char protocol[10]; +} ReqHeader; + +ReqHeader readReqHeader(int client) { + char buf[1024]; + read(client, buf, 1024); + ReqHeader header; + strcpy(header.method, strtok(buf, " ")); + strcpy(header.path, strtok(NULL, " ")); + strcpy(header.protocol, strtok(NULL, " ")); + if (header.path[1] == '\0') { + strcpy(header.path, "/index.html"); + } + return header; +} diff --git a/lib/resHeader.h b/lib/resHeader.h new file mode 100644 index 0000000..2051296 --- /dev/null +++ b/lib/resHeader.h @@ -0,0 +1,12 @@ +#include + +typedef struct { + int status; + char* notice; + char* mime; + char* protocol; +} ResHeader; + +void makeHeader(char* out, ResHeader* header) { + sprintf(out, "%s %d %s\r\nContent-Type: %s\r\n\n", header->protocol, header->status, header->notice, header->mime); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9c2deea --- /dev/null +++ b/src/main.c @@ -0,0 +1,69 @@ +#define _GNU_SOURCE +#include +#include + +#include "srv.c" + +#define DEFAULT_ADDR "127.0.0.1" +#define DEFAULT_PORT 7677 + +void help(char* exe) { + char* help = "%s - a simple webserver.\n" + " --help: this help message\n" + " --addr [addr], -a [addr]: set the ip address to bind to (default: 127.0.0.1)\n" + " --port [port], -p [port]: set the port to bind to (default: 7677)\n" + " --dir [directory], -d [directory]: set the directory to serve out of (default: the CWD)\n"; + printf(help, exe); +} + +int main(int argc, char** argv) { + char* ADDR = DEFAULT_ADDR; + int PORT = DEFAULT_PORT; + + int c; + int opt_idx; + char* longopt; + struct option long_options[] = { + {"addr", required_argument, 0, 'a'}, + {"port", required_argument, 0, 'p'}, + {"dir", required_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + while (1) { + c = getopt_long(argc, argv, "a:p:d:h", long_options, &opt_idx); + + if (c==-1) break; + + switch (c) { + case 0: + longopt = (char*)long_options[opt_idx].name; + printf("%s\n", longopt); + if (!strcmp(longopt, "help")) { + help(argv[0]); + exit(0); + } else if (!strcmp(longopt, "addr")) { + ADDR = optarg; + } else if (!strcmp(longopt, "port")) { + PORT = atoi(optarg); + } else if (!strcmp(longopt, "dir")) { + chdir(optarg); + } + break; + case 'a': + ADDR = optarg; + break; + case 'p': + PORT = atoi(optarg); + break; + case 'd': + chdir(optarg); + break; + case 'h': + help(argv[0]); + exit(0); + } + } + + srv(ADDR, PORT); +} diff --git a/src/srv.c b/src/srv.c new file mode 100644 index 0000000..7d001f3 --- /dev/null +++ b/src/srv.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resHeader.h" +#include "reqHeader.h" +#include "file.h" + +int srv(char* ADDR, int PORT) { + char buffer[BUFSIZ]; + int srvSocket; + struct sockaddr_in srvAddr; + + srvSocket = socket(AF_INET, SOCK_STREAM, 0); + + srvAddr.sin_family = AF_INET; + srvAddr.sin_port = htons(PORT); + srvAddr.sin_addr.s_addr = inet_addr(ADDR); + + if (bind( + srvSocket, + (struct sockaddr*) &srvAddr, + sizeof(srvAddr) + ) < 0) { + printf("Error: Failed to bind to %s:%d.\n", ADDR, PORT); + return 1; + } + + if (listen(srvSocket, 10) < 0) { + printf("Error: Unable to listen on %s:%d.\n", ADDR, PORT); + return 1; + } + + printf("Server (probably) listening on http://%s:%d\n", ADDR, PORT); + + while(1) { + int client = accept(srvSocket, NULL, NULL); + ReqHeader request = readReqHeader(client); + char* tPath = request.path; + char* dot = strrchr(tPath, '.'); + + char *file = readFile(request.path); + ResHeader rawHeader; + rawHeader.protocol = request.protocol; + rawHeader.status = 200; + rawHeader.notice = "OK"; + if (!dot || dot == tPath) { rawHeader.mime = "text/html"; } else { + /*switch (dot) { + "html": + rawHeader.mime = "text/html"; + "css": + rawHeader.mime = "text/css"; + "js": + rawHeader.mime = "text/javascript"; + default: + rawHeader.mime = "text/plain"; + }*/ + if (!strcmp(dot, ".html")) { + rawHeader.mime = "text/html"; + } else if (!strcmp(dot, ".css")) { + rawHeader.mime = "text/css"; + } else if (!strcmp(dot, ".ico")) { + rawHeader.mime = "image/vnd.microsoft.icon"; + } else if (!strcmp(dot, ".js")) { + rawHeader.mime = "text/javascript"; + } else { + rawHeader.mime = "text/plain"; + } + } + if (errno == -1) { + rawHeader.status = 404; + rawHeader.notice = "Not Found"; + } else if (errno > 0) { + rawHeader.status = 500; + rawHeader.notice = "Internal Server Error"; + } + errno = 0; + printf("%d %s\n", rawHeader.status, request.path); + char tmpHeader[1024]; + makeHeader(tmpHeader, &rawHeader); + char resHeader[1024]; //= "HTTP/1.1 200 OK\r\n\nHello, world!"; + sprintf(resHeader, "%s%s", (char*)tmpHeader, file); + send(client, resHeader, strlen(resHeader), 0); + close(client); + } +}