Streaming HTTP proxy library with O(1) memory body observation for Elixir.
Weir /wɪər/ - A low dam that measures water flow without blocking it.
- Zero buffering: Stream requests and responses without memory accumulation
- Body observation: Capture SHA256, size, preview, timing without buffering
- Plug integration: Use as Plug or call directly from controllers
- Configurable: Per-request overrides for all settings
- Observable: Lifecycle callbacks for monitoring and logging
Add weir to your list of dependencies in mix.exs:
def deps do
[
{:weir, "~> 0.1.0"}
]
end- Add Finch to your supervision tree:
children = [
{Finch, name: MyApp.Finch}
]- Configure Weir:
# config/config.exs
config :weir, finch_name: MyApp.Finch- Use in your controller:
def proxy(conn, _params) do
Weir.proxy(conn, upstream: "https://api.example.com")
endOr as a Plug in your router:
forward "/api", Weir.ProxyPlug, upstream: "https://api.example.com"Weir captures observations about request and response bodies without buffering:
conn = Weir.proxy(conn, upstream: "https://api.example.com")
# Access observations from conn.private
req_obs = conn.private[:weir_request_observation]
resp_obs = conn.private[:weir_response_observation]
# Each observation contains:
# - :hash - SHA256 hash of the body
# - :size - Total body size in bytes
# - :preview - First 64KB of the body (UTF-8 safe truncation)
# - :body - Full body (only if under max_payload_size and content-type matches)
# - :duration_us - Processing time in microsecondsImplement Weir.Observer to hook into the proxy lifecycle:
defmodule MyApp.ProxyObserver do
use Weir.Observer
@impl true
def handle_request_started(metadata) do
Logger.info("Proxying #{metadata.method} #{metadata.upstream_url}")
:ok
end
@impl true
def handle_response_started(metadata) do
Logger.info("TTFB: #{metadata.ttfb_ms}ms")
:ok
end
@impl true
def handle_response_finished(result) do
Logger.info("Completed: #{result.status} in #{result.duration_us}us")
# result contains :request_observation and :response_observation
:ok
end
end
# Use it:
Weir.proxy(conn,
upstream: "https://api.example.com",
observer: MyApp.ProxyObserver
)| Option | Default | Description |
|---|---|---|
:finch_name |
Weir.Finch |
Name of the Finch pool to use |
:receive_timeout |
15_000 |
Response timeout in milliseconds |
:max_payload_size |
1_048_576 |
Max body size for full accumulation (1MB) |
:persistable_content_types |
JSON/XML/text | Content types eligible for body storage |
Override per-request:
Weir.proxy(conn,
upstream: "https://api.example.com",
receive_timeout: 60_000,
max_payload_size: 5_242_880
)Or set application defaults:
# config/config.exs
config :weir,
finch_name: MyApp.Finch,
receive_timeout: 30_000,
max_payload_size: 5_242_880,
persistable_content_types: ["application/json", "text/*"]Full documentation: https://hexdocs.pm/weir
Apache-2.0