Configuration objects for dummy backend servers and reverse proxies.
::: httphound.backend.BackendConfig
options:
show_root_heading: true
show_source: false
BackendConfig defines the behavior of the dummy backend HTTP server used in tests.
from httphound.main import BackendConfig
# Minimal configuration (all defaults)
backend_config = BackendConfig()
# Custom configuration
backend_config = BackendConfig(
host="127.0.0.1",
port=9999,
response_status=200,
response_headers={"X-Custom": "value"},
response_body="Hello from backend"
)
host: strIP address for backend to bind to.
Default: "127.0.0.1"
BackendConfig(host="127.0.0.1") # Localhost only
BackendConfig(host="0.0.0.0") # All interfaces
This is used also to populate HAProxy configuration file (when in template mode).
port: intPort number for backend to listen on.
Default: 9999
!!! warning "Type Matters"
Must be an integer, not a string!
BackendConfig(port=9999) # correct
BackendConfig(port="9999") # wrong!
response_status: intHTTP status code to return when a request to the backend is made.
Default: 200
BackendConfig(response_status=200) # OK
BackendConfig(response_status=201) # Created
BackendConfig(response_status=301) # Moved Permanently
BackendConfig(response_status=404) # Not Found
BackendConfig(response_status=500) # Internal Server Error
response_headers: Dict[str, str]HTTP headers to include in response.
Default: {} (empty dict)
BackendConfig(response_headers={
"Content-Type": "application/json",
"X-Custom-Header": "value123",
"Cache-Control": "no-cache"
})
!!! tip "Header Case"
Header names will be sent as specified. HTTP is case-insensitive for headers, but preserve the case you want. HAProxy usually lowercase these.
response_body: strResponse body content.
Default: "OK"
BackendConfig(response_body="Hello World")
BackendConfig(response_body='{"status": "ok"}')
BackendConfig(response_body="<html><body>Test</body></html>")
backend_config = BackendConfig(
port=9999,
response_status=200,
response_headers={
"Content-Type": "application/json",
"X-API-Version": "1.0"
},
response_body='{"status": "success", "data": [1, 2, 3]}'
)
backend_config = BackendConfig(
response_status=503,
response_headers={
"Content-Type": "text/plain",
"Retry-After": "300"
},
response_body="Service temporarily unavailable"
)
backend_config = BackendConfig(
response_status=301,
response_headers={
"Location": "https://example.com/new-location"
},
response_body=""
)
::: httphound.proxy.ProxyConfig
options:
show_root_heading: true
show_source: false
ProxyConfig defines how HAProxy is configured and started. Supports two modes:
from httphound.main import ProxyConfig
from pathlib import Path
proxy_config = ProxyConfig(
binary_path=Path.home() / "bin/haproxy",
template_path="haproxy.cfg.tpl",
listen_port=4242
)
proxy_config = ProxyConfig(
binary_path="/usr/sbin/haproxy",
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
backend_name_to_patch="app_backend",
bind_address_override="*:4242"
)
binary_path: strPath to HAProxy binary. Must exists on the filesystem and executable as the user who runs the tests.
Default: "/usr/sbin/haproxy"
ProxyConfig(binary_path="/usr/sbin/haproxy") # Default path if HAProxy is installed as debian package
ProxyConfig(binary_path=Path.home() / "bin/haproxy")
ProxyConfig(binary_path="/usr/local/bin/haproxy")
working_dir: strTemporary directory for generated configs.
Default: "/tmp/httphound"
ProxyConfig(working_dir="/tmp/httphound")
ProxyConfig(working_dir="/tmp/my-tests")
config_mode: strConfiguration mode: "template" or "production".
Default: "template"
ProxyConfig(config_mode="template") # Use Jinja2 template
ProxyConfig(config_mode="production") # Use existing config
extra_args: List[str]Additional command-line arguments for HAProxy.
Default: []
ProxyConfig(extra_args=["-dM"]) # Memory debug mode
ProxyConfig(extra_args=["-D"]) # Daemon mode
Used when config_mode="template" (default).
template_path: strPath to Jinja2 template file.
Default: "haproxy.cfg.tpl"
ProxyConfig(template_path="haproxy.cfg.tpl")
ProxyConfig(template_path="templates/my-haproxy.tpl")
listen_addr: strAddress for HAProxy to bind to.
Default: "*" (all interfaces)
ProxyConfig(listen_addr="*") # All interfaces
ProxyConfig(listen_addr="127.0.0.1") # Localhost only
ProxyConfig(listen_addr="0.0.0.0") # Explicit all
listen_port: intPort for HAProxy to listen on.
Default: 4242
ProxyConfig(listen_port=4242) # Default
ProxyConfig(listen_port=8080) # Custom
template_vars: Optional[Dict[str, List[str]]]Additional variables to pass to template. Each key corresponds to the HAProxy template section where the directives must be added.
Default: {}
ProxyConfig(template_vars={
"max_connections": 1000,
"timeout_client": "30s",
"template_directives": {
"global": [
"maxconn 1000",
],
"defaults": [
"retries 3",
],
"frontend_http": [
"http-request deny deny_status 404 if path_beg -i /deny",
],
}
})
These variables are rendered
In template:
global
...
{%- if template_directives["global"] -%}
{{ for directive in template_directives["global"] }}
{{ directive }}
{%- endfor -%}
{%- endif -%}
defaults
...
{%- if template_directives["defaults"] -%}
{{ for directive in template_directives["defaults"] }}
{{ directive }}
{%- endfor -%}
{%- endif -%}
frontend http
{%- if template_directives["frontend_http"] -%}
{{ for directive in template_directives["frontend_http"] }}
{{ directive }}
{%- endfor -%}
{%- endif -%}
Used when config_mode="production".
production_config_path: strPath to main HAProxy configuration file.
Required in production mode
ProxyConfig(
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg"
)
production_config_base_dir: strBase directory containing config and includes (for multi-file configs).
Optional - if set, entire directory tree is copied.
ProxyConfig(
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
production_config_base_dir="/etc/haproxy/conf.d" # Copies whole directory
)
backend_name_to_patch: strName of backend section to patch with test backend address.
Default: "default_backend"
ProxyConfig(
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
backend_name_to_patch="app_backend" # Must match config
)
In HAProxy config:
backend app_backend
server app1 10.0.1.10:8080
server app2 10.0.1.11:8080
After patching:
backend app_backend
server app1 127.0.0.1:9999 # Test backend
server app2 127.0.0.1:9999 # Test backend
bind_address_override: strOverride all bind addresses to avoid port conflicts.
Optional - if not set, original bind addresses are used.
ProxyConfig(
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
bind_address_override="*:4242" # Override all binds
)
Original config:
frontend web
bind *:80
bind *:443 ssl crt /path/to/cert
After override:
frontend web
bind *:4242
bind *:4242 ssl crt /path/to/cert
skip_backend_injection: boolIf True, don't patch backend server addresses.
Default: False
ProxyConfig(
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
skip_backend_injection=True # Don't modify backends
)
Use this when:
from pathlib import Path
proxy_config = ProxyConfig(
binary_path=Path.home() / "bin/haproxy",
config_mode="template",
template_path="haproxy.cfg.tpl",
listen_port=4242
)
proxy_config = ProxyConfig(
binary_path="/usr/sbin/haproxy",
template_path="templates/advanced.tpl",
listen_port=8080,
template_directives={
"frontend_http": [
"http-request deny deny_status 404 if path_beg -i /deny",
],
},
)
proxy_config = ProxyConfig(
binary_path="/usr/sbin/haproxy",
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
backend_name_to_patch="app_backend",
bind_address_override="127.0.0.1:4242"
)
proxy_config = ProxyConfig(
binary_path="/usr/sbin/haproxy",
config_mode="production",
production_config_path="/etc/haproxy/haproxy.cfg",
production_config_base_dir="/etc/haproxy", # Copy whole tree
backend_name_to_patch="web_backend",
bind_address_override="*:4242"
)
# When production config already has correct backend address
proxy_config = ProxyConfig(
config_mode="production",
production_config_path="/tmp/test-haproxy.cfg",
skip_backend_injection=True, # Don't patch
bind_address_override="*:4242"
)
# Backend must match config
backend_config = BackendConfig(
host="127.0.0.1",
port=8080 # Must match what's in config
)