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