-
Notifications
You must be signed in to change notification settings - Fork 97
Support redirects in SERVICE requests
#2435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There is now a runtime parameter `service-max-redirects` (default: 1) that limits the number of redirects that will be followed for a single `SERVICE` request. To disable following redirects, set the parameter to 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for following HTTP redirects in SERVICE requests by introducing a configurable maximum redirect limit. The implementation handles redirect status codes (301, 302, 307, 308) and follows the Location header up to a specified limit.
Key changes:
- Added
service-max-redirectsruntime parameter (default: 1) to control redirect behavior - Extended HTTP client response structure to include Location header
- Implemented redirect following logic in Service.cpp with proper error handling
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/global/RuntimeParameters.h | Added serviceMaxRedirects_ parameter declaration |
| src/global/RuntimeParameters.cpp | Registered the new runtime parameter |
| src/util/http/HttpClient.h | Added location_ field to HttpOrHttpsResponse struct |
| src/util/http/HttpClient.cpp | Extract Location header from HTTP response |
| src/engine/Service.cpp | Implemented redirect following logic with configurable limits |
| test/util/HttpClientTestHelpers.h | Updated test helper to support location parameter |
| test/ServiceTest.cpp | Removed trailing periods from error messages |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
src/engine/Service.cpp
Outdated
| const size_t maxRedirects = | ||
| getRuntimeParameter<&RuntimeParameters::serviceMaxRedirects_>(); | ||
| size_t redirectCount = 0; | ||
| while (redirectCount <= maxRedirects) { |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The loop condition allows one more redirect than intended. When maxRedirects is 1, this allows 2 redirects (when redirectCount is 0 and 1). Change to redirectCount < maxRedirects to respect the exact limit.
| while (redirectCount <= maxRedirects) { | |
| while (redirectCount < maxRedirects) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first request is not a redirect. The second request is the first redirect. And so on ...
src/engine/Service.cpp
Outdated
|
|
||
| // If the loop exited because we exceeded the maximum number of redirects, | ||
| // throw a corresponding error. | ||
| if (redirectCount > maxRedirects) { |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition will never be true due to the loop condition above. The loop exits when redirectCount <= maxRedirects is false, meaning redirectCount equals maxRedirects + 1. Change to redirectCount == maxRedirects + 1 or adjust the loop condition.
| if (redirectCount > maxRedirects) { | |
| if (redirectCount == maxRedirects + 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not true, this condition is only true when there are redirects in every iteration, until the while condition is violated
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2435 +/- ##
=======================================
Coverage 91.45% 91.45%
=======================================
Files 462 462
Lines 46907 46925 +18
Branches 5240 5247 +7
=======================================
+ Hits 42899 42916 +17
Misses 2511 2511
- Partials 1497 1498 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
RobinTF
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think Service.cpp is the right place to add this functionality. This should be moved to HttpClient.cpp where HttpClientImpl<T>::sendRequest gets another parameter that allows it to follow redirects. This way different parts of the code could benefit from this behaviour.
Good point, I will try this |
src/util/http/HttpClient.cpp
Outdated
| std::string_view target, ad_utility::SharedCancellationHandle handle, | ||
| std::string_view requestBody, std::string_view contentTypeHeader, | ||
| std::string_view acceptHeader) { | ||
| std::string_view acceptHeader, [[maybe_unused]] size_t maxRedirects) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is unused this can be removed.
|
|
||
| // If it is a redirect, but there is no `Location` header, we cannot | ||
| // proceed and throw an error. | ||
| if (response.location_.empty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should comment that we explicitly don't want to change the request method here, even if browser would do it for 301 and 302.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobinTF I don't understand this comment, please explain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please have a look at the notes on https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/301 and https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302 .
Changing the request type to GET for 301 and 302 is technically allowed and is the standard behaviour for browsers, but this is of course not what we want here. So we should make it explicit that we just follow location headers and don't change behaviour based on status codes (technically the permanent redirects should be stored somewhere to avoid the extra step, but that's completely overkill for what we need here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobinTF By "make it explicit" you mean: Write it in the comment and the description? Or check it in the code and throw (or ignore) if it happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a comment should suffice
| std::string finalUrl = | ||
| absl::StrCat("http://localhost:", okServer.getPort()); | ||
|
|
||
| // Create `redirectServer` that points to `okServer`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating multiple servers that all run in their own threads. Couldn't you just create a single server, that returns a status code based on GET parameters? Like http://localhost/301?redirect=/200 conceptually? This way you can easily chain them by extending the parameters without having to write much more code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobinTF Is this important? It's just a test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not important from a functional perspective, but it avoids the overhead of starting and stopping servers which AFAIK takes rather long for a unit test.
Also I believe it would reduce the amount of code in the test.
hannahbast
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobinTF Thank your for your comments. I have addressed some of them, I am not sure about the others, see my comments there. Can I merge this now, once everything is green?
PS: Before my last changes, the partial test coverage of 90% in HttpTest was due to a branch that was already untested before.
Overview
Conformance check passed ✅No test result changes. |
|
RobinTF
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with your proposals, but I just realized that we should properly handle relative URLs, otherwise we end up having to deal with this issue again in the future.
| } | ||
|
|
||
| // Follow the redirect. | ||
| currentUrl = Url{response.location_}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something to consider still:
The Location header can be a relative URL. In that case it needs to be resolved relative to the previous URL, which is not the case here, so this currently might break on some redirects that don't post the whole URL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobinTF Yeah, I had it in the code, than removed it again, but should include it again



There is now a runtime parameter
service-max-redirectsthat limits the number of redirects that will be followed for a singleSERVICErequest. The default value is 1, that is, at most one redirect will be followed. To disable following redirects altogether, set the value to 0. In particular, this fixes the problem discussed in #2434