From 8ca8e07fe0c244e3bacfacea128cf784d136eb21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 08:54:18 +0000 Subject: [PATCH] Fix Vision timing issue: ensure complete TLS records before switching to direct copy The issue was in the timing/conditions for switching to direct copy mode. When Vision receives CommandPaddingDirect from the server, it would immediately switch to direct mode even if the current buffer contains incomplete TLS records. This caused SSL protocol errors, especially with testpre where connections may have fragmented data. The fix: Before actually performing the switch to direct copy mode, check if the current buffer contains complete TLS records using IsCompleteRecord(). If records are incomplete, return the buffer and delay the switch until the next read cycle when complete records are available. This ensures Vision only switches to direct mode at safe TLS record boundaries, preventing data corruption. Fixes #4878 Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> --- proxy/proxy.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index 0c0af0f5..02fc329d 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -256,6 +256,15 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } if *switchToDirectCopy { + // Before switching to direct copy, ensure current buffer contains complete TLS records + // This prevents corruption when switching mid-record, especially with testpre connections + if !buffer.IsEmpty() && w.trafficState.IsTLS && !IsCompleteRecord(buffer) { + // Buffer contains incomplete TLS records, cannot safely switch yet + // Return the buffer and the switch will be attempted on the next read + errors.LogDebug(w.ctx, "Vision: incomplete TLS records in buffer, delaying direct copy switch, len=", buffer.Len()) + return buffer, err + } + // XTLS Vision processes TLS-like conn's input and rawInput if w.input != nil { if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() {