Drivers
HTTP, browser, DNS, WebSocket, gRPC, and SFTP driver options and examples.
A driver is responsible for executing a single request and returning a result. Each target in your config specifies a type that selects the driver. All drivers map their results to HTTP-like status codes so the engine’s error classifier, backoff, and metrics work uniformly.
Replay support
sendit replay reuses HTTP, browser, DNS, WebSocket, and gRPC drivers with their captured expanded settings. Browser replay requires Chrome; gRPC still uses server reflection. Same-host HTTP redirects retain normal behavior and cross-host redirects follow the captured allow_cross_host_redirects setting, without the engine’s redirect rate limiter.
Targets with any populated auth field, custom HTTP headers (even in an inactive block), URL userinfo, or SFTP are marked non-replayable. Snapshots contain only the active block: HTTP method/body/timeout/redirect policy; browser scroll/selector/timeout; DNS resolver/record type; WebSocket messages/duration/expected count; gRPC body/timeout/TLS settings. Values already expanded by templating are replayed literally.
Replay does not recreate remote responses, DNS answers, proxy settings, system trust stores, browser versions, or connection state. Driver errors remain results and do not stop later requests. Cancellation waits for replay-owned driver calls; existing DNS exchange and WebSocket close latency still applies. See the CLI contract.
auth block
Any target (or target_defaults) can include an auth block to attach credentials to each request. The http and websocket drivers honour it; SFTP uses credentials from its sftp block; other drivers silently ignore it.
targets:
- url: "https://api.example.com/data"
type: http
auth:
type: bearer
token_env: API_TOKEN # resolved from environment at dispatch time| Field | Description |
|---|---|
type | bearer | basic | header | query |
token | Literal token value (triggers a startup warning — prefer token_env in production) |
token_env | Name of the environment variable holding the token |
username / username_env | Basic auth username (literal or env var) |
password / password_env | Basic auth password (literal or env var) — optional |
header_name | Header name for type: header (e.g. X-API-Key) |
param_name | Query parameter name for type: query (e.g. api_key) |
Auth types:
| Type | Effect |
|---|---|
bearer | Adds Authorization: Bearer <token> header |
basic | Adds Authorization: Basic <base64(user:pass)> header |
header | Adds <header_name>: <token> header |
query | Adds or replaces <param_name> while preserving other query values |
Authentication applies to HTTP and WebSocket targets. Query authentication adds or replaces the configured parameter while preserving other query values. Environment-backed tokens are resolved at dispatch time. Results and exported output retain the expanded target URL, not the credential-bearing dial URL. If an environment variable is unset when a request fires, the result carries an error and no request is made.
Shared credentials via target_defaults:
target_defaults:
auth:
type: bearer
token_env: API_TOKEN
targets_file: "config/targets.txt"All file-loaded targets inherit the shared auth. Inline targets can override or omit it.
Request templates
Request templates expand per dispatch before domain rate limiting and driver execution. The supported surfaces are:
- Every target’s
url http.bodygrpc.body- Each
websocket.send_messagesentry
The same selected or generated value is reused across all fields in one request. Headers, authentication values, browser options, DNS options, and SFTP options are not templated. See Configuration for vars, vars_file, built-ins, and validation rules.
http
Sends an HTTP/HTTPS request using Go’s standard net/http client.
targets:
- url: "https://example.com/api"
weight: 5
type: http
http:
http_version: 0 # 0 automatic | 1 HTTP/1.1 | 2 HTTPS-only HTTP/2
method: GET # GET | POST | PUT | DELETE | ...
headers:
User-Agent: "Mozilla/5.0 ..."
Accept: "application/json"
body: '{"key":"value"}' # optional request body (string)
timeout_s: 15 # per-request timeout in seconds
allow_cross_host_redirects: false # opt in to follow redirects to another host| Field | Default | Description |
|---|---|---|
method | GET | HTTP verb |
http_version | 0 | Integer 0: automatic; 1: HTTP/1.1; 2: HTTPS-only HTTP/2, without HTTP/1 fallback |
headers | {} | Key-value map of request headers |
body | "" | Optional request body; supports request templates |
timeout_s | 15 | Per-request timeout (seconds) |
allow_cross_host_redirects | false | Follow redirects to a different host. Redirected hosts still use per-domain rate limits. Keep disabled when sending auth headers unless that forwarding is intended. |
Note: HTTP header map keys are lowercased by the YAML parser (e.g.
User-Agentis stored asuser-agent). This is standard YAML behaviour.
Non-standard ports: include the port directly in the URL — Go’s net/http client handles it natively:
- url: "http://internal-api.example.com:8080/health"
type: http
- url: "https://staging.example.com:8443/api"
type: httpHTTP version selection
Mode 0 preserves automatic HTTP/1.1 for plaintext and ALPN negotiation of HTTP/1.1 or HTTP/2 for HTTPS. Mode 1 sends HTTP/1.1 requests. Mode 2 requires HTTPS with ALPN h2; an HTTP/1-only or no-ALPN server fails before the HTTP request is transmitted. Normal certificate verification remains enabled. No plaintext h2c or HTTP/3 is implemented; HTTP/3 is tracked in #320.
The policy applies to every redirect hop. A forced-H2 request cannot follow a redirect to plaintext or send an HTTP/1 request to an incompatible destination. Existing cross-host permission, auth, timeout, and engine redirect-limiter behavior is retained. Distinct transports are reused safely for concurrent mixed policies. HTTP protocol controls do not change browser, WebSocket or gRPC transports.
http_protocol in JSONL and debug logs reports the actual final response protocol (HTTP/1.1, HTTP/2.0, etc.), including policy-stopped redirects. No response means no protocol metadata. It is independent of requested http_version, which is preserved in v2 replay snapshots. V1 replay input remains automatic. Dry-run displays configured policy only and sends no traffic.
browser
Loads a page in a headless Chromium instance via chromedp. Each task spawns its own ExecAllocator — no shared browser state — which prevents memory accumulation across long runs.
targets:
- url: "https://news.ycombinator.com"
weight: 3
type: browser
browser:
scroll: true # scroll to mid-page then bottom
wait_for_selector: "#hnmain" # CSS selector to wait for before returning
timeout_s: 30 # page load timeout in seconds| Field | Default | Description |
|---|---|---|
scroll | false | Scroll to mid-page then bottom after load |
wait_for_selector | "" | Wait for this CSS selector to be visible |
timeout_s | 30 | Page load timeout (seconds) |
Prerequisite: Chrome or Chromium must be installed on the machine running sendit.
Use max_browser_workers in limits to cap concurrent browser instances independently of the global worker pool:
limits:
max_workers: 4
max_browser_workers: 1 # at most 1 Chrome instance at a timedns
Resolves a hostname using miekg/dns directly — no system resolver involved. DNS RCODEs are mapped to HTTP-like status codes:
| DNS RCODE | HTTP equivalent | Effect |
|---|---|---|
| NOERROR (0) | 200 | success |
| NXDOMAIN (3) | 404 | permanent skip (no retry) |
| REFUSED (5) | 403 | permanent skip (no retry) |
| SERVFAIL (2) | 503 | transient backoff |
| other | 502 | transient backoff |
targets:
- url: "example.com"
weight: 3
type: dns
dns:
resolver: "8.8.8.8:53" # DNS server address
record_type: A # A | AAAA | MX | TXT | CNAME | ...| Field | Default | Description |
|---|---|---|
resolver | 8.8.8.8:53 | DNS server host:port |
record_type | A | DNS record type to query |
The resolver field is always host:port, so non-standard DNS ports are supported directly:
dns:
resolver: "192.168.1.1:5353" # custom resolver on non-standard port
record_type: Awebsocket
Opens a WebSocket connection using coder/websocket, optionally sends messages, and holds the connection open for a configurable duration.
targets:
- url: "wss://stream.example.com/feed"
weight: 2
type: websocket
websocket:
duration_s: 30 # hold connection for this many seconds
send_messages: ['{"type":"subscribe"}'] # messages to send on connect
expect_messages: 1 # wait to receive this many messages| Field | Default | Description |
|---|---|---|
duration_s | 30 | How long to hold the connection open (seconds) |
send_messages | [] | List of text messages to send after connecting; supports request templates |
expect_messages | 0 | Minimum messages to receive before considering success |
Non-standard ports: include the port in the URL:
- url: "wss://stream.example.com:9443/feed"
type: websocketgrpc
Executes a unary gRPC call using google.golang.org/grpc. No .proto files are required — the driver uses server reflection to discover request and response types at runtime, then marshals the JSON body to protobuf automatically.
targets:
- url: grpc://localhost:50051/helloworld.Greeter/SayHello
weight: 10
type: grpc
grpc:
body: '{"name": "world"}' # JSON-encoded request (optional — defaults to empty message)
timeout_s: 15 # per-call timeout in seconds
tls: false # force TLS even when scheme is grpc://
insecure: false # skip TLS certificate verification| Field | Default | Description |
|---|---|---|
body | "" | JSON-encoded request body. Must match the method’s input proto type. Empty sends a default-constructed message. Supports request templates. |
timeout_s | 15 | Per-call timeout in seconds |
tls | false | Force TLS even when the URL scheme is grpc:// |
insecure | false | Skip TLS certificate verification (combine with tls: true or grpcs:// scheme) |
URL scheme selects transport security:
| Scheme | Transport |
|---|---|
grpc://host:port/Service/Method | Plaintext |
grpcs://host:port/Service/Method | TLS |
gRPC status codes are mapped to HTTP-like status codes so the engine’s backoff and error classifier work uniformly:
| gRPC code | HTTP equivalent | Effect |
|---|---|---|
| OK (0) | 200 | success |
| InvalidArgument (3), OutOfRange (11) | 400 | permanent skip |
| Unauthenticated (16) | 401 | permanent skip |
| PermissionDenied (7) | 403 | permanent skip |
| NotFound (5) | 404 | permanent skip |
| AlreadyExists (6) | 409 | permanent skip |
| ResourceExhausted (8) | 429 | transient backoff |
| Unimplemented (12) | 501 | permanent skip |
| Unavailable (14) | 503 | transient backoff |
| DeadlineExceeded (4) | 504 | transient backoff |
| other | 500 | transient backoff |
Prerequisite: the gRPC server must have the server reflection service enabled. Most frameworks enable it via a single line (e.g. reflection.Register(s) in Go). If reflection is not available, the driver returns an error immediately.
Connection and descriptor caching: connections and method descriptors are cached per address+TLS mode. Reflection is called only on the first request to each method; subsequent calls reuse the cached descriptor.
# Multiple gRPC targets on the same server — connection is shared
targets:
- url: grpc://api.example.com:50051/user.UserService/GetUser
type: grpc
weight: 8
grpc:
body: '{"user_id": "u-123"}'
- url: grpc://api.example.com:50051/user.UserService/ListUsers
type: grpc
weight: 2
grpc:
body: '{}'sftp
Executes SFTP operations over SSH using github.com/pkg/sftp and golang.org/x/crypto/ssh.
targets:
- url: sftp://sftp.example.com/uploads/test.bin
weight: 2
type: sftp
sftp:
username: testuser
password: secret
insecure: false
operation: upload
file_size_min_bytes: 1024
file_size_max_bytes: 1048576
allowed_host_key_types: [ssh-ed25519]| Field | Default | Description |
|---|---|---|
port | 22 | SSH port when the URL does not include one |
operation | upload | upload, download, or list |
timeout_s | 30 | Connection and operation timeout in seconds |
insecure | false | Skip ~/.ssh/known_hosts host-key verification; use only for trusted test hosts |
username | "" | SSH username; required |
password | "" | Password authentication; mutually exclusive with private_key |
private_key | "" | Private key file path or inline PEM string; mutually exclusive with password |
file_size_bytes | 0 | Fixed upload payload size; omit or leave 0 to use a range or the default |
file_size_min_bytes | 0 | Minimum random upload payload size |
file_size_max_bytes | 0 | Maximum random upload payload size |
eicar | false | Upload the EICAR test string instead of generated data; upload only |
allowed_ciphers | [] | SSH cipher allow-list; empty accepts the library defaults |
allowed_kex | [] | SSH key-exchange allow-list; empty accepts the library defaults |
allowed_host_key_types | [] | SSH host-key algorithm allow-list; empty accepts the library defaults |
allowed_macs | [] | SSH MAC allow-list; empty accepts the library defaults |
Operations:
| Operation | Effect |
|---|---|
upload | Creates or truncates the remote path and writes generated payload bytes |
download | Reads the remote path and reports bytes transferred |
list | Lists the remote directory and adds sftp_entry_count to JSONL metadata |
Payload sizing: set file_size_bytes for a fixed upload size, or set file_size_min_bytes and file_size_max_bytes for a random size chosen per request. If no size is set, uploads use a small default payload. Set eicar: true only when you intentionally want antivirus or malware-scanning infrastructure to see the EICAR standard test string.
Host keys: by default, SFTP verifies host keys against ~/.ssh/known_hosts. Set insecure: true only for trusted lab or ephemeral hosts where pinning is impractical.
Algorithm policy: use the allowed_* lists to enforce SSH policy. If the server cannot negotiate an allowed algorithm, the result is mapped to 502.
sftp:
allowed_ciphers: [aes256-gcm@openssh.com, chacha20-poly1305@openssh.com]
allowed_kex: [curve25519-sha256]
allowed_host_key_types: [ssh-ed25519]
allowed_macs: [hmac-sha2-256-etm@openssh.com]SFTP status codes are mapped to HTTP-like status codes:
| Condition | HTTP equivalent | Effect |
|---|---|---|
| Success | 200 | success |
| Auth failure | 401 | permanent skip |
| Permission denied | 403 | permanent skip |
| Missing download path | 404 | permanent skip |
| Host key or algorithm policy mismatch | 502 | transient backoff |
| SFTP protocol error | 502 | transient backoff |
| Timeout | 504 | transient backoff |
JSONL metadata: SFTP results include sftp_server_version, sftp_host_key_type, sftp_host_key_fp, and sftp_auth_methods when available. list results also include sftp_entry_count.