From 7b067c7ee849e2c16b57f67e4930fffddd2d1ced Mon Sep 17 00:00:00 2001 From: Simon Lynch Date: Wed, 23 Apr 2025 18:16:40 +1000 Subject: [PATCH 1/2] Implement follow_redirects feature in HTTP data source - Added a new optional attribute `follow_redirects` to control HTTP redirect behavior. - Updated the `Read` method to handle the no-follow behavior when `follow_redirects` is set to false. - Introduced a new test `TestDataSource_FollowRedirects` to verify the functionality of the redirect handling. --- internal/provider/data_source_http.go | 13 ++++++ internal/provider/data_source_http_test.go | 46 ++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/internal/provider/data_source_http.go b/internal/provider/data_source_http.go index 0ab7b095..3a6c40db 100644 --- a/internal/provider/data_source_http.go +++ b/internal/provider/data_source_http.go @@ -171,6 +171,11 @@ a 5xx-range (except 501) status code is received. For further details see Description: `The HTTP response status code.`, Computed: true, }, + + "follow_redirects": schema.BoolAttribute{ + Description: "If false, do not follow HTTP redirects. Defaults to true.", + Optional: true, + }, }, Blocks: map[string]schema.Block{ @@ -295,6 +300,13 @@ func (d *httpDataSource) Read(ctx context.Context, req datasource.ReadRequest, r retryClient := retryablehttp.NewClient() retryClient.HTTPClient.Transport = clonedTr + // Configure no-follow behavior when follow_redirects is explicitly false + if !model.FollowRedirects.IsNull() && !model.FollowRedirects.ValueBool() { + retryClient.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + } + var timeout time.Duration if model.RequestTimeout.ValueInt64() > 0 { @@ -437,6 +449,7 @@ type modelV0 struct { Body types.String `tfsdk:"body"` ResponseBodyBase64 types.String `tfsdk:"response_body_base64"` StatusCode types.Int64 `tfsdk:"status_code"` + FollowRedirects types.Bool `tfsdk:"follow_redirects"` } type retryModel struct { diff --git a/internal/provider/data_source_http_test.go b/internal/provider/data_source_http_test.go index 10dab105..30336662 100644 --- a/internal/provider/data_source_http_test.go +++ b/internal/provider/data_source_http_test.go @@ -1020,6 +1020,52 @@ func TestDataSource_ResponseBodyBinary(t *testing.T) { }) } +// TestDataSource_FollowRedirects verifies that the follow_redirects flag controls redirect behavior. +func TestDataSource_FollowRedirects(t *testing.T) { + // Target server returns 200 OK + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + })) + defer target.Close() + + // Redirect server sends 302 to target + redirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, target.URL, http.StatusFound) + })) + defer redirect.Close() + + resource.UnitTest(t, resource.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + data "http" "http_test" { + url = %q + follow_redirects = false + } + `, redirect.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "status_code", fmt.Sprintf("%d", http.StatusFound)), + resource.TestCheckResourceAttr("data.http.http_test", "response_headers.Location", target.URL), + ), + }, + { + Config: fmt.Sprintf(` + data "http" "http_test" { + url = %q + } + `, redirect.URL), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.http.http_test", "status_code", fmt.Sprintf("%d", http.StatusOK)), + resource.TestCheckResourceAttr("data.http.http_test", "response_body", "OK"), + ), + }, + }, + }) +} + func checkServerAndProxyRequestCount(proxyRequestCount, serverRequestCount *int) resource.TestCheckFunc { return func(_ *terraform.State) error { if *proxyRequestCount != *serverRequestCount { From 9343fd598e196bf67eb15c538fab6e65e4fcefb4 Mon Sep 17 00:00:00 2001 From: Simon Lynch Date: Mon, 28 Apr 2025 14:08:34 +1000 Subject: [PATCH 2/2] Add follow_redirects option to HTTP data source configuration --- docs/data-sources/http.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/data-sources/http.md b/docs/data-sources/http.md index 23ddb4e2..58cd916f 100644 --- a/docs/data-sources/http.md +++ b/docs/data-sources/http.md @@ -151,6 +151,7 @@ resource "null_resource" "example" { - `ca_cert_pem` (String) Certificate Authority (CA) in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. - `client_cert_pem` (String) Client certificate in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. - `client_key_pem` (String) Client key in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. +- `follow_redirects` (Boolean) If false, do not follow HTTP redirects. Defaults to true. - `insecure` (Boolean) Disables verification of the server's certificate chain and hostname. Defaults to `false` - `method` (String) The HTTP Method for the request. Allowed methods are a subset of methods defined in [RFC7231](https://datatracker.ietf.org/doc/html/rfc7231#section-4.3) namely, `GET`, `HEAD`, and `POST`. `POST` support is only intended for read-only URLs, such as submitting a search. - `request_body` (String) The request body as a string.