package fixture import ( "bufio" "context" "fmt" "io" "net" "net/http" "strings" "testing" "time" ) func TestFixture_NATEchoHalfClose(t *testing.T) { // Given backend, err := StartNATHalfCloseEchoBackend() requireNoFixtureError(t, err) t.Cleanup(func() { requireNoFixtureError(t, backend.Close()) }) connection, err := net.DialTimeout("tcp", backend.Address(), time.Second) requireNoFixtureError(t, err) tcpConnection := connection.(*net.TCPConn) defer tcpConnection.Close() requireNoFixtureError(t, tcpConnection.SetDeadline(time.Now().Add(2*time.Second))) requestBody := "half-closed" request := fmt.Sprintf("POST /echo?case=half-close HTTP/1.1\r\nHost: nat.invalid\r\nX-AgentCompat-Echo: fixture\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s", len(requestBody), requestBody) // When _, err = io.WriteString(tcpConnection, request) requireNoFixtureError(t, err) requireNoFixtureError(t, tcpConnection.CloseWrite()) response, err := http.ReadResponse(bufio.NewReader(tcpConnection), nil) requireNoFixtureError(t, err) responseBody, err := io.ReadAll(response.Body) requireNoFixtureError(t, err) requireNoFixtureError(t, response.Body.Close()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() record, err := backend.WaitRequest(ctx) requireNoFixtureError(t, err) // Then const expected = "method=POST\npath=/echo?case=half-close\nhost=nat.invalid\nx-agentcompat-echo=fixture\nbody=half-closed\n" if response.StatusCode != http.StatusOK || string(responseBody) != expected { t.Fatalf("NAT echo response = %d %q", response.StatusCode, responseBody) } if !record.RequestHalfClosed { t.Fatal("backend responded before observing request half-close") } if record.Method != "POST" || record.Path != "/echo?case=half-close" || record.Host != "nat.invalid" || record.HeaderValue != "fixture" || string(record.Body) != requestBody { t.Fatalf("NAT request record = %+v", record) } } func TestFixture_NATEcho(t *testing.T) { // Given backend, err := StartNATEchoBackend() requireNoFixtureError(t, err) t.Cleanup(func() { requireNoFixtureError(t, backend.Close()) }) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() request, err := http.NewRequestWithContext(ctx, http.MethodPut, "http://"+backend.Address()+"/echo?case=ordinary", strings.NewReader("ordinary")) requireNoFixtureError(t, err) request.Host = "nat.invalid" request.Header.Set("X-AgentCompat-Echo", "fixture") // When response, err := http.DefaultClient.Do(request) requireNoFixtureError(t, err) responseBody, err := io.ReadAll(response.Body) requireNoFixtureError(t, err) requireNoFixtureError(t, response.Body.Close()) record, err := backend.WaitRequest(ctx) requireNoFixtureError(t, err) // Then const expected = "method=PUT\npath=/echo?case=ordinary\nhost=nat.invalid\nx-agentcompat-echo=fixture\nbody=ordinary\n" if response.StatusCode != http.StatusOK || string(responseBody) != expected { t.Fatalf("NAT echo response = %d %q", response.StatusCode, responseBody) } if record.RequestHalfClosed || record.Method != http.MethodPut || record.Host != "nat.invalid" { t.Fatalf("NAT request record = %+v", record) } }