fix(remote): close failed handshakes cleanly
- commit
- ab88dd512968ea2179e8671e45c6033720a223dd
- author
- brice · · 2026-08-08 18:46 UTC
- parent
- 517c29e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
5 files · +34 -15
M internal/ports/wire_test.go +1 -1
| @@ -167,7 +167,7 @@ func TestHelloEnvironmentCodec(t *testing.T) { | |||
| 167 | 167 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | |
| 168 | 168 | withCount := func(count byte) []byte { | |
| 169 | 169 | payload := append([]byte(nil), base...) | |
| 170 | - | payload[len(payload)-3] = count | |
| 170 | + | payload[len(payload)-5] = count | |
| 171 | 171 | return payload | |
| 172 | 172 | } | |
| 173 | 173 | tests := []struct { | |
| @@ -167,7 +167,7 @@ func TestHelloEnvironmentCodec(t *testing.T) { | |||||
| 167 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | 167 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | ||
| 168 | withCount := func(count byte) []byte { | 168 | withCount := func(count byte) []byte { | ||
| 169 | payload := append([]byte(nil), base...) | 169 | payload := append([]byte(nil), base...) | ||
| 170 | - | payload[len(payload)-3] = count | 170 | + | payload[len(payload)-5] = count |
| 171 | return payload | 171 | return payload | ||
| 172 | } | 172 | } | ||
| 173 | tests := []struct { | 173 | tests := []struct { | ||
M internal/usecase/client/client.go +6 -2
| @@ -489,10 +489,14 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||
| 489 | 489 | returnNavigationPending = true | |
| 490 | 490 | dialer = route.dialer | |
| 491 | 491 | attemptRequest = route.request | |
| 492 | - | attemptRequest.Intent = ports.IntentResume | |
| 492 | + | if route.resumeToken != 0 { | |
| 493 | + | attemptRequest.Intent = ports.IntentResume | |
| 494 | + | } else { | |
| 495 | + | attemptRequest.Intent = ports.IntentAttach | |
| 496 | + | } | |
| 493 | 497 | attemptRequest.StartupOverlay = ports.StartupOverlayNone | |
| 494 | 498 | resumeToken = route.resumeToken | |
| 495 | - | returnResumeFallback = true | |
| 499 | + | returnResumeFallback = route.resumeToken != 0 | |
| 496 | 500 | remote = syncReconnectRemote(reconnect, attemptRequest.Remote || r.remote) | |
| 497 | 501 | backoff = defaultReconnectBackoff.initial | |
| 498 | 502 | continue | |
| @@ -489,10 +489,14 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||||
| 489 | returnNavigationPending = true | 489 | returnNavigationPending = true | ||
| 490 | dialer = route.dialer | 490 | dialer = route.dialer | ||
| 491 | attemptRequest = route.request | 491 | attemptRequest = route.request | ||
| 492 | - | attemptRequest.Intent = ports.IntentResume | 492 | + | if route.resumeToken != 0 { |
| 493 | + | attemptRequest.Intent = ports.IntentResume | |||
| 494 | + | } else { | |||
| 495 | + | attemptRequest.Intent = ports.IntentAttach | |||
| 496 | + | } | |||
| 493 | attemptRequest.StartupOverlay = ports.StartupOverlayNone | 497 | attemptRequest.StartupOverlay = ports.StartupOverlayNone | ||
| 494 | resumeToken = route.resumeToken | 498 | resumeToken = route.resumeToken | ||
| 495 | - | returnResumeFallback = true | 499 | + | returnResumeFallback = route.resumeToken != 0 |
| 496 | remote = syncReconnectRemote(reconnect, attemptRequest.Remote || r.remote) | 500 | remote = syncReconnectRemote(reconnect, attemptRequest.Remote || r.remote) | ||
| 497 | backoff = defaultReconnectBackoff.initial | 501 | backoff = defaultReconnectBackoff.initial | ||
| 498 | continue | 502 | continue | ||
M internal/usecase/daemon/client_transport_errors.go +18 -9
| @@ -5,6 +5,14 @@ import "github.com/bnema/vev/internal/ports" | |||
| 5 | 5 | // clientGone detaches ac if it is still the session's current client. The | |
| 6 | 6 | // session remains registered and headless after the client is gone. | |
| 7 | 7 | func (d *Daemon) clientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | |
| 8 | + | d.clientGoneWithNotice(sess, ac, failed, explicit, true) | |
| 9 | + | } | |
| 10 | + | ||
| 11 | + | func (d *Daemon) clientGoneWithoutNotice(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | |
| 12 | + | d.clientGoneWithNotice(sess, ac, failed, explicit, false) | |
| 13 | + | } | |
| 14 | + | ||
| 15 | + | func (d *Daemon) clientGoneWithNotice(sess *session, ac *attachedClient, failed ports.Transport, explicit, notice bool) { | |
| 8 | 16 | if sess == nil || ac == nil { | |
| 9 | 17 | return | |
| 10 | 18 | } | |
| @@ -28,7 +36,7 @@ func (d *Daemon) clientGone(sess *session, ac *attachedClient, failed ports.Tran | |||
| 28 | 36 | d.clearParkingInFlightIfAbandoned(sess, ac, parkingToken) | |
| 29 | 37 | return // displaced, or the link was rebound after the precheck | |
| 30 | 38 | } | |
| 31 | - | d.finishClientGone(sess, ac, failed, explicit) | |
| 39 | + | d.finishClientGone(sess, ac, failed, explicit, notice) | |
| 32 | 40 | } | |
| 33 | 41 | ||
| 34 | 42 | func (d *Daemon) clientGoneForAttachment(token attachmentConnectionToken, explicit bool) bool { | |
| @@ -49,11 +57,11 @@ func (d *Daemon) clientGoneForAttachment(token attachmentConnectionToken, explic | |||
| 49 | 57 | d.clearParkingInFlightIfAbandoned(sess, token.ac, parkingToken) | |
| 50 | 58 | return false | |
| 51 | 59 | } | |
| 52 | - | d.finishClientGone(sess, token.ac, token.transport.transport, explicit) | |
| 60 | + | d.finishClientGone(sess, token.ac, token.transport.transport, explicit, true) | |
| 53 | 61 | return true | |
| 54 | 62 | } | |
| 55 | 63 | ||
| 56 | - | func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | |
| 64 | + | func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit, notice bool) { | |
| 57 | 65 | if sess == nil || ac == nil { | |
| 58 | 66 | return | |
| 59 | 67 | } | |
| @@ -66,18 +74,19 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||
| 66 | 74 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | |
| 67 | 75 | sess.mu.Lock() | |
| 68 | 76 | ephemeral := sess.ephemeral | |
| 77 | + | name := sess.name | |
| 69 | 78 | sess.mu.Unlock() | |
| 70 | 79 | if !ephemeral { | |
| 71 | 80 | d.refreshSessionCwd(sess) | |
| 72 | 81 | } | |
| 73 | - | d.log.Info("client detach begin", "session", sess.name, "explicit", explicit, "ephemeral", ephemeral) | |
| 82 | + | d.log.Info("client detach begin", "session", name, "explicit", explicit, "ephemeral", ephemeral) | |
| 74 | 83 | oldTr := failed | |
| 75 | 84 | if oldTr == nil { | |
| 76 | 85 | oldTr = ac.transport() | |
| 77 | 86 | } | |
| 78 | 87 | if !explicit && d.parkAttachment(sess, ac) { | |
| 79 | 88 | _ = ac.closeCapturedTransport(oldTr) | |
| 80 | - | d.log.Info("client parked", "session", sess.name) | |
| 89 | + | d.log.Info("client parked", "session", sess.nameSnapshot()) | |
| 81 | 90 | return | |
| 82 | 91 | } | |
| 83 | 92 | // Explicit winners (and non-explicit park failures) must drop any same- | |
| @@ -88,7 +97,7 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||
| 88 | 97 | ||
| 89 | 98 | d.resetScreenDefaultColors(sess) | |
| 90 | 99 | ac.clearPreviousSession() | |
| 91 | - | if explicit { | |
| 100 | + | if explicit && notice { | |
| 92 | 101 | // Synchronous so the ack is delivered before the transport closes | |
| 93 | 102 | // (the client is actively awaiting it), but deadline-bounded so a | |
| 94 | 103 | // wedged client cannot pin this conn handler and hang Serve's | |
| @@ -96,7 +105,7 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||
| 96 | 105 | d.boundedSend(ac, frameDetached(ports.ReasonDetach)) | |
| 97 | 106 | } | |
| 98 | 107 | _ = ac.closeCapturedTransport(oldTr) | |
| 99 | - | d.log.Info("client detached", "session", sess.name, "explicit", explicit) | |
| 108 | + | d.log.Info("client detached", "session", sess.nameSnapshot(), "explicit", explicit) | |
| 100 | 109 | } | |
| 101 | 110 | ||
| 102 | 111 | // detachOnSendError drops a client whose transport failed, leaving the session | |
| @@ -167,7 +176,7 @@ func (d *Daemon) finishSendErrorDetach(sess *session, ac *attachedClient, failed | |||
| 167 | 176 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | |
| 168 | 177 | if d.parkAttachment(sess, ac) { | |
| 169 | 178 | _ = ac.closeCapturedTransport(failed) | |
| 170 | - | d.log.Warn("parked client after send error", "session", sess.name) | |
| 179 | + | d.log.Warn("parked client after send error", "session", sess.nameSnapshot()) | |
| 171 | 180 | return | |
| 172 | 181 | } | |
| 173 | 182 | d.clearParkingInFlight(d.resumeTokenSnapshot(ac), ac) | |
| @@ -175,5 +184,5 @@ func (d *Daemon) finishSendErrorDetach(sess *session, ac *attachedClient, failed | |||
| 175 | 184 | d.resetScreenDefaultColors(sess) | |
| 176 | 185 | ac.clearPreviousSession() | |
| 177 | 186 | _ = ac.closeCapturedTransport(failed) | |
| 178 | - | d.log.Warn("detached client after send error", "session", sess.name) | |
| 187 | + | d.log.Warn("detached client after send error", "session", sess.nameSnapshot()) | |
| 179 | 188 | } | |
| @@ -5,6 +5,14 @@ import "github.com/bnema/vev/internal/ports" | |||||
| 5 | // clientGone detaches ac if it is still the session's current client. The | 5 | // clientGone detaches ac if it is still the session's current client. The | ||
| 6 | // session remains registered and headless after the client is gone. | 6 | // session remains registered and headless after the client is gone. | ||
| 7 | func (d *Daemon) clientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | 7 | func (d *Daemon) clientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | ||
| 8 | + | d.clientGoneWithNotice(sess, ac, failed, explicit, true) | |||
| 9 | + | } | |||
| 10 | + | ||||
| 11 | + | func (d *Daemon) clientGoneWithoutNotice(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | |||
| 12 | + | d.clientGoneWithNotice(sess, ac, failed, explicit, false) | |||
| 13 | + | } | |||
| 14 | + | ||||
| 15 | + | func (d *Daemon) clientGoneWithNotice(sess *session, ac *attachedClient, failed ports.Transport, explicit, notice bool) { | |||
| 8 | if sess == nil || ac == nil { | 16 | if sess == nil || ac == nil { | ||
| 9 | return | 17 | return | ||
| 10 | } | 18 | } | ||
| @@ -28,7 +36,7 @@ func (d *Daemon) clientGone(sess *session, ac *attachedClient, failed ports.Tran | |||||
| 28 | d.clearParkingInFlightIfAbandoned(sess, ac, parkingToken) | 36 | d.clearParkingInFlightIfAbandoned(sess, ac, parkingToken) | ||
| 29 | return // displaced, or the link was rebound after the precheck | 37 | return // displaced, or the link was rebound after the precheck | ||
| 30 | } | 38 | } | ||
| 31 | - | d.finishClientGone(sess, ac, failed, explicit) | 39 | + | d.finishClientGone(sess, ac, failed, explicit, notice) |
| 32 | } | 40 | } | ||
| 33 | 41 | ||||
| 34 | func (d *Daemon) clientGoneForAttachment(token attachmentConnectionToken, explicit bool) bool { | 42 | func (d *Daemon) clientGoneForAttachment(token attachmentConnectionToken, explicit bool) bool { | ||
| @@ -49,11 +57,11 @@ func (d *Daemon) clientGoneForAttachment(token attachmentConnectionToken, explic | |||||
| 49 | d.clearParkingInFlightIfAbandoned(sess, token.ac, parkingToken) | 57 | d.clearParkingInFlightIfAbandoned(sess, token.ac, parkingToken) | ||
| 50 | return false | 58 | return false | ||
| 51 | } | 59 | } | ||
| 52 | - | d.finishClientGone(sess, token.ac, token.transport.transport, explicit) | 60 | + | d.finishClientGone(sess, token.ac, token.transport.transport, explicit, true) |
| 53 | return true | 61 | return true | ||
| 54 | } | 62 | } | ||
| 55 | 63 | ||||
| 56 | - | func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit bool) { | 64 | + | func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed ports.Transport, explicit, notice bool) { |
| 57 | if sess == nil || ac == nil { | 65 | if sess == nil || ac == nil { | ||
| 58 | return | 66 | return | ||
| 59 | } | 67 | } | ||
| @@ -66,18 +74,19 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||||
| 66 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | 74 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | ||
| 67 | sess.mu.Lock() | 75 | sess.mu.Lock() | ||
| 68 | ephemeral := sess.ephemeral | 76 | ephemeral := sess.ephemeral | ||
| 77 | + | name := sess.name | |||
| 69 | sess.mu.Unlock() | 78 | sess.mu.Unlock() | ||
| 70 | if !ephemeral { | 79 | if !ephemeral { | ||
| 71 | d.refreshSessionCwd(sess) | 80 | d.refreshSessionCwd(sess) | ||
| 72 | } | 81 | } | ||
| 73 | - | d.log.Info("client detach begin", "session", sess.name, "explicit", explicit, "ephemeral", ephemeral) | 82 | + | d.log.Info("client detach begin", "session", name, "explicit", explicit, "ephemeral", ephemeral) |
| 74 | oldTr := failed | 83 | oldTr := failed | ||
| 75 | if oldTr == nil { | 84 | if oldTr == nil { | ||
| 76 | oldTr = ac.transport() | 85 | oldTr = ac.transport() | ||
| 77 | } | 86 | } | ||
| 78 | if !explicit && d.parkAttachment(sess, ac) { | 87 | if !explicit && d.parkAttachment(sess, ac) { | ||
| 79 | _ = ac.closeCapturedTransport(oldTr) | 88 | _ = ac.closeCapturedTransport(oldTr) | ||
| 80 | - | d.log.Info("client parked", "session", sess.name) | 89 | + | d.log.Info("client parked", "session", sess.nameSnapshot()) |
| 81 | return | 90 | return | ||
| 82 | } | 91 | } | ||
| 83 | // Explicit winners (and non-explicit park failures) must drop any same- | 92 | // Explicit winners (and non-explicit park failures) must drop any same- | ||
| @@ -88,7 +97,7 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||||
| 88 | 97 | ||||
| 89 | d.resetScreenDefaultColors(sess) | 98 | d.resetScreenDefaultColors(sess) | ||
| 90 | ac.clearPreviousSession() | 99 | ac.clearPreviousSession() | ||
| 91 | - | if explicit { | 100 | + | if explicit && notice { |
| 92 | // Synchronous so the ack is delivered before the transport closes | 101 | // Synchronous so the ack is delivered before the transport closes | ||
| 93 | // (the client is actively awaiting it), but deadline-bounded so a | 102 | // (the client is actively awaiting it), but deadline-bounded so a | ||
| 94 | // wedged client cannot pin this conn handler and hang Serve's | 103 | // wedged client cannot pin this conn handler and hang Serve's | ||
| @@ -96,7 +105,7 @@ func (d *Daemon) finishClientGone(sess *session, ac *attachedClient, failed port | |||||
| 96 | d.boundedSend(ac, frameDetached(ports.ReasonDetach)) | 105 | d.boundedSend(ac, frameDetached(ports.ReasonDetach)) | ||
| 97 | } | 106 | } | ||
| 98 | _ = ac.closeCapturedTransport(oldTr) | 107 | _ = ac.closeCapturedTransport(oldTr) | ||
| 99 | - | d.log.Info("client detached", "session", sess.name, "explicit", explicit) | 108 | + | d.log.Info("client detached", "session", sess.nameSnapshot(), "explicit", explicit) |
| 100 | } | 109 | } | ||
| 101 | 110 | ||||
| 102 | // detachOnSendError drops a client whose transport failed, leaving the session | 111 | // detachOnSendError drops a client whose transport failed, leaving the session | ||
| @@ -167,7 +176,7 @@ func (d *Daemon) finishSendErrorDetach(sess *session, ac *attachedClient, failed | |||||
| 167 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | 176 | d.recalculateSessionGeometryAndInvalidate(sess, nil, "client_transport_errors.go") | ||
| 168 | if d.parkAttachment(sess, ac) { | 177 | if d.parkAttachment(sess, ac) { | ||
| 169 | _ = ac.closeCapturedTransport(failed) | 178 | _ = ac.closeCapturedTransport(failed) | ||
| 170 | - | d.log.Warn("parked client after send error", "session", sess.name) | 179 | + | d.log.Warn("parked client after send error", "session", sess.nameSnapshot()) |
| 171 | return | 180 | return | ||
| 172 | } | 181 | } | ||
| 173 | d.clearParkingInFlight(d.resumeTokenSnapshot(ac), ac) | 182 | d.clearParkingInFlight(d.resumeTokenSnapshot(ac), ac) | ||
| @@ -175,5 +184,5 @@ func (d *Daemon) finishSendErrorDetach(sess *session, ac *attachedClient, failed | |||||
| 175 | d.resetScreenDefaultColors(sess) | 184 | d.resetScreenDefaultColors(sess) | ||
| 176 | ac.clearPreviousSession() | 185 | ac.clearPreviousSession() | ||
| 177 | _ = ac.closeCapturedTransport(failed) | 186 | _ = ac.closeCapturedTransport(failed) | ||
| 178 | - | d.log.Warn("detached client after send error", "session", sess.name) | 187 | + | d.log.Warn("detached client after send error", "session", sess.nameSnapshot()) |
| 179 | } | 188 | } | ||
M internal/usecase/daemon/daemon.go +3 -1
| @@ -1147,8 +1147,9 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||
| 1147 | 1147 | return | |
| 1148 | 1148 | } | |
| 1149 | 1149 | ||
| 1150 | + | welcomeSent := false | |
| 1150 | 1151 | failAttachment := func() { | |
| 1151 | - | d.failHandshakeAttachment(sess, ac, tr) | |
| 1152 | + | d.failHandshakeAttachment(sess, ac, tr, welcomeSent) | |
| 1152 | 1153 | } | |
| 1153 | 1154 | if err := handshakeCtx.Err(); err != nil { | |
| 1154 | 1155 | failAttachment() | |
| @@ -1176,6 +1177,7 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||
| 1176 | 1177 | failAttachment() | |
| 1177 | 1178 | return | |
| 1178 | 1179 | } | |
| 1180 | + | welcomeSent = true | |
| 1179 | 1181 | // Release Welcome's effect before discovering post-handshake authority so a | |
| 1180 | 1182 | // replacement blocked behind the send can publish its generation and lease. | |
| 1181 | 1183 | welcomeTicket.End() | |
| @@ -1147,8 +1147,9 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||||
| 1147 | return | 1147 | return | ||
| 1148 | } | 1148 | } | ||
| 1149 | 1149 | ||||
| 1150 | + | welcomeSent := false | |||
| 1150 | failAttachment := func() { | 1151 | failAttachment := func() { | ||
| 1151 | - | d.failHandshakeAttachment(sess, ac, tr) | 1152 | + | d.failHandshakeAttachment(sess, ac, tr, welcomeSent) |
| 1152 | } | 1153 | } | ||
| 1153 | if err := handshakeCtx.Err(); err != nil { | 1154 | if err := handshakeCtx.Err(); err != nil { | ||
| 1154 | failAttachment() | 1155 | failAttachment() | ||
| @@ -1176,6 +1177,7 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||||
| 1176 | failAttachment() | 1177 | failAttachment() | ||
| 1177 | return | 1178 | return | ||
| 1178 | } | 1179 | } | ||
| 1180 | + | welcomeSent = true | |||
| 1179 | // Release Welcome's effect before discovering post-handshake authority so a | 1181 | // Release Welcome's effect before discovering post-handshake authority so a | ||
| 1180 | // replacement blocked behind the send can publish its generation and lease. | 1182 | // replacement blocked behind the send can publish its generation and lease. | ||
| 1181 | welcomeTicket.End() | 1183 | welcomeTicket.End() | ||
M internal/usecase/daemon/handshake.go +6 -2
| @@ -114,7 +114,7 @@ func handshakeContextError(parent context.Context, timedOut <-chan struct{}, fal | |||
| 114 | 114 | // failHandshakeAttachment synchronously retires the exact connection admitted | |
| 115 | 115 | // by route. A fresh route-owned session is purged only after its attachment is | |
| 116 | 116 | // removed; existing sessions and their unrelated attachments are preserved. | |
| 117 | - | func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr ports.Transport) { | |
| 117 | + | func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr ports.Transport, welcomed bool) { | |
| 118 | 118 | if sess == nil || ac == nil { | |
| 119 | 119 | if tr != nil { | |
| 120 | 120 | _ = tr.Close() | |
| @@ -130,7 +130,11 @@ func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr p | |||
| 130 | 130 | } | |
| 131 | 131 | return | |
| 132 | 132 | } | |
| 133 | - | d.clientGone(sess, ac, tr, true) | |
| 133 | + | if welcomed { | |
| 134 | + | d.clientGone(sess, ac, tr, true) | |
| 135 | + | } else { | |
| 136 | + | d.clientGoneWithoutNotice(sess, ac, tr, true) | |
| 137 | + | } | |
| 134 | 138 | if tr != nil { | |
| 135 | 139 | _ = tr.Close() | |
| 136 | 140 | } | |
| @@ -114,7 +114,7 @@ func handshakeContextError(parent context.Context, timedOut <-chan struct{}, fal | |||||
| 114 | // failHandshakeAttachment synchronously retires the exact connection admitted | 114 | // failHandshakeAttachment synchronously retires the exact connection admitted | ||
| 115 | // by route. A fresh route-owned session is purged only after its attachment is | 115 | // by route. A fresh route-owned session is purged only after its attachment is | ||
| 116 | // removed; existing sessions and their unrelated attachments are preserved. | 116 | // removed; existing sessions and their unrelated attachments are preserved. | ||
| 117 | - | func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr ports.Transport) { | 117 | + | func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr ports.Transport, welcomed bool) { |
| 118 | if sess == nil || ac == nil { | 118 | if sess == nil || ac == nil { | ||
| 119 | if tr != nil { | 119 | if tr != nil { | ||
| 120 | _ = tr.Close() | 120 | _ = tr.Close() | ||
| @@ -130,7 +130,11 @@ func (d *Daemon) failHandshakeAttachment(sess *session, ac *attachedClient, tr p | |||||
| 130 | } | 130 | } | ||
| 131 | return | 131 | return | ||
| 132 | } | 132 | } | ||
| 133 | - | d.clientGone(sess, ac, tr, true) | 133 | + | if welcomed { |
| 134 | + | d.clientGone(sess, ac, tr, true) | |||
| 135 | + | } else { | |||
| 136 | + | d.clientGoneWithoutNotice(sess, ac, tr, true) | |||
| 137 | + | } | |||
| 134 | if tr != nil { | 138 | if tr != nil { | ||
| 135 | _ = tr.Close() | 139 | _ = tr.Close() | ||
| 136 | } | 140 | } | ||