123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // Copyright 2016 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.
- // White-box tests for transport.go (in package http instead of http_test).
- package http
- import (
- "bytes"
- "crypto/tls"
- "errors"
- "io"
- "net"
- "net/http/internal/testcert"
- "strings"
- "testing"
- )
- // Issue 15446: incorrect wrapping of errors when server closes an idle connection.
- func TestTransportPersistConnReadLoopEOF(t *testing.T) {
- ln := newLocalListener(t)
- defer ln.Close()
- connc := make(chan net.Conn, 1)
- go func() {
- defer close(connc)
- c, err := ln.Accept()
- if err != nil {
- t.Error(err)
- return
- }
- connc <- c
- }()
- tr := new(Transport)
- req, _ := NewRequest("GET", "http://"+ln.Addr().String(), nil)
- req = req.WithT(t)
- treq := &transportRequest{Request: req}
- cm := connectMethod{targetScheme: "http", targetAddr: ln.Addr().String()}
- pc, err := tr.getConn(treq, cm)
- if err != nil {
- t.Fatal(err)
- }
- defer pc.close(errors.New("test over"))
- conn := <-connc
- if conn == nil {
- // Already called t.Error in the accept goroutine.
- return
- }
- conn.Close() // simulate the server hanging up on the client
- _, err = pc.roundTrip(treq)
- if !isNothingWrittenError(err) && !isTransportReadFromServerError(err) && err != errServerClosedIdle {
- t.Errorf("roundTrip = %#v, %v; want errServerClosedIdle, transportReadFromServerError, or nothingWrittenError", err, err)
- }
- <-pc.closech
- err = pc.closed
- if !isTransportReadFromServerError(err) && err != errServerClosedIdle {
- t.Errorf("pc.closed = %#v, %v; want errServerClosedIdle or transportReadFromServerError", err, err)
- }
- }
- func isNothingWrittenError(err error) bool {
- _, ok := err.(nothingWrittenError)
- return ok
- }
- func isTransportReadFromServerError(err error) bool {
- _, ok := err.(transportReadFromServerError)
- return ok
- }
- func newLocalListener(t *testing.T) net.Listener {
- ln, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- ln, err = net.Listen("tcp6", "[::1]:0")
- }
- if err != nil {
- t.Fatal(err)
- }
- return ln
- }
- func dummyRequest(method string) *Request {
- req, err := NewRequest(method, "http://fake.tld/", nil)
- if err != nil {
- panic(err)
- }
- return req
- }
- func dummyRequestWithBody(method string) *Request {
- req, err := NewRequest(method, "http://fake.tld/", strings.NewReader("foo"))
- if err != nil {
- panic(err)
- }
- return req
- }
- func dummyRequestWithBodyNoGetBody(method string) *Request {
- req := dummyRequestWithBody(method)
- req.GetBody = nil
- return req
- }
- // issue22091Error acts like a golang.org/x/net/http2.ErrNoCachedConn.
- type issue22091Error struct{}
- func (issue22091Error) IsHTTP2NoCachedConnError() {}
- func (issue22091Error) Error() string { return "issue22091Error" }
- func TestTransportShouldRetryRequest(t *testing.T) {
- tests := []struct {
- pc *persistConn
- req *Request
- err error
- want bool
- }{
- 0: {
- pc: &persistConn{reused: false},
- req: dummyRequest("POST"),
- err: nothingWrittenError{},
- want: false,
- },
- 1: {
- pc: &persistConn{reused: true},
- req: dummyRequest("POST"),
- err: nothingWrittenError{},
- want: true,
- },
- 2: {
- pc: &persistConn{reused: true},
- req: dummyRequest("POST"),
- err: http2ErrNoCachedConn,
- want: true,
- },
- 3: {
- pc: nil,
- req: nil,
- err: issue22091Error{}, // like an external http2ErrNoCachedConn
- want: true,
- },
- 4: {
- pc: &persistConn{reused: true},
- req: dummyRequest("POST"),
- err: errMissingHost,
- want: false,
- },
- 5: {
- pc: &persistConn{reused: true},
- req: dummyRequest("POST"),
- err: transportReadFromServerError{},
- want: false,
- },
- 6: {
- pc: &persistConn{reused: true},
- req: dummyRequest("GET"),
- err: transportReadFromServerError{},
- want: true,
- },
- 7: {
- pc: &persistConn{reused: true},
- req: dummyRequest("GET"),
- err: errServerClosedIdle,
- want: true,
- },
- 8: {
- pc: &persistConn{reused: true},
- req: dummyRequestWithBody("POST"),
- err: nothingWrittenError{},
- want: true,
- },
- 9: {
- pc: &persistConn{reused: true},
- req: dummyRequestWithBodyNoGetBody("POST"),
- err: nothingWrittenError{},
- want: false,
- },
- }
- for i, tt := range tests {
- got := tt.pc.shouldRetryRequest(tt.req, tt.err)
- if got != tt.want {
- t.Errorf("%d. shouldRetryRequest = %v; want %v", i, got, tt.want)
- }
- }
- }
- type roundTripFunc func(r *Request) (*Response, error)
- func (f roundTripFunc) RoundTrip(r *Request) (*Response, error) {
- return f(r)
- }
- // Issue 25009
- func TestTransportBodyAltRewind(t *testing.T) {
- cert, err := tls.X509KeyPair(testcert.LocalhostCert, testcert.LocalhostKey)
- if err != nil {
- t.Fatal(err)
- }
- ln := newLocalListener(t)
- defer ln.Close()
- go func() {
- tln := tls.NewListener(ln, &tls.Config{
- NextProtos: []string{"foo"},
- Certificates: []tls.Certificate{cert},
- })
- for i := 0; i < 2; i++ {
- sc, err := tln.Accept()
- if err != nil {
- t.Error(err)
- return
- }
- if err := sc.(*tls.Conn).Handshake(); err != nil {
- t.Error(err)
- return
- }
- sc.Close()
- }
- }()
- addr := ln.Addr().String()
- req, _ := NewRequest("POST", "https://example.org/", bytes.NewBufferString("request"))
- roundTripped := false
- tr := &Transport{
- DisableKeepAlives: true,
- TLSNextProto: map[string]func(string, *tls.Conn) RoundTripper{
- "foo": func(authority string, c *tls.Conn) RoundTripper {
- return roundTripFunc(func(r *Request) (*Response, error) {
- n, _ := io.Copy(io.Discard, r.Body)
- if n == 0 {
- t.Error("body length is zero")
- }
- if roundTripped {
- return &Response{
- Body: NoBody,
- StatusCode: 200,
- }, nil
- }
- roundTripped = true
- return nil, http2noCachedConnError{}
- })
- },
- },
- DialTLS: func(_, _ string) (net.Conn, error) {
- tc, err := tls.Dial("tcp", addr, &tls.Config{
- InsecureSkipVerify: true,
- NextProtos: []string{"foo"},
- })
- if err != nil {
- return nil, err
- }
- if err := tc.Handshake(); err != nil {
- return nil, err
- }
- return tc, nil
- },
- }
- c := &Client{Transport: tr}
- _, err = c.Do(req)
- if err != nil {
- t.Error(err)
- }
- }
|