Do not follow redirects for the health check URLs
This commit is contained in:
parent
8c271cf40c
commit
18d90ecd96
16 changed files with 145 additions and 27 deletions
|
@ -142,6 +142,7 @@
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.port=42"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.port=42"
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar"
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar"
|
||||||
|
- "traefik.http.services.service01.loadbalancer.healthcheck.followredirects=true"
|
||||||
- "traefik.http.services.service01.loadbalancer.passhostheader=true"
|
- "traefik.http.services.service01.loadbalancer.passhostheader=true"
|
||||||
- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar"
|
- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar"
|
||||||
- "traefik.http.services.service01.loadbalancer.sticky=true"
|
- "traefik.http.services.service01.loadbalancer.sticky=true"
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
interval = "foobar"
|
interval = "foobar"
|
||||||
timeout = "foobar"
|
timeout = "foobar"
|
||||||
hostname = "foobar"
|
hostname = "foobar"
|
||||||
|
followRedirects = true
|
||||||
[http.services.Service01.loadBalancer.healthCheck.headers]
|
[http.services.Service01.loadBalancer.healthCheck.headers]
|
||||||
name0 = "foobar"
|
name0 = "foobar"
|
||||||
name1 = "foobar"
|
name1 = "foobar"
|
||||||
|
|
|
@ -62,6 +62,7 @@ http:
|
||||||
interval: foobar
|
interval: foobar
|
||||||
timeout: foobar
|
timeout: foobar
|
||||||
hostname: foobar
|
hostname: foobar
|
||||||
|
followRedirects: true
|
||||||
headers:
|
headers:
|
||||||
name0: foobar
|
name0: foobar
|
||||||
name1: foobar
|
name1: foobar
|
||||||
|
|
|
@ -156,6 +156,7 @@
|
||||||
| `traefik/http/routers/Router1/tls/domains/1/sans/0` | `foobar` |
|
| `traefik/http/routers/Router1/tls/domains/1/sans/0` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/tls/domains/1/sans/1` | `foobar` |
|
| `traefik/http/routers/Router1/tls/domains/1/sans/1` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/tls/options` | `foobar` |
|
| `traefik/http/routers/Router1/tls/options` | `foobar` |
|
||||||
|
| `traefik/http/services/Service01/loadBalancer/healthCheck/followRedirects` | `true` |
|
||||||
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0` | `foobar` |
|
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0` | `foobar` |
|
||||||
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` |
|
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` |
|
||||||
| `traefik/http/services/Service01/loadBalancer/healthCheck/hostname` | `foobar` |
|
| `traefik/http/services/Service01/loadBalancer/healthCheck/hostname` | `foobar` |
|
||||||
|
|
|
@ -140,6 +140,7 @@
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.port": "42",
|
"traefik.http.services.service01.loadbalancer.healthcheck.port": "42",
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar",
|
"traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar",
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar",
|
"traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar",
|
||||||
|
"traefik.http.services.service01.loadbalancer.healthcheck.followredirects": "true",
|
||||||
"traefik.http.services.service01.loadbalancer.passhostheader": "true",
|
"traefik.http.services.service01.loadbalancer.passhostheader": "true",
|
||||||
"traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar",
|
"traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar",
|
||||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
|
"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
|
||||||
|
|
|
@ -193,6 +193,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass
|
||||||
traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10
|
traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||||
|
|
||||||
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
traefik.http.services.myservice.loadbalancer.healthcheck.followredirects=true
|
||||||
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
|
@ -326,6 +326,14 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||||
|
|
||||||
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.followredirects=true"
|
||||||
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
|
@ -224,6 +224,14 @@ For example, to change the passHostHeader behavior, you'd add the label `"traefi
|
||||||
"traefik.http.services.myservice.loadbalancer.healthcheck.timeout": "10"
|
"traefik.http.services.myservice.loadbalancer.healthcheck.timeout": "10"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||||
|
|
||||||
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"traefik.http.services.myservice.loadbalancer.healthcheck.followredirects": "true"
|
||||||
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
|
@ -230,6 +230,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||||
|
|
||||||
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.followredirects=true"
|
||||||
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
|
@ -240,6 +240,7 @@ Below are the available options for the health check mechanism:
|
||||||
- `interval` defines the frequency of the health check calls.
|
- `interval` defines the frequency of the health check calls.
|
||||||
- `timeout` defines the maximum duration Traefik will wait for a health check request before considering the server failed (unhealthy).
|
- `timeout` defines the maximum duration Traefik will wait for a health check request before considering the server failed (unhealthy).
|
||||||
- `headers` defines custom headers to be sent to the health check endpoint.
|
- `headers` defines custom headers to be sent to the health check endpoint.
|
||||||
|
- `followRedirects` defines whether redirects should be followed during the health check calls (default: true).
|
||||||
|
|
||||||
!!! info "Interval & Timeout Format"
|
!!! info "Interval & Timeout Format"
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,14 @@ type HealthCheck struct {
|
||||||
// FIXME change string to types.Duration
|
// FIXME change string to types.Duration
|
||||||
Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty"`
|
Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty"`
|
||||||
// FIXME change string to types.Duration
|
// FIXME change string to types.Duration
|
||||||
Timeout string `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty"`
|
Timeout string `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||||
Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"`
|
||||||
Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
|
FollowRedirects *bool `json:"followRedirects" toml:"followRedirects" yaml:"followRedirects"`
|
||||||
|
Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults Default values for a HealthCheck.
|
||||||
|
func (h *HealthCheck) SetDefaults() {
|
||||||
|
fr := true
|
||||||
|
h.FollowRedirects = &fr
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.http.services.Service0.loadbalancer.healthcheck.port": "42",
|
"traefik.http.services.Service0.loadbalancer.healthcheck.port": "42",
|
||||||
"traefik.http.services.Service0.loadbalancer.healthcheck.scheme": "foobar",
|
"traefik.http.services.Service0.loadbalancer.healthcheck.scheme": "foobar",
|
||||||
"traefik.http.services.Service0.loadbalancer.healthcheck.timeout": "foobar",
|
"traefik.http.services.Service0.loadbalancer.healthcheck.timeout": "foobar",
|
||||||
|
"traefik.http.services.Service0.loadbalancer.healthcheck.followredirects": "true",
|
||||||
"traefik.http.services.Service0.loadbalancer.passhostheader": "true",
|
"traefik.http.services.Service0.loadbalancer.passhostheader": "true",
|
||||||
"traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "foobar",
|
"traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "foobar",
|
||||||
"traefik.http.services.Service0.loadbalancer.server.scheme": "foobar",
|
"traefik.http.services.Service0.loadbalancer.server.scheme": "foobar",
|
||||||
|
@ -157,6 +158,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.http.services.Service1.loadbalancer.healthcheck.port": "42",
|
"traefik.http.services.Service1.loadbalancer.healthcheck.port": "42",
|
||||||
"traefik.http.services.Service1.loadbalancer.healthcheck.scheme": "foobar",
|
"traefik.http.services.Service1.loadbalancer.healthcheck.scheme": "foobar",
|
||||||
"traefik.http.services.Service1.loadbalancer.healthcheck.timeout": "foobar",
|
"traefik.http.services.Service1.loadbalancer.healthcheck.timeout": "foobar",
|
||||||
|
"traefik.http.services.Service1.loadbalancer.healthcheck.followredirects": "true",
|
||||||
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
|
"traefik.http.services.Service1.loadbalancer.passhostheader": "true",
|
||||||
"traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "foobar",
|
"traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "foobar",
|
||||||
"traefik.http.services.Service1.loadbalancer.server.scheme": "foobar",
|
"traefik.http.services.Service1.loadbalancer.server.scheme": "foobar",
|
||||||
|
@ -595,6 +597,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"name0": "foobar",
|
"name0": "foobar",
|
||||||
"name1": "foobar",
|
"name1": "foobar",
|
||||||
},
|
},
|
||||||
|
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||||
},
|
},
|
||||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
@ -621,6 +624,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"name0": "foobar",
|
"name0": "foobar",
|
||||||
"name1": "foobar",
|
"name1": "foobar",
|
||||||
},
|
},
|
||||||
|
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||||
},
|
},
|
||||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||||
|
|
|
@ -47,19 +47,20 @@ type metricsRegistry interface {
|
||||||
|
|
||||||
// Options are the public health check options.
|
// Options are the public health check options.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
Hostname string
|
Hostname string
|
||||||
Scheme string
|
Scheme string
|
||||||
Path string
|
Path string
|
||||||
Port int
|
Port int
|
||||||
Transport http.RoundTripper
|
FollowRedirects bool
|
||||||
Interval time.Duration
|
Transport http.RoundTripper
|
||||||
Timeout time.Duration
|
Interval time.Duration
|
||||||
LB Balancer
|
Timeout time.Duration
|
||||||
|
LB Balancer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt Options) String() string {
|
func (opt Options) String() string {
|
||||||
return fmt.Sprintf("[Hostname: %s Headers: %v Path: %s Port: %d Interval: %s Timeout: %s]", opt.Hostname, opt.Headers, opt.Path, opt.Port, opt.Interval, opt.Timeout)
|
return fmt.Sprintf("[Hostname: %s Headers: %v Path: %s Port: %d Interval: %s Timeout: %s FollowRedirects: %v]", opt.Hostname, opt.Headers, opt.Path, opt.Port, opt.Interval, opt.Timeout, opt.FollowRedirects)
|
||||||
}
|
}
|
||||||
|
|
||||||
type backendURL struct {
|
type backendURL struct {
|
||||||
|
@ -239,6 +240,12 @@ func checkHealth(serverURL *url.URL, backend *BackendConfig) error {
|
||||||
Transport: backend.Options.Transport,
|
Transport: backend.Options.Transport,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !backend.FollowRedirects {
|
||||||
|
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("HTTP request failed: %s", err)
|
return fmt.Errorf("HTTP request failed: %s", err)
|
||||||
|
|
|
@ -469,3 +469,57 @@ func TestLBStatusUpdater(t *testing.T) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotFollowingRedirects(t *testing.T) {
|
||||||
|
redirectServerCalled := false
|
||||||
|
redirectTestServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
redirectServerCalled = true
|
||||||
|
}))
|
||||||
|
defer redirectTestServer.Close()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
rw.Header().Add("location", redirectTestServer.URL)
|
||||||
|
rw.WriteHeader(http.StatusSeeOther)
|
||||||
|
cancel()
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
lb := &testLoadBalancer{
|
||||||
|
RWMutex: &sync.RWMutex{},
|
||||||
|
servers: []*url.URL{testhelpers.MustParseURL(server.URL)},
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := NewBackendConfig(Options{
|
||||||
|
Path: "/path",
|
||||||
|
Interval: healthCheckInterval,
|
||||||
|
Timeout: healthCheckTimeout,
|
||||||
|
LB: lb,
|
||||||
|
FollowRedirects: false,
|
||||||
|
}, "backendName")
|
||||||
|
|
||||||
|
check := HealthCheck{
|
||||||
|
Backends: make(map[string]*BackendConfig),
|
||||||
|
metrics: testhelpers.NewCollectingHealthCheckMetrics(),
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
check.execute(ctx, backend)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
timeout := time.Duration(int(healthCheckInterval) + 500)
|
||||||
|
select {
|
||||||
|
case <-time.After(timeout):
|
||||||
|
t.Fatal("test did not complete in time")
|
||||||
|
case <-ctx.Done():
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, redirectServerCalled, "HTTP redirect must not be followed")
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0": "foobar",
|
"traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0": "foobar",
|
||||||
"traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1": "foobar",
|
"traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1": "foobar",
|
||||||
"traefik/http/services/Service01/loadBalancer/healthCheck/scheme": "foobar",
|
"traefik/http/services/Service01/loadBalancer/healthCheck/scheme": "foobar",
|
||||||
|
"traefik/http/services/Service01/loadBalancer/healthCheck/followredirects": "true",
|
||||||
"traefik/http/services/Service01/loadBalancer/responseForwarding/flushInterval": "foobar",
|
"traefik/http/services/Service01/loadBalancer/responseForwarding/flushInterval": "foobar",
|
||||||
"traefik/http/services/Service01/loadBalancer/passHostHeader": "true",
|
"traefik/http/services/Service01/loadBalancer/passHostHeader": "true",
|
||||||
"traefik/http/services/Service01/loadBalancer/sticky/cookie/name": "foobar",
|
"traefik/http/services/Service01/loadBalancer/sticky/cookie/name": "foobar",
|
||||||
|
@ -608,12 +609,13 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HealthCheck: &dynamic.HealthCheck{
|
HealthCheck: &dynamic.HealthCheck{
|
||||||
Scheme: "foobar",
|
Scheme: "foobar",
|
||||||
Path: "foobar",
|
Path: "foobar",
|
||||||
Port: 42,
|
Port: 42,
|
||||||
Interval: "foobar",
|
Interval: "foobar",
|
||||||
Timeout: "foobar",
|
Timeout: "foobar",
|
||||||
Hostname: "foobar",
|
Hostname: "foobar",
|
||||||
|
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
"name0": "foobar",
|
"name0": "foobar",
|
||||||
"name1": "foobar",
|
"name1": "foobar",
|
||||||
|
|
|
@ -262,15 +262,21 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.Balancer, backe
|
||||||
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend, interval)
|
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
followRedirects := true
|
||||||
|
if hc.FollowRedirects != nil {
|
||||||
|
followRedirects = *hc.FollowRedirects
|
||||||
|
}
|
||||||
|
|
||||||
return &healthcheck.Options{
|
return &healthcheck.Options{
|
||||||
Scheme: hc.Scheme,
|
Scheme: hc.Scheme,
|
||||||
Path: hc.Path,
|
Path: hc.Path,
|
||||||
Port: hc.Port,
|
Port: hc.Port,
|
||||||
Interval: interval,
|
Interval: interval,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
LB: lb,
|
LB: lb,
|
||||||
Hostname: hc.Hostname,
|
Hostname: hc.Hostname,
|
||||||
Headers: hc.Headers,
|
Headers: hc.Headers,
|
||||||
|
FollowRedirects: followRedirects,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue