diff --git a/apidef/api_definitions.go b/apidef/api_definitions.go index 3e9875e3e381..92ed45f45e58 100644 --- a/apidef/api_definitions.go +++ b/apidef/api_definitions.go @@ -374,6 +374,7 @@ type APIDefinition struct { Transport struct { SSLCipherSuites []string `bson:"ssl_ciphers" json:"ssl_ciphers"` SSLMinVersion uint16 `bson:"ssl_min_version" json:"ssl_min_version"` + ProxyURL string `bson: "proxy_url" json: "proxy_url"` } `bson:"transport" json:"transport"` } `bson:"proxy" json:"proxy"` DisableRateLimit bool `bson:"disable_rate_limit" json:"disable_rate_limit"` diff --git a/batch_requests.go b/batch_requests.go index 6d7b55122763..137342dc69a7 100644 --- a/batch_requests.go +++ b/batch_requests.go @@ -51,6 +51,8 @@ func (b *BatchRequestHandler) doRequest(req *http.Request, relURL string) BatchR tr.DialTLS = dialTLSPinnedCheck(b.API, tr.TLSClientConfig) + tr.Proxy = proxyFromAPI(b.API) + client := &http.Client{Transport: tr} resp, err := client.Do(req) diff --git a/cert_test.go b/cert_test.go index 8a5a5e9ff9e4..93ab854cf925 100644 --- a/cert_test.go +++ b/cert_test.go @@ -709,7 +709,7 @@ func TestCipherSuites(t *testing.T) { }) } -func TestProxyCipherSuites(t *testing.T) { +func TestProxyTransport(t *testing.T) { upstream := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("test")) })) @@ -760,4 +760,17 @@ func TestProxyCipherSuites(t *testing.T) { ts.Run(t, test.TestCase{Path: "/", Code: 500}) }) + + t.Run("API: Proxy", func(t *testing.T) { + config.Global.ProxySSLMinVersion = 771 + buildAndLoadAPI(func(spec *APISpec) { + spec.Proxy.ListenPath = "/" + spec.Proxy.TargetURL = upstream.URL + spec.Proxy.Transport.SSLCipherSuites = []string{"TLS_RSA_WITH_AES_128_CBC_SHA"} + // Invalid proxy + spec.Proxy.Transport.ProxyURL = upstream.URL + }) + + ts.Run(t, test.TestCase{Path: "/", Code: 500}) + }) } diff --git a/mw_js_plugin.go b/mw_js_plugin.go index efc252c030b0..4e9d1505ee5f 100644 --- a/mw_js_plugin.go +++ b/mw_js_plugin.go @@ -498,6 +498,8 @@ func (j *JSVM) LoadTykJSApi() { tr.DialTLS = dialTLSPinnedCheck(j.Spec, tr.TLSClientConfig) + tr.Proxy = proxyFromAPI(j.Spec) + // using new Client each time should be ok, since we closing connection every time client := &http.Client{Transport: tr} resp, err := client.Do(r) diff --git a/reverse_proxy.go b/reverse_proxy.go index 41b4c500666c..116a9bfba27d 100644 --- a/reverse_proxy.go +++ b/reverse_proxy.go @@ -306,7 +306,6 @@ func defaultTransport() *http.Transport { // allocate a new one every time for now, to avoid modifying a // global variable for each request's needs (e.g. timeouts). return &http.Transport{ - Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, @@ -421,9 +420,19 @@ func (p *ReverseProxy) CheckCircuitBreakerEnforced(spec *APISpec, req *http.Requ return false, nil } +func proxyFromAPI(api *APISpec) func(*http.Request) (*url.URL, error) { + return func(req *http.Request) (*url.URL, error) { + if api != nil && api.Proxy.Transport.ProxyURL != "" { + return url.Parse(api.Proxy.Transport.ProxyURL) + } + return http.ProxyFromEnvironment(req) + } +} + func httpTransport(timeOut int, rw http.ResponseWriter, req *http.Request, p *ReverseProxy) http.RoundTripper { transport := defaultTransport() // modifies a newly created transport transport.TLSClientConfig = &tls.Config{} + transport.Proxy = proxyFromAPI(p.TykAPISpec) if config.Global.ProxySSLInsecureSkipVerify { transport.TLSClientConfig.InsecureSkipVerify = true