github.com/useflyent/fhttp@v0.0.0-20211004035111-333f430cfbbf/transport_internal_test.go (about)

     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  // White-box tests for transport.go (in package http instead of http_test).
     6  
     7  package http
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/tls"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"io/ioutil"
    16  	"net"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/useflyent/fhttp/internal"
    21  )
    22  
    23  // Issue 15446: incorrect wrapping of errors when server closes an idle connection.
    24  func TestTransportPersistConnReadLoopEOF(t *testing.T) {
    25  	ln := newLocalListener(t)
    26  	defer ln.Close()
    27  
    28  	connc := make(chan net.Conn, 1)
    29  	go func() {
    30  		defer close(connc)
    31  		c, err := ln.Accept()
    32  		if err != nil {
    33  			t.Error(err)
    34  			return
    35  		}
    36  		connc <- c
    37  	}()
    38  
    39  	tr := new(Transport)
    40  	req, _ := NewRequest("GET", "http://"+ln.Addr().String(), nil)
    41  	req = req.WithT(t)
    42  	treq := &transportRequest{Request: req}
    43  	cm := connectMethod{targetScheme: "http", targetAddr: ln.Addr().String()}
    44  	pc, err := tr.getConn(treq, cm)
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	defer pc.close(errors.New("test over"))
    49  
    50  	conn := <-connc
    51  	if conn == nil {
    52  		// Already called t.Error in the accept goroutine.
    53  		return
    54  	}
    55  	conn.Close() // simulate the server hanging up on the client
    56  
    57  	_, err = pc.roundTrip(treq)
    58  	if !isTransportReadFromServerError(err) && err != errServerClosedIdle {
    59  		t.Errorf("roundTrip = %#v, %v; want errServerClosedIdle or transportReadFromServerError", err, err)
    60  	}
    61  
    62  	<-pc.closech
    63  	err = pc.closed
    64  	if !isTransportReadFromServerError(err) && err != errServerClosedIdle {
    65  		t.Errorf("pc.closed = %#v, %v; want errServerClosedIdle or transportReadFromServerError", err, err)
    66  	}
    67  }
    68  
    69  func isTransportReadFromServerError(err error) bool {
    70  	_, ok := err.(transportReadFromServerError)
    71  	return ok
    72  }
    73  
    74  func newLocalListener(t *testing.T) net.Listener {
    75  	ln, err := net.Listen("tcp", "127.0.0.1:0")
    76  	if err != nil {
    77  		ln, err = net.Listen("tcp6", "[::1]:0")
    78  	}
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	return ln
    83  }
    84  
    85  func dummyRequest(method string) *Request {
    86  	req, err := NewRequest(method, "http://fake.tld/", nil)
    87  	if err != nil {
    88  		panic(err)
    89  	}
    90  	return req
    91  }
    92  func dummyRequestWithBody(method string) *Request {
    93  	req, err := NewRequest(method, "http://fake.tld/", strings.NewReader("foo"))
    94  	if err != nil {
    95  		panic(err)
    96  	}
    97  	return req
    98  }
    99  
   100  func dummyRequestWithBodyNoGetBody(method string) *Request {
   101  	req := dummyRequestWithBody(method)
   102  	req.GetBody = nil
   103  	return req
   104  }
   105  
   106  // issue22091Error acts like a golang.org/x/net/http2.ErrNoCachedConn.
   107  type issue22091Error struct{}
   108  
   109  func (issue22091Error) IsHTTP2NoCachedConnError() {}
   110  func (issue22091Error) Error() string             { return "issue22091Error" }
   111  
   112  func TestTransportShouldRetryRequest(t *testing.T) {
   113  	tests := []struct {
   114  		pc  *persistConn
   115  		req *Request
   116  
   117  		err  error
   118  		want bool
   119  	}{
   120  		0: {
   121  			pc:   &persistConn{reused: false},
   122  			req:  dummyRequest("POST"),
   123  			err:  nothingWrittenError{},
   124  			want: false,
   125  		},
   126  		1: {
   127  			pc:   &persistConn{reused: true},
   128  			req:  dummyRequest("POST"),
   129  			err:  nothingWrittenError{},
   130  			want: true,
   131  		},
   132  		2: {
   133  			pc:   &persistConn{reused: true},
   134  			req:  dummyRequest("POST"),
   135  			err:  http2ErrNoCachedConn,
   136  			want: true,
   137  		},
   138  		3: {
   139  			pc:   nil,
   140  			req:  nil,
   141  			err:  issue22091Error{}, // like an external http2ErrNoCachedConn
   142  			want: true,
   143  		},
   144  		4: {
   145  			pc:   &persistConn{reused: true},
   146  			req:  dummyRequest("POST"),
   147  			err:  errMissingHost,
   148  			want: false,
   149  		},
   150  		5: {
   151  			pc:   &persistConn{reused: true},
   152  			req:  dummyRequest("POST"),
   153  			err:  transportReadFromServerError{},
   154  			want: false,
   155  		},
   156  		6: {
   157  			pc:   &persistConn{reused: true},
   158  			req:  dummyRequest("GET"),
   159  			err:  transportReadFromServerError{},
   160  			want: true,
   161  		},
   162  		7: {
   163  			pc:   &persistConn{reused: true},
   164  			req:  dummyRequest("GET"),
   165  			err:  errServerClosedIdle,
   166  			want: true,
   167  		},
   168  		8: {
   169  			pc:   &persistConn{reused: true},
   170  			req:  dummyRequestWithBody("POST"),
   171  			err:  nothingWrittenError{},
   172  			want: true,
   173  		},
   174  		9: {
   175  			pc:   &persistConn{reused: true},
   176  			req:  dummyRequestWithBodyNoGetBody("POST"),
   177  			err:  nothingWrittenError{},
   178  			want: false,
   179  		},
   180  	}
   181  	for i, tt := range tests {
   182  		got := tt.pc.shouldRetryRequest(tt.req, tt.err)
   183  		if got != tt.want {
   184  			t.Errorf("%d. shouldRetryRequest = %v; want %v", i, got, tt.want)
   185  		}
   186  	}
   187  }
   188  
   189  type roundTripFunc func(r *Request) (*Response, error)
   190  
   191  func (f roundTripFunc) RoundTrip(r *Request) (*Response, error) {
   192  	return f(r)
   193  }
   194  
   195  // Issue 25009
   196  func TestTransportBodyAltRewind(t *testing.T) {
   197  	cert, err := tls.X509KeyPair(internal.LocalhostCert, internal.LocalhostKey)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	ln := newLocalListener(t)
   202  	defer ln.Close()
   203  
   204  	go func() {
   205  		tln := tls.NewListener(ln, &tls.Config{
   206  			NextProtos:   []string{"foo"},
   207  			Certificates: []tls.Certificate{cert},
   208  		})
   209  		for i := 0; i < 2; i++ {
   210  			sc, err := tln.Accept()
   211  			if err != nil {
   212  				t.Error(err)
   213  				return
   214  			}
   215  			if err := sc.(*tls.Conn).Handshake(); err != nil {
   216  				t.Error(err)
   217  				return
   218  			}
   219  			sc.Close()
   220  		}
   221  	}()
   222  
   223  	addr := ln.Addr().String()
   224  	req, _ := NewRequest("POST", "https://example.org/", bytes.NewBufferString("request"))
   225  	roundTripped := false
   226  	tr := &Transport{
   227  		DisableKeepAlives: true,
   228  		TLSNextProto: map[string]func(string, *tls.Conn) RoundTripper{
   229  			"foo": func(authority string, c *tls.Conn) RoundTripper {
   230  				return roundTripFunc(func(r *Request) (*Response, error) {
   231  					n, _ := io.Copy(io.Discard, r.Body)
   232  					if n == 0 {
   233  						t.Error("body length is zero")
   234  					}
   235  					if roundTripped {
   236  						return &Response{
   237  							Body:       NoBody,
   238  							StatusCode: 200,
   239  						}, nil
   240  					}
   241  					roundTripped = true
   242  					return nil, http2noCachedConnError{}
   243  				})
   244  			},
   245  		},
   246  		DialTLS: func(_, _ string) (net.Conn, error) {
   247  			tc, err := tls.Dial("tcp", addr, &tls.Config{
   248  				InsecureSkipVerify: true,
   249  				NextProtos:         []string{"foo"},
   250  			})
   251  			if err != nil {
   252  				return nil, err
   253  			}
   254  			if err := tc.Handshake(); err != nil {
   255  				return nil, err
   256  			}
   257  			return tc, nil
   258  		},
   259  	}
   260  	c := &Client{Transport: tr}
   261  	_, err = c.Do(req)
   262  	if err != nil {
   263  		t.Error(err)
   264  	}
   265  }
   266  
   267  // Tests that gzipReader doesn't crash on a second Read call following
   268  // the first Read call's gzip.NewReader returning an error.
   269  func TestGzipReader_DoubleReadCrash(t *testing.T) {
   270  	gz := &gzipReader{
   271  		body: ioutil.NopCloser(strings.NewReader("0123456789")),
   272  	}
   273  	var buf [1]byte
   274  	n, err1 := gz.Read(buf[:])
   275  	if n != 0 || !strings.Contains(fmt.Sprint(err1), "invalid header") {
   276  		t.Fatalf("Read = %v, %v; want 0, invalid header", n, err1)
   277  	}
   278  	n, err2 := gz.Read(buf[:])
   279  	if n != 0 || err2 != err1 {
   280  		t.Fatalf("second Read = %v, %v; want 0, %v", n, err2, err1)
   281  	}
   282  }