blob: 17c31a877c08f03d44595988b1f93705820ece96 [file] [edit]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http_test
import (
"bufio"
"errors"
"internal/nettest"
"internal/synctest"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// An http1ServerTest tests an HTTP/1 server using a fake network.
// It must be used in a synctest bubble.
type http1ServerTest struct {
t *testing.T
ts *httptest.Server
}
func newHTTP1ServerTest(t *testing.T, h http.HandlerFunc) *http1ServerTest {
if h == nil {
h = func(w http.ResponseWriter, req *http.Request) {}
}
st := &http1ServerTest{
t: t,
ts: httptest.NewTestServer(t, h),
}
return st
}
// client returns a Client that sends requests to the server.
func (st *http1ServerTest) client() *http.Client {
return st.ts.Client()
}
// transport returns a Transport that sends requests to the server.
func (st *http1ServerTest) transport() *http.Transport {
return st.ts.Client().Transport.(*http.Transport)
}
// dial returns a connection to the server.
func (st *http1ServerTest) dial() *http1TestConn {
t := st.t
t.Helper()
nc, err := st.transport().DialContext(st.t.Context(), "tcp", "example.tld")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
nc.Close()
})
conn := nc.(*nettest.Conn)
conn.SetReadError(errWouldBlock) // effectively make reads non-blocking
return &http1TestConn{
t: st.t,
conn: conn,
bufr: bufio.NewReader(conn),
}
}
var errWouldBlock = errors.New("would block")
type http1TestConn struct {
t *testing.T
conn *nettest.Conn
bufr *bufio.Reader
}
// writeMessage writes a number of CRLF-terminated lines to the connection.
func (tc *http1TestConn) writeMessage(lines ...string) {
t := tc.t
t.Helper()
if _, err := tc.conn.Write([]byte(strings.Join(lines, "\r\n") + "\r\n")); err != nil {
t.Fatalf("conn write: %v", err)
}
}
// readResponse reads a response from the connection (not including the response body).
func (tc *http1TestConn) readResponse() *http.Response {
t := tc.t
t.Helper()
synctest.Wait()
resp, err := http.ReadResponse(tc.bufr, nil)
if err != nil {
t.Fatalf("ReadResponse: %v", err)
}
return resp
}
// wantIdle asserts that the connection is not closed and has no pending data to read.
func (tc *http1TestConn) wantIdle() {
t := tc.t
t.Helper()
synctest.Wait()
if got, err := tc.bufr.Peek(32); len(got) != 0 || !errors.Is(err, errWouldBlock) {
t.Fatalf("read from conn: %q, %v; expect conn to be idle", got, err)
}
}
// wantClosed asserts that the connection is read-closed and has no pending data to read.
func (tc *http1TestConn) wantClosed() {
t := tc.t
t.Helper()
synctest.Wait()
if got, err := tc.bufr.Peek(32); len(got) != 0 || err != io.EOF {
t.Fatalf("read from conn: %q; expect conn to be closed", got)
}
}