aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-07-03 16:53:36 +0200
committerLoïc Hoguin <[email protected]>2019-07-03 16:53:36 +0200
commitd6b7ec654a876531dc0cfc7c65350d354bda2f52 (patch)
treea7f5e0eb9ff1647db963b204409f7d0bfe765ea7
parent4a6503186bf3a72880e7c99be76406550aeded96 (diff)
downloadgun-d6b7ec654a876531dc0cfc7c65350d354bda2f52.tar.gz
gun-d6b7ec654a876531dc0cfc7c65350d354bda2f52.tar.bz2
gun-d6b7ec654a876531dc0cfc7c65350d354bda2f52.zip
Add the response_start event
Thought it needed cow_http2_machine changes but everything was available. For HTTP/1.1 it is triggered when receiving data while expecting headers. For HTTP/2 it is triggered after we have received a HEADERS frame for streams in idle state.
-rw-r--r--src/gun_default_event_h.erl4
-rw-r--r--src/gun_event.erl12
-rw-r--r--src/gun_http.erl14
-rw-r--r--src/gun_http2.erl22
-rw-r--r--test/event_SUITE.erl16
5 files changed, 64 insertions, 4 deletions
diff --git a/src/gun_default_event_h.erl b/src/gun_default_event_h.erl
index 4156ed7..cd48fc2 100644
--- a/src/gun_default_event_h.erl
+++ b/src/gun_default_event_h.erl
@@ -21,6 +21,7 @@
-export([request_start/2]).
-export([request_headers/2]).
-export([request_end/2]).
+-export([response_start/2]).
-export([response_inform/2]).
-export([response_headers/2]).
-export([response_end/2]).
@@ -45,6 +46,9 @@ request_headers(_EventData, State) ->
request_end(_EventData, State) ->
State.
+response_start(_EventData, State) ->
+ State.
+
response_inform(_EventData, State) ->
State.
diff --git a/src/gun_event.erl b/src/gun_event.erl
index 8e66400..2a742a1 100644
--- a/src/gun_event.erl
+++ b/src/gun_event.erl
@@ -68,6 +68,15 @@
-callback request_end(request_end_event(), State) -> State.
+%% response_start.
+
+-type response_start_event() :: #{
+ stream_ref := reference(),
+ reply_to := pid()
+}.
+
+-callback response_start(response_start_event(), State) -> State.
+
%% response_inform/response_headers.
-type response_headers_event() :: #{
@@ -113,8 +122,7 @@
%% @todo origin_changed
%% @todo transport_changed
%% @todo protocol_changed
-%% @todo response_start (needs changes in cow_http2_machine to have an event before decoding headers)
-%% @todo response_trailers (same)
+%% @todo response_trailers
%% @todo push_promise_start
%% @todo push_promise_end
%% @todo cancel_start
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 7edaf14..b7c5bc1 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -100,7 +100,19 @@ handle(<<>>, State, _, EvHandlerState) ->
handle(_, #http_state{streams=[]}, _, EvHandlerState) ->
{close, EvHandlerState};
%% Wait for the full response headers before trying to parse them.
-handle(Data, State=#http_state{in=head, buffer=Buffer}, EvHandler, EvHandlerState) ->
+handle(Data, State=#http_state{in=head, buffer=Buffer,
+ streams=[#stream{ref=StreamRef, reply_to=ReplyTo}|_]}, EvHandler, EvHandlerState0) ->
+ %% Send the event only if there was no data in the buffer.
+ %% If there is data in the buffer then we already sent the event.
+ EvHandlerState = case Buffer of
+ <<>> ->
+ EvHandler:response_start(#{
+ stream_ref => StreamRef,
+ reply_to => ReplyTo
+ }, EvHandlerState0);
+ _ ->
+ EvHandlerState0
+ end,
Data2 = << Buffer/binary, Data/binary >>,
case binary:match(Data2, <<"\r\n\r\n">>) of
nomatch -> {{state, State#http_state{buffer=Data2}}, EvHandlerState};
diff --git a/src/gun_http2.erl b/src/gun_http2.erl
index 17bedde..4814818 100644
--- a/src/gun_http2.erl
+++ b/src/gun_http2.erl
@@ -113,7 +113,27 @@ parse(Data, State0=#http2_state{http2_machine=HTTP2Machine}, EvHandler, EvHandle
%% Frames received.
-frame(State=#http2_state{http2_machine=HTTP2Machine0}, Frame, EvHandler, EvHandlerState) ->
+frame(State=#http2_state{http2_machine=HTTP2Machine0}, Frame, EvHandler, EvHandlerState0) ->
+ EvHandlerState = if
+ is_tuple(Frame) andalso element(1, Frame) =:= headers ->
+ EvStreamID = element(2, Frame),
+ case cow_http2_machine:get_stream_remote_state(EvStreamID, HTTP2Machine0) of
+ {ok, idle} ->
+ #stream{ref=StreamRef, reply_to=ReplyTo} = get_stream_by_id(State, EvStreamID),
+ EvHandler:response_start(#{
+ stream_ref => StreamRef,
+ reply_to => ReplyTo
+ }, EvHandlerState0);
+ {ok, nofin} ->
+ %% @todo response_trailers.
+ EvHandlerState0;
+ %% This is an invalid headers frame.
+ _ ->
+ EvHandlerState0
+ end;
+ true ->
+ EvHandlerState0
+ end,
case cow_http2_machine:frame(Frame, HTTP2Machine0) of
{ok, HTTP2Machine} ->
{maybe_ack(State#http2_state{http2_machine=HTTP2Machine}, Frame),
diff --git a/test/event_SUITE.erl b/test/event_SUITE.erl
index 83edca9..88f67d7 100644
--- a/test/event_SUITE.erl
+++ b/test/event_SUITE.erl
@@ -209,6 +209,18 @@ do_request_end_headers_content_length(Config, EventName) ->
} = do_receive_event(EventName),
gun:close(Pid).
+response_start(Config) ->
+ doc("Confirm that the request_start event callback is called."),
+ {ok, Pid, _} = do_gun_open(Config),
+ {ok, _} = gun:await_up(Pid),
+ StreamRef = gun:get(Pid, "/"),
+ ReplyTo = self(),
+ #{
+ stream_ref := StreamRef,
+ reply_to := ReplyTo
+ } = do_receive_event(?FUNCTION_NAME),
+ gun:close(Pid).
+
response_inform(Config) ->
doc("Confirm that the request_inform event callback is called."),
{ok, Pid, _} = do_gun_open(Config),
@@ -335,6 +347,10 @@ request_end(EventData, Pid) ->
Pid ! {?FUNCTION_NAME, EventData},
Pid.
+response_start(EventData, Pid) ->
+ Pid ! {?FUNCTION_NAME, EventData},
+ Pid.
+
response_inform(EventData, Pid) ->
Pid ! {?FUNCTION_NAME, EventData},
Pid.