Skip to content

Commit

Permalink
Merge pull request #108 from redpanda-data/http-no-redirect
Browse files Browse the repository at this point in the history
feat: add follow_redirects field to http processor
  • Loading branch information
Jeffail authored Oct 18, 2024
2 parents 4e8383b + 02fc2ac commit 445ec88
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ Changelog

All notable changes to this project will be documented in this file.

## 4.40.0 - TBD

### Added

- Field `follow_redirects` added to the `http` processor. (@ooesili)

## 4.39.0 - 2024-10-14

### Added
Expand Down
23 changes: 17 additions & 6 deletions internal/httpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ type Client struct {
clientCancel func()

// Request execution and retry logic
rateLimit string
numRetries int
retryThrottle *throttle.Type
backoffOn map[int]struct{}
dropOn map[int]struct{}
successOn map[int]struct{}
rateLimit string
numRetries int
followRedirects bool
retryThrottle *throttle.Type
backoffOn map[int]struct{}
dropOn map[int]struct{}
successOn map[int]struct{}

// Response extraction
metaExtractFilter *service.MetadataFilter
Expand Down Expand Up @@ -75,6 +76,13 @@ func NewClientFromOldConfig(conf OldConfig, mgr *service.Resources, opts ...Requ
h.client.Timeout = conf.Timeout
}

h.followRedirects = conf.FollowRedirects
if !h.followRedirects {
h.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
}

if conf.TLSEnabled && conf.TLSConf != nil {
if c, ok := http.DefaultTransport.(*http.Transport); ok {
cloned := c.Clone()
Expand Down Expand Up @@ -293,6 +301,9 @@ func (h *Client) checkStatus(code int) (succeeded bool, retStrat retryStrategy)
if _, exists := h.successOn[code]; exists {
return true, noRetry
}
if !h.followRedirects && code >= 300 && code <= 399 {
return true, noRetry
}
if code < 200 || code > 299 {
return false, retryLinear
}
Expand Down
32 changes: 32 additions & 0 deletions internal/httpclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -794,3 +794,35 @@ tls:
require.NoError(t, err)
assert.Equal(t, "HELLO WORLD", string(mBytes))
}

func TestHTTPClientNoFollowRedirects(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://example.com", http.StatusMovedPermanently)
}))
defer ts.Close()

conf := clientConfig(t, `
url: %v
follow_redirects: false
extract_headers:
include_patterns:
- '^location$'
`, ts.URL)

h, err := NewClientFromOldConfig(conf, service.MockResources())
require.NoError(t, err)

resBatch, err := h.Send(context.Background(), service.MessageBatch{
service.NewMessage([]byte("hello world")),
})
require.NoError(t, err)
require.Len(t, resBatch, 1)

status, ok := resBatch[0].MetaGet("http_status_code")
require.True(t, ok)
require.Equal(t, "301", status)

location, ok := resBatch[0].MetaGet("location")
require.True(t, ok)
require.Equal(t, "https://example.com", location)
}
9 changes: 9 additions & 0 deletions internal/httpclient/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
hcFieldRetryPeriod = "retry_period"
hcFieldMaxRetryBackoff = "max_retry_backoff"
hcFieldRetries = "retries"
hcFieldFollowRedirects = "follow_redirects"
hcFieldBackoffOn = "backoff_on"
hcFieldDropOn = "drop_on"
hcFieldSuccessfulOn = "successful_on"
Expand Down Expand Up @@ -85,6 +86,10 @@ func ConfigField(defaultVerb string, forOutput bool, extraChildren ...*service.C
Description("The maximum number of retry attempts to make.").
Advanced().
Default(3),
service.NewBoolField(hcFieldFollowRedirects).
Description("Whether or not to transparently follow redirects, i.e. responses with 300-399 status codes. If disabled, the response message will contain the body, status, and headers from the redirect response and the processor will not make a request to the URL set in the Location header of the response.").
Advanced().
Default(true),
service.NewIntListField(hcFieldBackoffOn).
Description("A list of status codes whereby the request should be considered to have failed and retries should be attempted, but the period between them should be increased gradually.").
Advanced().
Expand Down Expand Up @@ -140,6 +145,9 @@ func ConfigFromParsed(pConf *service.ParsedConfig) (conf OldConfig, err error) {
if conf.NumRetries, err = pConf.FieldInt(hcFieldRetries); err != nil {
return
}
if conf.FollowRedirects, err = pConf.FieldBool(hcFieldFollowRedirects); err != nil {
return
}
if conf.BackoffOn, err = pConf.FieldIntList(hcFieldBackoffOn); err != nil {
return
}
Expand Down Expand Up @@ -175,6 +183,7 @@ type OldConfig struct {
Retry time.Duration
MaxBackoff time.Duration
NumRetries int
FollowRedirects bool
BackoffOn []int
DropOn []int
SuccessfulOn []int
Expand Down

0 comments on commit 445ec88

Please sign in to comment.