aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_clear.erl4
-rw-r--r--src/cowboy_http.erl56
-rw-r--r--src/cowboy_http2.erl2
-rw-r--r--src/cowboy_tls.erl6
4 files changed, 42 insertions, 26 deletions
diff --git a/src/cowboy_clear.erl b/src/cowboy_clear.erl
index eaeab74..3fe8db5 100644
--- a/src/cowboy_clear.erl
+++ b/src/cowboy_clear.erl
@@ -36,10 +36,6 @@ connection_process(Parent, Ref, Transport, Opts) ->
ProxyInfo = get_proxy_info(Ref, Opts),
{ok, Socket} = ranch:handshake(Ref),
%% Use cowboy_http2 directly only when 'http' is missing.
- %% Otherwise switch to cowboy_http2 from cowboy_http.
- %%
- %% @todo Extend this option to cowboy_tls and allow disabling
- %% the switch to cowboy_http2 in cowboy_http. Also document it.
Protocol = case maps:get(protocols, Opts, [http2, http]) of
[http2] -> cowboy_http2;
[_|_] -> cowboy_http
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index 476c694..9438c72 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -25,6 +25,7 @@
-type opts() :: #{
active_n => pos_integer(),
+ alpn_default_protocol => http | http2,
chunked => boolean(),
compress_buffering => boolean(),
compress_threshold => non_neg_integer(),
@@ -52,6 +53,7 @@
metrics_req_filter => fun((cowboy_req:req()) -> map()),
metrics_resp_headers_filter => fun((cowboy:http_headers()) -> cowboy:http_headers()),
middlewares => [module()],
+ protocols => [http | http2],
proxy_header => boolean(),
request_timeout => timeout(),
reset_idle_timeout_on_send => boolean(),
@@ -511,8 +513,13 @@ parse_request(Buffer, State=#state{opts=Opts, in_streamid=InStreamID}, EmptyLine
'The TRACE method is currently not implemented. (RFC7231 4.3.8)'});
%% Accept direct HTTP/2 only at the beginning of the connection.
<< "PRI * HTTP/2.0\r\n", _/bits >> when InStreamID =:= 1 ->
- %% @todo Might be worth throwing to get a clean stacktrace.
- http2_upgrade(State, Buffer);
+ case lists:member(http2, maps:get(protocols, Opts, [http2, http])) of
+ true ->
+ http2_upgrade(State, Buffer);
+ false ->
+ error_terminate(501, State, {connection_error, no_error,
+ 'Prior knowledge upgrade to HTTP/2 is disabled by configuration.'})
+ end;
_ ->
parse_method(Buffer, State, <<>>,
maps:get(max_method_length, Opts, 32))
@@ -800,7 +807,7 @@ default_port(_) -> 80.
%% End of request parsing.
request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock, cert=Cert,
- proxy_header=ProxyHeader, in_streamid=StreamID, in_state=
+ opts=Opts, proxy_header=ProxyHeader, in_streamid=StreamID, in_state=
PS=#ps_header{method=Method, path=Path, qs=Qs, version=Version}},
Headers, Host, Port) ->
Scheme = case Transport:secure() of
@@ -864,7 +871,7 @@ request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock
undefined -> Req0;
_ -> Req0#{proxy_header => ProxyHeader}
end,
- case is_http2_upgrade(Headers, Version) of
+ case is_http2_upgrade(Headers, Version, Opts) of
false ->
State = case HasBody of
true ->
@@ -886,12 +893,13 @@ request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock
%% HTTP/2 upgrade.
-%% @todo We must not upgrade to h2c over a TLS connection.
is_http2_upgrade(#{<<"connection">> := Conn, <<"upgrade">> := Upgrade,
- <<"http2-settings">> := HTTP2Settings}, 'HTTP/1.1') ->
+ <<"http2-settings">> := HTTP2Settings}, 'HTTP/1.1', Opts) ->
Conns = cow_http_hd:parse_connection(Conn),
- case {lists:member(<<"upgrade">>, Conns), lists:member(<<"http2-settings">>, Conns)} of
- {true, true} ->
+ case lists:member(<<"upgrade">>, Conns)
+ andalso lists:member(<<"http2-settings">>, Conns)
+ andalso lists:member(http2, maps:get(protocols, Opts, [http2, http])) of
+ true ->
Protocols = cow_http_hd:parse_upgrade(Upgrade),
case lists:member(<<"h2c">>, Protocols) of
true ->
@@ -902,7 +910,7 @@ is_http2_upgrade(#{<<"connection">> := Conn, <<"upgrade">> := Upgrade,
_ ->
false
end;
-is_http2_upgrade(_, _) ->
+is_http2_upgrade(_, _, _) ->
false.
%% Prior knowledge upgrade, without an HTTP/1.1 request.
@@ -922,18 +930,24 @@ http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Tran
http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Transport,
proxy_header=ProxyHeader, peer=Peer, sock=Sock, cert=Cert},
Buffer, HTTP2Settings, Req) ->
- %% @todo
- %% However if the client sent a body, we need to read the body in full
- %% and if we can't do that, return a 413 response. Some options are in order.
- %% Always half-closed stream coming from this side.
- try cow_http_hd:parse_http2_settings(HTTP2Settings) of
- Settings ->
- _ = cancel_timeout(State),
- cowboy_http2:init(Parent, Ref, Socket, Transport, ProxyHeader,
- opts_for_upgrade(State), Peer, Sock, Cert, Buffer, Settings, Req)
- catch _:_ ->
- error_terminate(400, State, {connection_error, protocol_error,
- 'The HTTP2-Settings header must contain a base64 SETTINGS payload. (RFC7540 3.2, RFC7540 3.2.1)'})
+ case Transport:secure() of
+ false ->
+ %% @todo
+ %% However if the client sent a body, we need to read the body in full
+ %% and if we can't do that, return a 413 response. Some options are in order.
+ %% Always half-closed stream coming from this side.
+ try cow_http_hd:parse_http2_settings(HTTP2Settings) of
+ Settings ->
+ _ = cancel_timeout(State),
+ cowboy_http2:init(Parent, Ref, Socket, Transport, ProxyHeader,
+ opts_for_upgrade(State), Peer, Sock, Cert, Buffer, Settings, Req)
+ catch _:_ ->
+ error_terminate(400, State, {connection_error, protocol_error,
+ 'The HTTP2-Settings header must contain a base64 SETTINGS payload. (RFC7540 3.2, RFC7540 3.2.1)'})
+ end;
+ true ->
+ error_terminate(400, State, {connection_error, protocol_error,
+ 'Clients that support HTTP/2 over TLS MUST use ALPN. (RFC7540 3.4)'})
end.
opts_for_upgrade(#state{opts=Opts, dynamic_buffer_size=false}) ->
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index 207a967..0944f91 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -25,6 +25,7 @@
-type opts() :: #{
active_n => pos_integer(),
+ alpn_default_protocol => http | http2,
compress_buffering => boolean(),
compress_threshold => non_neg_integer(),
connection_type => worker | supervisor,
@@ -62,6 +63,7 @@
metrics_resp_headers_filter => fun((cowboy:http_headers()) -> cowboy:http_headers()),
middlewares => [module()],
preface_timeout => timeout(),
+ protocols => [http | http2],
proxy_header => boolean(),
reset_idle_timeout_on_send => boolean(),
sendfile => boolean(),
diff --git a/src/cowboy_tls.erl b/src/cowboy_tls.erl
index 60ab2ed..51b2230 100644
--- a/src/cowboy_tls.erl
+++ b/src/cowboy_tls.erl
@@ -39,7 +39,11 @@ connection_process(Parent, Ref, Transport, Opts) ->
{ok, <<"h2">>} ->
init(Parent, Ref, Socket, Transport, ProxyInfo, Opts, cowboy_http2);
_ -> %% http/1.1 or no protocol negotiated.
- init(Parent, Ref, Socket, Transport, ProxyInfo, Opts, cowboy_http)
+ Protocol = case maps:get(alpn_default_protocol, Opts, http) of
+ http -> cowboy_http;
+ http2 -> cowboy_http2
+ end,
+ init(Parent, Ref, Socket, Transport, ProxyInfo, Opts, Protocol)
end.
init(Parent, Ref, Socket, Transport, ProxyInfo, Opts, Protocol) ->