@@ -13,6 +13,7 @@ import (
13
13
"reflect"
14
14
"regexp"
15
15
"strings"
16
+ "sync"
16
17
"time"
17
18
18
19
"github.com/buzzfeed/sso/internal/pkg/aead"
@@ -145,14 +146,23 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
145
146
}
146
147
147
148
// upstreamTransport is used to ensure that upstreams cannot override the
148
- // security headers applied by sso_proxy
149
+ // security headers applied by sso_proxy. Also includes functionality to rotate
150
+ // http.Transport objects to ensure tcp connection reset deadlines are not exceeded
149
151
type upstreamTransport struct {
150
- transport * http.Transport
152
+ mux sync.Mutex
153
+
154
+ deadAfter time.Time
155
+ resetDeadline time.Duration
156
+
157
+ transport * http.Transport
158
+ insecureSkipVerify bool
151
159
}
152
160
153
161
// RoundTrip round trips the request and deletes security headers before returning the response.
154
162
func (t * upstreamTransport ) RoundTrip (req * http.Request ) (* http.Response , error ) {
155
- resp , err := t .transport .RoundTrip (req )
163
+ transport := t .getTransport ()
164
+
165
+ resp , err := transport .RoundTrip (req )
156
166
if err != nil {
157
167
logger := log .NewLogEntry ()
158
168
logger .Error (err , "error in upstreamTransport RoundTrip" )
@@ -164,9 +174,14 @@ func (t *upstreamTransport) RoundTrip(req *http.Request) (*http.Response, error)
164
174
return resp , err
165
175
}
166
176
167
- func newUpstreamTransport (insecureSkipVerify bool ) * upstreamTransport {
168
- return & upstreamTransport {
169
- transport : & http.Transport {
177
+ // getTransport gets either a cached http transport or allocates a new one
178
+ func (t * upstreamTransport ) getTransport () * http.Transport {
179
+ t .mux .Lock ()
180
+ defer t .mux .Unlock ()
181
+
182
+ if t .transport == nil || time .Now ().After (t .deadAfter ) {
183
+ t .deadAfter = time .Now ().Add (t .resetDeadline )
184
+ t .transport = & http.Transport {
170
185
Proxy : http .ProxyFromEnvironment ,
171
186
DialContext : (& net.Dialer {
172
187
Timeout : 30 * time .Second ,
@@ -176,17 +191,22 @@ func newUpstreamTransport(insecureSkipVerify bool) *upstreamTransport {
176
191
MaxIdleConns : 100 ,
177
192
IdleConnTimeout : 90 * time .Second ,
178
193
TLSHandshakeTimeout : 10 * time .Second ,
179
- TLSClientConfig : & tls.Config {InsecureSkipVerify : insecureSkipVerify },
194
+ TLSClientConfig : & tls.Config {InsecureSkipVerify : t . insecureSkipVerify },
180
195
ExpectContinueTimeout : 1 * time .Second ,
181
- },
196
+ }
182
197
}
198
+
199
+ return t .transport
183
200
}
184
201
185
202
// NewReverseProxy creates a reverse proxy to a specified url.
186
203
// It adds an X-Forwarded-Host header that is the request's host.
187
204
func NewReverseProxy (to * url.URL , config * UpstreamConfig ) * httputil.ReverseProxy {
188
205
proxy := httputil .NewSingleHostReverseProxy (to )
189
- proxy .Transport = newUpstreamTransport (config .TLSSkipVerify )
206
+ proxy .Transport = & upstreamTransport {
207
+ resetDeadline : config .ResetDeadline ,
208
+ insecureSkipVerify : config .TLSSkipVerify ,
209
+ }
190
210
director := proxy .Director
191
211
proxy .Director = func (req * http.Request ) {
192
212
req .Header .Add ("X-Forwarded-Host" , req .Host )
@@ -203,7 +223,10 @@ func NewReverseProxy(to *url.URL, config *UpstreamConfig) *httputil.ReverseProxy
203
223
// It adds an X-Forwarded-Host header to the the upstream's request.
204
224
func NewRewriteReverseProxy (route * RewriteRoute , config * UpstreamConfig ) * httputil.ReverseProxy {
205
225
proxy := & httputil.ReverseProxy {}
206
- proxy .Transport = newUpstreamTransport (config .TLSSkipVerify )
226
+ proxy .Transport = & upstreamTransport {
227
+ resetDeadline : config .ResetDeadline ,
228
+ insecureSkipVerify : config .TLSSkipVerify ,
229
+ }
207
230
proxy .Director = func (req * http.Request ) {
208
231
// we do this to rewrite requests
209
232
rewritten := route .FromRegex .ReplaceAllString (req .Host , route .ToTemplate .Opaque )
0 commit comments