-
Notifications
You must be signed in to change notification settings - Fork 118
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
Disable following of redirects by default #454
Conversation
@macisamuele Are you able to review? Also, do you understand the failure in CI for mypy? I don't understand why attributes have gone missing. |
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.
@mattdowdell thanks a lot for contributing to the project.
I would suggest to add an additional test to ensure that redirects are followed as well as ensuring that requests and fido clients will behave consistently.
bravado/http_future.py
Outdated
@@ -199,7 +199,7 @@ def response( | |||
|
|||
swagger_result = self._get_swagger_result(incoming_response) | |||
|
|||
if self.operation is None and incoming_response.status_code >= 300: | |||
if self.operation is None and incoming_response.status_code >= 400: |
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 would not change this behavior (at least on this PR) as users might rely on this.
You're right to mention that RFC7231 does not consider them errors.
As this is independent from the specific issue you're trying to solve (following redirects) I would suggest to create an issue for this and eventually publish a new PR.
NOTE: changing this (so 3xx do not raise exception) requires a major version bump
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.
Done. Instead I check the response stored in the exception for the relevant information.
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.
Raised #455 to track not raising exceptions when redirects are returned.
bravado/testing/integration_test.py
Outdated
@@ -473,6 +492,16 @@ def test_msgpack_support(self, swagger_http_server): | |||
assert response.headers['Content-Type'] == APP_MSGPACK | |||
assert unpackb(response.raw_bytes, encoding='utf-8') == API_RESPONSE | |||
|
|||
def test_redirects_are_not_followed(self, swagger_http_server): |
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 would expect a test that ensures that redirects are followed as well.
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.
Done, but only for the RequestsClient
as it doesn't look like the FidoClient
supports the ability to follow redirects.
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 think that we should be doing a small effort here to ensure that the two clients behave the same here.
I'll try to look for pointers to help you out here
Fido does not really support following redirects. I would say that at this point is ok to allow following redirects as best-effort. Eventually we might just want to remove the redirect support completely.
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.
That was the conclusion I came to. The logigcal place to add support for following redirects in Fido, would be in prepare_request_for_twisted. The retuened value from that in turn goes into fido.fetch which attempts to look like the interface for requests, but isn't fully implemented (as demonstrated by the lack of allow_redirects parameter. For the record, I have no clue how the call to fido.fetch works in fido_client.py as it seems to be missing an import.
In summary, the effort required to make fido work with redirects is much more significant that getting requests to disable redirects by default. Given the bug I initially found where following redirects causes bravado to complain about unexpected HTTP statuses, I'd say that support for following redirects at all should probably be removed in the future or it should handle redirects at a higher level, e.g. response returns redirect, so now you should expect the response(s) from the target of the redirect. As not all redirects have a single target according to the spec, that gets even more complicated to handle at a library level, so I'd argue that's an implementation decision for the user of bravado to deal with.
@macisamuele Is there anything else you'd like me to address before this gets merged in? |
bravado/requests_client.py
Outdated
@@ -286,7 +286,6 @@ def __init__( | |||
ssl_cert=None, # type: typing.Any | |||
future_adapter_class=RequestsFutureAdapter, # type: typing.Type[RequestsFutureAdapter] | |||
response_adapter_class=RequestsResponseAdapter, # type: typing.Type[RequestsResponseAdapter] | |||
allow_redirects=False # type: bool |
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.
Is unclear to me why this parameter has been removed.
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 think you're looking at an old changeset. It was added and then later moved to be a parameter in the request
method.
bravado/testing/integration_test.py
Outdated
try: | ||
self.http_client.request({ | ||
'method': 'GET', | ||
'url': '{server_address}/redirect'.format(server_address=swagger_http_server), | ||
'params': {}, | ||
}).result(timeout=1) | ||
except HTTPMovedPermanently as exc: | ||
assert isinstance(exc.response, IncomingResponse) and exc.response.status_code == 301 | ||
assert isinstance(exc.response, IncomingResponse) and exc.response.headers['Location'] == '/json' | ||
else: | ||
pytest.fail("Expected exception was not raised") |
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'd recommend using pytest.raises
such that we don't need manual handling of different raised exception
with pytest.raises(HTTPMovedPermanently) as excinfo:
self.http_client.request({
'method': 'GET',
'url': '{server_address}/redirect'.format(server_address=swagger_http_server),
'params': {},
}).result(timeout=1)
exc = excinfo.value
assert isinstance(exc.response, IncomingResponse) and exc.response.status_code == 301
assert isinstance(exc.response, IncomingResponse) and exc.response.headers['Location'] == '/json'
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 hadn't realise that was support by pytest.raises
.
Changed to that method instead and removed the coverage ignore.
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.
Changes looks good to me.
Pinging @sjaensch to double check the PR and if all looks good to merge this.
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.
Looks good to me! I'm requesting one minor change, as we do use (and run) those integration tests for bravado-asyncio too.
bravado/testing/integration_test.py
Outdated
# the FidoClient doesn't have a way to turn off redirects being followed | ||
# so limit this test to the RequestsClient instead | ||
if not isinstance(self.http_client, RequestsClient): | ||
pytest.skip('Following redirects is only supported in the RequestsClient') |
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.
Can we make this a blacklist instead of a whitelist? bravado-asyncio does have a way to turn off following redirects, and I'd like to be able to run the test there too.
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.
Done, although it's somewhat clunky due to how coverage and test dependencies are loaded. If there's better suggestions on how to implement blacklisting without having to load the thing being blacklisted, I'm happy to change it.
Thanks for your efforts @mattdowdell! |
This has has been released (with a slightly different name for the option, see #457) in bravado 10.6.0. |
Per #449, redirects should not be silently followed when making requests. This disables that behaviour by default in the synchronous client, and also modifies the responses to no longer consider 3XX HTTP status errors. There's perhaps some nuance to whether all of them are applicable in API responses, but RFC 7231 does not appear to consider these to be errors anyway.
I've left in the exception classes for redirects and
make_http_exception
is untouched, largely because I wasn't sure if that was considered part of the public API or not. I've also added an integration test and bench tested using my code that originally found the issue and confirmed that it works as expected for my use case.Fixes #449.