Switch follow redirects default (#1808)
* Switch default on allow_redirects to False * allow_redirects -> follow_redirects * Update follow_redirects default in top-level API * Update docs on follow_redirects
This commit is contained in:
parent
0b4a83257b
commit
47266d763b
@ -4,11 +4,68 @@ HTTPX aims to be broadly compatible with the `requests` API.
|
||||
|
||||
This documentation outlines places where the API differs...
|
||||
|
||||
## Client instances
|
||||
|
||||
The HTTPX equivalent of `requests.Session` is `httpx.Client`.
|
||||
|
||||
```python
|
||||
session = requests.Session(**kwargs)
|
||||
```
|
||||
|
||||
is generally equivalent to
|
||||
|
||||
```python
|
||||
client = httpx.Client(**kwargs)
|
||||
```
|
||||
|
||||
## Request URLs
|
||||
|
||||
Accessing `response.url` will return a `URL` instance, rather than a string.
|
||||
|
||||
Use `str(response.url)` if you need a string instance.
|
||||
|
||||
## Redirects
|
||||
|
||||
Unlike `requests`, HTTPX does **not follow redirects by default**.
|
||||
|
||||
We differ in behaviour here [because auto-redirects can easily mask unnecessary network
|
||||
calls being made](https://github.com/encode/httpx/discussions/1785).
|
||||
|
||||
You can still enable behaviour to automatically follow redirects, but you need to
|
||||
do so explicitly...
|
||||
|
||||
```python
|
||||
respose = client.get(url, follow_redirects=True)
|
||||
```
|
||||
|
||||
Or else instantiate a client, with redirect following enabled by default...
|
||||
|
||||
```python
|
||||
client = httpx.Client(follow_redirects=True)
|
||||
```
|
||||
|
||||
## Determining the next redirect request
|
||||
|
||||
The `requests` library exposes an attribute `response.next`, which can be used to obtain the next redirect request.
|
||||
|
||||
```python
|
||||
session = requests.Session()
|
||||
request = requests.Request("GET", ...).prepare()
|
||||
while request is not None:
|
||||
response = session.send(request, allow_redirects=False)
|
||||
request = response.next
|
||||
```
|
||||
|
||||
In HTTPX, this attribute is instead named `response.next_request`. For example:
|
||||
|
||||
```python
|
||||
client = httpx.Client()
|
||||
request = client.build_request("GET", ...)
|
||||
while request is not None:
|
||||
response = client.send(request)
|
||||
request = response.next_request
|
||||
```
|
||||
|
||||
## Request Content
|
||||
|
||||
For uploading raw text or binary content we prefer to use a `content` parameter,
|
||||
@ -31,6 +88,12 @@ httpx.post(..., data={"message": "Hello, world"})
|
||||
Using the `data=<text/byte content>` will raise a deprecation warning,
|
||||
and is expected to be fully removed with the HTTPX 1.0 release.
|
||||
|
||||
## Upload files
|
||||
|
||||
HTTPX strictly enforces that upload files must be opened in binary mode, in order
|
||||
to avoid character encoding issues that can result from attempting to upload files
|
||||
opened in text mode.
|
||||
|
||||
## Content encoding
|
||||
|
||||
HTTPX uses `utf-8` for encoding `str` request bodies. For example, when using `content=<str>` the request body will be encoded to `utf-8` before being sent over the wire. This differs from Requests which uses `latin1`. If you need an explicit encoding, pass encoded bytes explictly, e.g. `content=<str>.encode("latin1")`.
|
||||
@ -110,25 +173,11 @@ If you really do need to send request data using these http methods you should u
|
||||
|
||||
We don't support `response.is_ok` since the naming is ambiguous there, and might incorrectly imply an equivalence to `response.status_code == codes.OK`. Instead we provide the `response.is_error` property. Use `if not response.is_error:` instead of `if response.is_ok:`.
|
||||
|
||||
## Client instances
|
||||
|
||||
The HTTPX equivalent of `requests.Session` is `httpx.Client`.
|
||||
|
||||
```python
|
||||
session = requests.Session(**kwargs)
|
||||
```
|
||||
|
||||
is generally equivalent to
|
||||
|
||||
```python
|
||||
client = httpx.Client(**kwargs)
|
||||
```
|
||||
|
||||
## Request instantiation
|
||||
|
||||
There is no notion of [prepared requests](https://requests.readthedocs.io/en/stable/user/advanced/#prepared-requests) in HTTPX. If you need to customize request instantiation, see [Request instances](advanced.md#request-instances).
|
||||
|
||||
Besides, `httpx.Request()` does not support the `auth`, `timeout`, `allow_redirects`, `proxies`, `verify` and `cert` parameters. However these are available in `httpx.request`, `httpx.get`, `httpx.post` etc., as well as on [`Client` instances](advanced.md#client-instances).
|
||||
Besides, `httpx.Request()` does not support the `auth`, `timeout`, `follow_redirects`, `proxies`, `verify` and `cert` parameters. However these are available in `httpx.request`, `httpx.get`, `httpx.post` etc., as well as on [`Client` instances](advanced.md#client-instances).
|
||||
|
||||
## Mocking
|
||||
|
||||
@ -144,25 +193,6 @@ On the other hand, HTTPX uses [HTTPCore](https://github.com/encode/httpcore) as
|
||||
|
||||
`requests` omits `params` whose values are `None` (e.g. `requests.get(..., params={"foo": None})`). This is not supported by HTTPX.
|
||||
|
||||
## HEAD redirection
|
||||
|
||||
In `requests`, all top-level API follow redirects by default except `HEAD`.
|
||||
In consideration of consistency, we make `HEAD` follow redirects by default in HTTPX.
|
||||
|
||||
## Determining the next redirect request
|
||||
|
||||
When using `allow_redirects=False`, the `requests` library exposes an attribute `response.next`, which can be used to obtain the next redirect request.
|
||||
|
||||
In HTTPX, this attribute is instead named `response.next_request`. For example:
|
||||
|
||||
```python
|
||||
client = httpx.Client()
|
||||
request = client.build_request("GET", ...)
|
||||
while request is not None:
|
||||
response = client.send(request, allow_redirects=False)
|
||||
request = response.next_request
|
||||
```
|
||||
|
||||
## Event Hooks
|
||||
|
||||
`requests` allows event hooks to mutate `Request` and `Response` objects. See [examples](https://requests.readthedocs.io/en/master/user/advanced/#event-hooks) given in the documentation for `requests`.
|
||||
|
||||
@ -128,7 +128,7 @@ Often Web API responses will be encoded as JSON.
|
||||
To include additional headers in the outgoing request, use the `headers` keyword argument:
|
||||
|
||||
```pycon
|
||||
>>> url = 'http://httpbin.org/headers'
|
||||
>>> url = 'https://httpbin.org/headers'
|
||||
>>> headers = {'user-agent': 'my-app/0.0.1'}
|
||||
>>> r = httpx.get(url, headers=headers)
|
||||
```
|
||||
@ -380,7 +380,7 @@ If you're using streaming responses in any of these ways then the `response.cont
|
||||
Any cookies that are set on the response can be easily accessed:
|
||||
|
||||
```pycon
|
||||
>>> r = httpx.get('http://httpbin.org/cookies/set?chocolate=chip', allow_redirects=False)
|
||||
>>> r = httpx.get('https://httpbin.org/cookies/set?chocolate=chip')
|
||||
>>> r.cookies['chocolate']
|
||||
'chip'
|
||||
```
|
||||
@ -389,7 +389,7 @@ To include cookies in an outgoing request, use the `cookies` parameter:
|
||||
|
||||
```pycon
|
||||
>>> cookies = {"peanut": "butter"}
|
||||
>>> r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
|
||||
>>> r = httpx.get('https://httpbin.org/cookies', cookies=cookies)
|
||||
>>> r.json()
|
||||
{'cookies': {'peanut': 'butter'}}
|
||||
```
|
||||
@ -408,17 +408,25 @@ with additional API for accessing cookies by their domain or path.
|
||||
|
||||
## Redirection and History
|
||||
|
||||
By default, HTTPX will follow redirects for all HTTP methods.
|
||||
|
||||
|
||||
The `history` property of the response can be used to inspect any followed redirects.
|
||||
It contains a list of any redirect responses that were followed, in the order
|
||||
in which they were made.
|
||||
By default, HTTPX will **not** follow redirects for all HTTP methods, although
|
||||
this can be explicitly enabled.
|
||||
|
||||
For example, GitHub redirects all HTTP requests to HTTPS.
|
||||
|
||||
```pycon
|
||||
>>> r = httpx.get('http://github.com/')
|
||||
>>> r.status_code
|
||||
301
|
||||
>>> r.history
|
||||
[]
|
||||
>>> r.next_request
|
||||
<Request('GET', 'https://github.com/')>
|
||||
```
|
||||
|
||||
You can modify the default redirection handling with the `follow_redirects` parameter:
|
||||
|
||||
```pycon
|
||||
>>> r = httpx.get('http://github.com/', follow_redirects=True)
|
||||
>>> r.url
|
||||
URL('https://github.com/')
|
||||
>>> r.status_code
|
||||
@ -427,15 +435,9 @@ URL('https://github.com/')
|
||||
[<Response [301 Moved Permanently]>]
|
||||
```
|
||||
|
||||
You can modify the default redirection handling with the allow_redirects parameter:
|
||||
|
||||
```pycon
|
||||
>>> r = httpx.get('http://github.com/', allow_redirects=False)
|
||||
>>> r.status_code
|
||||
301
|
||||
>>> r.history
|
||||
[]
|
||||
```
|
||||
The `history` property of the response can be used to inspect any followed redirects.
|
||||
It contains a list of any redirect responses that were followed, in the order
|
||||
in which they were made.
|
||||
|
||||
## Timeouts
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ def request(
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
verify: VerifyTypes = True,
|
||||
cert: CertTypes = None,
|
||||
trust_env: bool = True,
|
||||
@ -66,7 +66,7 @@ def request(
|
||||
* **proxies** - *(optional)* A dictionary mapping proxy keys to proxy URLs.
|
||||
* **timeout** - *(optional)* The timeout configuration to use when sending
|
||||
the request.
|
||||
* **allow_redirects** - *(optional)* Enables or disables HTTP redirects.
|
||||
* **follow_redirects** - *(optional)* Enables or disables HTTP redirects.
|
||||
* **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
|
||||
verify the identity of requested hosts. Either `True` (default CA bundle),
|
||||
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
|
||||
@ -107,7 +107,7 @@ def request(
|
||||
params=params,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ def stream(
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
verify: VerifyTypes = True,
|
||||
cert: CertTypes = None,
|
||||
trust_env: bool = True,
|
||||
@ -159,7 +159,7 @@ def stream(
|
||||
params=params,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
) as response:
|
||||
yield response
|
||||
|
||||
@ -172,7 +172,7 @@ def get(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -194,7 +194,7 @@ def get(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -210,7 +210,7 @@ def options(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -232,7 +232,7 @@ def options(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -248,7 +248,7 @@ def head(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -270,7 +270,7 @@ def head(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -290,7 +290,7 @@ def post(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -313,7 +313,7 @@ def post(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -333,7 +333,7 @@ def put(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -356,7 +356,7 @@ def put(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -376,7 +376,7 @@ def patch(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -399,7 +399,7 @@ def patch(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
@ -415,7 +415,7 @@ def delete(
|
||||
cookies: CookieTypes = None,
|
||||
auth: AuthTypes = None,
|
||||
proxies: ProxiesTypes = None,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
cert: CertTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
@ -437,7 +437,7 @@ def delete(
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
proxies=proxies,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
cert=cert,
|
||||
verify=verify,
|
||||
timeout=timeout,
|
||||
|
||||
120
httpx/_client.py
120
httpx/_client.py
@ -165,7 +165,7 @@ class BaseClient:
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
allow_redirects: bool = True,
|
||||
follow_redirects: bool = False,
|
||||
max_redirects: int = DEFAULT_MAX_REDIRECTS,
|
||||
event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
|
||||
base_url: URLTypes = "",
|
||||
@ -180,7 +180,7 @@ class BaseClient:
|
||||
self.headers = Headers(headers)
|
||||
self._cookies = Cookies(cookies)
|
||||
self._timeout = Timeout(timeout)
|
||||
self.allow_redirects = allow_redirects
|
||||
self.follow_redirects = follow_redirects
|
||||
self.max_redirects = max_redirects
|
||||
self._event_hooks = {
|
||||
"request": list(event_hooks.get("request", [])),
|
||||
@ -613,6 +613,7 @@ class Client(BaseClient):
|
||||
proxies: ProxiesTypes = None,
|
||||
mounts: typing.Mapping[str, BaseTransport] = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
follow_redirects: bool = False,
|
||||
limits: Limits = DEFAULT_LIMITS,
|
||||
max_redirects: int = DEFAULT_MAX_REDIRECTS,
|
||||
event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
|
||||
@ -627,6 +628,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
timeout=timeout,
|
||||
follow_redirects=follow_redirects,
|
||||
max_redirects=max_redirects,
|
||||
event_hooks=event_hooks,
|
||||
base_url=base_url,
|
||||
@ -746,7 +748,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -785,7 +787,7 @@ class Client(BaseClient):
|
||||
cookies=cookies,
|
||||
)
|
||||
return self.send(
|
||||
request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
|
||||
request, auth=auth, follow_redirects=follow_redirects, timeout=timeout
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
@ -802,7 +804,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> typing.Iterator[Response]:
|
||||
"""
|
||||
@ -829,7 +831,7 @@ class Client(BaseClient):
|
||||
response = self.send(
|
||||
request=request,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
stream=True,
|
||||
)
|
||||
@ -844,7 +846,7 @@ class Client(BaseClient):
|
||||
*,
|
||||
stream: bool = False,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -867,10 +869,10 @@ class Client(BaseClient):
|
||||
timeout = (
|
||||
self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
|
||||
)
|
||||
allow_redirects = (
|
||||
self.allow_redirects
|
||||
if isinstance(allow_redirects, UseClientDefault)
|
||||
else allow_redirects
|
||||
follow_redirects = (
|
||||
self.follow_redirects
|
||||
if isinstance(follow_redirects, UseClientDefault)
|
||||
else follow_redirects
|
||||
)
|
||||
|
||||
auth = self._build_request_auth(request, auth)
|
||||
@ -879,7 +881,7 @@ class Client(BaseClient):
|
||||
request,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
history=[],
|
||||
)
|
||||
try:
|
||||
@ -897,7 +899,7 @@ class Client(BaseClient):
|
||||
request: Request,
|
||||
auth: Auth,
|
||||
timeout: Timeout,
|
||||
allow_redirects: bool,
|
||||
follow_redirects: bool,
|
||||
history: typing.List[Response],
|
||||
) -> Response:
|
||||
auth_flow = auth.sync_auth_flow(request)
|
||||
@ -908,7 +910,7 @@ class Client(BaseClient):
|
||||
response = self._send_handling_redirects(
|
||||
request,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
history=history,
|
||||
)
|
||||
try:
|
||||
@ -932,7 +934,7 @@ class Client(BaseClient):
|
||||
self,
|
||||
request: Request,
|
||||
timeout: Timeout,
|
||||
allow_redirects: bool,
|
||||
follow_redirects: bool,
|
||||
history: typing.List[Response],
|
||||
) -> Response:
|
||||
while True:
|
||||
@ -956,7 +958,7 @@ class Client(BaseClient):
|
||||
request = self._build_redirect_request(request, response)
|
||||
history = history + [response]
|
||||
|
||||
if allow_redirects:
|
||||
if follow_redirects:
|
||||
response.read()
|
||||
else:
|
||||
response.next_request = request
|
||||
@ -1013,7 +1015,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1028,7 +1030,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1040,7 +1042,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1055,7 +1057,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1067,7 +1069,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1082,7 +1084,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1098,7 +1100,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1117,7 +1119,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1133,7 +1135,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1152,7 +1154,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1168,7 +1170,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1187,7 +1189,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1199,7 +1201,7 @@ class Client(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1214,7 +1216,7 @@ class Client(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1329,6 +1331,7 @@ class AsyncClient(BaseClient):
|
||||
proxies: ProxiesTypes = None,
|
||||
mounts: typing.Mapping[str, AsyncBaseTransport] = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
follow_redirects: bool = False,
|
||||
limits: Limits = DEFAULT_LIMITS,
|
||||
max_redirects: int = DEFAULT_MAX_REDIRECTS,
|
||||
event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
|
||||
@ -1343,6 +1346,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
timeout=timeout,
|
||||
follow_redirects=follow_redirects,
|
||||
max_redirects=max_redirects,
|
||||
event_hooks=event_hooks,
|
||||
base_url=base_url,
|
||||
@ -1461,7 +1465,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1492,7 +1496,7 @@ class AsyncClient(BaseClient):
|
||||
cookies=cookies,
|
||||
)
|
||||
response = await self.send(
|
||||
request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
|
||||
request, auth=auth, follow_redirects=follow_redirects, timeout=timeout
|
||||
)
|
||||
return response
|
||||
|
||||
@ -1510,7 +1514,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> typing.AsyncIterator[Response]:
|
||||
"""
|
||||
@ -1537,7 +1541,7 @@ class AsyncClient(BaseClient):
|
||||
response = await self.send(
|
||||
request=request,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
stream=True,
|
||||
)
|
||||
@ -1552,7 +1556,7 @@ class AsyncClient(BaseClient):
|
||||
*,
|
||||
stream: bool = False,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1575,10 +1579,10 @@ class AsyncClient(BaseClient):
|
||||
timeout = (
|
||||
self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
|
||||
)
|
||||
allow_redirects = (
|
||||
self.allow_redirects
|
||||
if isinstance(allow_redirects, UseClientDefault)
|
||||
else allow_redirects
|
||||
follow_redirects = (
|
||||
self.follow_redirects
|
||||
if isinstance(follow_redirects, UseClientDefault)
|
||||
else follow_redirects
|
||||
)
|
||||
|
||||
auth = self._build_request_auth(request, auth)
|
||||
@ -1587,7 +1591,7 @@ class AsyncClient(BaseClient):
|
||||
request,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
history=[],
|
||||
)
|
||||
try:
|
||||
@ -1605,7 +1609,7 @@ class AsyncClient(BaseClient):
|
||||
request: Request,
|
||||
auth: Auth,
|
||||
timeout: Timeout,
|
||||
allow_redirects: bool,
|
||||
follow_redirects: bool,
|
||||
history: typing.List[Response],
|
||||
) -> Response:
|
||||
auth_flow = auth.async_auth_flow(request)
|
||||
@ -1616,7 +1620,7 @@ class AsyncClient(BaseClient):
|
||||
response = await self._send_handling_redirects(
|
||||
request,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
history=history,
|
||||
)
|
||||
try:
|
||||
@ -1640,7 +1644,7 @@ class AsyncClient(BaseClient):
|
||||
self,
|
||||
request: Request,
|
||||
timeout: Timeout,
|
||||
allow_redirects: bool,
|
||||
follow_redirects: bool,
|
||||
history: typing.List[Response],
|
||||
) -> Response:
|
||||
while True:
|
||||
@ -1665,7 +1669,7 @@ class AsyncClient(BaseClient):
|
||||
request = self._build_redirect_request(request, response)
|
||||
history = history + [response]
|
||||
|
||||
if allow_redirects:
|
||||
if follow_redirects:
|
||||
await response.aread()
|
||||
else:
|
||||
response.next_request = request
|
||||
@ -1729,7 +1733,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1744,7 +1748,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1756,7 +1760,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1771,7 +1775,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1783,7 +1787,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1798,7 +1802,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1814,7 +1818,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1833,7 +1837,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1849,7 +1853,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1868,7 +1872,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1884,7 +1888,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1903,7 +1907,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@ -1915,7 +1919,7 @@ class AsyncClient(BaseClient):
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
follow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
|
||||
) -> Response:
|
||||
"""
|
||||
@ -1930,7 +1934,7 @@ class AsyncClient(BaseClient):
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
follow_redirects=follow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
@ -1209,7 +1209,7 @@ class Response:
|
||||
|
||||
self._request: typing.Optional[Request] = request
|
||||
|
||||
# When allow_redirects=False and a redirect is received,
|
||||
# When follow_redirects=False and a redirect is received,
|
||||
# the client will set `response.next_request`.
|
||||
self.next_request: typing.Optional[Request] = None
|
||||
|
||||
|
||||
@ -131,7 +131,9 @@ def test_event_hooks_with_redirect():
|
||||
event_hooks = {"request": [on_request], "response": [on_response]}
|
||||
|
||||
with httpx.Client(
|
||||
event_hooks=event_hooks, transport=httpx.MockTransport(app)
|
||||
event_hooks=event_hooks,
|
||||
transport=httpx.MockTransport(app),
|
||||
follow_redirects=True,
|
||||
) as http:
|
||||
http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
|
||||
|
||||
@ -186,7 +188,9 @@ async def test_async_event_hooks_with_redirect():
|
||||
event_hooks = {"request": [on_request], "response": [on_response]}
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
event_hooks=event_hooks, transport=httpx.MockTransport(app)
|
||||
event_hooks=event_hooks,
|
||||
transport=httpx.MockTransport(app),
|
||||
follow_redirects=True,
|
||||
) as http:
|
||||
await http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
|
||||
|
||||
|
||||
@ -113,7 +113,7 @@ def redirects(request: httpx.Request) -> httpx.Response:
|
||||
|
||||
def test_redirect_301():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.post("https://example.org/redirect_301")
|
||||
response = client.post("https://example.org/redirect_301", follow_redirects=True)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert len(response.history) == 1
|
||||
@ -121,7 +121,7 @@ def test_redirect_301():
|
||||
|
||||
def test_redirect_302():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.post("https://example.org/redirect_302")
|
||||
response = client.post("https://example.org/redirect_302", follow_redirects=True)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert len(response.history) == 1
|
||||
@ -129,7 +129,7 @@ def test_redirect_302():
|
||||
|
||||
def test_redirect_303():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("https://example.org/redirect_303")
|
||||
response = client.get("https://example.org/redirect_303", follow_redirects=True)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert len(response.history) == 1
|
||||
@ -138,12 +138,12 @@ def test_redirect_303():
|
||||
def test_next_request():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
request = client.build_request("POST", "https://example.org/redirect_303")
|
||||
response = client.send(request, allow_redirects=False)
|
||||
response = client.send(request, follow_redirects=False)
|
||||
assert response.status_code == httpx.codes.SEE_OTHER
|
||||
assert response.url == "https://example.org/redirect_303"
|
||||
assert response.next_request is not None
|
||||
|
||||
response = client.send(response.next_request, allow_redirects=False)
|
||||
response = client.send(response.next_request, follow_redirects=False)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert response.next_request is None
|
||||
@ -153,12 +153,12 @@ def test_next_request():
|
||||
async def test_async_next_request():
|
||||
async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client:
|
||||
request = client.build_request("POST", "https://example.org/redirect_303")
|
||||
response = await client.send(request, allow_redirects=False)
|
||||
response = await client.send(request, follow_redirects=False)
|
||||
assert response.status_code == httpx.codes.SEE_OTHER
|
||||
assert response.url == "https://example.org/redirect_303"
|
||||
assert response.next_request is not None
|
||||
|
||||
response = await client.send(response.next_request, allow_redirects=False)
|
||||
response = await client.send(response.next_request, follow_redirects=False)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert response.next_request is None
|
||||
@ -169,7 +169,7 @@ def test_head_redirect():
|
||||
Contrary to Requests, redirects remain enabled by default for HEAD requests.
|
||||
"""
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.head("https://example.org/redirect_302")
|
||||
response = client.head("https://example.org/redirect_302", follow_redirects=True)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert response.request.method == "HEAD"
|
||||
@ -179,7 +179,9 @@ def test_head_redirect():
|
||||
|
||||
def test_relative_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("https://example.org/relative_redirect")
|
||||
response = client.get(
|
||||
"https://example.org/relative_redirect", follow_redirects=True
|
||||
)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert len(response.history) == 1
|
||||
@ -188,7 +190,9 @@ def test_relative_redirect():
|
||||
def test_malformed_redirect():
|
||||
# https://github.com/encode/httpx/issues/771
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("http://example.org/malformed_redirect")
|
||||
response = client.get(
|
||||
"http://example.org/malformed_redirect", follow_redirects=True
|
||||
)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org:443/"
|
||||
assert len(response.history) == 1
|
||||
@ -197,12 +201,14 @@ def test_malformed_redirect():
|
||||
def test_invalid_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
with pytest.raises(httpx.RemoteProtocolError):
|
||||
client.get("http://example.org/invalid_redirect")
|
||||
client.get("http://example.org/invalid_redirect", follow_redirects=True)
|
||||
|
||||
|
||||
def test_no_scheme_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("https://example.org/no_scheme_redirect")
|
||||
response = client.get(
|
||||
"https://example.org/no_scheme_redirect", follow_redirects=True
|
||||
)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/"
|
||||
assert len(response.history) == 1
|
||||
@ -210,7 +216,9 @@ def test_no_scheme_redirect():
|
||||
|
||||
def test_fragment_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("https://example.org/relative_redirect#fragment")
|
||||
response = client.get(
|
||||
"https://example.org/relative_redirect#fragment", follow_redirects=True
|
||||
)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/#fragment"
|
||||
assert len(response.history) == 1
|
||||
@ -218,7 +226,9 @@ def test_fragment_redirect():
|
||||
|
||||
def test_multiple_redirects():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
response = client.get("https://example.org/multiple_redirects?count=20")
|
||||
response = client.get(
|
||||
"https://example.org/multiple_redirects?count=20", follow_redirects=True
|
||||
)
|
||||
assert response.status_code == httpx.codes.OK
|
||||
assert response.url == "https://example.org/multiple_redirects"
|
||||
assert len(response.history) == 20
|
||||
@ -232,26 +242,30 @@ def test_multiple_redirects():
|
||||
async def test_async_too_many_redirects():
|
||||
async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client:
|
||||
with pytest.raises(httpx.TooManyRedirects):
|
||||
await client.get("https://example.org/multiple_redirects?count=21")
|
||||
await client.get(
|
||||
"https://example.org/multiple_redirects?count=21", follow_redirects=True
|
||||
)
|
||||
|
||||
|
||||
def test_sync_too_many_redirects():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
with pytest.raises(httpx.TooManyRedirects):
|
||||
client.get("https://example.org/multiple_redirects?count=21")
|
||||
client.get(
|
||||
"https://example.org/multiple_redirects?count=21", follow_redirects=True
|
||||
)
|
||||
|
||||
|
||||
def test_redirect_loop():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
with pytest.raises(httpx.TooManyRedirects):
|
||||
client.get("https://example.org/redirect_loop")
|
||||
client.get("https://example.org/redirect_loop", follow_redirects=True)
|
||||
|
||||
|
||||
def test_cross_domain_redirect_with_auth_header():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.com/cross_domain"
|
||||
headers = {"Authorization": "abc"}
|
||||
response = client.get(url, headers=headers)
|
||||
response = client.get(url, headers=headers, follow_redirects=True)
|
||||
assert response.url == "https://example.org/cross_domain_target"
|
||||
assert "authorization" not in response.json()["headers"]
|
||||
|
||||
@ -259,7 +273,7 @@ def test_cross_domain_redirect_with_auth_header():
|
||||
def test_cross_domain_redirect_with_auth():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.com/cross_domain"
|
||||
response = client.get(url, auth=("user", "pass"))
|
||||
response = client.get(url, auth=("user", "pass"), follow_redirects=True)
|
||||
assert response.url == "https://example.org/cross_domain_target"
|
||||
assert "authorization" not in response.json()["headers"]
|
||||
|
||||
@ -268,7 +282,7 @@ def test_same_domain_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.org/cross_domain"
|
||||
headers = {"Authorization": "abc"}
|
||||
response = client.get(url, headers=headers)
|
||||
response = client.get(url, headers=headers, follow_redirects=True)
|
||||
assert response.url == "https://example.org/cross_domain_target"
|
||||
assert response.json()["headers"]["authorization"] == "abc"
|
||||
|
||||
@ -280,7 +294,7 @@ def test_body_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.org/redirect_body"
|
||||
content = b"Example request body"
|
||||
response = client.post(url, content=content)
|
||||
response = client.post(url, content=content, follow_redirects=True)
|
||||
assert response.url == "https://example.org/redirect_body_target"
|
||||
assert response.json()["body"] == "Example request body"
|
||||
assert "content-length" in response.json()["headers"]
|
||||
@ -293,7 +307,7 @@ def test_no_body_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.org/redirect_no_body"
|
||||
content = b"Example request body"
|
||||
response = client.post(url, content=content)
|
||||
response = client.post(url, content=content, follow_redirects=True)
|
||||
assert response.url == "https://example.org/redirect_body_target"
|
||||
assert response.json()["body"] == ""
|
||||
assert "content-length" not in response.json()["headers"]
|
||||
@ -302,7 +316,7 @@ def test_no_body_redirect():
|
||||
def test_can_stream_if_no_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.org/redirect_301"
|
||||
with client.stream("GET", url, allow_redirects=False) as response:
|
||||
with client.stream("GET", url, follow_redirects=False) as response:
|
||||
assert not response.is_closed
|
||||
assert response.status_code == httpx.codes.MOVED_PERMANENTLY
|
||||
assert response.headers["location"] == "https://example.org/"
|
||||
@ -316,13 +330,13 @@ def test_cannot_redirect_streaming_body():
|
||||
yield b"Example request body" # pragma: nocover
|
||||
|
||||
with pytest.raises(httpx.StreamConsumed):
|
||||
client.post(url, content=streaming_body())
|
||||
client.post(url, content=streaming_body(), follow_redirects=True)
|
||||
|
||||
|
||||
def test_cross_subdomain_redirect():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
url = "https://example.com/cross_subdomain"
|
||||
response = client.get(url)
|
||||
response = client.get(url, follow_redirects=True)
|
||||
assert response.url == "https://www.example.org/cross_subdomain"
|
||||
|
||||
|
||||
@ -360,7 +374,9 @@ def cookie_sessions(request: httpx.Request) -> httpx.Response:
|
||||
|
||||
|
||||
def test_redirect_cookie_behavior():
|
||||
client = httpx.Client(transport=httpx.MockTransport(cookie_sessions))
|
||||
client = httpx.Client(
|
||||
transport=httpx.MockTransport(cookie_sessions), follow_redirects=True
|
||||
)
|
||||
|
||||
# The client is not logged in.
|
||||
response = client.get("https://example.com/")
|
||||
@ -391,7 +407,7 @@ def test_redirect_cookie_behavior():
|
||||
def test_redirect_custom_scheme():
|
||||
client = httpx.Client(transport=httpx.MockTransport(redirects))
|
||||
with pytest.raises(httpx.UnsupportedProtocol) as e:
|
||||
client.post("https://example.org/redirect_custom_scheme")
|
||||
client.post("https://example.org/redirect_custom_scheme", follow_redirects=True)
|
||||
assert str(e.value) == "Scheme 'market' not supported."
|
||||
|
||||
|
||||
@ -399,4 +415,6 @@ def test_redirect_custom_scheme():
|
||||
async def test_async_invalid_redirect():
|
||||
async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client:
|
||||
with pytest.raises(httpx.RemoteProtocolError):
|
||||
await client.get("http://example.org/invalid_redirect")
|
||||
await client.get(
|
||||
"http://example.org/invalid_redirect", follow_redirects=True
|
||||
)
|
||||
|
||||
@ -120,7 +120,7 @@ async def test_logs_trace(server, capsys):
|
||||
@pytest.mark.asyncio
|
||||
async def test_logs_redirect_chain(server, capsys):
|
||||
with override_log_level("debug"):
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
response = await client.get(server.url.copy_with(path="/redirect_301"))
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user