aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2016-06-13 16:00:17 +0200
committerLoïc Hoguin <[email protected]>2016-06-13 16:00:17 +0200
commitb82bb92f7e6cf751ec74fc8aec0bfea84bfc5415 (patch)
tree5a77cc01846d9c342bf3a6e8098bb401591f178d
parent88227898edd26a823d0942fc7226adb61a20cb5d (diff)
downloadcowboy-b82bb92f7e6cf751ec74fc8aec0bfea84bfc5415.tar.gz
cowboy-b82bb92f7e6cf751ec74fc8aec0bfea84bfc5415.tar.bz2
cowboy-b82bb92f7e6cf751ec74fc8aec0bfea84bfc5415.zip
Fix streaming HTTP/2 responses
Error reporting for connection processes has been improved, using a similar proc_lib hack as was done for the stream processes.
-rw-r--r--src/cowboy_clear.erl15
-rw-r--r--src/cowboy_http2.erl17
-rw-r--r--src/cowboy_tls.erl15
3 files changed, 41 insertions, 6 deletions
diff --git a/src/cowboy_clear.erl b/src/cowboy_clear.erl
index cc6078e..713e11e 100644
--- a/src/cowboy_clear.erl
+++ b/src/cowboy_clear.erl
@@ -16,13 +16,24 @@
-behavior(ranch_protocol).
-export([start_link/4]).
--export([init/5]).
+-export([proc_lib_hack/5]).
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
start_link(Ref, Socket, Transport, Opts) ->
- Pid = proc_lib:spawn_link(?MODULE, init, [self(), Ref, Socket, Transport, Opts]),
+ Pid = proc_lib:spawn_link(?MODULE, proc_lib_hack, [self(), Ref, Socket, Transport, Opts]),
{ok, Pid}.
+-spec proc_lib_hack(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
+proc_lib_hack(Parent, Ref, Socket, Transport, Opts) ->
+ try
+ init(Parent, Ref, Socket, Transport, Opts)
+ catch
+ _:normal -> exit(normal);
+ _:shutdown -> exit(shutdown);
+ _:Reason = {shutdown, _} -> exit(Reason);
+ _:Reason -> exit({Reason, erlang:get_stacktrace()})
+ end.
+
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
init(Parent, Ref, Socket, Transport, Opts) ->
ok = ranch:accept_ack(Ref),
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index c12e4f3..9be3715 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -385,6 +385,13 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta
]),
commands(State#state{encode_state=EncodeState}, StreamID, Tail)
end;
+%% Send response headers and initiate chunked encoding.
+commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeState0}, StreamID,
+ [{headers, StatusCode, Headers0}|Tail]) ->
+ Headers = Headers0#{<<":status">> => integer_to_binary(StatusCode)},
+ {HeaderBlock, EncodeState} = headers_encode(Headers, EncodeState0),
+ Transport:send(Socket, cow_http2:headers(StreamID, nofin, HeaderBlock)),
+ commands(State#state{encode_state=EncodeState}, StreamID, Tail);
%% Send a response body chunk.
%%
%% @todo WINDOW_UPDATE stuff require us to buffer some data.
@@ -455,7 +462,7 @@ commands(State, StreamID, [{upgrade, _Mod, _ModState}|Tail]) ->
commands(State, StreamID, Tail);
commands(State, StreamID, [stop|_Tail]) ->
%% @todo Do we want to run the commands after a stop?
- stream_terminate(State, StreamID, stop).
+ stream_terminate(State, StreamID, normal).
terminate(#state{socket=Socket, transport=Transport, handler=Handler,
streams=Streams, children=Children}, Reason) ->
@@ -550,8 +557,14 @@ stream_reset(State=#state{socket=Socket, transport=Transport}, StreamID,
Transport:send(Socket, cow_http2:rst_stream(StreamID, Reason)),
stream_terminate(State, StreamID, StreamError).
-stream_terminate(State=#state{handler=Handler, streams=Streams0, children=Children0}, StreamID, Reason) ->
+stream_terminate(State=#state{socket=Socket, transport=Transport,
+ handler=Handler, streams=Streams0, children=Children0}, StreamID, Reason) ->
case lists:keytake(StreamID, #stream.id, Streams0) of
+ {value, #stream{state=StreamState, local=nofin}, Streams} when Reason =:= normal ->
+ Transport:send(Socket, cow_http2:data(StreamID, fin, <<>>)),
+ stream_call_terminate(StreamID, Reason, Handler, StreamState),
+ Children = stream_terminate_children(Children0, StreamID, []),
+ State#state{streams=Streams, children=Children};
{value, #stream{state=StreamState}, Streams} ->
stream_call_terminate(StreamID, Reason, Handler, StreamState),
Children = stream_terminate_children(Children0, StreamID, []),
diff --git a/src/cowboy_tls.erl b/src/cowboy_tls.erl
index 375b005..c9999e3 100644
--- a/src/cowboy_tls.erl
+++ b/src/cowboy_tls.erl
@@ -16,13 +16,24 @@
-behavior(ranch_protocol).
-export([start_link/4]).
--export([init/5]).
+-export([proc_lib_hack/5]).
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
start_link(Ref, Socket, Transport, Opts) ->
- Pid = proc_lib:spawn_link(?MODULE, init, [self(), Ref, Socket, Transport, Opts]),
+ Pid = proc_lib:spawn_link(?MODULE, proc_lib_hack, [self(), Ref, Socket, Transport, Opts]),
{ok, Pid}.
+-spec proc_lib_hack(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
+proc_lib_hack(Parent, Ref, Socket, Transport, Opts) ->
+ try
+ init(Parent, Ref, Socket, Transport, Opts)
+ catch
+ _:normal -> exit(normal);
+ _:shutdown -> exit(shutdown);
+ _:Reason = {shutdown, _} -> exit(Reason);
+ _:Reason -> exit({Reason, erlang:get_stacktrace()})
+ end.
+
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
init(Parent, Ref, Socket, Transport, Opts) ->
ok = ranch:accept_ack(Ref),