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