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