# Configuration Configuration objects for dummy backend servers and reverse proxies. ## BackendConfig ::: httphound.backend.BackendConfig options: show_root_heading: true show_source: false ### Overview `BackendConfig` defines the behavior of the dummy backend HTTP server used in tests. ### Basic Usage ```python 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" ) ``` ### Parameters #### `host: str` IP address for backend to bind to. **Default:** `"127.0.0.1"` ```python 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: int` Port number for backend to listen on. **Default:** `9999` !!! warning "Type Matters" Must be an integer, not a string! ```python BackendConfig(port=9999) # correct BackendConfig(port="9999") # wrong! ``` #### `response_status: int` HTTP status code to return when a request to the backend is made. **Default:** `200` ```python 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) ```python 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: str` Response body content. **Default:** `"OK"` ```python BackendConfig(response_body="Hello World") BackendConfig(response_body='{"status": "ok"}') BackendConfig(response_body="Test") ``` ### Examples #### Mock JSON API Backend ```python 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]}' ) ``` #### Error Response Backend ```python backend_config = BackendConfig( response_status=503, response_headers={ "Content-Type": "text/plain", "Retry-After": "300" }, response_body="Service temporarily unavailable" ) ``` #### Redirect Backend ```python backend_config = BackendConfig( response_status=301, response_headers={ "Location": "https://example.com/new-location" }, response_body="" ) ``` --- ## ProxyConfig ::: httphound.proxy.ProxyConfig options: show_root_heading: true show_source: false ### Overview `ProxyConfig` defines how HAProxy is configured and started. Supports two modes: * **Template mode**: Generate config from Jinja2 template (default) * **Production mode**: Use existing HAProxy configuration files ### Basic Usage #### Template Mode (Default) ```python 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 ) ``` #### Production Mode ```python 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" ) ``` ### Common Parameters #### `binary_path: str` Path to HAProxy binary. Must exists on the filesystem and executable as the user who runs the tests. **Default:** `"/usr/sbin/haproxy"` ```python 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: str` Temporary directory for generated configs. **Default:** `"/tmp/httphound"` ```python ProxyConfig(working_dir="/tmp/httphound") ProxyConfig(working_dir="/tmp/my-tests") ``` #### `config_mode: str` Configuration mode: `"template"` or `"production"`. **Default:** `"template"` ```python 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:** `[]` ```python ProxyConfig(extra_args=["-dM"]) # Memory debug mode ProxyConfig(extra_args=["-D"]) # Daemon mode ``` ### Template Mode Parameters Used when `config_mode="template"` (default). #### `template_path: str` Path to Jinja2 template file. **Default:** `"haproxy.cfg.tpl"` ```python ProxyConfig(template_path="haproxy.cfg.tpl") ProxyConfig(template_path="templates/my-haproxy.tpl") ``` #### `listen_addr: str` Address for HAProxy to bind to. **Default:** `"*"` (all interfaces) ```python 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: int` Port for HAProxy to listen on. **Default:** `4242` ```python ProxyConfig(listen_port=4242) # Default ProxyConfig(listen_port=8080) # Custom ``` #### `template_directives: 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:** `{}` ```python 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:** ```jinja 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 -%} ``` ### Production Mode Parameters Used when `config_mode="production"`. #### `production_config_path: str` Path to main HAProxy configuration file. **Required in production mode** ```python ProxyConfig( config_mode="production", production_config_path="/etc/haproxy/haproxy.cfg" ) ``` #### `production_config_base_dir: str` Base directory containing config and includes (for multi-file configs). **Optional** - if set, entire directory tree is copied. ```python 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: str` Name of backend section to patch with test backend address. **Default:** `"default_backend"` ```python ProxyConfig( config_mode="production", production_config_path="/etc/haproxy/haproxy.cfg", backend_name_to_patch="app_backend" # Must match config ) ``` **In HAProxy config:** ```haproxy backend app_backend server app1 10.0.1.10:8080 server app2 10.0.1.11:8080 ``` **After patching:** ```haproxy backend app_backend server app1 127.0.0.1:9999 # Test backend server app2 127.0.0.1:9999 # Test backend ``` #### `bind_address_override: str` Override all bind addresses to avoid port conflicts. **Optional** - if not set, original bind addresses are used. ```python ProxyConfig( config_mode="production", production_config_path="/etc/haproxy/haproxy.cfg", bind_address_override="*:4242" # Override all binds ) ``` **Original config:** ```haproxy frontend web bind *:80 bind *:443 ssl crt /path/to/cert ``` **After override:** ```haproxy frontend web bind *:4242 bind *:4242 ssl crt /path/to/cert ``` #### `skip_backend_injection: bool` If True, don't patch backend server addresses. **Default:** `False` ```python ProxyConfig( config_mode="production", production_config_path="/etc/haproxy/haproxy.cfg", skip_backend_injection=True # Don't modify backends ) ``` Use this when: - Production config already points to test backend - Tests doesn't need to receive backend responses (test proxy configuration only) - You've manually edited the config - Backend patching is too complex ### Examples #### Simple Template Mode ```python from pathlib import Path proxy_config = ProxyConfig( binary_path=Path.home() / "bin/haproxy", config_mode="template", template_path="haproxy.cfg.tpl", listen_port=4242 ) ``` #### Template with custom directives ```python 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", ], }, ) ``` #### Production Mode - Single File ```python 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" ) ``` #### Production Mode - Multi-File ```python 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" ) ``` #### Production Mode - Manual Backend ```python # 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 ) ```