From ef58e15547ee171a716eaa768374e2e7e2f7d397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 25 Oct 2017 20:17:21 +0100 Subject: Introduce cowboy_req:sock/1 and cowboy_req:cert/1 To obtain the local socket ip/port and the client TLS certificate, respectively. --- src/cowboy_http2.erl | 62 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 16 deletions(-) (limited to 'src/cowboy_http2.erl') diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index e57f02c..d863d1a 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -15,8 +15,8 @@ -module(cowboy_http2). -export([init/5]). --export([init/7]). -export([init/9]). +-export([init/11]). -export([system_continue/3]). -export([system_terminate/4]). @@ -64,6 +64,12 @@ %% Remote address and port for the connection. 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 %% 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. init(Parent, Ref, Socket, Transport, Opts) -> - case Transport:peername(Socket) of - {ok, Peer} -> - init(Parent, Ref, Socket, Transport, Opts, Peer, <<>>); - {error, Reason} -> - %% Couldn't read the peer address; connection is gone. - terminate(undefined, {socket_error, Reason, 'An error has occurred on the socket.'}) + Peer0 = Transport:peername(Socket), + 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}} -> + 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. -spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(), - {inet:ip_address(), inet:port_number()}, binary()) -> ok. -init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer) -> + {inet:ip_address(), inet:port_number()}, {inet:ip_address(), inet:port_number()}, + binary() | undefined, binary()) -> ok. +init(Parent, Ref, Socket, Transport, Opts, Peer, Sock, Cert, Buffer) -> 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)}}, preface(State), case Buffer of @@ -145,10 +171,11 @@ init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer) -> %% @todo Add an argument for the request body. -spec init(pid(), ranch:ref(), inet:socket(), module(), cowboy:opts(), - {inet:ip_address(), inet:port_number()}, binary(), map() | undefined, cowboy_req:req()) -> ok. -init(Parent, Ref, Socket, Transport, Opts, Peer, Buffer, _Settings, Req) -> + {inet:ip_address(), inet:port_number()}, {inet:ip_address(), inet:port_number()}, + 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, - transport=Transport, opts=Opts, peer=Peer, + transport=Transport, opts=Opts, peer=Peer, sock=Sock, cert=Cert, parse_state={preface, sequence, preface_timeout(Opts)}}, %% @todo Apply settings. %% 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)'}) end. -stream_req_init(State=#state{ref=Ref, peer=Peer}, StreamID, IsFin, Headers0=#{ - <<":method">> := Method, <<":scheme">> := Scheme, - <<":authority">> := Authority, <<":path">> := PathWithQs}) -> +stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert}, + StreamID, IsFin, Headers0=#{ + <<":method">> := Method, <<":scheme">> := Scheme, + <<":authority">> := Authority, <<":path">> := PathWithQs}) -> Headers = maps:without([<<":method">>, <<":scheme">>, <<":authority">>, <<":path">>], Headers0), BodyLength = case Headers of _ when IsFin =:= fin -> @@ -746,6 +774,8 @@ stream_req_init(State=#state{ref=Ref, peer=Peer}, StreamID, IsFin, Headers0=#{ pid => self(), streamid => StreamID, peer => Peer, + sock => Sock, + cert => Cert, method => Method, scheme => Scheme, host => Host, -- cgit v1.2.3