NewHTTP2Client constructs a connected ClientTransport to addr based on HTTP2 and starts to receive messages on it. Non-nil error returns if construction fails.
(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onClose OnCloseFunc)
| 208 | // and starts to receive messages on it. Non-nil error returns if construction |
| 209 | // fails. |
| 210 | func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onClose OnCloseFunc) (_ ClientTransport, err error) { |
| 211 | scheme := "http" |
| 212 | ctx, cancel := context.WithCancel(ctx) |
| 213 | defer func() { |
| 214 | if err != nil { |
| 215 | cancel() |
| 216 | } |
| 217 | }() |
| 218 | |
| 219 | // gRPC, resolver, balancer etc. can specify arbitrary data in the |
| 220 | // Attributes field of resolver.Address, which is shoved into connectCtx |
| 221 | // and passed to the dialer and credential handshaker. This makes it possible for |
| 222 | // address specific arbitrary data to reach custom dialers and credential handshakers. |
| 223 | connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes}) |
| 224 | |
| 225 | conn, err := dial(connectCtx, opts.Dialer, addr, opts.UserAgent) |
| 226 | if err != nil { |
| 227 | if opts.FailOnNonTempDialError { |
| 228 | return nil, connectionErrorf(isTemporary(err), err, "transport: error while dialing: %v", err) |
| 229 | } |
| 230 | return nil, connectionErrorf(true, err, "transport: Error while dialing: %v", err) |
| 231 | } |
| 232 | |
| 233 | // Any further errors will close the underlying connection |
| 234 | defer func(conn net.Conn) { |
| 235 | if err != nil { |
| 236 | conn.Close() |
| 237 | } |
| 238 | }(conn) |
| 239 | |
| 240 | // The following defer and goroutine monitor the connectCtx for cancellation |
| 241 | // and deadline. On context expiration, the connection is hard closed and |
| 242 | // this function will naturally fail as a result. Otherwise, the defer |
| 243 | // waits for the goroutine to exit to prevent the context from being |
| 244 | // monitored (and to prevent the connection from ever being closed) after |
| 245 | // returning from this function. |
| 246 | ctxMonitorDone := grpcsync.NewEvent() |
| 247 | newClientCtx, newClientDone := context.WithCancel(connectCtx) |
| 248 | defer func() { |
| 249 | newClientDone() // Awaken the goroutine below if connectCtx hasn't expired. |
| 250 | <-ctxMonitorDone.Done() // Wait for the goroutine below to exit. |
| 251 | }() |
| 252 | go func(conn net.Conn) { |
| 253 | defer ctxMonitorDone.Fire() // Signal this goroutine has exited. |
| 254 | <-newClientCtx.Done() // Block until connectCtx expires or the defer above executes. |
| 255 | if err := connectCtx.Err(); err != nil { |
| 256 | // connectCtx expired before exiting the function. Hard close the connection. |
| 257 | if logger.V(logLevel) { |
| 258 | logger.Infof("Aborting due to connect deadline expiring: %v", err) |
| 259 | } |
| 260 | conn.Close() |
| 261 | } |
| 262 | }(conn) |
| 263 | |
| 264 | kp := opts.KeepaliveParams |
| 265 | // Validate keepalive parameters. |
| 266 | if kp.Time == 0 { |
| 267 | kp.Time = defaultClientKeepaliveTime |