A flexible HTTP proxy server powered by Rune scripting. Every incoming request is first handled by a Rune script, which can either respond directly or indicate that the request should be proxied to an upstream server.
- Rune Scripting: Handle HTTP requests with dynamic Rune scripts
- Upstream Proxy: Automatically proxy unhandled requests to another web server
- Hot-reloadable: Modify scripts without restarting
- Full HTTP Support: Access method, path, headers, and body in scripts
Pre-Built Binaries (via cargo-binstall)
cargo binstall mockboxYou can download pre-built binaries from the latest release.
- Clone the repository
- Build the project:
cargo install mockbox- Generate the an example script (online version):
mockbox example > mockbox.rnIf you want to learn more about rune, check out the rune book.
- Run the server:
mockbox mockIf no script path is passed to mockbox, it will check for a file called mockbox.rn in the current directory and if it doesn't exist, it will check in $HOME/.local/share/mockbox for a mockbox.rn.
- The server will start on
http://127.0.0.1:3333
Configure the upstream server using the MOCKBOX_UPSTREAM environment variable:
MOCKBOX_UPSTREAM=http://localhost:8080 mockbox mock mockbox.rnor by using the appropriate cli option:
mockbox mock mockbox.rn --upstream http://localhost:8080Your mockbox.rn must export a handle_request function that receives a request object and returns either a string, an object, a tuple ((<status_code>, <response>)).
The request object passed to your handler contains:
method: HTTP method (e.g., "GET", "POST")path: Request path (e.g., "/api/users")body: Request body as a string
Return just a string for a 200 OK response:
pub fn handle_request(request) {
"Hello, World!"
}Return an object for a 200 OK response:
pub fn handle_request(request) {
#{some: 1, values: 2}
}The object will automatically be converted to json.
Return a tuple with status and response (string or object):
pub fn handle_request(request) {
(200, "Hello, World!")
}Explicitly return nothing to proxy the request:
pub fn handle_request(request) {
()
}pub fn handle_request(request) {
let path = request.path;
let method = request.method;
// Mock user API
match path {
"/api/users" if method == "GET" => {
[#{ "id": 1, "name": "John" }, #{ "id": 2, "name": "Jane" }]
}
// Mock authentication
"/api/login" if method == "POST" => {
#{ "token": "mock-jwt-token-12345" }
}
}
}pub fn handle_request(request) {
let path = request.path;
match path {
// Echo endpoint
"/echo" => request.body,
// Handle all /mock/* routes
_ if path.starts_with("/mock/") => #{mocked: true, path: path},
}
}pub fn handle_request(request) {
let path = request.path;
let body = request.body;
// Mock only if body contains "test"
if body.contains("test") {
return #{ "message": "Test mode response" };
}
}pub fn handle_request(request) {
let path = request.path;
// Simulate errors for testing
match path {
"/error/500" => (500, "Internal Server Error"),
"/error/404" => (404, "Not Found"),
}
}- Request Reception: Axum receives the HTTP request
- Rune Execution: The
handle_requestfunction inmockbox.rnis called - Response Decision:
- If the script returns a string, object or response tuple → respond directly
- If the script doesn't return anything or explicitly returns
()→ proxy to upstream server
- Upstream Proxy: Forward the original request to the configured upstream URL
- Response: Return the response from either Rune or the upstream server
- Mock APIs: Create mock responses for testing frontend applications
- Request Interception: Log, modify, or reject requests based on conditions
- A/B Testing: Route requests to different backends based on rules
- Development Environment: Override specific endpoints while keeping others real
Test your Rune scripts by making HTTP requests:
# Test a mocked endpoint
curl http://localhost:3333/demo
# Test the upstream proxy
curl http://localhost:3333/some/unhandled/pathEnables the cache API to persist data between requests.
This is enabled by default
// store a rune value
cache::set(key: &str, value: rune::Value) -> Result<()>;
// load a stored value
cache::get(key: &str) -> Result<rune::Value>;
// delete a stored value
cache::delete(key: &str) -> Result<()>;
// check if a value exists
cache::has(key: &str) -> bool;
// clear the whole cache
cache::clear() -> Result<()>;
// get keys of all stored values
cache::keys() -> Result<()>;["demo"] => {
let demo_count = cache::get("demo_count")?.unwrap_or(0);
let new_demo_count = demo_count + 1;
cache::set("demo_count", new_demo_count)?;
#{ message: `This demo endpoint has been called ${new_demo_count} times` }
},Enables the rugen API to build descriptions for generating random data.
This is enabled by default
This is what describing data with RuGen looks like:
use rugen as r;
#{
asdf: 1..10,
values: 5.values(55.0..128.0),
range_from: 100..,
range_to: ..100,
choice: r::choose([
#{ A: 100..=200 },
#{ B: -100..100 },
#{ C: 0.5..2.5 },
#{ D: r::string(10) },
]),
}For more information on RuGen, please visit the project repository.