mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
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.
This commit is contained in:
parent
88227898ed
commit
b82bb92f7e
3 changed files with 41 additions and 6 deletions
|
@ -16,13 +16,24 @@
|
||||||
-behavior(ranch_protocol).
|
-behavior(ranch_protocol).
|
||||||
|
|
||||||
-export([start_link/4]).
|
-export([start_link/4]).
|
||||||
-export([init/5]).
|
-export([proc_lib_hack/5]).
|
||||||
|
|
||||||
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
|
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
|
||||||
start_link(Ref, Socket, Transport, Opts) ->
|
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}.
|
{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.
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
||||||
init(Parent, Ref, Socket, Transport, Opts) ->
|
init(Parent, Ref, Socket, Transport, Opts) ->
|
||||||
ok = ranch:accept_ack(Ref),
|
ok = ranch:accept_ack(Ref),
|
||||||
|
|
|
@ -385,6 +385,13 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta
|
||||||
]),
|
]),
|
||||||
commands(State#state{encode_state=EncodeState}, StreamID, Tail)
|
commands(State#state{encode_state=EncodeState}, StreamID, Tail)
|
||||||
end;
|
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.
|
%% Send a response body chunk.
|
||||||
%%
|
%%
|
||||||
%% @todo WINDOW_UPDATE stuff require us to buffer some data.
|
%% @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, Tail);
|
||||||
commands(State, StreamID, [stop|_Tail]) ->
|
commands(State, StreamID, [stop|_Tail]) ->
|
||||||
%% @todo Do we want to run the commands after a stop?
|
%% @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,
|
terminate(#state{socket=Socket, transport=Transport, handler=Handler,
|
||||||
streams=Streams, children=Children}, Reason) ->
|
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)),
|
Transport:send(Socket, cow_http2:rst_stream(StreamID, Reason)),
|
||||||
stream_terminate(State, StreamID, StreamError).
|
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
|
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} ->
|
{value, #stream{state=StreamState}, Streams} ->
|
||||||
stream_call_terminate(StreamID, Reason, Handler, StreamState),
|
stream_call_terminate(StreamID, Reason, Handler, StreamState),
|
||||||
Children = stream_terminate_children(Children0, StreamID, []),
|
Children = stream_terminate_children(Children0, StreamID, []),
|
||||||
|
|
|
@ -16,13 +16,24 @@
|
||||||
-behavior(ranch_protocol).
|
-behavior(ranch_protocol).
|
||||||
|
|
||||||
-export([start_link/4]).
|
-export([start_link/4]).
|
||||||
-export([init/5]).
|
-export([proc_lib_hack/5]).
|
||||||
|
|
||||||
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
|
-spec start_link(ranch:ref(), inet:socket(), module(), cowboy:opts()) -> {ok, pid()}.
|
||||||
start_link(Ref, Socket, Transport, Opts) ->
|
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}.
|
{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.
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
||||||
init(Parent, Ref, Socket, Transport, Opts) ->
|
init(Parent, Ref, Socket, Transport, Opts) ->
|
||||||
ok = ranch:accept_ack(Ref),
|
ok = ranch:accept_ack(Ref),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue