roadrunner_send (roadrunner v0.13.0)
View SourceSend the response from inside the handler, as it runs.
A handler normally returns its response and the connection sends it. A
handler that returns {send, Headers, Fun} sends it itself instead: the
connection calls Fun(Req) and the fun sends the response with this
module's functions, then returns the request.
handle(Req) ->
{{send, [], fun send/1}, Req}.
send(Req0) ->
ok = roadrunner_send:inform(103, [{~"link", ~"</app.css>; rel=preload"}], Req0),
{ok, Body, Req} = roadrunner_req:read_body(Req0),
ok = roadrunner_send:reply(roadrunner_resp:text(200, Body), Req),
Req.Use it when the response can only be decided while the handler runs:
inform/3sends an interim 1xx response (such as103 Early Hints) before the final one, as many times as neededreply/2sends a complete response built withroadrunner_resp, andreply/4one given as its status, headers and bodystream/3sends the status and headers and returns the sameSendfun a{stream, ...}response gets, to write the body as it goesfile/4sends a file, as a{sendfile, ...}response doeswebsocket/4upgrades the connection to a WebSocket session, as a{websocket, Module, State, Opts}response does
The fun sends one final response (reply/2,4, stream/3, file/4 or
websocket/4). The calls take the request the fun holds now, so pass
the one returned by roadrunner_req:read_body/1,2, and return the last
one from the fun, as handle/1 does.
What a call returns and when it raises:
ok(or{ok, Send}fromstream/3) once the response went out.{error, Reason}when it could not: the transport failed, a header has CR/LF/NUL (unsafe_header), the headers are over the client's HTTP/2 or HTTP/3 limit,file/4's file is missing or shorter than its range, or an upgrade was refused. A final response that could not go out is answered500in its place.- It raises
already_sentfor a final response, or aninform/3, after the final response; andnot_in_send_funwhen called outside the fun's process, such as from a process the fun spawns. TheSendfunstream/3returns is the fun's too: call it from the fun's process.
A fun that exits with {shutdown, Reason} stops the request on purpose,
the OTP way: the response ends as far as it went (an open stream is
ended, no response at all is answered 500), nothing is logged, and the
request reports a normal stop. On HTTP/1.1 the connection then closes,
since how much of the body the fun read is unknown; on HTTP/2 and HTTP/3
only the stream ends.
The framework finishes what the fun leaves open. A stream the fun did
not end with Send(Data, fin) is ended when the fun returns. A fun that
returns without a final response gets a 500 sent for it. A fun that
crashes before a final response is answered 500; one that crashes
after it closes the connection (HTTP/1.1) or resets the stream (HTTP/2,
HTTP/3) when the body was still open. A file that fails to read once its
response has started cuts the response the same way.
Headers in the {send, Headers, Fun} tuple is where middlewares add
their headers (roadrunner_cors, roadrunner_security_headers). They
are added to the final response, unless the call already sets a header
with the same name. vary is the exception: both values are kept.
Interim responses get only the headers they are given.
roadrunner_compress does not compress a response sent this way.
The rules of the other response shapes apply. Header names must be
lowercase. A response with a CR, LF or NUL in a header, or (on HTTP/2
and HTTP/3) headers over the client's header size limit, is not sent:
the final ones are answered 500 and the call returns an error. A HEAD
request gets no body from reply/2,4 or file/4. An interim response
to an HTTP/1.0 client is skipped, as RFC 9110 §15.2 asks.
Summary
Types
The fun of a {send, Headers, Fun} response. It gets the request, sends
the response with this module's functions, and returns the request.
Functions
Send bytes [Offset, Offset + Length) of a file as the response body, as
a {sendfile, ...} response does: the headers must carry its
content-length and content-type. Returns ok once it is sent, or
{error, Reason} when it could not be. A file that is not there, or is
shorter than the range, is answered 500 before anything is sent, and
the call returns {error, enoent}, {error, file_too_short} or the
like.
Send an interim response: a 1xx status, 102 to 199, ahead of the
final response. Returns ok once it is written, or {error, Reason}
when it could not be sent.
Send a complete response built with roadrunner_resp, such as
roadrunner_resp:json(200, Term). The same as reply/4 with its
status, headers and body.
Send a complete response. Returns ok once it is handed to the
connection, or {error, Reason} when it could not be sent.
Send the status and headers of a streamed response. Returns {ok, Send}
with the roadrunner_handler:send_fun/0 that writes the body, the same
fun a {stream, ...} response gets, or {error, Reason} when the head
could not be sent.
Upgrade the connection to a WebSocket session run by Module (a
roadrunner_ws_handler) from State, with the upgrade options Opts
(roadrunner_ws_handler:upgrade_opts/0), as a {websocket, Module, State, Opts} response does. On HTTP/1.1 it sends the 101 and the
session takes the connection; the request ends when the session does.
Returns ok once the session runs, or {error, Reason} when the upgrade
was refused: an invalid handshake (answered 400 or 426), or HTTP/2 and
HTTP/3, which answer 501.
Types
-type sender() :: fun((roadrunner_req:request()) -> roadrunner_req:request()).
The fun of a {send, Headers, Fun} response. It gets the request, sends
the response with this module's functions, and returns the request.
Functions
-spec file(roadrunner_http:status(), roadrunner_http:headers(), roadrunner_handler:sendfile_spec(), roadrunner_req:request()) -> ok | {error, term()}.
Send bytes [Offset, Offset + Length) of a file as the response body, as
a {sendfile, ...} response does: the headers must carry its
content-length and content-type. Returns ok once it is sent, or
{error, Reason} when it could not be. A file that is not there, or is
shorter than the range, is answered 500 before anything is sent, and
the call returns {error, enoent}, {error, file_too_short} or the
like.
-spec inform(roadrunner_http:status(), roadrunner_http:headers(), roadrunner_req:request()) -> ok | {error, term()}.
Send an interim response: a 1xx status, 102 to 199, ahead of the
final response. Returns ok once it is written, or {error, Reason}
when it could not be sent.
-spec reply(roadrunner_resp:buffered_response(), roadrunner_req:request()) -> ok | {error, term()}.
Send a complete response built with roadrunner_resp, such as
roadrunner_resp:json(200, Term). The same as reply/4 with its
status, headers and body.
-spec reply(roadrunner_http:status(), roadrunner_http:headers(), iodata(), roadrunner_req:request()) -> ok | {error, term()}.
Send a complete response. Returns ok once it is handed to the
connection, or {error, Reason} when it could not be sent.
-spec stream(roadrunner_http:status(), roadrunner_http:headers(), roadrunner_req:request()) -> {ok, roadrunner_handler:send_fun()} | {error, term()}.
Send the status and headers of a streamed response. Returns {ok, Send}
with the roadrunner_handler:send_fun/0 that writes the body, the same
fun a {stream, ...} response gets, or {error, Reason} when the head
could not be sent.
-spec websocket(module(), term(), roadrunner_ws_handler:upgrade_opts(), roadrunner_req:request()) -> ok | {error, term()}.
Upgrade the connection to a WebSocket session run by Module (a
roadrunner_ws_handler) from State, with the upgrade options Opts
(roadrunner_ws_handler:upgrade_opts/0), as a {websocket, Module, State, Opts} response does. On HTTP/1.1 it sends the 101 and the
session takes the connection; the request ends when the session does.
Returns ok once the session runs, or {error, Reason} when the upgrade
was refused: an invalid handshake (answered 400 or 426), or HTTP/2 and
HTTP/3, which answer 501.