--- /dev/null
+*.o
\ No newline at end of file
--- /dev/null
+Addon Libraries to C Programming Language
\ No newline at end of file
--- /dev/null
+CC=gcc
+CFLAGS=-Wall -Wextra
+
+OBJS=test.o hashtable.o llist.o
+
+all: test
+
+test: $(OBJS)
+ gcc -o $@ $^
+
+test.o: test.c http.h
+
+hashtable.o: hashtable.c hashtable.h
+
+llist.o: llist.c llist.h
+
+clean:
+ rm -f $(OBJS)
+ rm -f test
+
+.PHONY: all, clean
--- /dev/null
+/**
+ * hashtable.c - A basic hashtable implementation
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "llist.h"
+#include "hashtable.h"
+
+#define SIZE 128
+
+typedef struct
+{
+ void* key;
+ void* data;
+}
+HTEntry;
+
+/**
+ * Simple hash function used
+ */
+int hash(char* key, int ht_size)
+{
+ int h = 0;
+
+ // basic modulo hash
+ for (size_t i = 0; i < strlen(key); i++)
+ {
+ h = (31 * h + key[i]) % ht_size;
+ }
+
+ return h;
+}
+
+/**
+ * Compare function for searching a bucket
+ */
+int compare(void* data, void* arg)
+{
+ char* key = arg;
+ HTEntry* entry = data;
+ return strcmp(entry->key, key);
+}
+
+/**
+ * Delete HT entry from bucket
+ */
+int delete_entry(void* data, void* arg)
+{
+ (void) arg;
+ HTEntry* entry = data;
+ free(entry);
+ return 0;
+}
+
+/**
+ * Create a new hashtable
+ */
+HT* ht_create(void)
+{
+ HT* ht = malloc(sizeof(HT));
+
+ ht->size = SIZE;
+ ht->entries = 0;
+ ht->store = malloc(ht->size * sizeof(LL*));
+
+ for (int i = 0; i < ht->size; i++)
+ {
+ ht->store[i] = ll_create();
+ }
+
+ return ht;
+}
+
+/**
+ * Add a new key value pair to a hashtable
+ */
+int ht_put(HT* ht, char* key, void* data)
+{
+ int addr = hash(key, ht->size);
+
+ LL* bucket = ht->store[addr];
+
+ HTEntry* entry = malloc(sizeof(HTEntry));
+ entry->key = strdup(key);
+ entry->data = data;
+
+ ll_push(bucket, entry);
+
+ ht->entries++;
+
+ return 0;
+}
+
+/**
+ * Get a value from a hashtable with a key
+ */
+void* ht_get(HT* ht, char* key)
+{
+ int addr = hash(key, ht->size);
+
+ LL* bucket = ht->store[addr];
+
+ HTEntry* entry = ll_search(bucket, compare, (void*) key);
+
+ if (entry == NULL)
+ {
+ return NULL;
+ }
+
+ return entry->data;
+}
+
+/**
+ * Delete a key value pair from a hashtable
+ */
+void* ht_delete(HT* ht, char* key)
+{
+ int addr = hash(key, ht->size);
+
+ LL* bucket = ht->store[addr];
+
+ HTEntry* entry = ll_delete(bucket, compare, key);
+
+ if (entry == NULL)
+ {
+ return NULL;
+ }
+
+ ht->entries--;
+
+ return entry->data;
+}
+
+
+/**
+ * Destroy/free an entire hashtable
+ */
+int ht_free(HT* ht)
+{
+ for (int i = 0; i < ht->size; i++)
+ {
+ LL* bucket = ht->store[i];
+ ll_loop(bucket, delete_entry, NULL);
+ ll_free(bucket);
+ }
+
+ free(ht);
+
+ return 0;
+}
--- /dev/null
+#ifndef _HASHTABLE_H_
+#define _HASHTABLE_H_
+
+#include "llist.h"
+
+typedef struct
+{
+ int size;
+ int entries;
+ LL** store;
+}
+HT;
+
+HT* ht_create(void);
+
+int ht_put(HT* ht, char* key, void* data);
+
+void* ht_get(HT* ht, char* key);
+
+void* ht_delete(HT* ht, char* key);
+
+int ht_free(HT* ht);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <assert.h>
+
+#include "hashtable.h"
+
+int main(void)
+{
+ puts("Creating new hashtable...");
+
+ HT* ht = ht_create();
+
+ char* login_data = "This is the login data!";
+ char* register_data = "This is the register data!";
+
+ puts("Putting in some data...");
+
+ ht_put(ht, "/login", login_data);
+ ht_put(ht, "/register", register_data);
+
+ puts("Getting values and asserting they are correct...");
+
+ assert(login_data == ht_get(ht, "/login"));
+ assert(register_data == ht_get(ht, "/register"));
+
+ puts("Checking that delete works...");
+
+ ht_delete(ht, "/login");
+
+ assert(ht_get(ht, "/login") == NULL);
+
+ puts("Tests complete!");
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+CC=gcc
+CFLAGS=-Wall -Wextra
+
+OBJS=test.o http.o net.o hashtable.o llist.o
+
+all: test
+
+test: $(OBJS)
+ gcc -o $@ $^
+
+test.o: test.c http.h
+
+http.o: http.c http.h
+
+net.o: net.c net.h
+
+hashtable.o: hashtable.c hashtable.h
+
+llist.o: llist.c llist.h
+
+clean:
+ rm -f $(OBJS)
+ rm -f test
+
+.PHONY: all, clean
--- /dev/null
+/**
+ * http.c - Basic HTTP framework implentation
+ *
+ * hashtable.c for routing
+ * listen.c for socket creation
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "hashtable.h"
+#include "listen.h"
+#include "http.h"
+
+#define BUF_SIZE 65536
+#define HTTP_RES "HTTP/1.1 %d MESSAGE\n"\
+"Connection: close\n"\
+"Content-Length: %d\n"\
+"Content-Type: %s\n"\
+"\n"\
+"%s"
+
+HT* routes;
+
+void use(char* path, Handler handler)
+{
+ if (routes == NULL)
+ {
+ routes = ht_create();
+ }
+
+ // TODO: handle for key exist
+ ht_put(routes, path, (void*) handler);
+}
+
+void send_response(int client, Res* res)
+{
+ char res_raw[BUF_SIZE];
+
+ sprintf( res_raw,
+ HTTP_RES,
+ res->status,
+ res->content_length,
+ res->content_type,
+ res->body) ;
+
+ int num_sent = send(client, res_raw, strlen(res_raw), 0);
+
+ if (num_sent < 0)
+ {
+ perror("send");
+ }
+}
+
+void fallback_handler(Res* res)
+{
+ char* content = "Bad request\n";
+
+ res->status = 400;
+ res->content_length = strlen(content);
+ res->content_type = "text/html";
+ res->body = content;
+}
+
+void parse_req_raw(char* raw, Req* req)
+{
+ printf("%s\n", raw);
+
+ sscanf(raw, "%s %s", req->type, req->path);
+}
+
+void handle_request(int client)
+{
+ char req_raw[BUF_SIZE];
+ Req req;
+ Res res;
+
+ int num_recvd = recv(client, req_raw, BUF_SIZE - 1, 0);
+
+ if (num_recvd < 0)
+ {
+ perror("recv");
+ return;
+ }
+
+ parse_req_raw(req_raw, &req); // TODO: handle for parse.
+
+ printf("Request: %s, %s\n", req.type, req.path);
+
+ Handler handler = ht_get(routes, req.path);
+
+ if (handler == NULL)
+ {
+ fallback_handler(&res);
+ }
+ else
+ {
+ handler(&req, &res);
+ }
+
+ send_response(client, &res);
+}
+
+int http_start(char* port)
+{
+ int client, server;
+
+ if ((server = init_server_socket(port)) < 0)
+ {
+ puts("Error when establishing the server.");
+ exit(1);
+ }
+
+ printf("Waiting for connections on port %s...\n", port);
+
+ while(1)
+ {
+ client = accept(server, (struct sockaddr*) NULL, NULL);
+
+ if (client == -1)
+ {
+ perror("accept");
+ continue;
+ }
+
+ printf("Client connected.\n");
+
+ handle_request(client);
+ close(client);
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#define PORT "3000"
+
+typedef struct
+{
+ char type[100];
+ char path[200];
+}
+Req;
+
+typedef struct
+{
+ int status;
+ char* content_type;
+ int content_length;
+ char* body;
+}
+Res;
+
+/* User defined handlers must be this shape */
+typedef void (*Handler)(Req* req, Res* res);
+
+/* Add an api endpoint */
+void use(char* path, Handler handler);
+
+/* Start the server */
+int http_start(char* port);
+
+#endif
--- /dev/null
+/**
+ * listen.c - Create and bind a server socket to a port
+ *
+ */
+
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "listen.h"
+
+#define QUEUE 8
+
+
+int init_server_socket(char* port)
+{
+ int fd, yes = 1;
+ struct addrinfo hints, *servinfo, *p;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ getaddrinfo(NULL, port, &hints, &servinfo);
+
+ for (p = servinfo; p != NULL; p = p->ai_next)
+ {
+ if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
+ {
+ continue;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
+ {
+ close(fd);
+ freeaddrinfo(servinfo);
+ return -1;
+ }
+
+ if (bind(fd, p->ai_addr, p->ai_addrlen) == -1)
+ {
+ close(fd);
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(servinfo);
+
+ if (p == NULL) { return -1; }
+
+ if (listen(fd, QUEUE) == -1)
+ {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
\ No newline at end of file
--- /dev/null
+#ifndef _LISTEN_H_
+#define _LISTEN_H_
+
+int init_server_socket(char *port);
+
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * main.c - HTTP server that uses http.c framework
+ *
+ * curl http://localhost:3000/api/login
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "http.h"
+
+void login(Req* req, Res* res)
+{
+ (void) req;
+
+ char* content = "Login works!\n";
+
+ res->status = 200;
+ res->content_length = strlen(content);
+ res->content_type = "text/html";
+ res->body = content;
+}
+
+void signup(Req* req, Res* res)
+{
+ (void) req;
+
+ char* content = "Sign Up works!\n";
+
+ res->status = 200;
+ res->content_length = strlen(content);
+ res->content_type = "text/html";
+ res->body = content;
+}
+
+/**
+ * Register the endpoints and start the server.
+ */
+int main(void)
+{
+ puts("Registering endpoints");
+
+ use("/signup", &signup);
+ use("/login", &login);
+
+ http_start(PORT);
+}
--- /dev/null
+CC=gcc
+CFLAGS=-Wall -Wextra
+
+OBJS=test.o llist.o
+
+all: test
+
+test: $(OBJS)
+ gcc -o $@ $^
+
+test.o: test.c
+
+llist.o: llist.c llist.h
+
+clean:
+ rm -f $(OBJS)
+ rm -f test
+
+.PHONY: all, clean
--- /dev/null
+/**
+ * llist.c - a basic linked list implementation.
+ *
+ * Used for the hashtable.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "llist.h"
+
+/**
+ * Node in the linked list
+ */
+typedef struct LLNodeT
+{
+ void* data;
+ struct LLNodeT* next;
+}
+LLNode;
+
+/**
+ * Create a new linked list
+ */
+LL* ll_create(void)
+{
+ LL* ll = malloc(sizeof(LL));
+ ll->entries = 0;
+ return ll;
+}
+
+/**
+ * Add item to beginning of a linked list
+ */
+int ll_push(LL* ll, void* data)
+{
+ LLNode* n = malloc(sizeof(*n));
+
+ n->data = data;
+
+ // Reassign the head
+ n->next = ll->head;
+ ll->head = n;
+
+ ll->entries++;
+
+ return 0;
+}
+
+/**
+ * Add item to end of a linked list
+ */
+int ll_append(LL* ll, void* data)
+{
+ LLNode* tail = ll->head;
+
+ // Just push if there are no nodes yet
+ if (tail == NULL)
+ {
+ return ll_push(ll, data);
+ }
+
+ // Travserse list to find tail
+ while (tail->next != NULL)
+ {
+ tail = tail->next;
+ }
+
+ // Create new node and assign it as tail
+ LLNode* n = malloc(sizeof(LLNode));
+ n->data = data;
+ tail->next = n;
+
+ ll->entries++;
+
+ return 0;
+}
+
+/**
+ * Get an item from the list via cb function
+ */
+void* ll_search(LL* ll, LLCb cb, void* arg)
+{
+ LLNode* n = ll->head;
+
+ // Keep going until tail is reached
+ while (n != NULL)
+ {
+ if (cb(n->data, arg) == 0)
+ {
+ break;
+ }
+
+ n = n->next;
+ }
+
+ if (n == NULL)
+ {
+ return NULL;
+ }
+
+ return n->data;
+}
+
+/**
+ * Find and delete an entry from a linked list
+ */
+void* ll_delete(LL* ll, LLCb cb, void* arg)
+{
+ LLNode* n = ll->head, *prev = NULL;
+
+ while (n != NULL)
+ {
+ if (cb(n->data, arg) == 0)
+ {
+ void* data = n->data;
+
+ // We are at the head
+ if (prev == NULL)
+ {
+ ll->head = n->next;
+ free(n);
+ }
+ else
+ {
+ prev->next = n->next;
+ free(n);
+ }
+
+ return data;
+ }
+
+ prev = n;
+ n = n->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * Apply a function to every node in a linked list
+ */
+int ll_loop(LL* ll, LLCb cb, void* arg)
+{
+ LLNode* n = ll->head, *next;
+
+ while (n != NULL)
+ {
+ next = n->next;
+ cb(n->data, arg);
+ n = next;
+ }
+
+ return 0;
+}
+
+/**
+ * Free a linked list
+ */
+void ll_free(LL* ll)
+{
+ LLNode* n = ll->head, *next;
+
+ while (n != NULL)
+ {
+ next = n->next;
+ free(n);
+ n = next;
+ }
+
+ free(ll);
+}
+
--- /dev/null
+#ifndef _LLIST_H_
+#define _LLIST_H_
+
+typedef struct
+{
+ struct LLNodeT* head;
+ int entries;
+}
+LL;
+
+typedef int (*LLCb)(void*, void*);
+
+LL* ll_create(void);
+
+int ll_push(LL* ll, void* data);
+
+int ll_append(LL* ll, void* data);
+
+void* ll_search(LL* ll, LLCb cb, void* arg);
+
+void* ll_delete(LL* ll, LLCb cb, void* arg);
+
+int ll_loop(LL* ll, LLCb cb, void* arg);
+
+void ll_free(LL* ll);
+
+#endif
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "llist.h"
+
+typedef struct
+{
+ char* name;
+ char* team;
+ int age;
+}
+Player;
+
+Player* create_player(char* name, char* team, int age)
+{
+ Player* p = malloc(sizeof(*p));
+ p->team = strdup(team);
+ p->name = strdup(name);
+ p->age = age;
+ return p;
+}
+
+int find_player(void* data, void* arg)
+{
+ Player* player = data;
+ return strcmp(player->name, (char*) arg);
+}
+
+int change_team(void* data, void* arg)
+{
+ Player* player = data;
+ player->team = (char*) arg;
+ return 0;
+}
+
+int main(void)
+{
+ puts("Creating new linked list...");
+
+ LL* ll = ll_create();
+
+ puts("Pushing some data...");
+
+ Player* lebron = create_player("Lebron James", "Lakers", 37);
+ Player* giannis = create_player("Giannis Antetokounmpo", "Bucks", 27);
+
+ ll_push(ll, lebron);
+ ll_push(ll, giannis);
+
+ puts("Finding the data that was pushed...");
+
+ assert(ll_search(ll, find_player, "Lebron James") == lebron);
+
+ puts("Appending some data...");
+
+ Player* kd = create_player("Kevin Durant", "Nets", 33);
+
+ ll_append(ll, kd);
+
+ puts("Finding the data that was appended...");
+
+ assert(ll_search(ll, find_player, "Kevin Durant") == kd);
+
+ puts("Looping over linked list and applying change...");
+
+ ll_loop(ll, change_team, "Bucks");
+
+ Player* kd_ref = ll_search(ll, find_player, "Kevin Durant");
+
+ assert(strcmp(kd_ref->team, "Bucks") == 0);
+
+ puts("Deleting data from linked list...");
+
+ ll_delete(ll, find_player, "Lebron James");
+
+ assert(ll_search(ll, find_player, "Lebron James") == NULL);
+
+ puts("Tests complete!");
+
+ return 0;
+}
\ No newline at end of file