aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http2.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-11-15 14:58:49 +0100
committerLoïc Hoguin <[email protected]>2017-11-15 14:58:49 +0100
commit39baed6c800fa9e756f7491063ead399a23083f4 (patch)
tree7c54a8b83b5957438e76c3a56996af4e4ca02c18 /src/cowboy_http2.erl
parenta1ad482eb442ea2bd54866656b7a7dc453e4bd8e (diff)
downloadcowboy-39baed6c800fa9e756f7491063ead399a23083f4.tar.gz
cowboy-39baed6c800fa9e756f7491063ead399a23083f4.tar.bz2
cowboy-39baed6c800fa9e756f7491063ead399a23083f4.zip
Add preliminary support for trailers in responses
This depends on changes in Cowlib that are only available on master.
Diffstat (limited to 'src/cowboy_http2.erl')
-rw-r--r--src/cowboy_http2.erl34
1 files changed, 29 insertions, 5 deletions
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index 9e81957..0f48e0e 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -49,7 +49,9 @@
%% Whether we finished receiving data.
remote = nofin :: cowboy_stream:fin(),
%% Remote flow control window (how much we accept to receive).
- remote_window :: integer()
+ remote_window :: integer(),
+ %% Unparsed te header. Used to know if we can send trailers.
+ te :: undefined | binary()
}).
-type stream() :: #stream{}.
@@ -537,9 +539,24 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta
commands(State0, Stream0=#stream{local=nofin}, [{data, IsFin, Data}|Tail]) ->
{State, Stream} = send_data(State0, Stream0, IsFin, Data),
commands(State, Stream, Tail);
-
%% @todo data when local!=nofin
-
+%% Send trailers.
+commands(State0, Stream0=#stream{local=nofin, te=TE0}, [{trailers, Trailers}|Tail]) ->
+ %% We only accept TE headers containing exactly "trailers" (RFC7540 8.1.2.1).
+ TE = try cow_http_hd:parse_te(TE0) of
+ {trailers, []} -> trailers;
+ _ -> no_trailers
+ catch _:_ ->
+ %% If we can't parse the TE header, assume we can't send trailers.
+ no_trailers
+ end,
+ {State, Stream} = case TE of
+ trailers ->
+ send_data(State0, Stream0, fin, {trailers, Trailers});
+ no_trailers ->
+ send_data(State0, Stream0, fin, <<>>)
+ end,
+ commands(State, Stream, Tail);
%% Send a file.
commands(State0, Stream0=#stream{local=nofin},
[{sendfile, IsFin, Offset, Bytes, Path}|Tail]) ->
@@ -664,6 +681,12 @@ send_data(State0, Stream0=#stream{local_buffer=Q0, local_buffer_size=BufferSize}
send_data(State, Stream, IsFin, Data) ->
send_data(State, Stream, IsFin, Data, in).
+%% Always send trailer frames even if the window is empty.
+send_data(State=#state{socket=Socket, transport=Transport, encode_state=EncodeState0},
+ Stream=#stream{id=StreamID}, fin, {trailers, Trailers}, _) ->
+ {HeaderBlock, EncodeState} = headers_encode(Trailers, EncodeState0),
+ Transport:send(Socket, cow_http2:headers(StreamID, fin, HeaderBlock)),
+ {State#state{encode_state=EncodeState}, Stream#stream{local=fin}};
%% Send data immediately if we can, buffer otherwise.
%% @todo We might want to print an error if local=fin.
send_data(State=#state{local_window=ConnWindow},
@@ -800,13 +823,14 @@ stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert},
stream_handler_init(State=#state{opts=Opts,
local_settings=#{initial_window_size := RemoteWindow},
remote_settings=#{initial_window_size := LocalWindow}},
- StreamID, RemoteIsFin, LocalIsFin, Req) ->
+ StreamID, RemoteIsFin, LocalIsFin, Req=#{headers := Headers}) ->
try cowboy_stream:init(StreamID, Req, Opts) of
{Commands, StreamState} ->
commands(State#state{client_streamid=StreamID},
#stream{id=StreamID, state=StreamState,
remote=RemoteIsFin, local=LocalIsFin,
- local_window=LocalWindow, remote_window=RemoteWindow},
+ local_window=LocalWindow, remote_window=RemoteWindow,
+ te=maps:get(<<"te">>, Headers, undefined)},
Commands)
catch Class:Exception ->
cowboy_stream:report_error(init,