Servex is an HTTP/1.1 server written in Rust using non-blocking I/O with mio.
It serves static files, supports uploads, executes CGI scripts, and supports virtual hosts through a config file.
- Event-driven runtime (
mio::Poll) with one process/thread. - HTTP request parsing (including
Content-LengthandTransfer-Encoding: chunked). - Route handling for
GET,POST,DELETE. - Static files, directory autoindex, redirects.
- Multipart and raw-body uploads.
- CGI execution via
std::process::Command(e.g..pyscripts). - Session cookie support with configurable timeout.
- Multiple server blocks and hostname-based virtual host selection on shared ports.
- Custom error pages (
400,403,404,405,413,500).
src/main.rs: boot sequence (load -> validate -> run).src/config/: INI parsing, loading, validation, models.src/runtime/: event loop, connection lifecycle, response generation.src/routing/: route resolution and virtual-host server selection.src/http/: request parsers and response builder.tests/integration_test.rs: process/socket end-to-end integration tests.external_tests/: black-box external/audit test scripts.application.conf: default local config.audit/application.audit.conf: audit-focused config.
cargo build --releaseServex expects application.conf in the current working directory.
cargo run --releaseDebug builds print parsed config at startup (#[cfg(debug_assertions)]).
Config file is INI-style (not YAML), for example:
[global]:max_body_size(bytes)timeout(seconds)keep_alive(true/false)
[error_pages]:status_code = path[server]or[server:<name>]:server_name(comma-separated hostnames)hostports(comma-separated)root
[route:<server_name>:<route_name>]:path,methods- optional:
root,index,autoindex - optional redirects:
redirect_status,redirect_target - optional uploads:
upload_dir,max_file_size - optional CGI:
cgi_extension,cgi_executor
[session]:enabled,timeout,cookie_name,secure,http_only
Notes:
- Route
pathmust start with/. - When multiple server blocks share the same
host:port, Host header selects virtual host. - In single-server configs, routes referencing another server name are attached to that one server as fallback.
- Poll for events from listeners and client sockets.
- Accept new clients on readable listeners.
- Read client bytes, parse one or more requests from buffer.
- Choose server block (listener candidates +
Hostheader). - Resolve route and method behavior.
- Queue response bytes.
- Write responses on writable events.
- Apply keep-alive and idle-timeout connection management.
- Unit tests:
- Located inside source modules under
src/(#[test]). - Validate parser logic, config loading/validation, routing decisions, runtime helper behavior, and HTTP builder/parsers.
- Located inside source modules under
- Integration tests:
- Located in
tests/integration_test.rs. - Spawn the real
servexbinary and verify end-to-end behavior through TCP sockets.
- Located in
- External black-box tests:
- Located in
external_tests/. - Execute shell-level checks against a running server (
run.sh,audit_run.sh,extra_tests.sh).
- Located in
- Stress/performance checks:
- Manual load testing via
siegefor availability/performance validation.
- Manual load testing via
# unit + integration tests (Rust test harness)
cargo test
# binary-target tests (useful in restricted environments)
cargo test --bin servex
# integration tests only
cargo test --test integration_test# basic external checks
./external_tests/run.sh
# full audit checks (uploads, CGI, redirects, virtual hosts, cookies)
./external_tests/audit_run.sh
# additional behavior checks
./external_tests/extra_tests.shaudit_run.sh now fails early if port 8080 is already in use.
siege -b http://127.0.0.1:8080/okHEAD,PUT, etc. are parsed but not implemented as handlers (typically405).- Session storage is in-memory (not persistent across restart).
- CGI output is treated as response body with
200 OKwhen script exits successfully.
Educational use.