Fast, unopinionated, minimalist web framework for C
Fast and loose! This is alpha software and is incomplete. It's not ready for production as it is most likely unstable and insecure.
Please open an issue or open a pull request if you have any suggestions or bug reports. Thanks!
- User-friendly API using blocks modeled on expressjs
- Uses libdispatch for concurrency
- Request-based memory management for middleware
#include "../src/express.h"
#include <dotenv-c/dotenv.h>
#include <stdio.h>
#include <stdlib.h>
char *styles = "<link rel='stylesheet' href='/demo/public/app.css'>";
char *createTodoForm =
"<form method='POST' action='/todo/create'>"
" <p><label>Title: <input type='text' name='title'></label></p>"
" <p><label>Body: <input type='text' name='body'></label></p>"
" </p><input type='submit' value='Create'></p>"
"</form>";
int main() {
app_t *app = express();
/* Load .env file */
env_load(".", false);
char *PORT = getenv("PORT");
int port = PORT ? atoi(PORT) : 3000;
/* Load static files middleware */
embedded_files_data_t embeddedFiles = {0};
char *staticFilesPath = cwdFullPath("demo/public");
app->use(expressStatic("demo/public", staticFilesPath, embeddedFiles));
/* Route handlers */
app->get("/", ^(UNUSED request_t *req, response_t *res) {
res->sendf("%s<h1>Todo App</h1><p>%s</p>", styles, createTodoForm);
});
app->post("/todo/create", ^(request_t *req, response_t *res) {
res->status = 201;
res->sendf("%s<h1>%s</h1><p>%s</p>", styles, req->body("title"), req->body("body"));
});
/* Start server */
app->listen(port, ^{
printf("express-c app listening at http://localhost:%d\n", port);
});
}
Take a look at the TodoMVC demo for a more complete example that includes Mustache templates middleware, cookie-based sessions middleware, thread-safe postgres middleware, and more.
Features req->malloc
and req->blockCopy
to allocate memory which is automatically freed when the request is done. This is powered by a simple and highly performant bump allocator in a per-request memory arena.
These are used internally meaning that calls to req->params
, req->body
, req->get
, req->query
, and req->cookie
will return a pointer to the memory which is automatically freed when the request is done.
Middleware is given a cleanup
callback which is also called at completion of request.
Apps and routers are also given cleanup
callback stacks which are handled during app.closeServer()
.
Take a look at the Todo store for an example.
$ brew install llvm clib fswatch dbmate jansson libjwt
The primary requirements are clang
, libbsd
, libblocksruntime
and libdispatch
.
The easiest way is to open this in a GitHub Codespace.
Otherwise see the Dockerfile for instructions on how to build the dependencies.
See it running: https://express-c-demo.herokuapp.com.
$ make TodoMVC
$ build/TodoMVC
Todo app listening at http://localhost:3000
$ open http://localhost:3000
^C
Closing server...
Please install the recommended extensions for VS Code:
The VS Code launch.json
and tasks.json
are set up to run and debug any .c
file in the demo/
directory using make
and the CodeLLDB
extension.
It is recommended that you set "C_Cpp.updateChannel": "Insiders",
in your local settings.json
file. This will give you access to the latest features including support for clang-tidy
.
Otherwise you can install the Clang-Tidy extension.
The build process is configured to add everything in the deps/
directory following the clib best practices.
The Makefile constructs CFLAGS
from the clang-tidy
compatible compile_flags.txt file.
There are additional platform dependent CFLAGS
for Linux and Darwin (OS X).
To recompile and restart a server app in the demo/
directory when a file changes:
$ make TodoMVC-watch
Uses a very simple testing harness to launch a test app and run integration tests using system calls to curl
. There are no unit tests which encourages refactoring.
$ make test
To test for memory leaks:
$ make test-leaks
Uses the native leaks
command line application. Build make build-test-trace
to enable tracing using the Instruments app.
Uses valgrind
with --tool=memcheck
and --leak-check=full
options.
Linting using clang-tidy
with -warnings-as-errors=*
:
$ make test-lint
Formatting using clang-format --dry-run --Werror
:
$ make test-format
To recompile and rerun the tests when a file changes:
$ make test-watch
There is a GitHub Actions workflow for continuous integration. It builds and runs the a number of tests on both Ubuntu and OS X.
make format
make lint
make test-leaks
make test
Uses clib for dependency management:
And: