commit 7f3fab3aab6a4de059277330fa52049033f022fc
Author: Enrique <enriquedelacalhacar@gmail.com>
Date:   Mon Jan 6 16:40:25 2025 +0100

    Initial commit

diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..07ff46c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+CC = cyg
+CFLAGS = -O0 -Wall -ggdb3
+LDFLAGS = -lssl -lcrypto
+
+SRCDIR = src
+OBJDIR = obj
+ifeq ($(OS),Windows_NT)
+	LIBDIR = C:/Users/quique/Documents/Programming/libs
+	RUNCMD = server.exe
+else
+	LIBDIR = /mnt/c/Users/quique/Documents/Programming/libs
+	RUNCMD = ./server.exe
+endif
+
+OBJS = $(addprefix $(OBJDIR)/, bit.o str.o log.o list.o crc64.o dir.o net.o ipc.o)
+
+INCL = -I$(LIBDIR) -I$(SRCDIR)
+
+
+
+all: server.exe worker.exe
+
+server.exe: $(SRCDIR)/main.c $(OBJS)
+	$(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o server.exe $(LDFLAGS)
+
+worker.exe: $(SRCDIR)/worker.c $(OBJS)
+	$(CC) $< $(OBJS) $(INCL) $(CFLAGS) -o worker.exe $(LDFLAGS)
+
+$(OBJDIR)/%.o: $(SRCDIR)/%/*.c $(SRCDIR)/%/*.h
+	$(CC) -c $(SRCDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)
+
+$(OBJDIR)/%.o: $(LIBDIR)/%/*.c $(LIBDIR)/%/*.h
+	$(CC) -c $(LIBDIR)/$*/$*.c $(INCL) $(CFLAGS) -o $(OBJDIR)/$*.o $(LDFLAGS)
+
+clean:
+	rm -f worker.exe server.exe $(OBJDIR)/*.o
+
+memcheck:
+	sudo valgrind --show-leak-kinds=all --leak-check=full --track-origins=yes --trace-children=yes -s $(RUNCMD) 443
+	
diff --git a/src/crc64/crc64.c b/src/crc64/crc64.c
new file mode 100755
index 0000000..9a3f845
--- /dev/null
+++ b/src/crc64/crc64.c
@@ -0,0 +1,191 @@
+/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
+ *
+ * Specification of this CRC64 variant follows:
+ * Name: crc-64-jones
+ * Width: 64 bites
+ * Poly: 0xad93d23594c935a9
+ * Reflected In: True
+ * Xor_In: 0xffffffffffffffff
+ * Reflected_Out: True
+ * Xor_Out: 0x0
+ * Check("123456789"): 0xe9c6d914c4b8d9ca
+ *
+ * Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. */
+
+#include <stdint.h>
+
+static const uint64_t crc64_tab[256] = {
+	UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
+	UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
+	UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
+	UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
+	UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
+	UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
+	UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
+	UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
+	UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
+	UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
+	UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
+	UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
+	UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
+	UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
+	UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
+	UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
+	UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
+	UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
+	UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
+	UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
+	UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
+	UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
+	UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
+	UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
+	UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
+	UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
+	UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
+	UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
+	UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
+	UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
+	UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
+	UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
+	UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
+	UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
+	UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
+	UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
+	UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
+	UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
+	UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
+	UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
+	UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
+	UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
+	UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
+	UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
+	UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
+	UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
+	UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
+	UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
+	UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
+	UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
+	UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
+	UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
+	UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
+	UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
+	UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
+	UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
+	UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
+	UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
+	UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
+	UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
+	UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
+	UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
+	UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
+	UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
+	UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
+	UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
+	UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
+	UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
+	UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
+	UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
+	UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
+	UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
+	UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
+	UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
+	UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
+	UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
+	UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
+	UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
+	UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
+	UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
+	UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
+	UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
+	UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
+	UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
+	UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
+	UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
+	UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
+	UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
+	UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
+	UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
+	UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
+	UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
+	UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
+	UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
+	UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
+	UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
+	UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
+	UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
+	UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
+	UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
+	UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
+	UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
+	UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
+	UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
+	UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
+	UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
+	UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
+	UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
+	UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
+	UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
+	UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
+	UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
+	UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
+	UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
+	UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
+	UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
+	UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
+	UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
+	UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
+	UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
+	UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
+	UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
+	UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
+	UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
+	UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
+	UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
+	UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
+	UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
+};
+
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
+	uint64_t j;
+
+	for (j = 0; j < l; j++) {
+		uint8_t byte = s[j];
+		crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
+	}
+	return crc;
+}
+
+/* Test main */
+#ifdef TEST_MAIN
+#include <stdio.h>
+int main(void) {
+	printf("e9c6d914c4b8d9ca == %016llx\n",
+		(unsigned long long) crc64(0,(unsigned char*)"123456789",9));
+	return 0;
+}
+#endif
diff --git a/src/crc64/crc64.h b/src/crc64/crc64.h
new file mode 100755
index 0000000..e031e76
--- /dev/null
+++ b/src/crc64/crc64.h
@@ -0,0 +1,11 @@
+#ifndef CRC64_H
+#define CRC64_H
+
+// Header file created because of project necessity
+// Source for c file: https://github.com/srned/baselib/blob/master/crc64.c
+
+#include <inttypes.h>
+
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
+
+#endif
diff --git a/src/dir/dir.c b/src/dir/dir.c
new file mode 100755
index 0000000..6c1f32d
--- /dev/null
+++ b/src/dir/dir.c
@@ -0,0 +1,72 @@
+#include "dir.h"
+
+
+uint64_t get_fd_size(int fd){
+	struct stat st;
+	if(fstat(fd, &st) == 0){
+		return st.st_size;
+	}
+	return 0;
+}
+
+uint64_t get_fp_size(FILE *fp){
+	if(fp != NULL){
+		return get_fd_size(fileno(fp));
+	}
+	return 0;
+}
+
+uint64_t get_file_size(char *filename){
+	struct stat st;
+	if(stat(filename, &st) == 0){
+		return st.st_size;
+	}
+	return 0;
+}
+
+struct str get_file_format(struct str filename){
+	int i = 0;
+	while(filename.len-i > 0){
+		if(filename.ptr[filename.len-i-1] == '.') break;
+		i++;
+	}
+	if(i == 0 || i == filename.len){
+		return ((struct str){0});
+	}
+	struct str fmt;
+	fmt.len = i;
+	fmt.ptr = calloc(fmt.len+1, sizeof(char));
+	if(fmt.ptr == NULL) return ((struct str){0});
+	memcpy(fmt.ptr, filename.ptr+filename.len-i, fmt.len);
+	return fmt;
+}
+
+uint64_t getNEntries(const char *dir){
+	uint64_t r = 0;
+	DIR *d = opendir(dir);
+	if(d){
+		seekdir(d, (unsigned)-1);
+		r = telldir(d) - 2; // . and ..
+		closedir(d);
+	}
+	return r;
+}
+
+char **getFiles(const char *dir){
+	/*int i = 0, n = getNEntries(dir);
+	if(n > 0){
+		char **r = calloc(n, sizeof(char*));
+		DIR *d = opendir(dir);
+		readdir(d); readdir(d); // . and ..
+		struct dirent *t;
+		while((t = readdir(d)) != NULL){
+			int l = len(t->d_name);
+			r[i] = calloc(l+1, sizeof(char));
+			memmove(r[i++], t->d_name, l);
+		}
+		closedir(d);
+		return r;
+	}*/
+	return NULL;
+}
+
diff --git a/src/dir/dir.h b/src/dir/dir.h
new file mode 100755
index 0000000..75ec02c
--- /dev/null
+++ b/src/dir/dir.h
@@ -0,0 +1,36 @@
+#ifndef DIR_H
+#define DIR_H
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "str/str.h"
+
+
+struct file {
+	struct str name;
+	bool temp;
+};
+
+
+uint64_t get_fd_size(int fd);
+
+uint64_t get_fp_size(FILE *fp);
+
+uint64_t get_file_size(char *filename);
+
+struct str get_file_format(struct str filename);
+
+uint64_t getNEntries(const char *dir);
+
+char **getFiles(const char *dir);
+
+#endif
diff --git a/src/ipc/ipc.c b/src/ipc/ipc.c
new file mode 100755
index 0000000..98ffe6e
--- /dev/null
+++ b/src/ipc/ipc.c
@@ -0,0 +1,143 @@
+#include "ipc.h"
+
+
+ipc_sender *setup_ipc_sender(struct str addr, int backlog){
+	ipc_sender *is = calloc(1, sizeof(ipc_sender));
+	is->addr = dup_str(addr);
+	is->ssocket = socket(AF_UNIX, SOCK_STREAM, 0);
+	if(is->ssocket == -1){
+		log_error("%s: socket: %s", __FUNCTION__, strerror(errno));
+		goto error;
+	}
+	struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
+	memcpy(sockaddr.sun_path, is->addr.ptr, is->addr.len);
+	sockaddr.sun_path[is->addr.len] = '\0';
+	unlink(is->addr.ptr);
+	if(bind(is->ssocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == -1){
+		log_error("%s: bind: %s", __FUNCTION__, strerror(errno));
+		goto error;
+	}
+	if(listen(is->ssocket, backlog) == -1){
+		log_error("%s: listen: %s", __FUNCTION__, strerror(errno));
+		goto error;
+	}
+
+	if(0){
+error:
+		destroy_ipc_sender(&is);
+	}
+	return is;
+}
+
+void destroy_ipc_sender(ipc_sender **is){
+	if(*is != NULL){
+		close((*is)->ssocket);
+		(*is)->ssocket = -1;
+		unlink((*is)->addr.ptr);
+		free_str(&(*is)->addr);
+		free(*is);
+		*is = NULL;
+	}
+}
+
+ipc_listener *setup_ipc_listener(struct str saddr){
+	ipc_listener *il = calloc(1, sizeof(ipc_listener));
+	il->saddr = dup_str(saddr);
+	il->csocket = socket(AF_UNIX, SOCK_STREAM, 0);
+	if(il->csocket == -1){
+		log_error("%s: socket: %s", __FUNCTION__, strerror(errno));
+		goto error;
+	}
+	struct sockaddr_un socksaddr = { .sun_family = AF_UNIX };
+	memcpy(socksaddr.sun_path, il->saddr.ptr, il->saddr.len);
+	socksaddr.sun_path[il->saddr.len] = '\0';
+	if(connect(il->csocket, (struct sockaddr *)&socksaddr, sizeof(socksaddr)) == -1){
+		log_error("%s: connect: %s", __FUNCTION__, strerror(errno));
+		goto error;
+	}
+
+	if(0){
+error:
+		destroy_ipc_listener(&il);
+	}
+	return il;
+}
+
+void destroy_ipc_listener(ipc_listener **il){
+	if(*il != NULL){
+		close((*il)->csocket);
+		(*il)->csocket = -1;
+		free_str(&(*il)->saddr);
+		free(*il);
+		*il = NULL;
+	}
+}
+
+void free_ipc_message(ipc_message im){
+	free_str(&im.key);
+	free_str(&im.val);
+}
+
+static inline struct str ipc_message_to_str(ipc_message msg){
+	struct str smsg = nstr(msg.key.len + msg.val.len + 2*sizeof(u32));
+	memcpy(smsg.ptr+smsg.len, &msg.key.len, sizeof(msg.key.len));
+	smsg.len += sizeof(msg.key.len);
+	copy_str(smsg, msg.key);
+	memcpy(smsg.ptr+smsg.len, &msg.val.len, sizeof(msg.val.len));
+	smsg.len += sizeof(msg.val.len);
+	copy_str(smsg, msg.val);
+	return smsg;
+}
+
+int send_ipc_message(int to, ipc_message msg){
+	struct str smsg = ipc_message_to_str(msg);
+	if(send(to, smsg.ptr, smsg.len, 0) == -1){
+		log_error("cant send message to socket %d: %s", to, strerror(errno));
+		free_str(&smsg);
+		return 1;
+	}
+	free_str(&smsg);
+	char buf[2];
+	if(recv(to, buf, 2, 0) == -1){
+		log_error("receiving OK from listener");
+		return 1;
+	}
+	if(strncmp(buf, "OK", 2) != 0){
+		log_error("received '%s' from listener instead of 'OK'", buf);
+		return 1;
+	}
+	return 0;
+}
+
+static inline ipc_message str_to_ipc_message(struct str smsg){
+	struct ipc_message msg;
+	u32 l;
+	memcpy(&l, smsg.ptr, sizeof(l));
+	smsg.ptr += sizeof(l);
+	msg.key = nstr(l);
+	msg.key.len = l;
+	memcpy(msg.key.ptr, smsg.ptr, l);
+	smsg.ptr += l;
+	memcpy(&l, smsg.ptr, sizeof(l));
+	smsg.ptr += sizeof(l);
+	msg.val = nstr(l);
+	msg.val.len = l;
+	memcpy(msg.val.ptr, smsg.ptr, l);
+	return msg;
+}
+
+ipc_message receive_ipc_message(ipc_listener *il){
+	struct str smsg = nstr(MAX_IPC_MSG_LEN); // we are gonna have to poll btw
+	ipc_message msg = {0};
+	smsg.len = recv(il->csocket, smsg.ptr, smsg.cap, 0);
+	if(smsg.len == -1){
+		log_error("cant receive message from socket %d: %s", il->csocket, strerror(errno));
+	}else{
+		msg = str_to_ipc_message(smsg);
+	}
+	if(send(il->csocket, "OK", slen("OK"), 0) == -1){
+		log_error("sending 'OK' to sender");
+	}
+	return msg;
+}
+
diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h
new file mode 100755
index 0000000..e9b5c25
--- /dev/null
+++ b/src/ipc/ipc.h
@@ -0,0 +1,42 @@
+#ifndef IPC_H
+#define IPC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "str/str.h"
+#include "log/log.h"
+#include "types/types.h"
+
+
+typedef struct ipc_sender {
+	struct str addr;
+	int ssocket;
+} ipc_sender;
+
+typedef struct ipc_listener {
+	struct str saddr;
+	int csocket;
+} ipc_listener;
+
+#define MAX_IPC_MSG_LEN 1024
+typedef struct ipc_message {
+	struct str key;
+	struct str val;
+} ipc_message;
+
+ipc_sender *setup_ipc_sender(struct str addr, int backlog);
+void destroy_ipc_sender(ipc_sender **is);
+
+ipc_listener *setup_ipc_listener(struct str saddr);
+void destroy_ipc_listener(ipc_listener **il);
+
+void free_ipc_message(ipc_message im);
+int send_ipc_message(int to, ipc_message msg);
+ipc_message receive_ipc_message(ipc_listener *il);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100755
index 0000000..65367d6
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,196 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <time.h>
+#include "str/str.h"
+#include "list/list.h"
+#include "net/net.h"
+#include "log/log.h"
+#include "ipc/ipc.h"
+
+#define BACKLOG 15
+
+struct str ipcaddr = sstr(".ipcserver");
+struct str port;
+http_server *server;
+ipc_sender *sender;
+struct worker {
+	pid_t pid;
+	int wsocket;
+} *workers;
+
+
+void worker_undertaker(int sig, siginfo_t *info, void *ucontext){
+	if(sig == SIGCHLD && list_entry_exists(workers, info->si_pid)){
+		list_remove_entry(workers, info->si_pid);
+		log_warn("worker %d is dead. now workers size is %d", info->si_pid, list_size(workers));
+	}
+}
+
+int init(char *argv[]){
+	port = snstr(argv[1], len(argv[1])); // error check
+	if(port.len <= 0 || port.len > 5){
+		log_error("wrong server port: '%.*s'", port.len, port.ptr);
+		return 1;
+	}
+	// decouple so the whole net.c doesnt get linked?
+	server = setup_http_server(port, BACKLOG);
+	if(server == NULL){
+		log_error("Setting up socket server");
+		return 1;
+	}
+	// configurable name?
+	sender = setup_ipc_sender(ipcaddr, BACKLOG);
+	if(sender == NULL){
+		log_error("setting up ipc sender");
+		return 1;
+	}
+	init_list(workers);
+	struct sigaction chld = { .sa_sigaction = worker_undertaker, .sa_flags = SA_SIGINFO };
+	if(sigaction(SIGCHLD, &chld, NULL) == -1){
+		log_error("init: SIGCHLD: %s", strerror(errno));
+		return 1;
+	}
+	return 0;
+}
+
+void deinit(void){
+	destroy_http_server(&server);
+	destroy_ipc_sender(&sender);
+	list_free(workers);
+}
+
+void print_usage(void){
+	printf("server [port]\n");
+}
+
+void show_commands(void){
+	printf(
+		"(case insensitive)\n"
+		"f: fork\n"
+		"s: signal\n"
+		"l: list\n"
+		"c: clear\n"
+		"[0-9]: turn off ssl for worker\n"
+		"h: help\n"
+		"q: quit\n"
+	);
+}
+
+int main(int argc, char *argv[]){
+
+	if(argc < 2){
+		print_usage();
+		return 1;
+	}
+
+	int return_value = 0;
+
+	if(init(argv) != 0){
+		return_value = 1;
+		goto DEINIT;
+	}
+	log_info("Config done");
+
+#ifdef SHOW_IP
+	system("curl -s http://ipinfo.io/ip && echo");
+#endif
+	
+/*
+	sqlite3 *db = setupDatabase("src/db/db.db");
+	if(db == NULL){
+		fprintf(stderr, "error setting up database\n");
+		return 1;
+	}
+
+	if(getNEntries("archive") != getCount(db, FILE_TABLE)){
+		int n = getNEntries("archive");
+		char **entries = getFiles("archive"); // getFiles deprecated btw
+		for(int i = 0; i < n; ++i){
+			insertName(db, FILE_TABLE, entries[i]);
+		}
+		printf("%d, %d\n", getNEntries("archive"), getCount(db, FILE_TABLE));
+	}
+*/
+
+
+	printf("press h for help\n");
+	bool end = false;
+	while(!end){
+		char c = getchar();
+		switch(c){
+			case 'f': case 'F':
+				pid_t nw = fork();
+				if(nw == 0){
+					char *args[] = {"./worker.exe", ipcaddr.ptr, NULL};
+					execv("./worker.exe", args);
+					log_error("Cannot exec worker: %s", strerror(errno));
+					return 1;
+				}
+				struct worker w = { .pid = nw, .wsocket = accept(sender->ssocket, NULL, NULL) };
+				list_push(workers, w);
+				ipc_message msg = { .key = sstr("socket"), .val = utostr(server->ssocket, 10) };
+				send_ipc_message(w.wsocket, msg);
+				free_ipc_message(msg);
+				msg = (ipc_message){ .key = sstr("rewrites"), .val = sstr("urirewrites") };
+				send_ipc_message(w.wsocket, msg);
+				break;
+			case 's': case 'S':
+				kill(0, SIGUSR1);
+				break;
+			case 'l': case 'L':
+				printf("|-%3d workers working for us rn-|\n", list_size(workers));
+				char *faces[] = {
+					"(^__^)", "(·__·)", "(>__>)", "(~ _~)", "(T__T)", "(º__º)"
+				};
+				for(int i = 0; i < list_size(workers); i++){
+					int index = rand()%sizeof(faces)/sizeof(faces[0]);
+					printf("| %d %s\t\t\t|\n", workers[i].pid, faces[index]);
+				}
+				printf("|-------------------------------|\n");
+				break;
+			case 'c': case 'C':
+				system("clear");
+				break;
+			case 'h': case 'H':
+				show_commands();
+				break;
+			case 'q': case 'Q':
+				while(list_size(workers) > 0){
+					kill(workers[0].pid, SIGKILL); // redo this PLEASE
+					waitpid(workers[0].pid, NULL, 0);
+				}
+				while(wait(NULL) > 0);
+				close(server->ssocket);
+				end = true;
+				log_info("%d children remaining alive (lie)\n", list_size(workers));
+				break;
+			case '0': case '1': case '2': case '3': case '4':
+			case '5': case '6': case '7': case '8': case '9':
+				if(list_size(workers) > c-'0'){
+					log_info("signaling worker[%d] %d to turn off ssl\n", c-'0', workers[c-'0'].pid);
+					sigqueue(workers[c-'0'].pid, SIGRTMIN, (union sigval){.sival_int = 0});
+				}
+				break;
+		}
+	}
+
+DEINIT:
+	deinit();
+
+	return return_value;
+}
+
+// inspiration: https://www.youtube.com/watch?v=cEH_ipqHbUw (https://github.com/infraredCoding/cerveur)
+// thanks to
+/*
+	https://beej.us/guide/bgnet/
+	https://dev.to/lloyds-digital/how-you-can-host-websites-from-home-4pke
+	https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
+	https://www.favicon.cc
+	https://www.cssscript.com/demo/animated-snowfall-effect/
+		https://www.cssscript.com/demo/animated-snowfall-effect/PureSnow.js
+	https://www.tutorialspoint.com/http/http_requests.htm
+	https://codepen.io/afarrar/pen/JRaEjP
+*/
+
diff --git a/src/net/net.c b/src/net/net.c
new file mode 100755
index 0000000..30793de
--- /dev/null
+++ b/src/net/net.c
@@ -0,0 +1,641 @@
+#include "net.h"
+
+
+struct str response_headers[] = {
+	sstr("Content-Length"),
+	sstr("Content-Type"),
+	sstr("Transfer-Encoding"),
+};
+
+struct {
+	struct str fmt;
+	struct str type;
+} mime_types[] = {
+	{.fmt = sstr("avif"), .type = sstr("image/avif")},
+	{.fmt = sstr("bmp"), .type = sstr("image/bmp")},
+	{.fmt = sstr("css"), .type = sstr("text/css")},
+	{.fmt = sstr("csv"), .type = sstr("text/csv")},
+	{.fmt = sstr("gz"), .type = sstr("application/gzip")},
+	{.fmt = sstr("gif"), .type = sstr("image/gif")},
+	{.fmt = sstr("html"), .type = sstr("text/html")},
+	{.fmt = sstr("ico"), .type = sstr("image/vnd.microsoft.icon")},
+	{.fmt = sstr("jpg"), .type = sstr("image/jpeg")},
+	{.fmt = sstr("jpeg"), .type = sstr("image/jpeg")},
+	{.fmt = sstr("js"), .type = sstr("text/javascript")},
+	{.fmt = sstr("json"), .type = sstr("application/json")},
+	{.fmt = sstr("midi"), .type = sstr("audio/midi")},
+	{.fmt = sstr("mp3"), .type = sstr("audio/mpeg")},
+	{.fmt = sstr("mp4"), .type = sstr("video/mp4")},
+	{.fmt = sstr("mpeg"), .type = sstr("video/mpeg")},
+	{.fmt = sstr("png"), .type = sstr("image/png")},
+	{.fmt = sstr("pdf"), .type = sstr("application/pdf")},
+	{.fmt = sstr("php"), .type = sstr("application/x-httpd-php")},
+	{.fmt = sstr("rar"), .type = sstr("application/vnd.rar")},
+	{.fmt = sstr("tiff"), .type = sstr("image/tiff")},
+	{.fmt = sstr("ts"), .type = sstr("video/mp2t")},
+	{.fmt = sstr("txt"), .type = sstr("text/plain")},
+	{.fmt = sstr("wav"), .type = sstr("audio/wav")},
+	{.fmt = sstr("weba"), .type = sstr("audio/webm")},
+	{.fmt = sstr("webm"), .type = sstr("video/webm")},
+	{.fmt = sstr("webp"), .type = sstr("image/webp")},
+	{.fmt = sstr("xml"), .type = sstr("application/xml")},
+	{.fmt = sstr("zip"), .type = sstr("application/zip")},
+	{.fmt = sstr("7z"), .type = sstr("application/x-7z-compressed")},
+};
+
+LIST(struct uri_mod) uri_rewrites = NULL;
+
+static int pleasesslgivemetheerror(int ssl_get_error){
+	char *error;
+	switch(ssl_get_error){
+		case SSL_ERROR_NONE: error = "SSL_ERROR_NONE"; break;
+		case SSL_ERROR_ZERO_RETURN: error = "SSL_ERROR_ZERO_RETURN"; break;
+		case SSL_ERROR_WANT_READ: error = "SSL_ERROR_WANT_READ"; break;
+		case SSL_ERROR_WANT_WRITE: error = "SSL_ERROR_WANT_WRITE"; break;
+		case SSL_ERROR_WANT_CONNECT: error = "SSL_ERROR_WANT_CONNECT"; break;
+		case SSL_ERROR_WANT_ACCEPT: error = "SSL_ERROR_WANT_ACCEPT"; break;
+		case SSL_ERROR_WANT_X509_LOOKUP: error = "SSL_ERROR_WANT_X509_LOOKUP"; break;
+		case SSL_ERROR_WANT_ASYNC: error = "SSL_ERROR_WANT_ASYNC"; break;
+		case SSL_ERROR_WANT_ASYNC_JOB: error = "SSL_ERROR_WANT_ASYNC_JOB"; break;
+		case SSL_ERROR_WANT_CLIENT_HELLO_CB: error = "SSL_ERROR_WANT_CLIENT_HELLO_CB"; break;
+		case SSL_ERROR_SYSCALL: error = "SSL_ERROR_SYSCALL"; break;
+		case SSL_ERROR_SSL: error = "SSL_ERROR_SSL"; break;
+	}
+	int err = ERR_get_error();
+	log_error("%s (%d): %s", error, err, ERR_error_string(err, NULL));
+	return ssl_get_error;
+}
+
+http_server *setup_http_server(struct str port, int backlog){
+	http_server *hs = calloc(1, sizeof(http_server));
+	hs->port = dup_str(port);
+	hs->backlog = backlog;
+	hs->ssocket = -1;
+
+	int ec, val = 1;
+	struct addrinfo *res, hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE };
+
+	if((ec = getaddrinfo(NULL, hs->port.ptr, &hints, &res)) != 0){
+		log_error("getaddrinfo: %s", gai_strerror(ec));
+		goto error;
+	}
+	if((hs->ssocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){
+		log_error("socket: %s", strerror(errno));
+		goto error;
+	}
+
+	if(setsockopt(hs->ssocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))){
+		log_error("server: SO_REUSEADDR: %s", strerror(errno));
+		goto error;
+	}
+	
+	if(bind(hs->ssocket, res->ai_addr, res->ai_addrlen) == -1){
+		log_error("server: bind: %s", strerror(errno));
+		goto error;
+	}
+	
+	if(listen(hs->ssocket, backlog) == -1){
+		log_error("server: listen: %s", strerror(errno));
+		goto error;
+	}
+
+	if(0){
+error:
+		destroy_http_server(&hs);
+	}
+	freeaddrinfo(res);
+
+	return hs;
+}
+
+void destroy_http_server(http_server **hs){
+	if(*hs != NULL){
+		free_str(&(*hs)->port);
+		(*hs)->backlog = 0;
+		close((*hs)->ssocket);
+		free(*hs);
+		*hs = NULL;
+	}
+}
+
+http_worker *setup_http_worker(int ssocket, int secure, struct str certfile, struct str keyfile){
+	http_worker *hw = calloc(1, sizeof(http_worker));
+	hw->ssocket = ssocket;
+	hw->csocket = -1;
+	hw->secure = secure;
+	hw->receive = recv;
+	hw->ssl_receive = SSL_read;
+
+	SSL_library_init();
+	hw->ssl_ctx = SSL_CTX_new(TLS_server_method());
+	// need to compile openssl with ktls on for this (v) to work
+	SSL_CTX_set_options(hw->ssl_ctx, SSL_OP_ENABLE_KTLS);
+	if(hw->ssl_ctx == NULL){
+		goto error;
+	}
+	if(SSL_CTX_use_certificate_file(hw->ssl_ctx, certfile.ptr, SSL_FILETYPE_PEM) <= 0){
+		log_error("worker_cert");
+		goto error;
+	}
+	if(SSL_CTX_use_PrivateKey_file(hw->ssl_ctx, keyfile.ptr, SSL_FILETYPE_PEM) <= 0 ){
+		log_error("worker: key");
+		goto error;
+	}
+	hw->ssl = SSL_new(hw->ssl_ctx);
+	SSL_set_accept_state(hw->ssl);
+	if(hw->ssl == NULL){
+		goto error;
+	}
+
+	if(0){
+error:
+		destroy_http_worker(&hw);
+	}
+
+	return hw;
+}
+
+void destroy_http_worker(http_worker **hw){
+	if(*hw != NULL){
+		(*hw)->ssocket = -1;
+		close((*hw)->csocket);
+		(*hw)->secure = 0;
+		SSL_free((*hw)->ssl);
+		SSL_CTX_free((*hw)->ssl_ctx);
+		(*hw)->receive = NULL;
+		(*hw)->ssl_receive = NULL;
+		free(*hw);
+		*hw = NULL;
+	}
+}
+
+void reset_worker_ssl(http_worker *hw){
+	close(hw->csocket);
+	SSL_free(hw->ssl);
+	hw->ssl = SSL_new(hw->ssl_ctx);
+	SSL_set_accept_state(hw->ssl);
+}
+
+int accept_connection(http_worker *hw, char ip[INET_ADDRSTRLEN]){
+	struct sockaddr_storage caddr;
+	int casize = sizeof(caddr);
+	log_info("waiting...");
+	if((hw->csocket = accept(hw->ssocket, (struct sockaddr *)&caddr, (socklen_t*)&casize)) == -1){
+		log_error("accept_socket() -> accept(): %s", strerror(errno));
+		return -1;
+	}
+	log_info("accepted");
+	if(hw->secure){
+		int err = SSL_set_fd(hw->ssl, hw->csocket);
+		if(err != 1){
+			log_error("setting fd %d", hw->csocket);
+			return pleasesslgivemetheerror(SSL_get_error(hw->ssl, err));
+		}
+		if((err = SSL_accept(hw->ssl)) != 1){
+			log_error("couldnt accept");
+			return pleasesslgivemetheerror(SSL_get_error(hw->ssl, err));
+		}
+	}
+	inet_ntop(caddr.ss_family, &(((struct sockaddr_in*)&caddr)->sin_addr), ip, INET_ADDRSTRLEN);
+	return 0;
+}
+
+static inline int worker_read(http_worker *hw, struct str *buf){
+	return hw->secure ?
+		SSL_read(hw->ssl, buf->ptr+buf->len, buf->cap-buf->len) :
+		recv(hw->csocket, buf->ptr+buf->len, buf->cap-buf->len, 0);
+}
+
+int receive_request(http_worker *hw, struct str *request){
+	// for some reason SSL_has_pending can return 0 but we can still read data
+	struct pollfd pfd[1] = { {.fd = hw->csocket, .events = POLLIN } };
+	while((hw->secure && SSL_has_pending(hw->ssl)) || poll(pfd, 1, 0)){
+		int new = worker_read(hw, request);
+		if(new < 0 || (hw->secure && new == 0)){
+			int error = new;
+			if(hw->secure) error = pleasesslgivemetheerror(SSL_get_error(hw->ssl, new));
+			else log_error("http (no s) error: %s", strerror(errno));
+			return error;
+		}
+		request->len += new;
+		if(request->len == request->cap){
+			log_info("gotta resize buffer");
+			if(resize_str(request, request->cap*2) != 0){
+				log_error("Not enough memory in reply str");
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+struct file generate_resource(struct uri resource, struct str url){
+	struct file file = {0};
+	/*
+		generate if both of these are true
+		1) no file specified (aka we need index.html)
+		2) theres an index.html.php
+		3) index.html isnt cached (future)
+	*/
+	struct str phpfile = nstr(resource.path.len + slen(".php"));
+	copy_strs(phpfile, resource.path, sstr(".php"));
+	if(access(phpfile.ptr, F_OK) == 0){
+		// we need a str_copy or something
+		struct str command = nstr(
+			slen("REQUEST_URI='") + resource.path.len + slen("' QUERY_STRING='") + resource.query.len +
+			slen("' URL='") + url.len +
+			slen("' php -c ./ ") + phpfile.len + slen(" > ") + resource.path.len
+		);
+		copy_strs(command,
+			sstr("REQUEST_URI='"), resource.path, sstr("' QUERY_STRING='"), resource.query,
+			sstr("' URL='"), url,
+			sstr("' php -c ./ "), phpfile, sstr(" > "), resource.path
+		);
+		log_warn("command: %s", command.ptr);
+		system(command.ptr);
+		free_str(&command);
+	}
+	free_str(&phpfile);
+	file.name = dup_str(resource.path);
+/*
+	if(uri.query.len > 0){
+		if(access(uri.path.ptr, F_OK) != 0){
+			file.name.ptr = calloc(slen("localc/404/index.html")+1, sizeof(char));
+			copy_str(file.name, sstr("localc/404/index.html"));
+			return file;
+		}
+
+		struct str pid = utostr(getpid(), 10);
+		file.name.ptr = calloc(uri.path.len + pid.len + 1, sizeof(char));
+		copy_strs(file.name, uri.path, pid);
+
+		struct str command = {0};
+		command.ptr = calloc(
+			slen("REQUEST_URI='") + uri.path.len + slen("' REQUEST_QUERY='") + uri.query.len +
+			slen("' php -c ./ ") + uri.path.len + slen(" > ") + file.name.len,
+			sizeof(char)
+		);
+		copy_strs(command,
+			sstr("REQUEST_URI='"), uri.path, sstr("' REQUEST_QUERY='"), uri.query,
+			sstr("' php -c ./ "), uri.path, sstr(" > "), file.name
+		);
+		log_warn("command: %s", command.ptr);
+		system(command.ptr);
+		file.temp = true;
+
+		free_str(&pid);
+		free_str(&command);
+	}else{
+		file.name.ptr = calloc(uri.path.len+1, sizeof(char));
+		copy_strs(file.name, uri.path);
+	}*/
+	
+	return file;
+}
+
+// TODO: REVISE; return 201/204
+char *handlePOST(char *request/*struct str uri, struct str body*/){
+/*
+	char *resource = malloc(1), *uri = getURI(request), *params = getPOSTParams(request);
+
+	if(isPost(uri)){
+		rlen = attachBlock(&resource, rlen, HOMEFOLDER, strlen(HOMEFOLDER));
+		rlen = attachBlock(&resource, rlen, uri, ulen);
+		if(uri[ulen-1] != '/'){
+			rlen = attachBlock(&resource, rlen, "/", len("/"));
+		}
+		generatePagefile(resource, rlen, uri, ulen, params, prlen);
+
+		rlen = attachBlock(&resource, rlen, PAGEFILE, len(PAGEFILE));
+	}
+	resource[rlen] = '\0';
+	free(uri);
+	free(params);
+	return resource;
+*/
+	return NULL;
+}
+
+static uint32_t request_line_len(char *request){
+	uint32_t len = 0;
+	while(request[len] && request[len] != '\r') len++;
+	return len;
+}
+
+// why is this done like this??
+void build_http_message(char *request, int rlen, struct http_message *hm){
+	char *end = request + rlen;
+	memset(hm, 0, sizeof(struct http_message));
+	
+	hm->method.ptr = request;
+	while(request < end && *request != ' ') hm->method.len++, request++;
+	if(++request >= end) return;
+
+	hm->uri.ptr = request;
+	while(request < end && *request != ' ') hm->uri.len++, request++;
+	if(++request >= end) return;
+
+	hm->req_ver.ptr = request;
+	while(request < end && request[hm->req_ver.len] != '\r') hm->req_ver.len++;
+	request += hm->req_ver.len + 2;
+	if(request > end) return;
+
+	uint32_t llen = 0;
+	while((llen = request_line_len(request)) != 0){
+		hm->headers[hm->hlen].name.ptr = request;
+		while(
+			hm->headers[hm->hlen].name.len < llen &&
+			request[hm->headers[hm->hlen].name.len] != ':'
+		) hm->headers[hm->hlen].name.len++;
+		
+		request += hm->headers[hm->hlen].name.len + 2;
+		if(request > end) return;
+		hm->headers[hm->hlen].value.ptr = request;
+		hm->headers[hm->hlen].value.len = llen - hm->headers[hm->hlen].name.len - 2;
+		request += hm->headers[hm->hlen].value.len + 2;
+		hm->hlen++;
+		if(request > end) return;
+	}
+	request += 2;
+	if(request > end) return;
+
+	hm->body.ptr = request;
+	while(request < end && request[hm->body.len] != '\0') hm->body.len++;
+}
+
+// TODO: check if endianness affects this
+__attribute__ ((optimize(3))) enum http_method get_http_method(struct str method){
+	uint64_t m;
+	memmove(&m, method.ptr, sizeof(uint64_t));
+	if(((m & 0x0000000000FFFFFF) - 0x0000000000544547) == 0){
+		return GET;
+	}else if(((m & 0x00000000FFFFFFFF) - 0x0000000054534F50) == 0){
+		return POST;
+	}else if(((m & 0x0000000000FFFFFF) - 0x0000000000545550) == 0){
+		return PUT;
+	}else if(((m & 0x0000FFFFFFFFFFFF) - 0x00004554454C4544) == 0){
+		return DELETE;
+	}
+	return GET;
+}
+
+static uint64_t http_len(struct http_message *hm){
+	uint64_t len = 0;
+	len += hm->method.len + hm->uri.len + hm->req_ver.len + 5 + 2;
+	for(int i = 0; i < hm->hlen; ++i){
+		len += hm->headers[i].name.len + hm->headers[i].value.len + 4;
+	}
+	return len;
+}
+
+/*static struct str assemble_with_body(struct http_message *hm, FILE *body, uint64_t len){
+	struct str s = {0};
+	s.ptr = calloc(http_len(hm) + len + 1, sizeof(char));
+	copy_strs(s, hm->method, sstr(" "), hm->uri, sstr(" "), hm->req_ver, sstr("\r\n"));
+
+	for(int i = 0; i < hm->hlen; i++){
+		copy_strs(s, hm->headers[i].name, sstr(": "), hm->headers[i].value, sstr("\r\n"));
+	}
+	copy_str(s, sstr("\r\n"));
+
+	fread(s.ptr+s.len, sizeof(char), len, body);
+	s.len += len;
+	//copy_str(s, sstr("\r\n"));
+
+	return s;
+}*/
+
+static struct str http_header_to_str(struct http_message *hm){
+	struct str s = {0};
+	s.ptr = calloc(http_len(hm) + 1, sizeof(char));
+	copy_strs(s, hm->method, sstr(" "), hm->uri, sstr(" "), hm->req_ver, sstr("\r\n"));
+	for(int i = 0; i < hm->hlen; i++){
+		copy_strs(s, hm->headers[i].name, sstr(": "), hm->headers[i].value, sstr("\r\n"));
+	}
+	copy_str(s, sstr("\r\n"));
+	return s;	
+}
+
+/*
+	Given two strings p and u, and a uri (a struct with two strings) o
+	where p is a pattern, u is a url that is gonna checked for the pattern
+	and o is the uri output blueprint that must be returned should u follow p
+
+	There is a tokens array where tokens found in the url can be stored (up to 9 tokens)
+
+	In p:
+		<X> optionally matches a character X and stores it in the tokens array 
+		^ optionally matches any character and stores it in the tokens array
+		* optionally matches a string of character until another match is found
+		  or the url to match ends, and stores it in the tokens array
+
+	In o:
+		$1 through $9 reference the tokens in the token array (I could make it 10 tokens tbh)
+		Both the strings in the uri can access these tokens
+		Referencing a token writes it to the output uri
+		If theres a token before a <X>, say $3<a> that means that the character
+		between the less than and greater than signs will only be written
+		to the output if the token number 3 in the array exists (has length > 0)
+		All other characters get outputted normally
+*/
+static struct uri uri_rewrite(struct str p, struct str u, struct uri o){
+	int i = 0, j = 0, in = 0, ti = 0;
+	struct str tokens[9] = {0};
+	for(; j < p.len; i += 1){
+		if(i < u.len && p.ptr[j+in] == '<' && p.ptr[j+in+1] == u.ptr[i]){
+			if(ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 1};
+			j += 3+in, in = 0;
+		}else if(i < u.len && (p.ptr[j+in] == '^' || p.ptr[j+in] == u.ptr[i])){
+			if(p.ptr[j] == '^' && ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 1};
+			j += 1+in, in = 0;
+		}else if(i < u.len && p.ptr[j] == '*'){
+			if(!in){
+				if(ti < 9) tokens[ti++] = (struct str){.ptr = u.ptr+i, .len = 0};
+				in = 1;
+			}
+			if(ti-in < 9) tokens[ti-in].len++;
+		}else{
+			if(i >= u.len && p.ptr[j] == '*') j++;
+			else if(i >= u.len && p.ptr[j] == '<') j += 3;
+			else return (struct uri){0};
+		}
+	}
+	if(i < u.len) return (struct uri){0};
+
+	struct uri r = {0};
+	struct str *no = &o.path, *nr = &r.path;
+	for(int k = 0, rlen = 0; k < 2; k++, no = &o.query, nr = &r.query){
+		for(int i = 0; i < no->len; i++, rlen++){
+			if(no->ptr[i] == '$') rlen += tokens[no->ptr[++i]-'1'].len-1;
+		}
+		nr->ptr = calloc(rlen+1, sizeof(char));
+		if(nr->ptr == NULL){
+			if(r.path.ptr != NULL) free(r.path.ptr);
+			return (struct uri){0};
+		}
+
+		for(int i = 0; i < no->len; i++){
+			if(no->ptr[i] == '$'){
+				if(++i+1 < no->len && no->ptr[i+1] == '<'){
+					if(tokens[no->ptr[i]-'1'].len > 0) i++;
+					else while(no->ptr[++i] != '>');
+				}else{
+					copy_str((*nr), tokens[no->ptr[i]-'1']);
+				}
+			}else{
+				if(no->ptr[i] != '>') nr->ptr[nr->len++] = no->ptr[i];
+			}
+		}
+	}
+
+	return r;
+}
+
+struct uri sanitize_uri(struct str uri){
+	struct str suri = {.ptr = calloc(uri.len+1, sizeof(char)), .len = 0};
+	if(suri.ptr == NULL) return (struct uri){0};
+	int i = 0, o = 0;
+	while(i+o < uri.len){
+		suri.ptr[i] = lowerchar(uri.ptr[i+o]);
+		if(i > 0 && (
+			(uri.ptr[i+o] == '/' && uri.ptr[i+o-1] == '/') ||
+			(uri.ptr[i+o] == '.' && uri.ptr[i+o-1] == '.') ||
+			(uri.ptr[i+o] == '/' && i+o+1 == uri.len) ||
+			uri.ptr[i+o] == '%')
+		){
+			++o;
+		}else{
+			++i;
+		}
+	}
+	suri.ptr[suri.len = i] = '\0';
+	
+	struct uri u = {0};
+	for(int i = 0; i < list_size(uri_rewrites); i++){
+		u = uri_rewrite(uri_rewrites[i].pattern, suri, uri_rewrites[i].output);
+		if(u.path.len > 0) break;
+	}
+	free_str(&suri);
+	if(u.path.len == 0){
+		u.path = str("localc/404/index.html");
+		free_str(&u.query);
+	}
+	return u;
+}
+
+static inline int worker_write(http_worker *hw, struct str buf){
+	return hw->secure ?
+		SSL_write(hw->ssl, buf.ptr, buf.len) :
+		send(hw->csocket, buf.ptr, buf.len, 0);
+}
+
+// use sendfile(2). reason:
+// "sendfile() copies data between one file descriptor and another.
+// Because this copying is done within the kernel, sendfile() is
+// more efficient than the combination of read(2) and write(2),
+// which would require transferring data to and from user space."
+void send_file(http_worker *hw, struct str filename){
+	log_info("requested '%.*s' -> ", filename.len, filename.ptr);
+	uint64_t fsize = get_file_size(filename.ptr);
+	if(fsize == 0){
+		// we should free it or something here idk maybe not
+		filename = sstr("localc/404/index.html");
+		fsize = get_file_size(filename.ptr);
+	}
+	log_info("sending '%.*s'", filename.len, filename.ptr);
+
+	enum mime_type type = TXT;
+	struct str fmt = get_file_format(filename);
+	for(int i = 0; i < sizeof(mime_types)/sizeof(mime_types[0]); i++){
+		if(strncmp(fmt.ptr, mime_types[i].fmt.ptr, fmt.len) == 0){
+			type = i;
+			break;
+		}
+	}
+	free_str(&fmt);
+
+	struct http_message hm = {
+		.resp_ver = sstr("HTTP/1.1"), .status = sstr("200"), .reason = sstr("OK"),
+		.hlen = 2, .headers = {
+			{ .name = response_headers[CONTENT_TYPE], .value = mime_types[type].type },
+			{ .name = response_headers[CONTENT_LENGTH], .value = utostr(fsize, 10) }
+		},
+		.body = {0},
+	};
+
+	struct str header = http_header_to_str(&hm);
+	free_str(&hm.headers[1].value);
+	int sent = worker_write(hw, header);
+	if(sent != header.len){
+		if(hw->secure){
+			pleasesslgivemetheerror(SSL_get_error(hw->ssl, sent));
+		}else{
+			log_error("send_file: %s", strerror(errno));
+		}
+	}
+	free_str(&header);
+
+	// make sure system is 64bits before compiling with O_LARGEFILE
+	// use fopen maybe?
+	int fd = open(filename.ptr, O_RDONLY | /*O_LARGEFILE |*/ O_NONBLOCK);
+	while(fsize > 0){
+		// CYGWIN DOESNT HAVE SENDFILE OR SPLICE FOR
+		// SOME REASON WHEN THIS IS A REAL PROGRAM
+		// RUNNING ON LINUX USE SENDFILE (<sys/sendfile.h>)
+		// AND COMPILE OPENSSL TO USE KTLS
+		// sent = using_ssl ?
+		// 	SSL_sendfile(ssl, fd, 0, fsize, 0) :
+		// 	sendfile(socket, fd, NULL, fsize);
+
+		// we're ignoring MAX_BODY_SIZE
+		struct str fuckcygwinineedsendfile;
+		fd_to_str(&fuckcygwinineedsendfile, fd, fsize);
+		sent = worker_write(hw, fuckcygwinineedsendfile);
+		free_str(&fuckcygwinineedsendfile);
+
+		if(sent > 0){ fsize -= sent;
+		}else if(sent == 0){ break;
+		}else{
+			if(hw->secure){
+				pleasesslgivemetheerror(SSL_get_error(hw->ssl, sent));
+			}else{
+				perror("send_file (fuck cygwin btw)");
+			}
+		}
+		log_info("sent %d more bytes", sent);
+	}
+	close(fd);
+}
+
+// this shouldnt be here
+int read_uri_rewrites(char *map, uint64_t size){
+	uint64_t i = 0;
+	if(strncmp(map, "rewrites", slen("rewrites")) != 0) return 1;
+	i += slen("rewrites");
+	while(charisspace(map[i])) i++;
+	if(map[i++] != '{') return 1;
+	list_free(uri_rewrites);
+	init_nlist(uri_rewrites);
+	while(i < size && map[i] != '}'){
+		struct uri_mod um = {0};
+		i += ({sread_delim_f(map+i, charisspace, false);}).len;
+		i += ({um.pattern = read_delim_f(map+i, charisspace, true);}).len;
+		i += ({sread_delim_f(map+i, charisblank, false);}).len;
+		i += ({um.output.path = read_delim_f(map+i, charisspace, true);}).len;
+		i += ({sread_delim_f(map+i, charisblank, false);}).len;
+		i += ({um.output.query = read_delim_f(map+i, charisspace, true);}).len;
+		i += ({sread_delim(map+i, '\n');}).len + 1;
+		list_push(uri_rewrites, um);
+	}
+	return 0;
+}
+
+void free_uri_rewrites(void){
+	for(uint32_t i = 0; i < list_size(uri_rewrites); i++){
+		free_str(&uri_rewrites[i].pattern);
+		free_str(&uri_rewrites[i].output.path);
+		free_str(&uri_rewrites[i].output.query);
+	}
+	list_free(uri_rewrites);
+}
+
diff --git a/src/net/net.h b/src/net/net.h
new file mode 100755
index 0000000..d7459da
--- /dev/null
+++ b/src/net/net.h
@@ -0,0 +1,144 @@
+#ifndef NET_H
+#define NET_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+//#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <time.h>
+#include "str/str.h"
+#include "list/list.h"
+#include "dir/dir.h"
+#include "log/log.h"
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <poll.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/sendfile.h>
+#endif
+
+
+enum http_method {
+	GET,
+	POST,
+	PUT,
+	DELETE
+};
+
+enum response_header {
+	CONTENT_LENGTH,
+	CONTENT_TYPE,
+	TRANSFER_ENCODING,
+};
+
+enum mime_type {
+	AVIF, BMP, CSS, CSV, GZ, GIF, HTML,
+	ICO, JPG, JPEG, JS, JSON, MIDI, MP3,
+	MP4, MPEG, PNG, PDF, PHP, RAR, TIFF, TS,
+	TXT, WAV, WEBA, WEBM, WEBP, XML, ZIP,
+	_7Z,
+};
+
+
+struct uri {
+	struct str path;
+	struct str query;
+};
+
+struct uri_mod {
+	struct str pattern;
+	struct uri output;
+};
+
+struct header {
+	struct str name;
+	struct str value;
+};
+
+#define MAX_HEADERS 16
+
+
+struct http_message {
+	union {
+		struct str method;
+		struct str resp_ver;
+	};
+	union {
+		struct str uri;
+		struct str status;
+	};
+	union {
+		struct str req_ver;
+		struct str reason;
+	};
+	int hlen;
+	struct header headers[MAX_HEADERS];
+	struct str body;
+};
+
+typedef struct http_server {
+	struct str port;
+	int backlog;
+	int ssocket;
+} http_server;
+
+typedef ssize_t (*recv_func)(int,void*,size_t,int);
+typedef int (*ssl_recv_func)(SSL*,void*,int);
+typedef struct http_worker {
+	int ssocket;
+	int csocket;
+	int secure;
+	SSL_CTX *ssl_ctx;
+	SSL *ssl;
+	recv_func receive;
+	ssl_recv_func ssl_receive;
+} http_worker;
+
+#define MAX_RESPONSE_SIZE 0x0FFFFFFF
+#define MAX_BODY_SIZE (MAX_RESPONSE_SIZE - 0x0FFF)
+
+#define insert_header(hm, h) \
+	(hm).headers[(hm).hlen++] = (h)
+
+
+http_server *setup_http_server(struct str port, int backlog);
+
+void destroy_http_server(http_server **hs);
+
+http_worker *setup_http_worker(int ssocket, int secure, struct str certfile, struct str keyfile);
+
+void destroy_http_worker(http_worker **hw);
+
+void reset_worker_ssl(http_worker *hw);
+
+int accept_connection(http_worker *hw, char ip[INET_ADDRSTRLEN]);
+
+int receive_request(http_worker *hw, struct str *request);
+
+struct file generate_resource(struct uri resource, struct str url);
+
+char *handlePOST(char *request);
+
+void build_http_message(char *request, int len, struct http_message *hm);
+
+enum http_method get_http_method(struct str method);
+
+struct uri sanitize_uri(struct str uri);
+
+void send_file(http_worker *hw, struct str filename);
+
+int read_uri_rewrites(char *map, uint64_t size);
+
+void free_uri_rewrites(void);
+
+#endif
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100755
index 0000000..6f447e6
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,13 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#define ARRAY_LEN(first, ...) \
+	(sizeof(((typeof(first) []){first, __VA_ARGS__}))/(sizeof(typeof(first))))
+
+#define bool_equal(a,b) ({	\
+	typeof(a) _a = (a);	\
+	typeof(b) _b = (b);	\
+	((_a) ? (_b) : (!(_b)));	\
+})
+
+#endif
diff --git a/src/worker.c b/src/worker.c
new file mode 100755
index 0000000..0b4cb07
--- /dev/null
+++ b/src/worker.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include "str/str.h"
+#include "ipc/ipc.h"
+#include "net/net.h"
+
+
+ipc_listener *listener;
+http_worker *worker;
+struct str rewritesfile;
+
+
+int init(char *argv[]){
+	// replace signals with unix sockets
+	// reinit
+	// finish
+	// toggle ssl
+	//log_set_level(LOG_DEBUG, 0);
+	//log_set_level(LOG_INFO, 0);
+	//log_set_level(LOG_WARN, 0);
+
+	struct str saddr = str(argv[1]);
+	listener = setup_ipc_listener(saddr);
+	free_str(&saddr);
+	if(listener == NULL){
+		log_error("cant set up ipc listener on worker %d", getpid());
+		return 1;
+	}
+
+	// check key for value maybe idk
+	ipc_message msg = receive_ipc_message(listener);
+	int ssocket = strtou(msg.val);
+	free_ipc_message(msg);	// v configurable certificate locations?
+	worker = setup_http_worker(ssocket, 1, sstr("ssl/cert.pem"), sstr("ssl/key.pem"));
+	if(worker == NULL){
+		log_error("setting up http worker");
+		return 1;
+	}
+	// this is disgusting and should be done elsewhere
+	msg = receive_ipc_message(listener); // check for value
+	rewritesfile = dup_str(msg.val);
+	free_ipc_message(msg);
+	int fsize = get_file_size(rewritesfile.ptr);
+	int fd = open(rewritesfile.ptr, O_RDONLY | O_NONBLOCK);
+	char *rewrites = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
+	if(rewrites == (void*)-1){
+		log_error("cant mmap rewrites: %s", strerror(errno));
+		return 1;
+	}
+	if(read_uri_rewrites(rewrites, fsize) != 0){
+		log_error("init: read_uri_rewrites: %s", strerror(errno));
+		return 1;
+	}
+	munmap(rewritesfile.ptr, fsize);
+
+	return 0;
+}
+
+void deinit(void){
+	destroy_ipc_listener(&listener);
+	destroy_http_worker(&worker);
+	free_str(&rewritesfile);
+	free_uri_rewrites();
+}
+
+
+int main(int argc, char **argv){
+
+	int return_value = 0;
+
+	if(init(argv) != 0){
+		return_value = 1;
+		goto DEINIT;
+	}
+	log_info("init'd");
+
+	bool end = false;
+	struct str request = {.cap = 8192, .len = 0, .ptr = alloca(8192)};
+	while(!end){
+		char cip[INET_ADDRSTRLEN] = {0};
+		return_value = accept_connection(worker, cip);
+		switch(return_value){
+			case -1: // couldnt accept, do something ig
+				continue;
+			case SSL_ERROR_SSL:
+				reset_worker_ssl(worker);
+				log_info("continuing\n");
+				continue;
+		}
+		log_info("socket %d accepted with ip %s", worker->csocket, cip);
+		return_value = receive_request(worker, &request);
+		switch(return_value){
+			case -1: // couldnt accept, do something ig
+				continue;
+			case SSL_ERROR_SSL:
+				reset_worker_ssl(worker);
+				log_info("continuing\n");
+				continue;
+		}
+
+		printf("'%.*s'\n", request.len, request.ptr);
+
+		struct http_message hm = {0};
+		build_http_message(request.ptr, request.len, &hm);
+		log_info("uri before: %.*s", hm.uri.len, hm.uri.ptr);
+		struct uri suri = sanitize_uri(hm.uri);
+		log_info("uri after: %.*s", suri.path.len, suri.path.ptr);
+		enum http_method method = get_http_method(hm.method);
+
+		switch(method){
+			case GET:
+				struct file resource = generate_resource(suri, hm.uri);
+				send_file(worker, resource.name);
+				//if(resource.temp == true) remove(resource.name.ptr);
+				free_str(&resource.name);
+				break;
+			case POST:
+				//handlePOST(request);
+				send(worker->csocket, "HTTP/1.1 201 Created\r\n\r\n", len("HTTP/1.1 201 Created\r\n\r\n"), 0);
+				break;
+			case PUT:
+				break;
+			case DELETE:
+				break;		
+		}
+		free_str(&suri.path);
+		free_str(&suri.query);
+		request.len = 0;
+
+		SSL_clear(worker->ssl);
+		close(worker->csocket);
+
+		//SSL_shutdown(config.ssl); // look into SSL_clear()
+	}
+
+DEINIT:
+	log_info("dieing :(");
+	deinit();
+
+	return return_value;
+}
+
+/*
+struct timespec t_start, t_end;
+
+#define start_timing() clock_gettime(CLOCK_MONOTONIC, &t_start)
+#define end_timing() clock_gettime(CLOCK_MONOTONIC, &t_end)
+
+double get_timing(void){
+	uint64_t diff = (t_end.tv_sec*1000000000 + t_end.tv_nsec) - (t_start.tv_sec*1000000000 + t_start.tv_nsec);
+	return diff/1000000000.0;
+}
+*/