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,
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")`.
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
`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`.
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)
```
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'
```
```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'}}
```
## 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.url
-URL('https://github.com/')
>>> r.status_code
-200
+301
>>> r.history
-[<Response [301 Moved Permanently]>]
+[]
+>>> r.next_request
+<Request('GET', 'https://github.com/')>
```
-You can modify the default redirection handling with the allow_redirects parameter:
+You can modify the default redirection handling with the `follow_redirects` parameter:
```pycon
->>> r = httpx.get('http://github.com/', allow_redirects=False)
+>>> r = httpx.get('http://github.com/', follow_redirects=True)
+>>> r.url
+URL('https://github.com/')
>>> r.status_code
-301
+200
>>> r.history
-[]
+[<Response [301 Moved Permanently]>]
```
+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
HTTPX defaults to including reasonable timeouts for all network operations,
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,
* **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`
params=params,
headers=headers,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
)
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,
params=params,
headers=headers,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
) as response:
yield response
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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,
cookies=cookies,
auth=auth,
proxies=proxies,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
cert=cert,
verify=verify,
timeout=timeout,
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 = "",
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", [])),
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,
headers=headers,
cookies=cookies,
timeout=timeout,
+ follow_redirects=follow_redirects,
max_redirects=max_redirects,
event_hooks=event_hooks,
base_url=base_url,
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:
"""
cookies=cookies,
)
return self.send(
- request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
+ request, auth=auth, follow_redirects=follow_redirects, timeout=timeout
)
@contextmanager
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]:
"""
response = self.send(
request=request,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
stream=True,
)
*,
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:
"""
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)
request,
auth=auth,
timeout=timeout,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
history=[],
)
try:
request: Request,
auth: Auth,
timeout: Timeout,
- allow_redirects: bool,
+ follow_redirects: bool,
history: typing.List[Response],
) -> Response:
auth_flow = auth.sync_auth_flow(request)
response = self._send_handling_redirects(
request,
timeout=timeout,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
history=history,
)
try:
self,
request: Request,
timeout: Timeout,
- allow_redirects: bool,
+ follow_redirects: bool,
history: typing.List[Response],
) -> Response:
while True:
request = self._build_redirect_request(request, response)
history = history + [response]
- if allow_redirects:
+ if follow_redirects:
response.read()
else:
response.next_request = request
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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,
headers=headers,
cookies=cookies,
timeout=timeout,
+ follow_redirects=follow_redirects,
max_redirects=max_redirects,
event_hooks=event_hooks,
base_url=base_url,
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:
"""
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
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]:
"""
response = await self.send(
request=request,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
stream=True,
)
*,
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:
"""
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)
request,
auth=auth,
timeout=timeout,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
history=[],
)
try:
request: Request,
auth: Auth,
timeout: Timeout,
- allow_redirects: bool,
+ follow_redirects: bool,
history: typing.List[Response],
) -> Response:
auth_flow = auth.async_auth_flow(request)
response = await self._send_handling_redirects(
request,
timeout=timeout,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
history=history,
)
try:
self,
request: Request,
timeout: Timeout,
- allow_redirects: bool,
+ follow_redirects: bool,
history: typing.List[Response],
) -> Response:
while True:
request = self._build_redirect_request(request, response)
history = history + [response]
- if allow_redirects:
+ if follow_redirects:
await response.aread()
else:
response.next_request = request
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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:
"""
headers=headers,
cookies=cookies,
auth=auth,
- allow_redirects=allow_redirects,
+ follow_redirects=follow_redirects,
timeout=timeout,
)
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
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"))
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"))
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
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
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
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
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
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"
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
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
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
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
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
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"]
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"]
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"
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"]
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"]
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/"
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"
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/")
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."
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
+ )
@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