mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Introduce cowboy_req:sock/1 and cowboy_req:cert/1
To obtain the local socket ip/port and the client TLS certificate, respectively.
This commit is contained in:
parent
4090adaecc
commit
ef58e15547
9 changed files with 268 additions and 35 deletions
|
@ -29,6 +29,12 @@ and to read the body once.
|
||||||
|
|
||||||
== Exports
|
== Exports
|
||||||
|
|
||||||
|
Connection:
|
||||||
|
|
||||||
|
* link:man:cowboy_req:peer(3)[cowboy_req:peer(3)] - Peer address and port
|
||||||
|
* link:man:cowboy_req:sock(3)[cowboy_req:sock(3)] - Socket address and port
|
||||||
|
* link:man:cowboy_req:cert(3)[cowboy_req:cert(3)] - Client TLS certificate
|
||||||
|
|
||||||
Raw request:
|
Raw request:
|
||||||
|
|
||||||
* link:man:cowboy_req:method(3)[cowboy_req:method(3)] - HTTP method
|
* link:man:cowboy_req:method(3)[cowboy_req:method(3)] - HTTP method
|
||||||
|
@ -41,7 +47,6 @@ Raw request:
|
||||||
* link:man:cowboy_req:uri(3)[cowboy_req:uri(3)] - Reconstructed URI
|
* link:man:cowboy_req:uri(3)[cowboy_req:uri(3)] - Reconstructed URI
|
||||||
* link:man:cowboy_req:header(3)[cowboy_req:header(3)] - HTTP header
|
* link:man:cowboy_req:header(3)[cowboy_req:header(3)] - HTTP header
|
||||||
* link:man:cowboy_req:headers(3)[cowboy_req:headers(3)] - HTTP headers
|
* link:man:cowboy_req:headers(3)[cowboy_req:headers(3)] - HTTP headers
|
||||||
* link:man:cowboy_req:peer(3)[cowboy_req:peer(3)] - Peer address and port
|
|
||||||
|
|
||||||
Processed request:
|
Processed request:
|
||||||
|
|
||||||
|
@ -129,7 +134,9 @@ req() :: #{
|
||||||
path := binary(), %% case sensitive
|
path := binary(), %% case sensitive
|
||||||
qs := binary(), %% case sensitive
|
qs := binary(), %% case sensitive
|
||||||
headers := cowboy:http_headers(),
|
headers := cowboy:http_headers(),
|
||||||
peer := {inet:ip_address(), inet:port_number()}
|
peer := {inet:ip_address(), inet:port_number()},
|
||||||
|
sock := {inet:ip_address(), inet:port_number()},
|
||||||
|
cert := binary() | undefined
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
71
doc/src/manual/cowboy_req.cert.asciidoc
Normal file
71
doc/src/manual/cowboy_req.cert.asciidoc
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
= cowboy_req:cert(3)
|
||||||
|
|
||||||
|
== Name
|
||||||
|
|
||||||
|
cowboy_req:cert - Client TLS certificate
|
||||||
|
|
||||||
|
== Description
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
cert(Req :: cowboy_req:req()) -> binary() | undefined
|
||||||
|
----
|
||||||
|
|
||||||
|
Return the peer's TLS certificate.
|
||||||
|
|
||||||
|
Using the default configuration this function will always return
|
||||||
|
`undefined`. You need to explicitly configure Cowboy to request
|
||||||
|
the client certificate. To do this you need to set the `verify`
|
||||||
|
transport option to `verify_peer`:
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
{ok, _} = cowboy:start_tls(example, [
|
||||||
|
{port, 8443},
|
||||||
|
{cert, "path/to/cert.pem"},
|
||||||
|
{verify, verify_peer}
|
||||||
|
], #{
|
||||||
|
env => #{dispatch => Dispatch}
|
||||||
|
}).
|
||||||
|
----
|
||||||
|
|
||||||
|
You may also want to customize the `verify_fun` function. Please
|
||||||
|
consult the `ssl` application's manual for more details.
|
||||||
|
|
||||||
|
TCP connections do not allow a certificate and this function
|
||||||
|
will therefore always return `undefined`.
|
||||||
|
|
||||||
|
The certificate can also be obtained using pattern matching:
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
#{cert := Cert} = Req.
|
||||||
|
----
|
||||||
|
|
||||||
|
== Arguments
|
||||||
|
|
||||||
|
Req::
|
||||||
|
|
||||||
|
The Req object.
|
||||||
|
|
||||||
|
== Return value
|
||||||
|
|
||||||
|
The client TLS certificate.
|
||||||
|
|
||||||
|
== Changelog
|
||||||
|
|
||||||
|
* *2.0*: Function introduced.
|
||||||
|
|
||||||
|
== Examples
|
||||||
|
|
||||||
|
.Get the client TLS certificate.
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
Cert = cowboy_req:cert(Req).
|
||||||
|
----
|
||||||
|
|
||||||
|
== See also
|
||||||
|
|
||||||
|
link:man:cowboy_req(3)[cowboy_req(3)],
|
||||||
|
link:man:cowboy_req:peer(3)[cowboy_req:peer(3)],
|
||||||
|
link:man:cowboy_req:sock(3)[cowboy_req:sock(3)]
|
|
@ -8,14 +8,14 @@ cowboy_req:peer - Peer address and port
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
----
|
----
|
||||||
peer(Req :: cowboy_req:req()) -> Peer
|
peer(Req :: cowboy_req:req()) -> Info
|
||||||
|
|
||||||
Peer :: {inet:ip_address(), inet:port_number()}
|
Info :: {inet:ip_address(), inet:port_number()}
|
||||||
----
|
----
|
||||||
|
|
||||||
Return the peer's IP address and port number.
|
Return the peer's IP address and port number.
|
||||||
|
|
||||||
The peer can also be obtained using pattern matching:
|
The peer information can also be obtained using pattern matching:
|
||||||
|
|
||||||
[source,erlang]
|
[source,erlang]
|
||||||
----
|
----
|
||||||
|
@ -56,4 +56,6 @@ way of determining the source of an HTTP request.
|
||||||
|
|
||||||
== See also
|
== See also
|
||||||
|
|
||||||
link:man:cowboy_req(3)[cowboy_req(3)]
|
link:man:cowboy_req(3)[cowboy_req(3)],
|
||||||
|
link:man:cowboy_req:sock(3)[cowboy_req:sock(3)],
|
||||||
|
link:man:cowboy_req:cert(3)[cowboy_req:cert(3)]
|
||||||
|
|
51
doc/src/manual/cowboy_req.sock.asciidoc
Normal file
51
doc/src/manual/cowboy_req.sock.asciidoc
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
= cowboy_req:sock(3)
|
||||||
|
|
||||||
|
== Name
|
||||||
|
|
||||||
|
cowboy_req:sock - Socket address and port
|
||||||
|
|
||||||
|
== Description
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
sock(Req :: cowboy_req:req()) -> Info
|
||||||
|
|
||||||
|
Info :: {inet:ip_address(), inet:port_number()}
|
||||||
|
----
|
||||||
|
|
||||||
|
Return the socket's IP address and port number.
|
||||||
|
|
||||||
|
The socket information can also be obtained using pattern matching:
|
||||||
|
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
#{sock := {IP, Port}} = Req.
|
||||||
|
----
|
||||||
|
|
||||||
|
== Arguments
|
||||||
|
|
||||||
|
Req::
|
||||||
|
|
||||||
|
The Req object.
|
||||||
|
|
||||||
|
== Return value
|
||||||
|
|
||||||
|
The socket's local IP address and port number.
|
||||||
|
|
||||||
|
== Changelog
|
||||||
|
|
||||||
|
* *2.0*: Function introduced.
|
||||||
|
|
||||||
|
== Examples
|
||||||
|
|
||||||
|
.Get the socket's IP address and port number.
|
||||||
|
[source,erlang]
|
||||||
|
----
|
||||||
|
{IP, Port} = cowboy_req:sock(Req).
|
||||||
|
----
|
||||||
|
|
||||||
|
== See also
|
||||||
|
|
||||||
|
link:man:cowboy_req(3)[cowboy_req(3)],
|
||||||
|
link:man:cowboy_req:peer(3)[cowboy_req:peer(3)],
|
||||||
|
link:man:cowboy_req:cert(3)[cowboy_req:cert(3)]
|
|
@ -85,6 +85,12 @@
|
||||||
%% Remote address and port for the connection.
|
%% Remote address and port for the connection.
|
||||||
peer = undefined :: {inet:ip_address(), inet:port_number()},
|
peer = undefined :: {inet:ip_address(), inet:port_number()},
|
||||||
|
|
||||||
|
%% Local address and port for the connection.
|
||||||
|
sock = undefined :: {inet:ip_address(), inet:port_number()},
|
||||||
|
|
||||||
|
%% Client certificate (TLS only).
|
||||||
|
cert :: undefined | binary(),
|
||||||
|
|
||||||
timer = undefined :: undefined | reference(),
|
timer = undefined :: undefined | reference(),
|
||||||
|
|
||||||
%% Identifier for the stream currently being read (or waiting to be received).
|
%% Identifier for the stream currently being read (or waiting to be received).
|
||||||
|
@ -115,16 +121,36 @@
|
||||||
|
|
||||||
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
||||||
init(Parent, Ref, Socket, Transport, Opts) ->
|
init(Parent, Ref, Socket, Transport, Opts) ->
|
||||||
case Transport:peername(Socket) of
|
Peer0 = Transport:peername(Socket),
|
||||||
{ok, Peer} ->
|
Sock0 = Transport:sockname(Socket),
|
||||||
|
Cert1 = case Transport:name() of
|
||||||
|
ssl ->
|
||||||
|
case ssl:peercert(Socket) of
|
||||||
|
{error, no_peercert} ->
|
||||||
|
{ok, undefined};
|
||||||
|
Cert0 ->
|
||||||
|
Cert0
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{ok, undefined}
|
||||||
|
end,
|
||||||
|
case {Peer0, Sock0, Cert1} of
|
||||||
|
{{ok, Peer}, {ok, Sock}, {ok, Cert}} ->
|
||||||
LastStreamID = maps:get(max_keepalive, Opts, 100),
|
LastStreamID = maps:get(max_keepalive, Opts, 100),
|
||||||
before_loop(set_timeout(#state{
|
before_loop(set_timeout(#state{
|
||||||
parent=Parent, ref=Ref, socket=Socket,
|
parent=Parent, ref=Ref, socket=Socket,
|
||||||
transport=Transport, opts=Opts,
|
transport=Transport, opts=Opts,
|
||||||
peer=Peer, last_streamid=LastStreamID}), <<>>);
|
peer=Peer, sock=Sock, cert=Cert,
|
||||||
{error, Reason} ->
|
last_streamid=LastStreamID}), <<>>);
|
||||||
%% Couldn't read the peer address; connection is gone.
|
{{error, Reason}, _, _} ->
|
||||||
terminate(undefined, {socket_error, Reason, 'An error has occurred on the socket.'})
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the peer name.'});
|
||||||
|
{_, {error, Reason}, _} ->
|
||||||
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the sock name.'});
|
||||||
|
{_, _, {error, Reason}} ->
|
||||||
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the client TLS certificate.'})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
before_loop(State=#state{socket=Socket, transport=Transport}, Buffer) ->
|
before_loop(State=#state{socket=Socket, transport=Transport}, Buffer) ->
|
||||||
|
@ -559,8 +585,9 @@ default_port(_) -> 80.
|
||||||
|
|
||||||
%% End of request parsing.
|
%% End of request parsing.
|
||||||
|
|
||||||
request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, in_streamid=StreamID,
|
request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock, cert=Cert,
|
||||||
in_state=PS=#ps_header{method=Method, path=Path, qs=Qs, version=Version}},
|
in_streamid=StreamID, in_state=
|
||||||
|
PS=#ps_header{method=Method, path=Path, qs=Qs, version=Version}},
|
||||||
Headers, Host, Port) ->
|
Headers, Host, Port) ->
|
||||||
Scheme = case Transport:secure() of
|
Scheme = case Transport:secure() of
|
||||||
true -> <<"https">>;
|
true -> <<"https">>;
|
||||||
|
@ -589,6 +616,8 @@ request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, in_stream
|
||||||
pid => self(),
|
pid => self(),
|
||||||
streamid => StreamID,
|
streamid => StreamID,
|
||||||
peer => Peer,
|
peer => Peer,
|
||||||
|
sock => Sock,
|
||||||
|
cert => Cert,
|
||||||
method => Method,
|
method => Method,
|
||||||
scheme => Scheme,
|
scheme => Scheme,
|
||||||
host => Host,
|
host => Host,
|
||||||
|
@ -644,11 +673,12 @@ is_http2_upgrade(_, _) ->
|
||||||
|
|
||||||
%% Prior knowledge upgrade, without an HTTP/1.1 request.
|
%% Prior knowledge upgrade, without an HTTP/1.1 request.
|
||||||
http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Transport,
|
http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Transport,
|
||||||
opts=Opts, peer=Peer}, Buffer) ->
|
opts=Opts, peer=Peer, sock=Sock, cert=Cert}, Buffer) ->
|
||||||
case Transport:secure() of
|
case Transport:secure() of
|
||||||
false ->
|
false ->
|
||||||
_ = cancel_timeout(State),
|
_ = cancel_timeout(State),
|
||||||
cowboy_http2:init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer);
|
cowboy_http2:init(Parent, Ref, Socket, Transport, Opts,
|
||||||
|
Peer, Sock, Cert, Buffer);
|
||||||
true ->
|
true ->
|
||||||
error_terminate(400, State, {connection_error, protocol_error,
|
error_terminate(400, State, {connection_error, protocol_error,
|
||||||
'Clients that support HTTP/2 over TLS MUST use ALPN. (RFC7540 3.4)'})
|
'Clients that support HTTP/2 over TLS MUST use ALPN. (RFC7540 3.4)'})
|
||||||
|
@ -656,7 +686,7 @@ http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Tran
|
||||||
|
|
||||||
%% Upgrade via an HTTP/1.1 request.
|
%% Upgrade via an HTTP/1.1 request.
|
||||||
http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Transport,
|
http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Transport,
|
||||||
opts=Opts, peer=Peer}, Buffer, HTTP2Settings, Req) ->
|
opts=Opts, peer=Peer, sock=Sock, cert=Cert}, Buffer, HTTP2Settings, Req) ->
|
||||||
%% @todo
|
%% @todo
|
||||||
%% However if the client sent a body, we need to read the body in full
|
%% 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.
|
%% and if we can't do that, return a 413 response. Some options are in order.
|
||||||
|
@ -664,7 +694,8 @@ http2_upgrade(State=#state{parent=Parent, ref=Ref, socket=Socket, transport=Tran
|
||||||
try cow_http_hd:parse_http2_settings(HTTP2Settings) of
|
try cow_http_hd:parse_http2_settings(HTTP2Settings) of
|
||||||
Settings ->
|
Settings ->
|
||||||
_ = cancel_timeout(State),
|
_ = cancel_timeout(State),
|
||||||
cowboy_http2:init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer, Settings, Req)
|
cowboy_http2:init(Parent, Ref, Socket, Transport, Opts,
|
||||||
|
Peer, Sock, Cert, Buffer, Settings, Req)
|
||||||
catch _:_ ->
|
catch _:_ ->
|
||||||
error_terminate(400, State, {connection_error, protocol_error,
|
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)'})
|
'The HTTP2-Settings header must contain a base64 SETTINGS payload. (RFC7540 3.2, RFC7540 3.2.1)'})
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
-module(cowboy_http2).
|
-module(cowboy_http2).
|
||||||
|
|
||||||
-export([init/5]).
|
-export([init/5]).
|
||||||
-export([init/7]).
|
|
||||||
-export([init/9]).
|
-export([init/9]).
|
||||||
|
-export([init/11]).
|
||||||
|
|
||||||
-export([system_continue/3]).
|
-export([system_continue/3]).
|
||||||
-export([system_terminate/4]).
|
-export([system_terminate/4]).
|
||||||
|
@ -64,6 +64,12 @@
|
||||||
%% Remote address and port for the connection.
|
%% Remote address and port for the connection.
|
||||||
peer = undefined :: {inet:ip_address(), inet:port_number()},
|
peer = undefined :: {inet:ip_address(), inet:port_number()},
|
||||||
|
|
||||||
|
%% Local address and port for the connection.
|
||||||
|
sock = undefined :: {inet:ip_address(), inet:port_number()},
|
||||||
|
|
||||||
|
%% Client certificate (TLS only).
|
||||||
|
cert :: undefined | binary(),
|
||||||
|
|
||||||
%% Settings are separate for each endpoint. In addition, settings
|
%% Settings are separate for each endpoint. In addition, settings
|
||||||
%% must be acknowledged before they can be expected to be applied.
|
%% must be acknowledged before they can be expected to be applied.
|
||||||
%%
|
%%
|
||||||
|
@ -123,19 +129,39 @@
|
||||||
|
|
||||||
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts()) -> ok.
|
||||||
init(Parent, Ref, Socket, Transport, Opts) ->
|
init(Parent, Ref, Socket, Transport, Opts) ->
|
||||||
case Transport:peername(Socket) of
|
Peer0 = Transport:peername(Socket),
|
||||||
{ok, Peer} ->
|
Sock0 = Transport:sockname(Socket),
|
||||||
init(Parent, Ref, Socket, Transport, Opts, Peer, <<>>);
|
Cert1 = case Transport:name() of
|
||||||
{error, Reason} ->
|
ssl ->
|
||||||
%% Couldn't read the peer address; connection is gone.
|
case ssl:peercert(Socket) of
|
||||||
terminate(undefined, {socket_error, Reason, 'An error has occurred on the socket.'})
|
{error, no_peercert} ->
|
||||||
|
{ok, undefined};
|
||||||
|
Cert0 ->
|
||||||
|
Cert0
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{ok, undefined}
|
||||||
|
end,
|
||||||
|
case {Peer0, Sock0, Cert1} of
|
||||||
|
{{ok, Peer}, {ok, Sock}, {ok, Cert}} ->
|
||||||
|
init(Parent, Ref, Socket, Transport, Opts, Peer, Sock, Cert, <<>>);
|
||||||
|
{{error, Reason}, _, _} ->
|
||||||
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the peer name.'});
|
||||||
|
{_, {error, Reason}, _} ->
|
||||||
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the sock name.'});
|
||||||
|
{_, _, {error, Reason}} ->
|
||||||
|
terminate(undefined, {socket_error, Reason,
|
||||||
|
'A socket error occurred when retrieving the client TLS certificate.'})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(),
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(),
|
||||||
{inet:ip_address(), inet:port_number()}, binary()) -> ok.
|
{inet:ip_address(), inet:port_number()}, {inet:ip_address(), inet:port_number()},
|
||||||
init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer) ->
|
binary() | undefined, binary()) -> ok.
|
||||||
|
init(Parent, Ref, Socket, Transport, Opts, Peer, Sock, Cert, Buffer) ->
|
||||||
State = #state{parent=Parent, ref=Ref, socket=Socket,
|
State = #state{parent=Parent, ref=Ref, socket=Socket,
|
||||||
transport=Transport, opts=Opts, peer=Peer,
|
transport=Transport, opts=Opts, peer=Peer, sock=Sock, cert=Cert,
|
||||||
parse_state={preface, sequence, preface_timeout(Opts)}},
|
parse_state={preface, sequence, preface_timeout(Opts)}},
|
||||||
preface(State),
|
preface(State),
|
||||||
case Buffer of
|
case Buffer of
|
||||||
|
@ -145,10 +171,11 @@ init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer) ->
|
||||||
|
|
||||||
%% @todo Add an argument for the request body.
|
%% @todo Add an argument for the request body.
|
||||||
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(),
|
-spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(),
|
||||||
{inet:ip_address(), inet:port_number()}, binary(), map() | undefined, cowboy_req:req()) -> ok.
|
{inet:ip_address(), inet:port_number()}, {inet:ip_address(), inet:port_number()},
|
||||||
init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer, _Settings, Req) ->
|
binary() | undefined, binary(), map() | undefined, cowboy_req:req()) -> ok.
|
||||||
|
init(Parent, Ref, Socket, Transport, Opts, Peer, Sock, Cert, Buffer, _Settings, Req) ->
|
||||||
State0 = #state{parent=Parent, ref=Ref, socket=Socket,
|
State0 = #state{parent=Parent, ref=Ref, socket=Socket,
|
||||||
transport=Transport, opts=Opts, peer=Peer,
|
transport=Transport, opts=Opts, peer=Peer, sock=Sock, cert=Cert,
|
||||||
parse_state={preface, sequence, preface_timeout(Opts)}},
|
parse_state={preface, sequence, preface_timeout(Opts)}},
|
||||||
%% @todo Apply settings.
|
%% @todo Apply settings.
|
||||||
%% StreamID from HTTP/1.1 Upgrade requests is always 1.
|
%% StreamID from HTTP/1.1 Upgrade requests is always 1.
|
||||||
|
@ -720,9 +747,10 @@ stream_decode_init(State=#state{socket=Socket, transport=Transport,
|
||||||
'Error while trying to decode HPACK-encoded header block. (RFC7540 4.3)'})
|
'Error while trying to decode HPACK-encoded header block. (RFC7540 4.3)'})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
stream_req_init(State=#state{ref=Ref, peer=Peer}, StreamID, IsFin, Headers0=#{
|
stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert},
|
||||||
<<":method">> := Method, <<":scheme">> := Scheme,
|
StreamID, IsFin, Headers0=#{
|
||||||
<<":authority">> := Authority, <<":path">> := PathWithQs}) ->
|
<<":method">> := Method, <<":scheme">> := Scheme,
|
||||||
|
<<":authority">> := Authority, <<":path">> := PathWithQs}) ->
|
||||||
Headers = maps:without([<<":method">>, <<":scheme">>, <<":authority">>, <<":path">>], Headers0),
|
Headers = maps:without([<<":method">>, <<":scheme">>, <<":authority">>, <<":path">>], Headers0),
|
||||||
BodyLength = case Headers of
|
BodyLength = case Headers of
|
||||||
_ when IsFin =:= fin ->
|
_ when IsFin =:= fin ->
|
||||||
|
@ -746,6 +774,8 @@ stream_req_init(State=#state{ref=Ref, peer=Peer}, StreamID, IsFin, Headers0=#{
|
||||||
pid => self(),
|
pid => self(),
|
||||||
streamid => StreamID,
|
streamid => StreamID,
|
||||||
peer => Peer,
|
peer => Peer,
|
||||||
|
sock => Sock,
|
||||||
|
cert => Cert,
|
||||||
method => Method,
|
method => Method,
|
||||||
scheme => Scheme,
|
scheme => Scheme,
|
||||||
host => Host,
|
host => Host,
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
-export([method/1]).
|
-export([method/1]).
|
||||||
-export([version/1]).
|
-export([version/1]).
|
||||||
-export([peer/1]).
|
-export([peer/1]).
|
||||||
|
-export([sock/1]).
|
||||||
|
-export([cert/1]).
|
||||||
-export([scheme/1]).
|
-export([scheme/1]).
|
||||||
-export([host/1]).
|
-export([host/1]).
|
||||||
-export([host_info/1]).
|
-export([host_info/1]).
|
||||||
|
@ -151,6 +153,14 @@ version(#{version := Version}) ->
|
||||||
peer(#{peer := Peer}) ->
|
peer(#{peer := Peer}) ->
|
||||||
Peer.
|
Peer.
|
||||||
|
|
||||||
|
-spec sock(req()) -> {inet:ip_address(), inet:port_number()}.
|
||||||
|
sock(#{sock := Sock}) ->
|
||||||
|
Sock.
|
||||||
|
|
||||||
|
-spec cert(req()) -> binary() | undefined.
|
||||||
|
cert(#{cert := Cert}) ->
|
||||||
|
Cert.
|
||||||
|
|
||||||
-spec scheme(req()) -> binary().
|
-spec scheme(req()) -> binary().
|
||||||
scheme(#{scheme := Scheme}) ->
|
scheme(#{scheme := Scheme}) ->
|
||||||
Scheme.
|
Scheme.
|
||||||
|
|
|
@ -110,6 +110,7 @@ gun_open(Config, Opts) ->
|
||||||
{ok, ConnPid} = gun:open("localhost", config(port, Config), Opts#{
|
{ok, ConnPid} = gun:open("localhost", config(port, Config), Opts#{
|
||||||
retry => 0,
|
retry => 0,
|
||||||
transport => config(type, Config),
|
transport => config(type, Config),
|
||||||
|
transport_opts => proplists:get_value(transport_opts, Config, []),
|
||||||
protocols => [config(protocol, Config)]
|
protocols => [config(protocol, Config)]
|
||||||
}),
|
}),
|
||||||
ConnPid.
|
ConnPid.
|
||||||
|
|
|
@ -134,6 +134,30 @@ bindings(Config) ->
|
||||||
<<"#{key => <<\"bindings\">>}">> = do_get_body("/bindings", Config),
|
<<"#{key => <<\"bindings\">>}">> = do_get_body("/bindings", Config),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
cert(Config) ->
|
||||||
|
case config(type, Config) of
|
||||||
|
tcp -> doc("TLS certificates can only be provided over TLS.");
|
||||||
|
ssl -> do_cert(Config)
|
||||||
|
end.
|
||||||
|
|
||||||
|
do_cert(Config0) ->
|
||||||
|
doc("A client TLS certificate was provided."),
|
||||||
|
{CaCert, Cert, Key} = ct_helper:make_certs(),
|
||||||
|
Config = [{transport_opts, [
|
||||||
|
{cert, Cert},
|
||||||
|
{key, Key},
|
||||||
|
{cacerts, [CaCert]}
|
||||||
|
]}|Config0],
|
||||||
|
Cert = do_get_body("/cert", Config),
|
||||||
|
Cert = do_get_body("/direct/cert", Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
cert_undefined(Config) ->
|
||||||
|
doc("No client TLS certificate was provided."),
|
||||||
|
<<"undefined">> = do_get_body("/cert", Config),
|
||||||
|
<<"undefined">> = do_get_body("/direct/cert", Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
header(Config) ->
|
header(Config) ->
|
||||||
doc("Request header with/without default."),
|
doc("Request header with/without default."),
|
||||||
<<"value">> = do_get_body("/args/header/defined", [{<<"defined">>, "value"}], Config),
|
<<"value">> = do_get_body("/args/header/defined", [{<<"defined">>, "value"}], Config),
|
||||||
|
@ -274,7 +298,7 @@ path_info(Config) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
peer(Config) ->
|
peer(Config) ->
|
||||||
doc("Request peer."),
|
doc("Remote socket address."),
|
||||||
<<"{{127,0,0,1},", _/bits >> = do_get_body("/peer", Config),
|
<<"{{127,0,0,1},", _/bits >> = do_get_body("/peer", Config),
|
||||||
<<"{{127,0,0,1},", _/bits >> = do_get_body("/direct/peer", Config),
|
<<"{{127,0,0,1},", _/bits >> = do_get_body("/direct/peer", Config),
|
||||||
ok.
|
ok.
|
||||||
|
@ -309,6 +333,12 @@ do_scheme(Path, Config) ->
|
||||||
<<"https">> when Transport =:= ssl -> ok
|
<<"https">> when Transport =:= ssl -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
sock(Config) ->
|
||||||
|
doc("Local socket address."),
|
||||||
|
<<"{{127,0,0,1},", _/bits >> = do_get_body("/sock", Config),
|
||||||
|
<<"{{127,0,0,1},", _/bits >> = do_get_body("/direct/sock", Config),
|
||||||
|
ok.
|
||||||
|
|
||||||
uri(Config) ->
|
uri(Config) ->
|
||||||
doc("Request URI building/modification."),
|
doc("Request URI building/modification."),
|
||||||
Scheme = case config(type, Config) of
|
Scheme = case config(type, Config) of
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue