1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package httptest provides utilities for HTTP testing.
6package httptest
7
8import (
9	"bufio"
10	"bytes"
11	"context"
12	"crypto/tls"
13	"io"
14	"net/http"
15	"strings"
16)
17
18// NewRequest wraps NewRequestWithContext using context.Background.
19func NewRequest(method, target string, body io.Reader) *http.Request {
20	return NewRequestWithContext(context.Background(), method, target, body)
21}
22
23// NewRequestWithContext returns a new incoming server Request, suitable
24// for passing to an [http.Handler] for testing.
25//
26// The target is the RFC 7230 "request-target": it may be either a
27// path or an absolute URL. If target is an absolute URL, the host name
28// from the URL is used. Otherwise, "example.com" is used.
29//
30// The TLS field is set to a non-nil dummy value if target has scheme
31// "https".
32//
33// The Request.Proto is always HTTP/1.1.
34//
35// An empty method means "GET".
36//
37// The provided body may be nil. If the body is of type *bytes.Reader,
38// *strings.Reader, or *bytes.Buffer, the Request.ContentLength is
39// set.
40//
41// NewRequest panics on error for ease of use in testing, where a
42// panic is acceptable.
43//
44// To generate a client HTTP request instead of a server request, see
45// the NewRequest function in the net/http package.
46func NewRequestWithContext(ctx context.Context, method, target string, body io.Reader) *http.Request {
47	if method == "" {
48		method = "GET"
49	}
50	req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))
51	if err != nil {
52		panic("invalid NewRequest arguments; " + err.Error())
53	}
54	req = req.WithContext(ctx)
55
56	// HTTP/1.0 was used above to avoid needing a Host field. Change it to 1.1 here.
57	req.Proto = "HTTP/1.1"
58	req.ProtoMinor = 1
59	req.Close = false
60
61	if body != nil {
62		switch v := body.(type) {
63		case *bytes.Buffer:
64			req.ContentLength = int64(v.Len())
65		case *bytes.Reader:
66			req.ContentLength = int64(v.Len())
67		case *strings.Reader:
68			req.ContentLength = int64(v.Len())
69		default:
70			req.ContentLength = -1
71		}
72		if rc, ok := body.(io.ReadCloser); ok {
73			req.Body = rc
74		} else {
75			req.Body = io.NopCloser(body)
76		}
77	}
78
79	// 192.0.2.0/24 is "TEST-NET" in RFC 5737 for use solely in
80	// documentation and example source code and should not be
81	// used publicly.
82	req.RemoteAddr = "192.0.2.1:1234"
83
84	if req.Host == "" {
85		req.Host = "example.com"
86	}
87
88	if strings.HasPrefix(target, "https://") {
89		req.TLS = &tls.ConnectionState{
90			Version:           tls.VersionTLS12,
91			HandshakeComplete: true,
92			ServerName:        req.Host,
93		}
94	}
95
96	return req
97}
98