]> Repos - http-server/commitdiff
init commit master
authorAndrew Gundersen <gundersena@crimata.com>
Mon, 2 May 2022 13:00:44 +0000 (08:00 -0500)
committerAndrew Gundersen <gundersena@crimata.com>
Mon, 2 May 2022 13:00:44 +0000 (08:00 -0500)
16 files changed:
.gitignore [new file with mode: 0644]
README.md [new file with mode: 0644]
hashtable/Makefile [new file with mode: 0644]
hashtable/hashtable.c [new file with mode: 0644]
hashtable/hashtable.h [new file with mode: 0644]
hashtable/test.c [new file with mode: 0644]
http/Makefile [new file with mode: 0644]
http/http.c [new file with mode: 0644]
http/http.h [new file with mode: 0644]
http/listen.c [new file with mode: 0644]
http/listen.h [new file with mode: 0644]
http/main.c [new file with mode: 0644]
llist/Makefile [new file with mode: 0644]
llist/llist.c [new file with mode: 0644]
llist/llist.h [new file with mode: 0644]
llist/test.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..1530978
--- /dev/null
@@ -0,0 +1 @@
+*.o
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..aca5503
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+Addon Libraries to C Programming Language
\ No newline at end of file
diff --git a/hashtable/Makefile b/hashtable/Makefile
new file mode 100644 (file)
index 0000000..c4d0f72
--- /dev/null
@@ -0,0 +1,21 @@
+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
diff --git a/hashtable/hashtable.c b/hashtable/hashtable.c
new file mode 100644 (file)
index 0000000..51eb5c1
--- /dev/null
@@ -0,0 +1,153 @@
+/**
+ * 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;
+}
diff --git a/hashtable/hashtable.h b/hashtable/hashtable.h
new file mode 100644 (file)
index 0000000..b974cb8
--- /dev/null
@@ -0,0 +1,24 @@
+#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
diff --git a/hashtable/test.c b/hashtable/test.c
new file mode 100644 (file)
index 0000000..5b3636d
--- /dev/null
@@ -0,0 +1,34 @@
+#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
diff --git a/http/Makefile b/http/Makefile
new file mode 100644 (file)
index 0000000..a8303f5
--- /dev/null
@@ -0,0 +1,25 @@
+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
diff --git a/http/http.c b/http/http.c
new file mode 100644 (file)
index 0000000..007ca2a
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * 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;
+}
diff --git a/http/http.h b/http/http.h
new file mode 100644 (file)
index 0000000..db8b1b6
--- /dev/null
@@ -0,0 +1,31 @@
+#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
diff --git a/http/listen.c b/http/listen.c
new file mode 100644 (file)
index 0000000..91bc90e
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * 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
diff --git a/http/listen.h b/http/listen.h
new file mode 100644 (file)
index 0000000..c65a0a6
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _LISTEN_H_
+#define _LISTEN_H_
+
+int init_server_socket(char *port);
+
+#endif
\ No newline at end of file
diff --git a/http/main.c b/http/main.c
new file mode 100644 (file)
index 0000000..9cc5e50
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * 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);
+}
diff --git a/llist/Makefile b/llist/Makefile
new file mode 100644 (file)
index 0000000..1a1b704
--- /dev/null
@@ -0,0 +1,19 @@
+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
diff --git a/llist/llist.c b/llist/llist.c
new file mode 100644 (file)
index 0000000..5f3921c
--- /dev/null
@@ -0,0 +1,174 @@
+/**
+ * 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);
+}
+
diff --git a/llist/llist.h b/llist/llist.h
new file mode 100644 (file)
index 0000000..e063e13
--- /dev/null
@@ -0,0 +1,27 @@
+#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
diff --git a/llist/test.c b/llist/test.c
new file mode 100644 (file)
index 0000000..8ee3fe3
--- /dev/null
@@ -0,0 +1,83 @@
+#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