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  }