From 84bee50bda70652c72302c98513df7c7dd0be514 Mon Sep 17 00:00:00 2001 From: Chris Roche Date: Fri, 3 Feb 2017 17:01:38 -0800 Subject: [PATCH 1/3] Add DialOption to overwrite :authority pseudo-header The :authority pseudo-header for a gRPC Client defaults to the host portion of the dialed target and can only be overwritten by providing a TransportCredentials. However, there are cases where setting this header independent of any tranport security is valid. In my particular case, in order to leverage Envoy for request routing, the cluster/service name must be provided in the :authority header. This may also be useful in a testing context. This patch adds a DialOption to overwrite the authority header, even if TransportCredentials are provided (I'd imagine you'd only ever need to specify one or the other). --- clientconn.go | 13 ++++++++++++- clientconn_test.go | 16 ++++++++++++++++ transport/transport.go | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clientconn.go b/clientconn.go index 146166a7..a814048c 100644 --- a/clientconn.go +++ b/clientconn.go @@ -263,6 +263,15 @@ func WithStreamInterceptor(f StreamClientInterceptor) DialOption { } } +// WithAuthority returns a DialOption that specifies the value to be used as +// the :authority pseudo-header. This value overrides the :authority value +// provided by TransportCredentials if present. +func WithAuthority(a string) DialOption { + return func(o *dialOptions) { + o.copts.Authority = a + } +} + // Dial creates a client connection to the given target. func Dial(target string, opts ...DialOption) (*ClientConn, error) { return DialContext(context.Background(), target, opts...) @@ -319,7 +328,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * cc.dopts.bs = DefaultBackoffConfig } creds := cc.dopts.copts.TransportCredentials - if creds != nil && creds.Info().ServerName != "" { + if cc.dopts.copts.Authority != "" { + cc.authority = cc.dopts.copts.Authority + } else if creds != nil && creds.Info().ServerName != "" { cc.authority = creds.Info().ServerName } else { colonPos := strings.LastIndex(target, ":") diff --git a/clientconn_test.go b/clientconn_test.go index 9b759a1f..0cfd521d 100644 --- a/clientconn_test.go +++ b/clientconn_test.go @@ -85,6 +85,22 @@ func TestTLSServerNameOverwrite(t *testing.T) { } } +func TestWithAuthority(t *testing.T) { + overwriteServerName := "over.write.server.name" + creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "transport.security.overwrite") + if err != nil { + t.Fatalf("Failed to create credentials %v", err) + } + conn, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds), WithAuthority(overwriteServerName)) + if err != nil { + t.Fatalf("Dial(_, _) = _, %v, want _, ", err) + } + conn.Close() + if conn.authority != overwriteServerName { + t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName) + } +} + func TestDialContextCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() diff --git a/transport/transport.go b/transport/transport.go index d4659918..18d15f7e 100644 --- a/transport/transport.go +++ b/transport/transport.go @@ -374,6 +374,8 @@ func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (S type ConnectOptions struct { // UserAgent is the application user agent. UserAgent string + // Authority is the :authority pseudo-header to use. + Authority string // Dialer specifies how to dial a network address. Dialer func(context.Context, string) (net.Conn, error) // FailOnNonTempDialError specifies if gRPC fails on non-temporary dial errors. From c7430a063ee8b2f0a999d4bb200b0ab406829603 Mon Sep 17 00:00:00 2001 From: Chris Roche Date: Thu, 9 Feb 2017 10:44:13 -0800 Subject: [PATCH 2/3] Only override :authority for insecure dials --- clientconn.go | 10 +++++----- clientconn_test.go | 6 +----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/clientconn.go b/clientconn.go index a814048c..459ce0b6 100644 --- a/clientconn.go +++ b/clientconn.go @@ -264,8 +264,8 @@ func WithStreamInterceptor(f StreamClientInterceptor) DialOption { } // WithAuthority returns a DialOption that specifies the value to be used as -// the :authority pseudo-header. This value overrides the :authority value -// provided by TransportCredentials if present. +// the :authority pseudo-header. This value only works with WithInsecure and +// has no effect if TransportCredentials are present. func WithAuthority(a string) DialOption { return func(o *dialOptions) { o.copts.Authority = a @@ -328,10 +328,10 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * cc.dopts.bs = DefaultBackoffConfig } creds := cc.dopts.copts.TransportCredentials - if cc.dopts.copts.Authority != "" { - cc.authority = cc.dopts.copts.Authority - } else if creds != nil && creds.Info().ServerName != "" { + if creds != nil && creds.Info().ServerName != "" { cc.authority = creds.Info().ServerName + } else if cc.dopts.insecure && cc.dopts.copts.Authority != "" { + cc.authority = cc.dopts.copts.Authority } else { colonPos := strings.LastIndex(target, ":") if colonPos == -1 { diff --git a/clientconn_test.go b/clientconn_test.go index 0cfd521d..ee159955 100644 --- a/clientconn_test.go +++ b/clientconn_test.go @@ -87,11 +87,7 @@ func TestTLSServerNameOverwrite(t *testing.T) { func TestWithAuthority(t *testing.T) { overwriteServerName := "over.write.server.name" - creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "transport.security.overwrite") - if err != nil { - t.Fatalf("Failed to create credentials %v", err) - } - conn, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds), WithAuthority(overwriteServerName)) + conn, err := Dial("Non-Existent.Server:80", WithInsecure(), WithAuthority(overwriteServerName)) if err != nil { t.Fatalf("Dial(_, _) = _, %v, want _, ", err) } From 4ad16bc34a278f301153df9f06a506080730dec6 Mon Sep 17 00:00:00 2001 From: Chris Roche Date: Mon, 13 Feb 2017 10:25:06 -0800 Subject: [PATCH 3/3] Authority overwrite only works if TLS is not present --- clientconn_test.go | 16 ++++++++++++++++ transport/transport.go | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clientconn_test.go b/clientconn_test.go index ee159955..93e78a5a 100644 --- a/clientconn_test.go +++ b/clientconn_test.go @@ -97,6 +97,22 @@ func TestWithAuthority(t *testing.T) { } } +func TestWithAuthorityAndTLS(t *testing.T) { + overwriteServerName := "over.write.server.name" + creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", overwriteServerName) + if err != nil { + t.Fatalf("Failed to create credentials %v", err) + } + conn, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds), WithAuthority("no.effect.authority")) + if err != nil { + t.Fatalf("Dial(_, _) = _, %v, want _, ", err) + } + conn.Close() + if conn.authority != overwriteServerName { + t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName) + } +} + func TestDialContextCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() diff --git a/transport/transport.go b/transport/transport.go index 18d15f7e..caee54a8 100644 --- a/transport/transport.go +++ b/transport/transport.go @@ -374,7 +374,8 @@ func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (S type ConnectOptions struct { // UserAgent is the application user agent. UserAgent string - // Authority is the :authority pseudo-header to use. + // Authority is the :authority pseudo-header to use. This field has no effect if + // TransportCredentials is set. Authority string // Dialer specifies how to dial a network address. Dialer func(context.Context, string) (net.Conn, error)