7
7
"net"
8
8
"net/http"
9
9
"net/url"
10
+ "strings"
10
11
"sync"
11
12
"time"
12
13
22
23
23
24
// HTTPFetcher is a simple interface for types that are able to fetch data over HTTP.
24
25
type HTTPFetcher interface {
25
- Do (req * http.Request ) (* http.Response , error )
26
+ Do (* http.Request ) (* http.Response , error )
26
27
}
27
28
28
29
func success (resp * http.Response ) bool {
@@ -94,19 +95,49 @@ type dualFetcher struct {
94
95
// arrive. Callers MUST use the Lantern-Fronted-URL HTTP header to
95
96
// specify the fronted URL to use.
96
97
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 ) {
97
112
log .Debugf ("Using dual fronter" )
98
- frontedUrl := req .Header .Get ("Lantern-Fronted-URL" )
113
+ frontedURL := req .Header .Get ("Lantern-Fronted-URL" )
99
114
req .Header .Del ("Lantern-Fronted-URL" )
100
115
101
- if frontedUrl == "" {
116
+ if frontedURL == "" {
102
117
return nil , errors .New ("Callers MUST specify the fronted URL in the Lantern-Fronted-URL header" )
103
118
}
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
+
104
135
responses := make (chan * http.Response , 2 )
105
136
errs := make (chan error , 2 )
106
137
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 )
110
141
errs <- err
111
142
return err
112
143
} else {
@@ -115,43 +146,42 @@ func (df *dualFetcher) Do(req *http.Request) (*http.Response, error) {
115
146
responses <- resp
116
147
return nil
117
148
} 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,
119
150
// it will return a 502.
120
151
err := fmt .Errorf ("Bad response code: %v" , resp .StatusCode )
121
- _ = resp .Body .Close ()
152
+ if resp .Body != nil {
153
+ _ = resp .Body .Close ()
154
+ }
122
155
errs <- err
123
156
return err
124
157
}
125
158
}
126
159
}
127
160
128
161
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 )
131
164
errs <- err
132
165
} else {
133
166
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 {
136
170
log .Errorf ("Fronted request failed: %v" , err )
137
171
} else {
138
172
log .Debug ("Fronted request succeeded" )
139
173
}
140
174
}
141
175
}()
142
176
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 )
146
180
} 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 })
154
183
}
184
+
155
185
}()
156
186
157
187
// Create channels for the final response or error. The response channel will be filled
0 commit comments