From 4af132cf6adeeeeb5d6764c378bec2d05cad042f Mon Sep 17 00:00:00 2001 From: frosty Date: Sun, 28 Dec 2025 03:26:05 -0500 Subject: Migrated from GitHub --- .gitignore | 1 + LICENSE | 28 + Makefile | 56 ++ README.md | 19 + beaker.h | 101 +++ examples/hello-world/Makefile | 6 + examples/hello-world/main.c | 20 + examples/template-demo/Makefile | 6 + examples/template-demo/main.c | 74 +++ examples/template-demo/static/style.css | 67 ++ examples/template-demo/templates/header.html | 8 + examples/template-demo/templates/index.html | 48 ++ src/beaker_globals.c | 13 + src/beaker_globals.h | 18 + src/http.c | 216 ++++++ src/routing.c | 226 +++++++ src/server.c | 181 +++++ src/template.c | 961 +++++++++++++++++++++++++++ 18 files changed, 2049 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 beaker.h create mode 100644 examples/hello-world/Makefile create mode 100644 examples/hello-world/main.c create mode 100644 examples/template-demo/Makefile create mode 100644 examples/template-demo/main.c create mode 100644 examples/template-demo/static/style.css create mode 100644 examples/template-demo/templates/header.html create mode 100644 examples/template-demo/templates/index.html create mode 100644 src/beaker_globals.c create mode 100644 src/beaker_globals.h create mode 100644 src/http.c create mode 100644 src/routing.c create mode 100644 src/server.c create mode 100644 src/template.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d6eac31 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2025, gabrielf35 + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder 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 HOLDER 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..66e5016 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +CC = gcc +AR = ar +CFLAGS = -Wall -fPIC -I. -Isrc +LDFLAGS = -shared +BUILD_DIR ?= build +OBJ_DIR = $(BUILD_DIR)/obj +INSTALL_PREFIX ?= /usr/ + +SRCS = $(wildcard src/*.c) +OBJS = $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(SRCS)) + +LIB = $(BUILD_DIR)/libbeaker +HEADER = beaker.h + +.PHONY: all clean install uninstall + +all: $(LIB).so $(LIB).a + + +$(OBJ_DIR): + @mkdir -p $(OBJ_DIR) + +$(LIB).a: $(OBJS) + @echo "Linking shared library $@..." + $(AR) rcs $@ $^ + @echo "Successfully built $@" + +$(LIB).so: $(OBJS) + @echo "Linking shared library $@..." + $(CC) $(LDFLAGS) $(OBJS) -o $@ -lm + @echo "Successfully built $@" + +$(OBJ_DIR)/%.o: src/%.c | $(OBJ_DIR) + @echo "Compiling $<..." + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + @echo "Cleaning up object files and shared library..." + @rm -rf $(OBJ_DIR) $(LIB) + @echo "Clean complete." + +install: all + @echo "Installing $(LIB) to $(INSTALL_PREFIX)/lib" + @cp $(LIB).so $(INSTALL_PREFIX)/lib/ + @ldconfig + @echo "Installing $(HEADER) to $(INSTALL_PREFIX)/include" + @cp $(HEADER) $(INSTALL_PREFIX)/include + @echo "Installation complete." + +uninstall: + @echo "Uninstalling $(LIB) from $(INSTALL_PREFIX)/lib..." + @rm -f $(INSTALL_PREFIX)/lib/$(LIB) + @ldconfig + @echo "Removing $(HEADER) from $(INSTALL_PREFIX)/include" + @rm -f $(INSTALL_PREFIX)/include/$(HEADER) + @echo "Uninstallation complete." diff --git a/README.md b/README.md new file mode 100644 index 0000000..998fdbf --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Beaker +Beaker is a lightweight web framework written in C, designed for building simple and efficient web applications. It currently provides handling HTTP requests, routing, serving static files, rendering HTML templates and managing cookies. + +## Usage +Look at the files in examples for usage + +## Installation +```bash + git clone https://github.com/gabrielf35/beaker.git + cd beaker + sudo make install +``` + +## Roadmap + +- Handle different types of POST requests +- Write documentation +- Add more robust logging +- Write more examples \ No newline at end of file diff --git a/beaker.h b/beaker.h new file mode 100644 index 0000000..d19d4c6 --- /dev/null +++ b/beaker.h @@ -0,0 +1,101 @@ +#ifndef BEAKER_H +#define BEAKER_H + +#include +#include +#include +#include +#include + +#define MAX_CONTEXT_VARS 32 +#define MAX_KEY_LEN 64 +#define MAX_VALUE_LEN 256 +#define MAX_PATH_LEN 256 +#define MAX_HANDLERS 32 +#define BUFFER_SIZE 4096 +#define MAX_URL_PARAMS 16 +#define MAX_COOKIES 10 +#define MAX_OUTER_ARRAY_ITEMS 100 +#define MAX_INNER_ARRAY_ITEMS 200 + +#define TEMPLATES_DIR "templates/" +#define STATIC_DIR "static/" + +typedef enum { + CONTEXT_TYPE_STRING, + CONTEXT_TYPE_STRING_ARRAY, + CONTEXT_TYPE_STRING_2D_ARRAY, +} ContextType; + +typedef struct { + char key[MAX_KEY_LEN]; + ContextType type; + union { + char string_val[MAX_VALUE_LEN]; + struct { + char **values; + int count; + } string_array_data; + struct { + char ***values; + int outer_count; + int *inner_counts; + } string_2d_array_data; + } value; +} ContextVar; + +typedef struct { + ContextVar vars[MAX_CONTEXT_VARS]; + int count; +} TemplateContext; + +typedef struct { + char key[MAX_KEY_LEN]; + char value[MAX_VALUE_LEN]; +} UrlParam; + +typedef struct { + UrlParam params[MAX_URL_PARAMS]; + int count; +} UrlParams; + +typedef struct { + char name[MAX_KEY_LEN]; + char value[MAX_VALUE_LEN]; + char expires[MAX_VALUE_LEN]; + char path[MAX_KEY_LEN]; + bool http_only; + bool secure; +} Cookie; + +typedef int (*RequestHandler)(UrlParams *params); + +typedef struct { + char path[MAX_PATH_LEN]; + RequestHandler handler; +} RouteHandler; + +TemplateContext new_context(); +void context_set(TemplateContext *ctx, const char *key, const char *value); +void context_set_string_array(TemplateContext *ctx, const char *key, + char *values[], int count); +void context_set_array_of_arrays(TemplateContext *ctx, const char *key, + char **values_2d[], int outer_count, + int inner_counts[]); +void free_context(TemplateContext *ctx); +char *render_template(const char *template_file, TemplateContext *ctx); + +void send_response(const char *html); +void send_redirect(const char *location); +void set_cookie(const char *name, const char *value, const char *expires, + const char *path, bool http_only, bool secure); +char *get_cookie(const char *cookie_name); + +void set_handler(const char *path, RequestHandler handler); +char *parse_request_url(const char *request_buffer, UrlParams *params); +const char *get_mime_type(const char *file_path); +bool serve_static_file(const char *request_path_relative_to_static); + +int beaker_run(const char *ip, int port); + +#endif diff --git a/examples/hello-world/Makefile b/examples/hello-world/Makefile new file mode 100644 index 0000000..56813e3 --- /dev/null +++ b/examples/hello-world/Makefile @@ -0,0 +1,6 @@ +CC = gcc + +hello-world: + $(CC) -o hello-world main.c -lbeaker +clean: + rm hello-world \ No newline at end of file diff --git a/examples/hello-world/main.c b/examples/hello-world/main.c new file mode 100644 index 0000000..bf3c8b0 --- /dev/null +++ b/examples/hello-world/main.c @@ -0,0 +1,20 @@ +#include +#include +#include + +int hello_world_handler(UrlParams *params) { + send_response("Hello, World!"); + return 0; +} + +int main() { + set_handler("/", hello_world_handler); + int result = beaker_run("127.0.0.1", 8080); + + if (result != 0) { + fprintf(stderr, "[APP] Error: Beaker server failed to start.\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/examples/template-demo/Makefile b/examples/template-demo/Makefile new file mode 100644 index 0000000..ff0acd4 --- /dev/null +++ b/examples/template-demo/Makefile @@ -0,0 +1,6 @@ +CC = gcc + +template-demo: + $(CC) -o template-demo main.c -lbeaker +clean: + rm template-demo \ No newline at end of file diff --git a/examples/template-demo/main.c b/examples/template-demo/main.c new file mode 100644 index 0000000..2adbb52 --- /dev/null +++ b/examples/template-demo/main.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +int templating_handler(UrlParams *params) { + + TemplateContext ctx = new_context(); + + context_set(&ctx, "title", "Beaker Example"); + context_set(&ctx, "page_heading", "Dynamic Content with Beaker Templates"); + context_set(&ctx, "username", "John Doe"); + context_set(&ctx, "favourite_colour", "blue"); + + time_t t = time(NULL); + struct tm *tm = localtime(&t); + char current_year_str[5]; + snprintf(current_year_str, sizeof(current_year_str), "%d", + tm->tm_year + 1900); + context_set(&ctx, "current_year", current_year_str); + + char timestamp_str[64]; + strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", tm); + context_set(&ctx, "timestamp", timestamp_str); + + context_set(&ctx, "safe_html", "This is bold and italic HTML."); + context_set(&ctx, "unsafe_html", "