From a97640d56dcb1a32ea44cb3be6575d2a4d6136c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Tue, 31 Oct 2017 15:04:00 +0000 Subject: Add informational responses to metrics --- src/cowboy_http.erl | 4 ++-- src/cowboy_metrics_h.erl | 27 ++++++++++++++++++++++++++- test/metrics_SUITE.erl | 23 ++++++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index f6d064e..7d19c05 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -908,9 +908,9 @@ commands(State0=#state{ref=Ref, parent=Parent, socket=Socket, transport=Transpor [{switch_protocol, Headers, Protocol, InitialState}|_Tail]) -> %% @todo This should be the last stream running otherwise we need to wait before switching. %% @todo If there's streams opened after this one, fail instead of 101. - State = cancel_timeout(State0), + State1 = cancel_timeout(State0), %% Send a 101 response, then terminate the stream. - State = #state{streams=Streams} = commands(State, StreamID, [{inform, 101, Headers}]), + State = #state{streams=Streams} = info(State1, StreamID, {inform, 101, Headers}), #stream{state=StreamState} = lists:keyfind(StreamID, #stream.id, Streams), %% @todo We need to shutdown processes here first. stream_call_terminate(StreamID, switch_protocol, StreamState), diff --git a/src/cowboy_metrics_h.erl b/src/cowboy_metrics_h.erl index 1203518..03bac36 100644 --- a/src/cowboy_metrics_h.erl +++ b/src/cowboy_metrics_h.erl @@ -32,6 +32,17 @@ reason => any() }}. +-type informational_metrics() :: #{ + %% Informational response status. + status := cowboy:http_status(), + + %% Headers sent with the informational response. + headers := cowboy:http_headers(), + + %% Time when the informational response was sent. + time := integer() +}. + -type metrics() :: #{ %% The identifier for this listener. ref := ranch:ref(), @@ -86,6 +97,9 @@ %% process: the request process. procs => proc_metrics(), + %% Informational responses sent before the final response. + informational => [informational_metrics()], + %% Length of the request and response bodies. This does %% not include the framing. req_body_length => non_neg_integer(), @@ -108,6 +122,7 @@ resp_start :: undefined | integer(), resp_end :: undefined | integer(), procs = #{} :: proc_metrics(), + informational = [] :: [informational_metrics()], req_body_length = 0 :: non_neg_integer(), resp_body_length = 0 :: non_neg_integer() }). @@ -184,6 +199,14 @@ fold([{spawn, Pid, _}|Tail], State0=#state{procs=Procs}) -> ProcStart = erlang:monotonic_time(), State = State0#state{procs=Procs#{Pid => #{spawn => ProcStart}}}, fold(Tail, State); +fold([{inform, Status, Headers}|Tail], + State=#state{informational=Infos}) -> + Time = erlang:monotonic_time(), + fold(Tail, State#state{informational=[#{ + status => Status, + headers => Headers, + time => Time + }|Infos]}); fold([{response, Status, Headers, Body}|Tail], State=#state{resp_headers_filter=RespHeadersFilter}) -> Resp = erlang:monotonic_time(), @@ -226,7 +249,8 @@ terminate(StreamID, Reason, #state{next=Next, callback=Fun, req=Req, resp_status=RespStatus, resp_headers=RespHeaders, ref=Ref, req_start=ReqStart, req_body_start=ReqBodyStart, req_body_end=ReqBodyEnd, resp_start=RespStart, resp_end=RespEnd, - procs=Procs, req_body_length=ReqBodyLen, resp_body_length=RespBodyLen}) -> + procs=Procs, informational=Infos, + req_body_length=ReqBodyLen, resp_body_length=RespBodyLen}) -> Res = cowboy_stream:terminate(StreamID, Reason, Next), ReqEnd = erlang:monotonic_time(), Metrics = #{ @@ -244,6 +268,7 @@ terminate(StreamID, Reason, #state{next=Next, callback=Fun, resp_start => RespStart, resp_end => RespEnd, procs => Procs, + informational => lists:reverse(Infos), req_body_length => ReqBodyLen, resp_body_length => RespBodyLen }, diff --git a/test/metrics_SUITE.erl b/test/metrics_SUITE.erl index d41ae1d..1883014 100644 --- a/test/metrics_SUITE.erl +++ b/test/metrics_SUITE.erl @@ -143,7 +143,8 @@ do_get(Path, Config) -> pid := From, streamid := 1, reason := normal, - req := #{} + req := #{}, + informational := [] } = Metrics, %% All good! ok @@ -205,7 +206,8 @@ post_body(Config) -> pid := From, streamid := 1, reason := normal, - req := #{} + req := #{}, + informational := [] } = Metrics, %% All good! ok @@ -261,7 +263,8 @@ no_resp_body(Config) -> pid := From, streamid := 1, reason := normal, - req := #{} + req := #{}, + informational := [] } = Metrics, %% All good! ok @@ -353,7 +356,7 @@ do_ws(Config) -> %% is called. We therefore only check when it spawned. #{procs := Procs} = Metrics, [{_, #{ - spawn := ProcSpawn + spawn := _ }}] = maps:to_list(Procs), %% Confirm other metadata are as expected. #{ @@ -361,7 +364,17 @@ do_ws(Config) -> pid := From, streamid := 1, reason := switch_protocol, - req := #{} + req := #{}, + %% A 101 upgrade response was sent. + informational := [#{ + status := 101, + headers := #{ + <<"connection">> := <<"Upgrade">>, + <<"upgrade">> := <<"websocket">>, + <<"sec-websocket-accept">> := _ + }, + time := _ + }] } = Metrics, %% All good! ok -- cgit v1.2.3