Skip to content

Commit cf7e4c5

Browse files
committed
Added individual request proxy support
Signed-off-by: Levi Gross <[email protected]>
1 parent aa5c51a commit cf7e4c5

File tree

3 files changed

+91
-5
lines changed

3 files changed

+91
-5
lines changed

base_get_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"encoding/xml"
77
"io"
88
"net/http"
9+
"net/http/httptest"
10+
"net/url"
911
"os"
1012
"testing"
1113
)
@@ -94,6 +96,54 @@ func TestGetNoOptions(t *testing.T) {
9496
verifyOkResponse(<-GetAsync("http://httpbin.org/get", nil), t)
9597
}
9698

99+
func TestGetProxy(t *testing.T) {
100+
ch := make(chan string, 1)
101+
102+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103+
ch <- "real server"
104+
}))
105+
106+
defer ts.Close()
107+
108+
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
109+
ch <- "proxy for " + r.URL.String()
110+
}))
111+
112+
defer proxy.Close()
113+
114+
pu, err := url.Parse(proxy.URL)
115+
if err != nil {
116+
t.Fatal(err)
117+
}
118+
119+
resp, err := Head(ts.URL, &RequestOptions{Proxies: map[string]*url.URL{pu.Scheme: pu}})
120+
121+
defer http.DefaultTransport.(*http.Transport).CloseIdleConnections()
122+
123+
if err != nil {
124+
t.Error("Unable to make request: ", err)
125+
}
126+
127+
if resp.Ok != true {
128+
t.Error("Response is not OK for some reason: ", resp.StatusCode)
129+
}
130+
131+
got := <-ch
132+
want := "proxy for " + ts.URL + "/"
133+
if got != want {
134+
t.Errorf("want %q, got %q", want, got)
135+
}
136+
}
137+
138+
func TestGetSyncInvalidProxyScheme(t *testing.T) {
139+
resp, err := Get("http://httpbin.org/get", &RequestOptions{Proxies: map[string]*url.URL{"gopher": nil}})
140+
if err != nil {
141+
t.Error("Request failed: ", err)
142+
}
143+
144+
verifyOkResponse(resp, t)
145+
}
146+
97147
func TestGetSyncNoOptions(t *testing.T) {
98148
resp, err := Get("http://httpbin.org/get", nil)
99149
if err != nil {

request.go

+40-3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ type RequestOptions struct {
5858

5959
// Cookies is an array of `http.Cookie` that allows you to attach cookies to your request
6060
Cookies []http.Cookie
61+
62+
// Proxies is a map in the following format *protocol* => proxy address e.g http => http://127.0.0.1:8080
63+
Proxies map[string]*url.URL
6164
}
6265

6366
func doRequest(requestVerb, url string, ro *RequestOptions) (*Response, error) {
@@ -133,9 +136,13 @@ func createFileUploadRequest(httpMethod, userURL string, ro *RequestOptions) (*h
133136
return createMultiPartPostRequest(httpMethod, userURL, ro)
134137
}
135138

136-
// This may be a PUT or PATCH request so we will just put the raw io.ReadCloser in the request body
139+
// This may be a PUT or PATCH request so we will just put the raw
140+
// io.ReadCloser in the request body
137141
// and guess the MIME type from the file name
138142

143+
// At the moment, we will only support 1 file upload as a time
144+
// when uploading using PUT or PATCH
145+
139146
req, err := http.NewRequest(httpMethod, userURL, ro.File.FileContents)
140147

141148
if err != nil {
@@ -246,16 +253,46 @@ func encodePostValues(postValues map[string]string) string {
246253
return urlValues.Encode() // This will sort all of the string values
247254
}
248255

256+
// proxySettings will default to the default proxy settings if none are provided
257+
// if settings are provided – they will override the environment variables
258+
func (ro RequestOptions) proxySettings(req *http.Request) (*url.URL, error) {
259+
// No proxies – lets use the default
260+
if len(ro.Proxies) == 0 {
261+
return http.ProxyFromEnvironment(req)
262+
}
263+
264+
// There was a proxy specified – do we support the protocol?
265+
if _, ok := ro.Proxies[req.URL.Scheme]; ok {
266+
return ro.Proxies[req.URL.Scheme], nil
267+
}
268+
269+
// Proxies were specified but not for any protocol that we use
270+
return http.ProxyFromEnvironment(req)
271+
272+
}
273+
274+
// useDefaultClient will tell the "client creator" if a custom client is needed
275+
// it checks the following items (and will create a custom client of these are)
276+
// true
277+
// 1. Do we want to accept invalid SSL certificates?
278+
// 2. Do we want to disable compression?
279+
// 3. Do we want a custom proxy?
280+
func (ro RequestOptions) dontUseDefaultClient() bool {
281+
return ro.InsecureSkipVerify == true ||
282+
ro.DisableCompression == true ||
283+
len(ro.Proxies) != 0
284+
}
285+
249286
func buildHTTPClient(ro *RequestOptions) *http.Client {
250287
// Does the user want to change the defaults?
251-
if ro.InsecureSkipVerify != true && ro.DisableCompression != true {
288+
if !ro.dontUseDefaultClient() {
252289
return http.DefaultClient
253290
}
254291

255292
return &http.Client{
256293
Transport: &http.Transport{
257294
// These are borrowed from the default transporter
258-
Proxy: http.ProxyFromEnvironment,
295+
Proxy: ro.proxySettings,
259296
Dial: (&net.Dialer{
260297
Timeout: 30 * time.Second,
261298
KeepAlive: 30 * time.Second,

response_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package grequests
22

33
import (
4-
"net/http"
54
"strconv"
65
"testing"
76
)
@@ -15,7 +14,7 @@ func TestResponseOk(t *testing.T) {
1514

1615
func verifyResponseOkForStatus(status int, t *testing.T) {
1716
url := "http://httpbin.org/status/" + strconv.Itoa(status)
18-
resp, err := buildResponse(http.Get(url))
17+
resp, err := Get(url, nil)
1918

2019
if err != nil {
2120
t.Error("Unable to make request", err)

0 commit comments

Comments
 (0)