fix(handler/http2): apply traffic limiter + stats to forward proxy path, decouple auth I/O, add idle timeout
- Wrap non-CONNECT upstream with traffic_wrapper and stats_wrapper (was only on CONNECT path) - Write 503 error response on forwardRequest failure instead of silent connection close - Decouple resp.Header from w.Header() to prevent metadata header leakage/doubling in 407 responses - Return pipeTo signal from authenticate instead of dialing inline (matches http handler pattern) - Add idleTimeout metadata field and pass to xnet.Pipe for CONNECT tunnels - Add 4 tests for probe resistance host forwarding and knock bypass
This commit is contained in:
@@ -156,3 +156,50 @@ func TestRoundTrip_AuthFailed(t *testing.T) {
|
||||
t.Errorf("err = %v, want ErrAuthFailed", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTrip_ProbeResistanceHost(t *testing.T) {
|
||||
decoy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("decoy response"))
|
||||
}))
|
||||
defer decoy.Close()
|
||||
|
||||
h := newTestHandler(handler.AutherOption(&testAuther{ok: false}))
|
||||
decoyAddr := strings.TrimPrefix(decoy.URL, "http://")
|
||||
h.md.probeResistance = &probeResistance{Type: "host", Value: decoyAddr}
|
||||
h.Init(testMD(map[string]any{}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
ro := &xrecorder.HandlerRecorderObject{}
|
||||
|
||||
err := h.roundTrip(context.Background(), w, req, ro, &testLogger{})
|
||||
if err != ErrAuthFailed {
|
||||
t.Errorf("err = %v, want ErrAuthFailed", err)
|
||||
}
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("decoy status = %d, want %d", w.Code, http.StatusOK)
|
||||
}
|
||||
if w.Body.String() != "decoy response" {
|
||||
t.Errorf("decoy body = %q, want %q", w.Body.String(), "decoy response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTrip_ProbeResistanceHostDialError(t *testing.T) {
|
||||
h := newTestHandler(handler.AutherOption(&testAuther{ok: false}))
|
||||
h.md.probeResistance = &probeResistance{Type: "host", Value: "127.0.0.1:1"} // unreachable
|
||||
h.Init(testMD(map[string]any{}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
ro := &xrecorder.HandlerRecorderObject{}
|
||||
|
||||
err := h.roundTrip(context.Background(), w, req, ro, &testLogger{})
|
||||
if err != ErrAuthFailed {
|
||||
t.Errorf("err = %v, want ErrAuthFailed", err)
|
||||
}
|
||||
// Should get a 503 on dial failure.
|
||||
if w.Code != http.StatusServiceUnavailable {
|
||||
t.Errorf("status = %d, want %d", w.Code, http.StatusServiceUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user