From d8dfcbdd7304945d61d7fd14daa5eada6998661d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 09:49:31 +0000 Subject: [PATCH] Fix Vision by processing rawInput before input when switching to direct mode Per RPRX's feedback, the original code logic was correct, but the processing order matters. When switching to direct copy mode, rawInput (encrypted TLS records from the outer layer) should be processed and merged into the buffer before input (decrypted application data). This ensures proper ordering of data when transitioning from Vision-padded mode to direct copy mode, preventing SSL errors especially with testpre connections. Fixes #4878 Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> --- proxy/proxy.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 2022bc37..182cd1a1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -256,26 +256,18 @@ 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() { - buffer, _ = buf.MergeMulti(buffer, inputBuffer) - } - } + // Process rawInput first, then input, then current buffer if w.rawInput != nil { if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() { buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) } } + if w.input != nil { + if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, inputBuffer) + } + } if w.input != nil { *w.input = bytes.Reader{} // release memory w.input = nil