Просмотр исходного кода

Add cowboy_req:cast/2

Better than sending messages manually.
Loïc Hoguin 5 лет назад
Родитель
Сommit
2e8fcb9a9e

+ 4 - 0
doc/src/manual/cowboy_req.asciidoc

@@ -90,6 +90,10 @@ Response:
 * link:man:cowboy_req:stream_trailers(3)[cowboy_req:stream_trailers(3)] - Send the response trailers
 * link:man:cowboy_req:push(3)[cowboy_req:push(3)] - Push a resource to the client
 
+Stream handlers:
+
+* link:man:cowboy_req:cast(3)[cowboy_req:cast(3)] - Cast a stream handler event
+
 == Types
 
 === push_opts()

+ 64 - 0
doc/src/manual/cowboy_req.cast.asciidoc

@@ -0,0 +1,64 @@
+= cowboy_req:cast(3)
+
+== Name
+
+cowboy_req:cast - Cast a stream handler event
+
+== Description
+
+[source,erlang]
+----
+cast(Event :: any(), Req :: cowboy_req:req()) -> ok
+----
+
+Cast a stream handler event.
+
+The event will be passed to stream handlers through the
+`info/3` callback.
+
+== Arguments
+
+Event::
+
+The event to be sent to stream handlers.
+
+Req::
+
+The Req object.
+
+== Return value
+
+The atom `ok` is always returned. It can be safely ignored.
+
+== Changelog
+
+* *2.7*: Function introduced.
+
+== Examples
+
+.Increase the HTTP/1.1 idle timeout
+[source,erlang]
+----
+cowboy_req:cast({set_options, #{
+    idle_timeout => 3600000
+}}, Req).
+----
+
+.Add user data to metrics
+----
+cowboy_req:cast({set_options, #{
+    metrics_user_data => #{handler => ?MODULE}
+}}, Req).
+----
+
+.Enable compression buffering
+----
+cowboy_req:cast({set_options, #{
+    compress_buffering => true
+}}, Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_stream(3)[cowboy_stream(3)]

+ 4 - 4
doc/src/manual/cowboy_stream.asciidoc

@@ -277,9 +277,8 @@ relevant option's documentation for details.
 
 Cowboy will forward all messages sent to the stream to
 the `info/3` callback. To send a message to a stream,
-send a message to the connection process with the form
-`{{Pid, StreamID}, Msg}`. The connection process will
-then forward `Msg` to the stream handlers.
+the function link:man:cowboy_req:cast(3)[cowboy_req:cast(3)]
+can be used.
 
 Cowboy will also forward the exit signals for the
 processes that the stream spawned.
@@ -403,4 +402,5 @@ tuple.
 
 link:man:cowboy(7)[cowboy(7)],
 link:man:cowboy_http(3)[cowboy_http(3)],
-link:man:cowboy_http2(3)[cowboy_http2(3)]
+link:man:cowboy_http2(3)[cowboy_http2(3)],
+link:man:cowboy_req:cast(3)[cowboy_req:cast(3)]

+ 25 - 21
src/cowboy_req.erl

@@ -92,6 +92,9 @@
 -export([push/3]).
 -export([push/4]).
 
+%% Stream handlers.
+-export([cast/2]).
+
 %% Internal.
 -export([response_headers/2]).
 
@@ -516,12 +519,12 @@ read_body(Req=#{has_body := false}, _) ->
 	{ok, <<>>, Req};
 read_body(Req=#{has_read_body := true}, _) ->
 	{ok, <<>>, Req};
-read_body(Req=#{pid := Pid, streamid := StreamID}, Opts) ->
+read_body(Req, Opts) ->
 	Length = maps:get(length, Opts, 8000000),
 	Period = maps:get(period, Opts, 15000),
 	Timeout = maps:get(timeout, Opts, Period + 1000),
 	Ref = make_ref(),
-	Pid ! {{Pid, StreamID}, {read_body, self(), Ref, Length, Period}},
+	cast({read_body, self(), Ref, Length, Period}, Req),
 	receive
 		{request_body, Ref, nofin, Body} ->
 			{more, Body, Req};
@@ -775,10 +778,8 @@ inform(Status, Req) ->
 -spec inform(cowboy:http_status(), cowboy:http_headers(), req()) -> ok.
 inform(_, _, #{has_sent_resp := _}) ->
 	error(function_clause); %% @todo Better error message.
-inform(Status, Headers, #{pid := Pid, streamid := StreamID})
-		when is_integer(Status); is_binary(Status) ->
-	Pid ! {{Pid, StreamID}, {inform, Status, Headers}},
-	ok.
+inform(Status, Headers, Req) when is_integer(Status); is_binary(Status) ->
+	cast({inform, Status, Headers}, Req).
 
 -spec reply(cowboy:http_status(), Req) -> Req when Req::req().
 reply(Status, Req) ->
@@ -823,11 +824,11 @@ reply(Status, Headers, Body, Req)
 %% Don't send any body for HEAD responses. While the protocol code is
 %% supposed to enforce this rule, we prefer to avoid copying too much
 %% data around if we can avoid it.
-do_reply(Status, Headers, _, Req=#{pid := Pid, streamid := StreamID, method := <<"HEAD">>}) ->
-	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), <<>>}},
+do_reply(Status, Headers, _, Req=#{method := <<"HEAD">>}) ->
+	cast({response, Status, response_headers(Headers, Req), <<>>}, Req),
 	done_replying(Req, true);
-do_reply(Status, Headers, Body, Req=#{pid := Pid, streamid := StreamID}) ->
-	Pid ! {{Pid, StreamID}, {response, Status, response_headers(Headers, Req), Body}},
+do_reply(Status, Headers, Body, Req) ->
+	cast({response, Status, response_headers(Headers, Req), Body}, Req),
 	done_replying(Req, true).
 
 done_replying(Req, HasSentResp) ->
@@ -841,9 +842,8 @@ stream_reply(Status, Req) ->
 	-> Req when Req::req().
 stream_reply(_, _, #{has_sent_resp := _}) ->
 	error(function_clause);
-stream_reply(Status, Headers=#{}, Req=#{pid := Pid, streamid := StreamID})
-		when is_integer(Status); is_binary(Status) ->
-	Pid ! {{Pid, StreamID}, {headers, Status, response_headers(Headers, Req)}},
+stream_reply(Status, Headers=#{}, Req) when is_integer(Status); is_binary(Status) ->
+	cast({headers, Status, response_headers(Headers, Req)}, Req),
 	done_replying(Req, headers).
 
 -spec stream_body(resp_body(), fin | nofin, req()) -> ok.
@@ -872,8 +872,8 @@ stream_body(Data, IsFin, Req=#{has_sent_resp := headers})
 	stream_body({data, self(), IsFin, Data}, Req).
 
 %% @todo Do we need a timeout?
-stream_body(Msg, #{pid := Pid, streamid := StreamID}) ->
-	Pid ! {{Pid, StreamID}, Msg},
+stream_body(Msg, Req=#{pid := Pid}) ->
+	cast(Msg, Req),
 	receive {data_ack, Pid} -> ok end.
 
 -spec stream_events(cow_sse:event() | [cow_sse:event()], fin | nofin, req()) -> ok.
@@ -883,9 +883,8 @@ stream_events(Events, IsFin, Req=#{has_sent_resp := headers}) ->
 	stream_body({data, self(), IsFin, cow_sse:events(Events)}, Req).
 
 -spec stream_trailers(cowboy:http_headers(), req()) -> ok.
-stream_trailers(Trailers, #{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
-	Pid ! {{Pid, StreamID}, {trailers, Trailers}},
-	ok.
+stream_trailers(Trailers, Req=#{has_sent_resp := headers}) ->
+	cast({trailers, Trailers}, Req).
 
 -spec push(iodata(), cowboy:http_headers(), req()) -> ok.
 push(Path, Headers, Req) ->
@@ -895,14 +894,19 @@ push(Path, Headers, Req) ->
 %% @todo Path, Headers, Opts, everything should be in proper binary,
 %% or normalized when creating the Req object.
 -spec push(iodata(), cowboy:http_headers(), req(), push_opts()) -> ok.
-push(Path, Headers, #{pid := Pid, streamid := StreamID,
-		scheme := Scheme0, host := Host0, port := Port0}, Opts) ->
+push(Path, Headers, Req=#{scheme := Scheme0, host := Host0, port := Port0}, Opts) ->
 	Method = maps:get(method, Opts, <<"GET">>),
 	Scheme = maps:get(scheme, Opts, Scheme0),
 	Host = maps:get(host, Opts, Host0),
 	Port = maps:get(port, Opts, Port0),
 	Qs = maps:get(qs, Opts, <<>>),
-	Pid ! {{Pid, StreamID}, {push, Method, Scheme, Host, Port, Path, Qs, Headers}},
+	cast({push, Method, Scheme, Host, Port, Path, Qs, Headers}, Req).
+
+%% Stream handlers.
+
+-spec cast(any(), req()) -> ok.
+cast(Msg, #{pid := Pid, streamid := StreamID}) ->
+	Pid ! {{Pid, StreamID}, Msg},
 	ok.
 
 %% Internal.

+ 3 - 9
test/handlers/compress_h.erl

@@ -24,9 +24,7 @@ init(Req0, State=reply) ->
 			Size = filelib:file_size(AppFile),
 			cowboy_req:reply(200, #{}, {sendfile, 0, Size, AppFile}, Req0);
 		<<"set_options_threshold0">> ->
-			%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-			#{pid := Pid, streamid := StreamID} = Req0,
-			Pid ! {{Pid, StreamID}, {set_options, #{compress_threshold => 0}}},
+			cowboy_req:cast({set_options, #{compress_threshold => 0}}, Req0),
 			cowboy_req:reply(200, #{}, lists:duplicate(100, $a), Req0)
 	end,
 	{ok, Req, State};
@@ -62,14 +60,10 @@ init(Req0, State=stream_reply) ->
 		<<"delayed">> ->
 			stream_delayed(Req0);
 		<<"set_options_buffering_false">> ->
-			%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-			#{pid := Pid, streamid := StreamID} = Req0,
-			Pid ! {{Pid, StreamID}, {set_options, #{compress_buffering => false}}},
+			cowboy_req:cast({set_options, #{compress_buffering => false}}, Req0),
 			stream_delayed(Req0);
 		<<"set_options_buffering_true">> ->
-			%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-			#{pid := Pid, streamid := StreamID} = Req0,
-			Pid ! {{Pid, StreamID}, {set_options, #{compress_buffering => true}}},
+			cowboy_req:cast({set_options, #{compress_buffering => true}}, Req0),
 			stream_delayed(Req0)
 	end,
 	{ok, Req, State}.

+ 5 - 15
test/handlers/set_options_h.erl

@@ -9,32 +9,22 @@ init(Req, State) ->
 	set_options(cowboy_req:binding(key, Req), Req, State).
 
 set_options(<<"chunked_false">>, Req0, State) ->
-	%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-	#{pid := Pid, streamid := StreamID} = Req0,
-	Pid ! {{Pid, StreamID}, {set_options, #{chunked => false}}},
+	cowboy_req:cast({set_options, #{chunked => false}}, Req0),
 	Req = cowboy_req:stream_reply(200, Req0),
 	cowboy_req:stream_body(<<0:8000000>>, fin, Req),
 	{ok, Req, State};
 set_options(<<"chunked_false_ignored">>, Req0, State) ->
-	%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-	#{pid := Pid, streamid := StreamID} = Req0,
-	Pid ! {{Pid, StreamID}, {set_options, #{chunked => false}}},
+	cowboy_req:cast({set_options, #{chunked => false}}, Req0),
 	Req = cowboy_req:reply(200, #{}, <<"Hello world!">>, Req0),
 	{ok, Req, State};
 set_options(<<"idle_timeout_short">>, Req0, State) ->
-	%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-	#{pid := Pid, streamid := StreamID} = Req0,
-	Pid ! {{Pid, StreamID}, {set_options, #{idle_timeout => 500}}},
+	cowboy_req:cast({set_options, #{idle_timeout => 500}}, Req0),
 	{_, Body, Req} = cowboy_req:read_body(Req0),
 	{ok, cowboy_req:reply(200, #{}, Body, Req), State};
 set_options(<<"idle_timeout_long">>, Req0, State) ->
-	%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-	#{pid := Pid, streamid := StreamID} = Req0,
-	Pid ! {{Pid, StreamID}, {set_options, #{idle_timeout => 60000}}},
+	cowboy_req:cast({set_options, #{idle_timeout => 60000}}, Req0),
 	{_, Body, Req} = cowboy_req:read_body(Req0),
 	{ok, cowboy_req:reply(200, #{}, Body, Req), State};
 set_options(<<"metrics_user_data">>, Req, State) ->
-	%% @todo This should be replaced by a cowboy_req:cast/cowboy_stream:cast.
-	#{pid := Pid, streamid := StreamID} = Req,
-	Pid ! {{Pid, StreamID}, {set_options, #{metrics_user_data => #{handler => ?MODULE}}}},
+	cowboy_req:cast({set_options, #{metrics_user_data => #{handler => ?MODULE}}}, Req),
 	{ok, cowboy_req:reply(200, #{}, <<"Hello world!">>, Req), State}.