- Fast: Multi-threaded, non-blocking I/O, lock-free, copy-free, I/O multiplexing, SO_REUSEPORT (native load balancing on Linux)
- Modular: Easy to extend with custom controllers and handlers.
- Memory Safety: No race conditions.
- No Magic: Transparent and straightforward.
- E2E Testing: Allows end-to-end testing and scripting without running the server. Pass raw requests to
handle_request(). - SSE Friendly: Built-in Server-Sent Events support.
- ETag Friendly: Conditional GETs with ETag and
If-None-Matchheaders. - Database Friendly: Example with PostgreSQL connection pool.
- Graceful Shutdown: Automatic shutdown after test mode or on signal (W.I.P.).
- Multiple Backends: epoll, io_uring, kqueue (platform-dependent).
import http_server
fn handle_request(req_buffer []u8, client_conn_fd int) ![]u8 {
// ...parse request and return response...
}
fn main() {
mut server := http_server.new_server(http_server.ServerConfig{
port: 3000
request_handler: handle_request
io_multiplexing: $if linux {
.epoll
} $else $if darwin {
.kqueue
} $else {
.iocp
}
})
server.run()
}fn test_simple_without_init_the_server() {
request := 'GET / HTTP/1.1\r\n\r\n'.bytes()
assert handle_request(request, -1)! == http_ok_response
}Or use the server’s test mode:
mut server := http_server.new_server(http_server.ServerConfig{ ... })
responses := server.test([request1, request2]) or { panic(err) }Server:
v -prod run examples/sseFront-end:
<script>
const eventSource = new EventSource("http://localhost:3001/sse");
eventSource.onmessage = function (event) {
document.body.innerHTML += `<p>${event.data}</p>`;
};
</script>Send notification:
curl -X POST http://localhost:3001/notificationcurl -v http://localhost:3001/user/1
curl -v -H "If-None-Match: c4ca4238a0b923820dcc509a6f75849b" http://localhost:3001/user/1Start database:
docker-compose -f examples/database/docker-compose.yml up -dRun server:
v -prod run examples/databaseExample handler:
fn handle_request(req_buffer []u8, client_conn_fd int, mut pool ConnectionPool) ![]u8 {
// Use pool.acquire() and pool.release() for DB access
}- use
-d force_keep_aliveto make sure that client will not be "blocked" by having to create a new connection at each request
wrk -t16 -c512 -d30s http://localhost:3001
wrk -t16 -c512 -d30s -H "If-None-Match: c4ca4238a0b923820dcc509a6f75849b" http://localhost:3001/user/1examples/simple/– Basic CRUDexamples/etag/– ETag and conditional requestsexamples/sse/– Server-Sent Eventsexamples/database/– PostgreSQL integrationexamples/hexagonal/– Hexagonal architecture
The Server provides a test method that accepts an array of raw HTTP requests, sends them directly to the socket, and processes each one sequentially. After receiving the response for the last request, the loop ends and the server shuts down automatically. This enables efficient end-to-end testing without running a persistent server process longer that needed.
- Create the required directories:
mkdir -p ~/.vmodules/enghitalo/vanilla- Copy the
vanilladirectory to the target location:
cp -r ./ ~/.vmodules/enghitalo/vanilla- Run the example:
v -prod crun examples/simpleThis sets up the module in your ~/.vmodules directory for use.
Install directly from the repository:
v install https://github.com/enghitalo/vanilla- use
-d force_keep_aliveto make sure that client will not be "blocked" by having to create a new connection at each request
Run the following commands to benchmark the server:
- Test with
curl:
curl -v http://localhost:3001- Test with
wrk:
wrk -H 'Connection: "keep-alive"' --connection 512 --threads 16 --duration 60s http://localhost:3001Example output:
Running 1m test @ http://localhost:3001
16 threads and 512 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.25ms 1.46ms 35.70ms 84.67%
Req/Sec 32.08k 2.47k 57.85k 71.47%
30662010 requests in 1.00m, 2.68GB read
Requests/sec: 510197.97
Transfer/sec: 45.74MB