HTTP transport adapter for RpcElixir, implemented as a Plug.
It mounts a router at a path prefix. It decodes JSON bodies and dispatches to the procedure pipeline. It drains response cookies and headers onto the conn, then renders JSON. See Getting started for the mounting walkthrough.
Options
:router(required) — a module usingRpcElixir.Router.:path_prefix(optional, default"/rpc") — stripped from the request path before dispatch.POST /rpc/users.getdispatches"users.get".:ctx_builder(optional) —(Plug.Conn.t() -> RpcElixir.Context.t()). Its result is the base context. The transport always overwrites:reqwith conn-derived metadata (cookies, headers, remote_ip, session). Those fields are always present, whatever the builder returns.:max_body_size(optional, default 8 MB) — request body cap in bytes. Larger bodies are rejected with 413:payload_too_large.:max_body_depth(optional, default 64) — nesting-depth cap on the decoded JSON body. Deeper payloads are rejected with 400:input_validation_failed, before validation runs. This bounds stack and CPU use that the byte cap alone cannot.:require_content_type(optional, defaulttrue) — the request must carrycontent-type: application/json. A charset and other params are allowed. Other types are rejected with 415:unsupported_media_type. See## Security / CSRF.:allowed_origins(optional, defaultnil= disabled) — allow-list of origin strings. Anoriginheader outside the list is rejected with 403:forbidden. See## Security / CSRF.
Security / CSRF
This adapter dispatches state-changing RPC over POST. It can pair with
cookie-based sessions (see ## Session integration). That combination is a
CSRF surface. A cross-site page can auto-submit a form to an RPC endpoint.
The browser then attaches the session cookie. Undefended, that lets an
attacker trigger authenticated calls. Two mitigations are enforced here:
- Content-Type enforcement (
:require_content_type, defaulttrue). Requiringapplication/jsonstops the request being a "simple" cross-site request. Browsers must then send a CORS preflight, which the server never approves. HTML forms can only sendapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain. They are blocked outright. This is the primary CSRF defense. - Origin allow-listing (
:allowed_origins, default disabled). When configured, a present but non-allow-listedoriginis rejected with 403. This is defense-in-depth for browsers that sendOriginon state-changing requests.
The bundled JS client always sends Content-Type: application/json. So the
default-on enforcement does not break legitimate use.
Session integration
Configure Plug.Session earlier in your pipeline. The adapter reads it into
ctx.req.session. It drains Resolution.resp_session back after dispatch.
defmodule MyApp.Router do
use Plug.Builder
plug Plug.Session,
store: :cookie, key: "_my_app_session", signing_salt: "my_salt"
plug :fetch_session
plug RpcElixir.Plug, router: MyApp.RpcRouter
endIn middleware, use Resolution.put_session/3, delete_session/2, or
clear_session/1 to change the session. Read it via res.ctx.req[:session].
Response draining order
After dispatch, session mutations, cookies, and headers are applied to the conn first. The response body is written only after that. Plug requires this order: session and cookie mutations must precede the response.