@@ -29,6 +29,19 @@ To use a Server, create it, and then connect to it with no security:
2929 client, err := bigtable.NewClient(ctx, proj, instance,
3030 option.WithGRPCConn(conn))
3131 ...
32+
33+ To use a Server with an in-memory connection, provide a bufconn listener:
34+
35+ l := bufconn.Listen(1024 * 1024)
36+ srv, err := bttest.NewServerWithListener(l)
37+ ...
38+ conn, err := grpc.Dial(
39+ "bufnet",
40+ grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
41+ return l.Dial()
42+ }),
43+ grpc.WithTransportCredentials(insecure.NewCredentials()))
44+ ...
3245*/
3346package bttest // import "cloud.google.com/go/bigtable/bttest"
3447
@@ -85,9 +98,10 @@ var validLabelTransformer = regexp.MustCompile(`[a-z0-9\-]{1,15}`)
8598type Server struct {
8699 Addr string
87100
88- l net.Listener
89- srv * grpc.Server
90- s * server
101+ l net.Listener
102+ srv * grpc.Server
103+ s * server
104+ ownsListener bool
91105}
92106
93107// server is the real implementation of the fake.
@@ -105,6 +119,15 @@ type server struct {
105119 btpb.BigtableServer
106120}
107121
122+ // noopCloserListener wraps a net.Listener, but its Close method is a no-op.
123+ // This is used to prevent the gRPC server from closing a listener that was
124+ // provided by a user.
125+ type noopCloserListener struct {
126+ net.Listener
127+ }
128+
129+ func (n * noopCloserListener ) Close () error { return nil }
130+
108131// NewServer creates a new Server.
109132// The Server will be listening for gRPC connections, without TLS,
110133// on the provided address. The resolved address is named by the Addr field.
@@ -121,7 +144,10 @@ func NewServer(laddr string, opt ...grpc.ServerOption) (*Server, error) {
121144 if err != nil {
122145 return nil , err
123146 }
147+ return newServer (l , true , opt ... ), nil
148+ }
124149
150+ func newServer (l net.Listener , ownsListener bool , opt ... grpc.ServerOption ) * Server {
125151 s := & Server {
126152 Addr : l .Addr ().String (),
127153 l : l ,
@@ -130,14 +156,29 @@ func NewServer(laddr string, opt ...grpc.ServerOption) (*Server, error) {
130156 tables : make (map [string ]* table ),
131157 instances : make (map [string ]* btapb.Instance ),
132158 },
159+ ownsListener : ownsListener ,
133160 }
134161 btapb .RegisterBigtableInstanceAdminServer (s .srv , s .s )
135162 btapb .RegisterBigtableTableAdminServer (s .srv , s .s )
136163 btpb .RegisterBigtableServer (s .srv , s .s )
137164
138- go s .srv .Serve (s .l )
165+ listenerForGRPC := l
166+ if ! ownsListener {
167+ // If the user owns the listener, wrap it so srv.Stop() doesn't close it.
168+ listenerForGRPC = & noopCloserListener {Listener : l }
169+ }
170+ go s .srv .Serve (listenerForGRPC )
171+ return s
172+ }
139173
140- return s , nil
174+ // NewServerWithListener creates a new Server using the provided listener.
175+ // The Addr field of the returned Server will be the listener's address.
176+ //
177+ // The caller is responsible for closing the listener. The server's Close method
178+ // will not close the provided listener, nor will it clean up any underlying
179+ // resources like unix socket files.
180+ func NewServerWithListener (l net.Listener , opt ... grpc.ServerOption ) (* Server , error ) {
181+ return newServer (l , false , opt ... ), nil
141182}
142183
143184// Close shuts down the server.
@@ -149,10 +190,12 @@ func (s *Server) Close() {
149190 s .s .mu .Unlock ()
150191
151192 s .srv .Stop ()
152- s .l .Close ()
193+ if s .ownsListener {
194+ s .l .Close ()
195+ }
153196
154197 // clean up unix socket
155- if strings .Contains (s .Addr , "/" ) {
198+ if s . ownsListener && strings .Contains (s .Addr , "/" ) {
156199 _ = os .Remove (s .Addr )
157200 }
158201}
0 commit comments