Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 06baf8e

Browse files
committed
Merge pull request getlantern#4247 from getlantern/issue-4246
Added copying of headers to the fronted request closes #4246
2 parents 6c18b63 + 1ff6bed commit 06baf8e

File tree

2 files changed

+110
-22
lines changed

2 files changed

+110
-22
lines changed

src/github.com/getlantern/flashlight/util/http.go

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"net/http"
99
"net/url"
10+
"strings"
1011
"sync"
1112
"time"
1213

@@ -22,7 +23,7 @@ var (
2223

2324
// HTTPFetcher is a simple interface for types that are able to fetch data over HTTP.
2425
type HTTPFetcher interface {
25-
Do(req *http.Request) (*http.Response, error)
26+
Do(*http.Request) (*http.Response, error)
2627
}
2728

2829
func success(resp *http.Response) bool {
@@ -94,19 +95,49 @@ type dualFetcher struct {
9495
// arrive. Callers MUST use the Lantern-Fronted-URL HTTP header to
9596
// specify the fronted URL to use.
9697
func (df *dualFetcher) Do(req *http.Request) (*http.Response, error) {
98+
if directClient, err := HTTPClient("", df.cf.proxyAddrFN); err != nil {
99+
log.Errorf("Could not create http client? %v", err)
100+
return nil, err
101+
} else {
102+
frontedClient := fronted.NewDirectHttpClient(5 * time.Minute)
103+
return df.do(req, directClient.Do, frontedClient.Do)
104+
}
105+
}
106+
107+
// Do will attempt to execute the specified HTTP request using both
108+
// chained and fronted servers, simply returning the first response to
109+
// arrive. Callers MUST use the Lantern-Fronted-URL HTTP header to
110+
// specify the fronted URL to use.
111+
func (df *dualFetcher) do(req *http.Request, chainedFunc func(*http.Request) (*http.Response, error), ddfFunc func(*http.Request) (*http.Response, error)) (*http.Response, error) {
97112
log.Debugf("Using dual fronter")
98-
frontedUrl := req.Header.Get("Lantern-Fronted-URL")
113+
frontedURL := req.Header.Get("Lantern-Fronted-URL")
99114
req.Header.Del("Lantern-Fronted-URL")
100115

101-
if frontedUrl == "" {
116+
if frontedURL == "" {
102117
return nil, errors.New("Callers MUST specify the fronted URL in the Lantern-Fronted-URL header")
103118
}
119+
120+
// Make a copy of the original requeest headers to include in the fronted
121+
// request. This will ensure that things like the caching headers are
122+
// included in both requests.
123+
headersCopy := make(http.Header, len(req.Header))
124+
for k, vv := range req.Header {
125+
// Since we're doing domain fronting don't copy the host just in case
126+
// it ever makes any difference under the covers.
127+
if strings.EqualFold("Host", k) {
128+
continue
129+
}
130+
vv2 := make([]string, len(vv))
131+
copy(vv2, vv)
132+
headersCopy[k] = vv2
133+
}
134+
104135
responses := make(chan *http.Response, 2)
105136
errs := make(chan error, 2)
106137

107-
request := func(client HTTPFetcher, req *http.Request) error {
108-
if resp, err := client.Do(req); err != nil {
109-
log.Errorf("Could not complete request with: %v, %v", frontedUrl, err)
138+
request := func(clientFunc func(*http.Request) (*http.Response, error), req *http.Request) error {
139+
if resp, err := clientFunc(req); err != nil {
140+
log.Errorf("Could not complete request with: %v, %v", frontedURL, err)
110141
errs <- err
111142
return err
112143
} else {
@@ -115,43 +146,42 @@ func (df *dualFetcher) Do(req *http.Request) (*http.Response, error) {
115146
responses <- resp
116147
return nil
117148
} else {
118-
// If the local proxy can't connect to any upstread proxies, for example,
149+
// If the local proxy can't connect to any upstream proxies, for example,
119150
// it will return a 502.
120151
err := fmt.Errorf("Bad response code: %v", resp.StatusCode)
121-
_ = resp.Body.Close()
152+
if resp.Body != nil {
153+
_ = resp.Body.Close()
154+
}
122155
errs <- err
123156
return err
124157
}
125158
}
126159
}
127160

128161
go func() {
129-
if req, err := http.NewRequest("GET", frontedUrl, nil); err != nil {
130-
log.Errorf("Could not create request for: %v, %v", frontedUrl, err)
162+
if frontedReq, err := http.NewRequest("GET", frontedURL, nil); err != nil {
163+
log.Errorf("Could not create request for: %v, %v", frontedURL, err)
131164
errs <- err
132165
} else {
133166
log.Debug("Sending request via DDF")
134-
direct := fronted.NewDirectHttpClient(5 * time.Minute)
135-
if err := request(direct, req); err != nil {
167+
frontedReq.Header = headersCopy
168+
169+
if err := request(ddfFunc, frontedReq); err != nil {
136170
log.Errorf("Fronted request failed: %v", err)
137171
} else {
138172
log.Debug("Fronted request succeeded")
139173
}
140174
}
141175
}()
142176
go func() {
143-
if client, err := HTTPClient("", df.cf.proxyAddrFN); err != nil {
144-
log.Errorf("Could not create HTTP client: %v", err)
145-
errs <- err
177+
log.Debug("Sending chained request")
178+
if err := request(chainedFunc, req); err != nil {
179+
log.Errorf("Chained request failed %v", err)
146180
} else {
147-
log.Debug("Sending chained request")
148-
if err := request(client, req); err != nil {
149-
log.Errorf("Chained request failed %v", err)
150-
} else {
151-
log.Debug("Switching to chained fronter for future requests since it succeeded")
152-
df.cf.setFetcher(&chainedFetcher{df.cf.proxyAddrFN})
153-
}
181+
log.Debug("Switching to chained fronter for future requests since it succeeded")
182+
df.cf.setFetcher(&chainedFetcher{df.cf.proxyAddrFN})
154183
}
184+
155185
}()
156186

157187
// Create channels for the final response or error. The response channel will be filled

src/github.com/getlantern/flashlight/util/util_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package util
22

33
import (
4+
"bytes"
45
"crypto/x509"
56
"io/ioutil"
67
"net"
78
"net/http"
9+
"net/http/httputil"
810
"os"
911
"strings"
1012
"testing"
@@ -30,6 +32,62 @@ func TestGetFileHash(t *testing.T) {
3032
"hashes not equal! has hashes.go changed?")
3133
}
3234

35+
// TestChainedAndFrontedHeaders tests to make sure headers are correctly
36+
// copied to the fronted request from the original chained request.
37+
func TestChainedAndFrontedHeaders(t *testing.T) {
38+
geo := "http://d3u5fqukq7qrhd.cloudfront.net/lookup/198.199.72.101"
39+
req, err := http.NewRequest("GET", geo, nil)
40+
if !assert.NoError(t, err) {
41+
return
42+
}
43+
req.Header.Set("Lantern-Fronted-URL", geo)
44+
req.Header.Set("Accept", "application/x-gzip")
45+
// Prevents intermediate nodes (domain-fronters) from caching the content
46+
req.Header.Set("Cache-Control", "no-cache")
47+
etag := "473892jdfda"
48+
req.Header.Set("X-Lantern-If-None-Match", etag)
49+
50+
// Make sure the chained response fails.
51+
chainedFunc := func(req *http.Request) (*http.Response, error) {
52+
headers, _ := httputil.DumpRequest(req, false)
53+
log.Debugf("Got chained request headers:\n%v", string(headers))
54+
return &http.Response{
55+
Status: "503 OK",
56+
StatusCode: 503,
57+
}, nil
58+
}
59+
60+
frontedHeaders := eventual.NewValue()
61+
frontedFunc := func(req *http.Request) (*http.Response, error) {
62+
headers, _ := httputil.DumpRequest(req, false)
63+
log.Debugf("Got FRONTED request headers:\n%v", string(headers))
64+
frontedHeaders.Set(req.Header)
65+
return &http.Response{
66+
Status: "200 OK",
67+
StatusCode: 200,
68+
Body: ioutil.NopCloser(bytes.NewBufferString("Fronted")),
69+
}, nil
70+
}
71+
72+
df := &dualFetcher{&chainedAndFronted{}}
73+
74+
df.do(req, chainedFunc, frontedFunc)
75+
76+
headersVal, ok := frontedHeaders.Get(2 * time.Second)
77+
if !assert.True(t, ok) {
78+
return
79+
}
80+
headers := headersVal.(http.Header)
81+
assert.Equal(t, etag, headers.Get("X-Lantern-If-None-Match"))
82+
assert.Equal(t, "no-cache", headers.Get("Cache-Control"))
83+
84+
// There should not be a host header here -- the go http client will populate
85+
// it automatically based on the URL.
86+
assert.Equal(t, "", headers.Get("Host"))
87+
}
88+
89+
// TestChainedAndFronted tests to make sure chained and fronted requests are
90+
// both working in parallel.
3391
func TestChainedAndFronted(t *testing.T) {
3492
fwd, _ := forward.New()
3593

0 commit comments

Comments
 (0)