aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/manual/cowboy_app.asciidoc3
-rw-r--r--doc/src/manual/cowboy_stream.asciidoc392
2 files changed, 394 insertions, 1 deletions
diff --git a/doc/src/manual/cowboy_app.asciidoc b/doc/src/manual/cowboy_app.asciidoc
index 29c4497..4c5ad69 100644
--- a/doc/src/manual/cowboy_app.asciidoc
+++ b/doc/src/manual/cowboy_app.asciidoc
@@ -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
diff --git a/doc/src/manual/cowboy_stream.asciidoc b/doc/src/manual/cowboy_stream.asciidoc
new file mode 100644
index 0000000..d332752
--- /dev/null
+++ b/doc/src/manual/cowboy_stream.asciidoc
@@ -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)]