diff --git a/clientconn.go b/clientconn.go index de6d41c2384..0d21f2210b6 100644 --- a/clientconn.go +++ b/clientconn.go @@ -146,6 +146,10 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) cc.ctx, cc.cancel = context.WithCancel(context.Background()) + for _, opt := range extraDialOptions { + opt.apply(&cc.dopts) + } + for _, opt := range opts { opt.apply(&cc.dopts) } diff --git a/default_dial_option_server_option_test.go b/default_dial_option_server_option_test.go new file mode 100644 index 00000000000..952b1c4a2b1 --- /dev/null +++ b/default_dial_option_server_option_test.go @@ -0,0 +1,82 @@ +/* + * + * Copyright 2022 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "strings" + "testing" + + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/internal" +) + +func (s) TestAddExtraDialOptions(t *testing.T) { + // Ensure the Dial fails without credentials + if _, err := Dial("fake"); err == nil { + t.Fatalf("Dialing without a credential did not fail") + } else { + if !strings.Contains(err.Error(), "no transport security set") { + t.Fatalf("Dialing failed with unexpected error: %v", err) + } + } + + // Set and check the DialOptions + opts := []DialOption{WithTransportCredentials(insecure.NewCredentials()), WithTransportCredentials(insecure.NewCredentials()), WithTransportCredentials(insecure.NewCredentials())} + internal.AddExtraDialOptions.(func(opt ...DialOption))(opts...) + for i, opt := range opts { + if extraDialOptions[i] != opt { + t.Fatalf("Unexpected extra dial option at index %d: %v != %v", i, extraDialOptions[i], opt) + } + } + + // Ensure the Dial passes with the extra dial options + if cc, err := Dial("fake"); err != nil { + t.Fatalf("Dialing with insecure credential failed: %v", err) + } else { + cc.Close() + } + + internal.ClearExtraDialOptions() + if len(extraDialOptions) != 0 { + t.Fatalf("Unexpected len of extraDialOptions: %d != 0", len(extraDialOptions)) + } +} + +func (s) TestAddExtraServerOptions(t *testing.T) { + const maxRecvSize = 998765 + // Set and check the ServerOptions + opts := []ServerOption{StatsHandler(nil), Creds(insecure.NewCredentials()), MaxRecvMsgSize(maxRecvSize)} + internal.AddExtraServerOptions.(func(opt ...ServerOption))(opts...) + for i, opt := range opts { + if extraServerOptions[i] != opt { + t.Fatalf("Unexpected extra server option at index %d: %v != %v", i, extraServerOptions[i], opt) + } + } + + // Ensure the extra server options applies to new servers + s := NewServer() + if s.opts.maxReceiveMessageSize != maxRecvSize { + t.Fatalf("Unexpected s.opts.maxReceiveMessageSize: %d != %d", s.opts.maxReceiveMessageSize, maxRecvSize) + } + + internal.ClearExtraServerOptions() + if len(extraServerOptions) != 0 { + t.Fatalf("Unexpected len of extraServerOptions: %d != 0", len(extraServerOptions)) + } +} diff --git a/dialoptions.go b/dialoptions.go index f2f605a17c4..4cccebe8fcc 100644 --- a/dialoptions.go +++ b/dialoptions.go @@ -35,6 +35,15 @@ import ( "google.golang.org/grpc/stats" ) +func init() { + internal.AddExtraDialOptions = func(opt ...DialOption) { + extraDialOptions = append(extraDialOptions, opt...) + } + internal.ClearExtraDialOptions = func() { + extraDialOptions = nil + } +} + // dialOptions configure a Dial call. dialOptions are set by the DialOption // values passed to Dial. type dialOptions struct { @@ -70,6 +79,8 @@ type DialOption interface { apply(*dialOptions) } +var extraDialOptions []DialOption + // EmptyDialOption does not alter the dial configuration. It can be embedded in // another structure to build custom dial options. // diff --git a/internal/internal.go b/internal/internal.go index 0f451224817..59352cc958e 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -63,6 +63,20 @@ var ( // xDS-enabled server invokes this method on a grpc.Server when a particular // listener moves to "not-serving" mode. DrainServerTransports interface{} // func(*grpc.Server, string) + // AddExtraServerOptions adds an array of ServerOption that will be + // effective globally for newly created servers. The priority will be: 1. + // user-provided; 2. this method; 3. default values. + AddExtraServerOptions interface{} // func(opt ...ServerOption) + // ClearExtraServerOptions clears the array of extra ServerOption. This + // method is useful in testing and benchmarking. + ClearExtraServerOptions func() + // AddExtraDialOptions adds an array of DialOption that will be effective + // globally for newly created client channels. The priority will be: 1. + // user-provided; 2. this method; 3. default values. + AddExtraDialOptions interface{} // func(opt ...DialOption) + // ClearExtraDialOptions clears the array of extra DialOption. This + // method is useful in testing and benchmarking. + ClearExtraDialOptions func() // NewXDSResolverWithConfigForTesting creates a new xds resolver builder using // the provided xds bootstrap config instead of the global configuration from diff --git a/server.go b/server.go index 65de84b3007..7dc6a7b2477 100644 --- a/server.go +++ b/server.go @@ -73,6 +73,12 @@ func init() { internal.DrainServerTransports = func(srv *Server, addr string) { srv.drainServerTransports(addr) } + internal.AddExtraServerOptions = func(opt ...ServerOption) { + extraServerOptions = opt + } + internal.ClearExtraServerOptions = func() { + extraServerOptions = nil + } } var statusOK = status.New(codes.OK, "") @@ -174,6 +180,7 @@ var defaultServerOptions = serverOptions{ writeBufferSize: defaultWriteBufSize, readBufferSize: defaultReadBufSize, } +var extraServerOptions []ServerOption // A ServerOption sets options such as credentials, codec and keepalive parameters, etc. type ServerOption interface { @@ -560,6 +567,9 @@ func (s *Server) stopServerWorkers() { // started to accept requests yet. func NewServer(opt ...ServerOption) *Server { opts := defaultServerOptions + for _, o := range extraServerOptions { + o.apply(&opts) + } for _, o := range opt { o.apply(&opts) }