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>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-11 09:49:31 +00:00
parent 6ae3bcbb3f
commit d8dfcbdd73

View File

@@ -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