mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
127 lines
3.4 KiB
Go
127 lines
3.4 KiB
Go
package fixture
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func TestNATHoldBackendObservesBeforeReleaseAndRespondsAfterRelease(t *testing.T) {
|
|
backend, err := StartNATHoldBackend()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
if err := backend.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
connection, err := net.Dial("tcp", backend.Address())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer connection.Close()
|
|
_, err = io.WriteString(connection, "PATCH /hold HTTP/1.1\r\nHost: hold.invalid\r\nX-AgentCompat-Echo: exact\r\nContent-Length: 4\r\n\r\nbody")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
record, err := backend.WaitRequest(context.Background())
|
|
if err != nil || record.Method != "PATCH" || record.Path != "/hold" || record.Host != "hold.invalid" || record.HeaderValue != "exact" || string(record.Body) != "body" {
|
|
t.Fatalf("record=%+v err=%v", record, err)
|
|
}
|
|
select {
|
|
case <-backend.ResponseReleased():
|
|
t.Fatal("response released before Release")
|
|
default:
|
|
}
|
|
if err := backend.Release(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
response, err := httpReadResponse(connection)
|
|
if err != nil || response != "method=PATCH\npath=/hold\nhost=hold.invalid\nx-agentcompat-echo=exact\nbody=body\n" {
|
|
t.Fatalf("response=%q err=%v", response, err)
|
|
}
|
|
}
|
|
|
|
func TestNATHoldBackendCloseInterruptsIncompleteRequest(t *testing.T) {
|
|
backend, err := StartNATHoldBackend()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
connection, err := net.Dial("tcp", backend.Address())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := io.WriteString(connection, "GET /incomplete HTTP/1.1\r\nHost: hold.invalid\r\n"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := backend.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := connection.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestNATHoldBackendCloseIsConcurrentAndRetainsListenerError(t *testing.T) {
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
closeFailure := errors.New("hold listener close failed")
|
|
backend := newNATHoldBackend(closeErrorListener{Listener: listener, err: closeFailure})
|
|
connection, err := net.Dial("tcp", backend.Address())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer connection.Close()
|
|
if _, err := io.WriteString(connection, "GET /hold HTTP/1.1\r\nHost: hold.invalid\r\n"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
const callers = 8
|
|
errorsSeen := make(chan error, callers)
|
|
var group sync.WaitGroup
|
|
group.Add(callers)
|
|
for range callers {
|
|
go func() {
|
|
defer group.Done()
|
|
errorsSeen <- backend.Close()
|
|
}()
|
|
}
|
|
group.Wait()
|
|
|
|
for range callers {
|
|
if err := <-errorsSeen; !errors.Is(err, closeFailure) {
|
|
t.Fatalf("Close error=%v, want %v", err, closeFailure)
|
|
}
|
|
}
|
|
if err := backend.Close(); !errors.Is(err, closeFailure) {
|
|
t.Fatalf("repeated Close error=%v, want %v", err, closeFailure)
|
|
}
|
|
}
|
|
|
|
func newNATHoldBackend(listener net.Listener) *NATHoldBackend {
|
|
backend := &NATHoldBackend{listener: listener, results: make(chan natHoldResult, 1), done: make(chan struct{}), released: make(chan struct{}), observed: make(chan struct{})}
|
|
backend.waitGroup.Add(2)
|
|
go backend.accept()
|
|
return backend
|
|
}
|
|
|
|
func httpReadResponse(connection net.Conn) (string, error) {
|
|
response, err := http.ReadResponse(bufio.NewReader(connection), nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
body, err := io.ReadAll(response.Body)
|
|
if closeErr := response.Body.Close(); err == nil {
|
|
err = closeErr
|
|
}
|
|
return string(body), err
|
|
}
|