1// Copyright (c) 2015-2019 Jeevanandam M (jeeva@myjeeva.com) 2// 2016 Andrew Grigorev (https://github.com/ei-grad) 3// All rights reserved. 4// resty source code and usage is governed by a MIT style 5// license that can be found in the LICENSE file. 6 7package resty 8 9import ( 10 "context" 11 "net/http" 12 "net/url" 13 "strings" 14 "sync/atomic" 15 "testing" 16 "time" 17) 18 19func TestSetContext(t *testing.T) { 20 ts := createGetServer(t) 21 defer ts.Close() 22 23 resp, err := dc().R(). 24 SetContext(context.Background()). 25 Get(ts.URL + "/") 26 27 assertError(t, err) 28 assertEqual(t, http.StatusOK, resp.StatusCode()) 29 assertEqual(t, "200 OK", resp.Status()) 30 assertEqual(t, true, resp.Body() != nil) 31 assertEqual(t, "TestGet: text response", resp.String()) 32 33 logResponse(t, resp) 34} 35 36func TestSetContextWithError(t *testing.T) { 37 ts := createGetServer(t) 38 defer ts.Close() 39 40 resp, err := dcr(). 41 SetContext(context.Background()). 42 Get(ts.URL + "/mypage") 43 44 assertError(t, err) 45 assertEqual(t, http.StatusBadRequest, resp.StatusCode()) 46 assertEqual(t, "", resp.String()) 47 48 logResponse(t, resp) 49} 50 51func TestSetContextCancel(t *testing.T) { 52 ch := make(chan struct{}) 53 ts := createTestServer(func(w http.ResponseWriter, r *http.Request) { 54 defer func() { 55 ch <- struct{}{} // tell test request is finished 56 }() 57 t.Logf("Server: %v %v", r.Method, r.URL.Path) 58 ch <- struct{}{} 59 <-ch // wait for client to finish request 60 n, err := w.Write([]byte("TestSetContextCancel: response")) 61 // FIXME? test server doesn't handle request cancellation 62 t.Logf("Server: wrote %d bytes", n) 63 t.Logf("Server: err is %v ", err) 64 }) 65 defer ts.Close() 66 67 ctx, cancel := context.WithCancel(context.Background()) 68 69 go func() { 70 <-ch // wait for server to start request handling 71 cancel() 72 }() 73 74 _, err := dc().R(). 75 SetContext(ctx). 76 Get(ts.URL + "/") 77 78 ch <- struct{}{} // tell server to continue request handling 79 80 <-ch // wait for server to finish request handling 81 82 t.Logf("Error: %v", err) 83 if !errIsContextCanceled(err) { 84 t.Errorf("Got unexpected error: %v", err) 85 } 86} 87 88func TestSetContextCancelRetry(t *testing.T) { 89 reqCount := 0 90 ch := make(chan struct{}) 91 ts := createTestServer(func(w http.ResponseWriter, r *http.Request) { 92 reqCount++ 93 defer func() { 94 ch <- struct{}{} // tell test request is finished 95 }() 96 t.Logf("Server: %v %v", r.Method, r.URL.Path) 97 ch <- struct{}{} 98 <-ch // wait for client to finish request 99 n, err := w.Write([]byte("TestSetContextCancel: response")) 100 // FIXME? test server doesn't handle request cancellation 101 t.Logf("Server: wrote %d bytes", n) 102 t.Logf("Server: err is %v ", err) 103 }) 104 defer ts.Close() 105 106 ctx, cancel := context.WithCancel(context.Background()) 107 108 go func() { 109 <-ch // wait for server to start request handling 110 cancel() 111 }() 112 113 c := dc(). 114 SetTimeout(time.Second * 3). 115 SetRetryCount(3) 116 117 _, err := c.R(). 118 SetContext(ctx). 119 Get(ts.URL + "/") 120 121 ch <- struct{}{} // tell server to continue request handling 122 123 <-ch // wait for server to finish request handling 124 125 t.Logf("Error: %v", err) 126 if !errIsContextCanceled(err) { 127 t.Errorf("Got unexpected error: %v", err) 128 } 129 130 if reqCount != 1 { 131 t.Errorf("Request was retried %d times instead of 1", reqCount) 132 } 133} 134 135func TestSetContextCancelWithError(t *testing.T) { 136 ch := make(chan struct{}) 137 ts := createTestServer(func(w http.ResponseWriter, r *http.Request) { 138 defer func() { 139 ch <- struct{}{} // tell test request is finished 140 }() 141 t.Logf("Server: %v %v", r.Method, r.URL.Path) 142 t.Log("Server: sending StatusBadRequest response") 143 w.WriteHeader(http.StatusBadRequest) 144 ch <- struct{}{} 145 <-ch // wait for client to finish request 146 n, err := w.Write([]byte("TestSetContextCancelWithError: response")) 147 // FIXME? test server doesn't handle request cancellation 148 t.Logf("Server: wrote %d bytes", n) 149 t.Logf("Server: err is %v ", err) 150 }) 151 defer ts.Close() 152 153 ctx, cancel := context.WithCancel(context.Background()) 154 155 go func() { 156 <-ch // wait for server to start request handling 157 cancel() 158 }() 159 160 _, err := dc().R(). 161 SetContext(ctx). 162 Get(ts.URL + "/") 163 164 ch <- struct{}{} // tell server to continue request handling 165 166 <-ch // wait for server to finish request handling 167 168 t.Logf("Error: %v", err) 169 if !errIsContextCanceled(err) { 170 t.Errorf("Got unexpected error: %v", err) 171 } 172} 173 174func TestClientRetryWithSetContext(t *testing.T) { 175 var attemptctx int32 176 ts := createTestServer(func(w http.ResponseWriter, r *http.Request) { 177 t.Logf("Method: %v", r.Method) 178 t.Logf("Path: %v", r.URL.Path) 179 attp := atomic.AddInt32(&attemptctx, 1) 180 if attp <= 3 { 181 time.Sleep(time.Second * 2) 182 } 183 _, _ = w.Write([]byte("TestClientRetry page")) 184 }) 185 defer ts.Close() 186 187 c := dc(). 188 SetTimeout(time.Second * 1). 189 SetRetryCount(3) 190 191 _, err := c.R(). 192 SetContext(context.Background()). 193 Get(ts.URL + "/") 194 195 assertEqual(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/")) 196} 197 198func TestRequestContext(t *testing.T) { 199 client := dc() 200 r := client.NewRequest() 201 assertNotNil(t, r.Context()) 202 203 r.SetContext(context.Background()) 204 assertNotNil(t, r.Context()) 205} 206 207func errIsContextCanceled(err error) bool { 208 ue, ok := err.(*url.Error) 209 if !ok { 210 return false 211 } 212 return ue.Err == context.Canceled 213} 214