Finds a free port to listen on and creates a new RPC invoker that connects to that port
(ctx context.Context, fwd portforwarder.PortForwarder)
| 74 | |
| 75 | // Finds a free port to listen on and creates a new RPC invoker that connects to that port |
| 76 | func connect(ctx context.Context, fwd portforwarder.PortForwarder) (Invoker, error) { |
| 77 | listener, err := listenTCP() |
| 78 | if err != nil { |
| 79 | return nil, err |
| 80 | } |
| 81 | localAddress := listener.Addr().String() |
| 82 | |
| 83 | invoker := &invoker{ |
| 84 | fwd: fwd, |
| 85 | listener: listener, |
| 86 | } |
| 87 | |
| 88 | // Create a cancelable context to be able to cancel background tasks |
| 89 | // if we encounter an error while connecting to the gRPC server |
| 90 | connectctx, cancel := context.WithCancel(context.Background()) |
| 91 | defer func() { |
| 92 | if err != nil { |
| 93 | cancel() |
| 94 | } |
| 95 | }() |
| 96 | |
| 97 | ch := make(chan error, 2) // Buffered channel to ensure we don't block on the goroutine |
| 98 | |
| 99 | // Ensure we close the port forwarder if we encounter an error |
| 100 | // or once the gRPC connection is closed. pfcancel is retained |
| 101 | // to close the PF whenever we close the gRPC connection. |
| 102 | pfctx, pfcancel := context.WithCancel(connectctx) |
| 103 | invoker.cancelPF = pfcancel |
| 104 | |
| 105 | // Tunnel the remote gRPC server port to the local port |
| 106 | go func() { |
| 107 | // Start forwarding the port locally |
| 108 | opts := portforwarder.ForwardPortOpts{ |
| 109 | Port: codespacesInternalPort, |
| 110 | Internal: true, |
| 111 | } |
| 112 | ch <- fwd.ForwardPortToListener(pfctx, opts, listener) |
| 113 | }() |
| 114 | |
| 115 | var conn *grpc.ClientConn |
| 116 | go func() { |
| 117 | // Attempt to connect to the port |
| 118 | opts := []grpc.DialOption{ |
| 119 | grpc.WithTransportCredentials(insecure.NewCredentials()), |
| 120 | } |
| 121 | conn, err = grpc.NewClient(localAddress, opts...) |
| 122 | ch <- err // nil if we successfully connected |
| 123 | }() |
| 124 | |
| 125 | // Wait for the connection to be established or for the context to be cancelled |
| 126 | select { |
| 127 | case <-ctx.Done(): |
| 128 | return nil, ctx.Err() |
| 129 | case err := <-ch: |
| 130 | if err != nil { |
| 131 | return nil, err |
| 132 | } |
| 133 | } |
no test coverage detected