mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-15 20:50:24 +00:00
Initial cowboy_stream manual
This commit is contained in:
parent
10dfd8c910
commit
c2beff7126
2 changed files with 394 additions and 1 deletions
|
@ -34,6 +34,7 @@ Handlers:
|
|||
* link:man:cowboy_static(3)[cowboy_static(3)] - Static file handler
|
||||
|
||||
// @todo What about cowboy_stream_h?
|
||||
// @todo cowboy_compress_h
|
||||
|
||||
Behaviors:
|
||||
|
||||
|
@ -41,7 +42,7 @@ Behaviors:
|
|||
* link:man:cowboy_loop(3)[cowboy_loop(3)] - Loop handlers
|
||||
* link:man:cowboy_middleware(3)[cowboy_middleware(3)] - Middlewares
|
||||
* link:man:cowboy_rest(3)[cowboy_rest(3)] - REST handlers
|
||||
// @todo * link:man:cowboy_stream(3)[cowboy_stream(3)] - Stream handlers
|
||||
* link:man:cowboy_stream(3)[cowboy_stream(3)] - Stream handlers
|
||||
// @todo * link:man:cowboy_sub_protocol(3)[cowboy_sub_protocol(3)] - Sub protocols
|
||||
* link:man:cowboy_websocket(3)[cowboy_websocket(3)] - Websocket handlers
|
||||
|
||||
|
|
392
doc/src/manual/cowboy_stream.asciidoc
Normal file
392
doc/src/manual/cowboy_stream.asciidoc
Normal file
|
@ -0,0 +1,392 @@
|
|||
= cowboy_stream(3)
|
||||
|
||||
== Name
|
||||
|
||||
cowboy_handler - Stream handlers
|
||||
|
||||
== Description
|
||||
|
||||
The module `cowboy_stream` defines a callback interface
|
||||
and a protocol for handling HTTP streams.
|
||||
|
||||
An HTTP request and its associated response is called
|
||||
a stream. A connection may have many streams. In HTTP/1.1
|
||||
they are executed sequentially, while in HTTP/2 they are
|
||||
executed concurrently.
|
||||
|
||||
Cowboy calls the stream handler for nearly all events
|
||||
related to a stream. Exceptions vary depending on the
|
||||
protocol.
|
||||
|
||||
== Callbacks
|
||||
|
||||
Stream handlers must implement the following interface:
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
init(StreamID, Req, Opts) -> {Commands, State}
|
||||
data(StreamID, IsFin, Data, State) -> {Commands, State}
|
||||
info(StreamID, Info, State) -> {Commands, State}
|
||||
terminate(StreamID, Reason, State) -> any()
|
||||
early_error(StreamID, Reason, PartialReq, Resp, Opts) -> Resp
|
||||
|
||||
StreamID :: cowboy_stream:streamid()
|
||||
Req :: cowboy_req:req()
|
||||
Opts :: cowboy:opts()
|
||||
Commands :: cowboy_stream:commands()
|
||||
State :: any()
|
||||
IsFin :: cowboy_stream:fin()
|
||||
Data :: binary()
|
||||
Info :: any()
|
||||
Reason :: cowboy_stream:reason()
|
||||
PartialReq - cowboy_req:req(), except all fields are optional
|
||||
Resp :: cowboy_stream:resp_command()
|
||||
----
|
||||
|
||||
HTTP/1.1 will initialize a stream only when the request-line
|
||||
and all headers have been received. When errors occur before
|
||||
that point Cowboy will call the callback `early_error/5`
|
||||
with a partial request, the error reason and the response
|
||||
Cowboy intends to send. All other events go throuh the
|
||||
stream handler using the normal callbacks.
|
||||
|
||||
HTTP/2 will initialize the stream when the `HEADERS` block has
|
||||
been fully received and decoded. Any protocol error occuring
|
||||
before that will not result in a response being sent and
|
||||
will therefore not go through the stream handler. In addition
|
||||
Cowboy may terminate streams without sending an HTTP response
|
||||
back.
|
||||
|
||||
The stream is initialized by calling `init/3`. All streams
|
||||
that are initialized will eventually be terminated by calling
|
||||
`terminate/3`.
|
||||
|
||||
When Cowboy receives data for the stream it will call `data/4`.
|
||||
The data given is the request body after any transfer decoding
|
||||
has been applied.
|
||||
|
||||
When Cowboy receives a message addressed to a stream, or when
|
||||
Cowboy needs to inform the stream handler that an internal
|
||||
event has occurred, it will call `info/3`.
|
||||
|
||||
[[commands]]
|
||||
== Commands
|
||||
|
||||
Stream handlers can return a list of commands to be executed
|
||||
from the `init/3`, `data/4` and `info/3` callbacks. In addition,
|
||||
the `early_error/5` callback must return a response command.
|
||||
|
||||
// @todo We need a 'log' command that would call error_logger.
|
||||
// It's better than doing in the handlers directly because
|
||||
// then we can have other stream handlers manipulate those logs.
|
||||
|
||||
// @todo We need a command to send a message so that other
|
||||
// stream handlers can manipulate these messages if necessary.
|
||||
|
||||
The following commands are defined:
|
||||
|
||||
[[response_command]]
|
||||
=== response
|
||||
|
||||
Send a response to the client.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{response, cowboy:http_status(), cowboy:http_headers(),
|
||||
cowboy_req:resp_body()}
|
||||
----
|
||||
|
||||
No more data can be sent after this command.
|
||||
|
||||
[[headers_command]]
|
||||
=== headers
|
||||
|
||||
Initiate a response to the client.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{headers, cowboy:http_status(), cowboy:http_headers()}
|
||||
----
|
||||
|
||||
This initiates a response to the client. The stream
|
||||
will end when a data command with the `fin` flag is
|
||||
returned.
|
||||
|
||||
[[data_command]]
|
||||
=== data
|
||||
|
||||
Send data to the client.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{data, fin(), iodata()}
|
||||
----
|
||||
|
||||
[[push_command]]
|
||||
=== push
|
||||
|
||||
Push a resource to the client.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{push, Method, Scheme, Host, inet:port_number(),
|
||||
Path, Qs, cowboy:http_headers()}
|
||||
|
||||
Method = Scheme = Host = Path = Qs = binary()
|
||||
----
|
||||
|
||||
The command will be ignored if the protocol does not provide
|
||||
any server push mechanism.
|
||||
|
||||
=== flow
|
||||
|
||||
TODO
|
||||
|
||||
=== spawn
|
||||
|
||||
Inform Cowboy that a process was spawned and should be
|
||||
supervised.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{spawn, pid(), timeout()}
|
||||
----
|
||||
|
||||
=== error_response
|
||||
|
||||
Send an error response if no response was sent previously.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{error_response, cowboy:http_status(), cowboy:http_headers(), iodata()}
|
||||
----
|
||||
|
||||
[[switch_protocol_command]]
|
||||
=== switch_protocol
|
||||
|
||||
Switch to a different protocol.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{switch_protocol, cowboy:http_headers(), module(), state()}
|
||||
----
|
||||
|
||||
Contains the headers that will be sent in the 101 response,
|
||||
along with the module implementing the protocol we are
|
||||
switching to and its initial state.
|
||||
|
||||
=== stop
|
||||
|
||||
Stop the stream.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
stop
|
||||
----
|
||||
|
||||
While no more data can be sent after the `fin` flag was set,
|
||||
the stream is still tracked by Cowboy until it is stopped by
|
||||
the handler.
|
||||
|
||||
The behavior when stopping a stream for which no response
|
||||
has been sent will vary depending on the protocol. The stream
|
||||
will end successfully as far as the client is concerned.
|
||||
|
||||
To indicate that an error occurred, either use `error_response`
|
||||
before stopping, or use `internal_error`.
|
||||
|
||||
=== internal_error
|
||||
|
||||
Stop the stream with an error.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{internal_error, Reason, HumanReadable}
|
||||
|
||||
Reason = any()
|
||||
HumanReadable = atom()
|
||||
----
|
||||
|
||||
This command should be used when the stream cannot continue
|
||||
because of an internal error. An `error_response` command
|
||||
may be sent before that to advertise to the client why the
|
||||
stream is dropped.
|
||||
|
||||
== Predefined events
|
||||
|
||||
Cowboy will forward all messages sent to the stream to
|
||||
the `info/3` callback. To send a message to a stream,
|
||||
send a message to the connection process with the form
|
||||
`{{Pid, StreamID}, Msg}`. The connection process will
|
||||
then forward `Msg` to the stream handlers.
|
||||
|
||||
Cowboy will also forward the exit signals for the
|
||||
processes that the stream spawned.
|
||||
|
||||
=== EXIT
|
||||
|
||||
//info(_StreamID, {'EXIT', Pid, normal}, State=#state{pid=Pid}) ->
|
||||
//info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) ->
|
||||
//info(StreamID, Exit = {'EXIT', Pid, {Reason, Stacktrace}}, State=#state{ref=Ref, pid=Pid}) ->
|
||||
|
||||
A process spawned by this stream has exited.
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
{'EXIT', pid(), any()}
|
||||
----
|
||||
|
||||
This is the raw exit message without any modification.
|
||||
|
||||
// === read_body
|
||||
//
|
||||
// //info(_StreamID, {read_body, Ref, Length, _},
|
||||
// //info(StreamID, {read_body, Ref, Length, Period}, State) ->
|
||||
//
|
||||
// TODO yeah I am not actually sure this one should be public just yet
|
||||
// TODO if it is, then we probably shouldn't send a message directly,
|
||||
// TODO but rather return a command that will end up sending the message
|
||||
//
|
||||
// TODO The problem being that no stream handler has access to that
|
||||
// TODO message if we send it directly. So we should have a command
|
||||
// TODO send_message or something that can be seen from all handlers.
|
||||
//
|
||||
// TODO The thing is that stream handlers can have 0 to N processes
|
||||
// TODO so we have to make it easy to say which process should
|
||||
// TODO receive the message, and perhaps *identify* which process
|
||||
// TODO gets it?
|
||||
|
||||
=== response
|
||||
|
||||
Same as the xref:response_command[response command].
|
||||
|
||||
Usually sent when the request process replies to the client.
|
||||
May also be sent by Cowboy internally.
|
||||
|
||||
=== headers
|
||||
|
||||
Same as the xref:headers_command[headers command].
|
||||
|
||||
Sent when the request process starts replying to the client.
|
||||
|
||||
=== data
|
||||
|
||||
Same as the xref:data_command[data command].
|
||||
|
||||
Sent when the request process streams data to the client.
|
||||
|
||||
=== push
|
||||
|
||||
Same as the xref:push_command[push command].
|
||||
|
||||
Sent when the request process pushes a resource to the client.
|
||||
|
||||
=== switch_protocol
|
||||
|
||||
Same as the xref:switch_protocol_command[switch_protocol command].
|
||||
|
||||
// @todo Not done for HTTP/2 yet.
|
||||
Sent when switching to the HTTP/2 or Websocket protocol.
|
||||
|
||||
== Exports
|
||||
|
||||
The following function should be called by modules implementing
|
||||
stream handlers to execute the next stream handler in the list:
|
||||
|
||||
* link:man:cowboy_stream:init(3)[cowboy_stream:init(3)] - Initialize a stream
|
||||
* link:man:cowboy_stream:data(3)[cowboy_stream:data(3)] - Handle data for a stream
|
||||
* link:man:cowboy_stream:info(3)[cowboy_stream:info(3)] - Handle a message for a stream
|
||||
* link:man:cowboy_stream:terminate(3)[cowboy_stream:terminate(3)] - Terminate a stream
|
||||
* link:man:cowboy_stream:early_error(3)[cowboy_stream:early_error(3)] - Handle an early error for a stream
|
||||
|
||||
== Types
|
||||
|
||||
=== commands()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
commands() :: [Command]
|
||||
----
|
||||
|
||||
See the xref:commands[list of commands] for details.
|
||||
|
||||
=== fin()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
fin() :: fin | nofin
|
||||
----
|
||||
|
||||
Used in commands and events to indicate that this is
|
||||
the end of the stream.
|
||||
|
||||
=== partial_req()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
req() :: #{
|
||||
method => binary(), %% case sensitive
|
||||
version => cowboy:http_version() | atom(),
|
||||
scheme => binary(), %% lowercase; case insensitive
|
||||
host => binary(), %% lowercase; case insensitive
|
||||
port => inet:port_number(),
|
||||
path => binary(), %% case sensitive
|
||||
qs => binary(), %% case sensitive
|
||||
headers => cowboy:http_headers(),
|
||||
peer => {inet:ip_address(), inet:port_number()}
|
||||
}
|
||||
----
|
||||
|
||||
Partial request information received when an early error is
|
||||
detected.
|
||||
|
||||
=== reason()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
reason() :: normal
|
||||
| {internal_error, timeout | {error | exit | throw, any()}, HumanReadable}
|
||||
| {socket_error, closed | atom(), HumanReadable}
|
||||
| {stream_error, Error, HumanReadable}
|
||||
| {connection_error, Error, HumanReadable}
|
||||
| {stop, cow_http2:frame(), HumanReadable}
|
||||
|
||||
Error = atom()
|
||||
HumanReadable = atom()
|
||||
----
|
||||
|
||||
Reason for the stream termination.
|
||||
|
||||
=== resp_command()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
resp_command() :: {response, cowboy:http_status(),
|
||||
cowboy:http_headers(), cowboy_req:resp_body()}
|
||||
----
|
||||
|
||||
See the xref:response_command[response command] for details.
|
||||
|
||||
=== streamid()
|
||||
|
||||
[source,erlang]
|
||||
----
|
||||
streamid() :: any()
|
||||
----
|
||||
|
||||
The identifier for this stream.
|
||||
|
||||
The identifier is unique over the connection process.
|
||||
It is possible to form a unique identifier node-wide and
|
||||
cluster-wide by wrapping it in a `{self(), StreamID}`
|
||||
tuple.
|
||||
|
||||
== Changelog
|
||||
|
||||
* *2.0*: Module introduced.
|
||||
|
||||
== See also
|
||||
|
||||
link:man:cowboy(7)[cowboy(7)],
|
||||
link:man:cowboy_http(3)[cowboy_http(3)],
|
||||
link:man:cowboy_http2(3)[cowboy_http2(3)]
|
Loading…
Add table
Add a link
Reference in a new issue