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 }