mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 07:14:57 +00:00
Revert "TUN-10621: Propagate max wait timeout"
This reverts merge request !1859
This commit is contained in:
committed by
João "Pisco" Fernandes
parent
86fccede6d
commit
43bfec0bcd
@@ -5,12 +5,8 @@ import (
|
||||
crand "crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -21,7 +17,6 @@ import (
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"github.com/cloudflare/cloudflared/hello"
|
||||
"github.com/cloudflare/cloudflared/stream"
|
||||
"github.com/cloudflare/cloudflared/tlsconfig"
|
||||
cfwebsocket "github.com/cloudflare/cloudflared/websocket"
|
||||
)
|
||||
@@ -132,137 +127,3 @@ func TestWebsocketWrapper(t *testing.T) {
|
||||
require.Equal(t, 2, n)
|
||||
require.Equal(t, "bc", string(buf[:n]))
|
||||
}
|
||||
|
||||
// halfClosePipeEnd is a bidirectional pipe built from two independent io.Pipe
|
||||
// pairs. Unlike net.Pipe, closing one direction does not affect the other, which
|
||||
// lets us simulate a half-close: the local client can signal EOF on its write
|
||||
// side while still reading a delayed response from the remote side.
|
||||
type halfClosePipeEnd struct {
|
||||
r *io.PipeReader // data flowing into this end
|
||||
w *io.PipeWriter // data flowing out of this end
|
||||
}
|
||||
|
||||
func newHalfClosePipe() (local, remote *halfClosePipeEnd) {
|
||||
// Pipe A carries data from local → remote.
|
||||
ar, aw := io.Pipe()
|
||||
// Pipe B carries data from remote → local.
|
||||
br, bw := io.Pipe()
|
||||
|
||||
local = &halfClosePipeEnd{r: br, w: aw}
|
||||
remote = &halfClosePipeEnd{r: ar, w: bw}
|
||||
return local, remote
|
||||
}
|
||||
|
||||
func (p *halfClosePipeEnd) Read(b []byte) (int, error) { return p.r.Read(b) }
|
||||
func (p *halfClosePipeEnd) Write(b []byte) (int, error) { return p.w.Write(b) }
|
||||
|
||||
// CloseWrite signals EOF to the remote reader without closing the read side.
|
||||
func (p *halfClosePipeEnd) CloseWrite() error { return p.w.Close() }
|
||||
|
||||
// Close shuts down both directions.
|
||||
func (p *halfClosePipeEnd) Close() error {
|
||||
_ = p.r.Close()
|
||||
return p.w.Close()
|
||||
}
|
||||
|
||||
// TestServeStreamWaitsForResponseAfterLocalClose exercises the websocket path
|
||||
// It verifies that the pipe does not tear down immediately after the client closes
|
||||
// its write side.
|
||||
//
|
||||
// The setup mirrors the cloudflared access tcp path:
|
||||
//
|
||||
// local app -> halfClosePipe -> ServeStream -> mock WS echo server
|
||||
//
|
||||
// Sequence:
|
||||
// 1. Write payload to the mock server through ServeStream.
|
||||
// 2. Half-close the write side (CloseWrite) — signals EOF upstream.
|
||||
// 3. Sleep for halfCloseWait to make the race window explicit: a buggy
|
||||
// (timeout=0) pipe would already have torn down the connection here.
|
||||
// 4. Read the echo — must succeed because the patch keeps the pipe alive.
|
||||
func TestServeStreamWaitsForResponseAfterLocalClose(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
payload = "half-close-test"
|
||||
// halfCloseWait makes the race window visible: if ServeStream tears
|
||||
// down the connection on CloseWrite the read that follows will fail
|
||||
// immediately, mirroring the 3-second sleep in the cftunnel reference
|
||||
// test. It must be shorter than DefaultTimeoutAfterFirstClose (10s).
|
||||
halfCloseWait = 3 * time.Second
|
||||
// testTimeout is an upper bound for the whole test.
|
||||
testTimeout = stream.DefaultTimeoutAfterFirstClose + 5*time.Second
|
||||
)
|
||||
|
||||
server := websocketServer()
|
||||
defer server.Close()
|
||||
|
||||
localEnd, remoteEnd := newHalfClosePipe()
|
||||
|
||||
log := zerolog.Nop()
|
||||
wsConn := NewWSConnection(&log)
|
||||
options := &StartOptions{
|
||||
OriginURL: "ws://" + server.Listener.Addr().String(),
|
||||
}
|
||||
|
||||
serveErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
serveErrCh <- wsConn.ServeStream(options, remoteEnd)
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(t.Context(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 1. Write the payload. ServeStream forwards it as a WS binary frame.
|
||||
_, err := localEnd.Write([]byte(payload))
|
||||
require.NoError(t, err)
|
||||
|
||||
// 2. Half-close the write side
|
||||
require.NoError(t, localEnd.CloseWrite())
|
||||
|
||||
// 3. Wait to make the race window explicit.
|
||||
time.Sleep(halfCloseWait)
|
||||
|
||||
// 4. Read the echo.
|
||||
got := make([]byte, len(payload))
|
||||
_, err = io.ReadFull(localEnd, got)
|
||||
require.NoError(t, err, "read after half-close failed: pipe was torn down too early")
|
||||
require.Equal(t, payload, string(got))
|
||||
|
||||
// Drain ServeStream.
|
||||
_ = localEnd.Close()
|
||||
_ = remoteEnd.Close()
|
||||
|
||||
select {
|
||||
case err := <-serveErrCh:
|
||||
if err != nil && err != io.EOF && !errors.Is(err, io.ErrClosedPipe) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("ServeStream did not return in time")
|
||||
}
|
||||
}
|
||||
|
||||
func websocketServer() *httptest.Server {
|
||||
upgrader := gws.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(*http.Request) bool { return true },
|
||||
}
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
_, msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.WriteMessage(gws.BinaryMessage, msg); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user