From d6b7ec654a876531dc0cfc7c65350d354bda2f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 3 Jul 2019 16:53:36 +0200 Subject: 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. --- src/gun_default_event_h.erl | 4 ++++ src/gun_event.erl | 12 ++++++++++-- src/gun_http.erl | 14 +++++++++++++- src/gun_http2.erl | 22 +++++++++++++++++++++- test/event_SUITE.erl | 16 ++++++++++++++++ 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. -- cgit v1.2.3