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 }