github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/x/net/context/ctxhttp/ctxhttp_test.go (about)

     1  // Copyright 2015 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  // +build !plan9
     6  
     7  package ctxhttp
     8  
     9  import (
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/insionng/yougam/libraries/x/net/context"
    19  )
    20  
    21  const (
    22  	requestDuration = 100 * time.Millisecond
    23  	requestBody     = "ok"
    24  )
    25  
    26  func TestNoTimeout(t *testing.T) {
    27  	ctx := context.Background()
    28  	resp, err := doRequest(ctx)
    29  
    30  	if resp == nil || err != nil {
    31  		t.Fatalf("error received from client: %v %v", err, resp)
    32  	}
    33  }
    34  
    35  func TestCancel(t *testing.T) {
    36  	ctx, cancel := context.WithCancel(context.Background())
    37  	go func() {
    38  		time.Sleep(requestDuration / 2)
    39  		cancel()
    40  	}()
    41  
    42  	resp, err := doRequest(ctx)
    43  
    44  	if resp != nil || err == nil {
    45  		t.Fatalf("expected error, didn't get one. resp: %v", resp)
    46  	}
    47  	if err != ctx.Err() {
    48  		t.Fatalf("expected error from context but got: %v", err)
    49  	}
    50  }
    51  
    52  func TestCancelAfterRequest(t *testing.T) {
    53  	ctx, cancel := context.WithCancel(context.Background())
    54  
    55  	resp, err := doRequest(ctx)
    56  
    57  	// Cancel before reading the body.
    58  	// Request.Body should still be readable after the context is canceled.
    59  	cancel()
    60  
    61  	b, err := ioutil.ReadAll(resp.Body)
    62  	if err != nil || string(b) != requestBody {
    63  		t.Fatalf("could not read body: %q %v", b, err)
    64  	}
    65  }
    66  
    67  func TestCancelAfterHangingRequest(t *testing.T) {
    68  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    69  		w.WriteHeader(http.StatusOK)
    70  		w.(http.Flusher).Flush()
    71  		<-w.(http.CloseNotifier).CloseNotify()
    72  	})
    73  
    74  	serv := httptest.NewServer(handler)
    75  	defer serv.Close()
    76  
    77  	ctx, cancel := context.WithCancel(context.Background())
    78  	resp, err := Get(ctx, nil, serv.URL)
    79  	if err != nil {
    80  		t.Fatalf("unexpected error in Get: %v", err)
    81  	}
    82  
    83  	// Cancel befer reading the body.
    84  	// Reading Request.Body should fail, since the request was
    85  	// canceled before anything was written.
    86  	cancel()
    87  
    88  	done := make(chan struct{})
    89  
    90  	go func() {
    91  		b, err := ioutil.ReadAll(resp.Body)
    92  		if len(b) != 0 || err == nil {
    93  			t.Errorf(`Read got (%q, %v); want ("", error)`, b, err)
    94  		}
    95  		close(done)
    96  	}()
    97  
    98  	select {
    99  	case <-time.After(1 * time.Second):
   100  		t.Errorf("Test timed out")
   101  	case <-done:
   102  	}
   103  }
   104  
   105  func doRequest(ctx context.Context) (*http.Response, error) {
   106  	var okHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   107  		time.Sleep(requestDuration)
   108  		w.Write([]byte(requestBody))
   109  	})
   110  
   111  	serv := httptest.NewServer(okHandler)
   112  	defer serv.Close()
   113  
   114  	return Get(ctx, nil, serv.URL)
   115  }
   116  
   117  // yougam/libraries/issue/14065
   118  func TestClosesResponseBodyOnCancel(t *testing.T) {
   119  	defer func() { testHookContextDoneBeforeHeaders = nop }()
   120  	defer func() { testHookDoReturned = nop }()
   121  	defer func() { testHookDidBodyClose = nop }()
   122  
   123  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
   124  	defer ts.Close()
   125  
   126  	ctx, cancel := context.WithCancel(context.Background())
   127  
   128  	// closed when Do enters select case <-ctx.Done()
   129  	enteredDonePath := make(chan struct{})
   130  
   131  	testHookContextDoneBeforeHeaders = func() {
   132  		close(enteredDonePath)
   133  	}
   134  
   135  	testHookDoReturned = func() {
   136  		// We now have the result (the Flush'd headers) at least,
   137  		// so we can cancel the request.
   138  		cancel()
   139  
   140  		// But block the client.Do goroutine from sending
   141  		// until Do enters into the <-ctx.Done() path, since
   142  		// otherwise if both channels are readable, select
   143  		// picks a random one.
   144  		<-enteredDonePath
   145  	}
   146  
   147  	sawBodyClose := make(chan struct{})
   148  	testHookDidBodyClose = func() { close(sawBodyClose) }
   149  
   150  	tr := &http.Transport{}
   151  	defer tr.CloseIdleConnections()
   152  	c := &http.Client{Transport: tr}
   153  	req, _ := http.NewRequest("GET", ts.URL, nil)
   154  	_, doErr := Do(ctx, c, req)
   155  
   156  	select {
   157  	case <-sawBodyClose:
   158  	case <-time.After(5 * time.Second):
   159  		t.Fatal("timeout waiting for body to close")
   160  	}
   161  
   162  	if doErr != ctx.Err() {
   163  		t.Errorf("Do error = %v; want %v", doErr, ctx.Err())
   164  	}
   165  }
   166  
   167  type noteCloseConn struct {
   168  	net.Conn
   169  	onceClose sync.Once
   170  	closefn   func()
   171  }
   172  
   173  func (c *noteCloseConn) Close() error {
   174  	c.onceClose.Do(c.closefn)
   175  	return c.Conn.Close()
   176  }