src/crypto/tls/conn.go | 11 +++++++++++ diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 969f357834c0a5c1733b7b939248ebf07d514510..d0a2550cbc6ff90a9b6f43147b402e35e102012f 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -32,6 +32,7 @@ handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake // handshakeStatus is 1 if the connection is currently transferring // application data (i.e. is not currently processing a handshake). + // handshakeStatus == 1 implies handshakeErr == nil. // This field is only to be accessed with sync/atomic. handshakeStatus uint32 // constant after handshake; protected by handshakeMutex @@ -1396,6 +1397,13 @@ return c.handshakeContext(ctx) } func (c *Conn) handshakeContext(ctx context.Context) (ret error) { + // Fast sync/atomic-based exit if there is no handshake in flight and the + // last one succeeded without an error. Avoids the expensive context setup + // and mutex for most Read and Write calls. + if c.handshakeComplete() { + return nil + } + handshakeCtx, cancel := context.WithCancel(ctx) // Note: defer this before starting the "interrupter" goroutine // so that we can tell the difference between the input being canceled and @@ -1453,6 +1461,9 @@ } if c.handshakeErr == nil && !c.handshakeComplete() { c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") + } + if c.handshakeErr != nil && c.handshakeComplete() { + panic("tls: internal error: handshake returned an error but is marked successful") } return c.handshakeErr