Rename http2 switch (#586)
* Simplify HTTP version config, and switch HTTP/2 off by default * HTTP/2 docs * HTTP/2 interlinking in docs * Add concurrency auto-detection * Add sniffio * Rename HTTP2 switch on client * http_2 -> http2
This commit is contained in:
parent
3cbe7315e8
commit
5076952202
@ -30,7 +30,7 @@ trying out our HTTP/2 support. You can do so by instantiating a client with
|
||||
HTTP/2 support enabled:
|
||||
|
||||
```python
|
||||
client = httpx.Client(http_2=True)
|
||||
client = httpx.Client(http2=True)
|
||||
...
|
||||
```
|
||||
|
||||
@ -39,7 +39,7 @@ HTTP connections are nicely scoped, and will be closed once the context block
|
||||
is exited.
|
||||
|
||||
```python
|
||||
async with httpx.Client(http_2=True) as client:
|
||||
async with httpx.Client(http2=True) as client:
|
||||
...
|
||||
```
|
||||
|
||||
@ -54,7 +54,7 @@ You can determine which version of the HTTP protocol was used by examining
|
||||
the `.http_version` property on the response.
|
||||
|
||||
```python
|
||||
client = httpx.Client(http_2=True)
|
||||
client = httpx.Client(http2=True)
|
||||
response = await client.get(...)
|
||||
print(response.http_version) # "HTTP/1.0", "HTTP/1.1", or "HTTP/2".
|
||||
```
|
||||
|
||||
@ -77,7 +77,7 @@ class Client:
|
||||
to authenticate the client. Either a path to an SSL certificate file, or
|
||||
two-tuple of (certificate file, key file), or a three-tuple of (certificate
|
||||
file, key file, password).
|
||||
* **http_2** - *(optional)* A boolean indicating if HTTP/2 support should be
|
||||
* **http2** - *(optional)* A boolean indicating if HTTP/2 support should be
|
||||
enabled. Defaults to `False`.
|
||||
* **proxies** - *(optional)* A dictionary mapping HTTP protocols to proxy
|
||||
URLs.
|
||||
@ -110,7 +110,7 @@ class Client:
|
||||
cookies: CookieTypes = None,
|
||||
verify: VerifyTypes = True,
|
||||
cert: CertTypes = None,
|
||||
http_2: bool = False,
|
||||
http2: bool = False,
|
||||
proxies: ProxiesTypes = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
pool_limits: PoolLimits = DEFAULT_POOL_LIMITS,
|
||||
@ -130,7 +130,7 @@ class Client:
|
||||
verify=verify,
|
||||
cert=cert,
|
||||
timeout=timeout,
|
||||
http_2=http_2,
|
||||
http2=http2,
|
||||
pool_limits=pool_limits,
|
||||
backend=backend,
|
||||
trust_env=trust_env,
|
||||
@ -162,7 +162,7 @@ class Client:
|
||||
verify=verify,
|
||||
cert=cert,
|
||||
timeout=timeout,
|
||||
http_2=http_2,
|
||||
http2=http2,
|
||||
pool_limits=pool_limits,
|
||||
backend=backend,
|
||||
trust_env=trust_env,
|
||||
@ -828,13 +828,13 @@ def _proxies_to_dispatchers(
|
||||
verify: VerifyTypes,
|
||||
cert: typing.Optional[CertTypes],
|
||||
timeout: TimeoutTypes,
|
||||
http_2: bool,
|
||||
http2: bool,
|
||||
pool_limits: PoolLimits,
|
||||
backend: typing.Union[str, ConcurrencyBackend],
|
||||
trust_env: bool,
|
||||
) -> typing.Dict[str, Dispatcher]:
|
||||
def _proxy_from_url(url: URLTypes) -> Dispatcher:
|
||||
nonlocal verify, cert, timeout, http_2, pool_limits, backend, trust_env
|
||||
nonlocal verify, cert, timeout, http2, pool_limits, backend, trust_env
|
||||
url = URL(url)
|
||||
if url.scheme in ("http", "https"):
|
||||
return HTTPProxy(
|
||||
@ -845,7 +845,7 @@ def _proxies_to_dispatchers(
|
||||
pool_limits=pool_limits,
|
||||
backend=backend,
|
||||
trust_env=trust_env,
|
||||
http_2=http_2,
|
||||
http2=http2,
|
||||
)
|
||||
raise ValueError(f"Unknown proxy for {url!r}")
|
||||
|
||||
|
||||
@ -86,35 +86,35 @@ class SSLConfig:
|
||||
return self
|
||||
return SSLConfig(cert=cert, verify=verify)
|
||||
|
||||
def load_ssl_context(self, http_2: bool = False) -> ssl.SSLContext:
|
||||
def load_ssl_context(self, http2: bool = False) -> ssl.SSLContext:
|
||||
logger.trace(
|
||||
f"load_ssl_context "
|
||||
f"verify={self.verify!r} "
|
||||
f"cert={self.cert!r} "
|
||||
f"trust_env={self.trust_env!r} "
|
||||
f"http_2={http_2!r}"
|
||||
f"http2={http2!r}"
|
||||
)
|
||||
|
||||
if self.ssl_context is None:
|
||||
self.ssl_context = (
|
||||
self.load_ssl_context_verify(http_2=http_2)
|
||||
self.load_ssl_context_verify(http2=http2)
|
||||
if self.verify
|
||||
else self.load_ssl_context_no_verify(http_2=http_2)
|
||||
else self.load_ssl_context_no_verify(http2=http2)
|
||||
)
|
||||
|
||||
assert self.ssl_context is not None
|
||||
return self.ssl_context
|
||||
|
||||
def load_ssl_context_no_verify(self, http_2: bool = False) -> ssl.SSLContext:
|
||||
def load_ssl_context_no_verify(self, http2: bool = False) -> ssl.SSLContext:
|
||||
"""
|
||||
Return an SSL context for unverified connections.
|
||||
"""
|
||||
context = self._create_default_ssl_context(http_2=http_2)
|
||||
context = self._create_default_ssl_context(http2=http2)
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
context.check_hostname = False
|
||||
return context
|
||||
|
||||
def load_ssl_context_verify(self, http_2: bool = False) -> ssl.SSLContext:
|
||||
def load_ssl_context_verify(self, http2: bool = False) -> ssl.SSLContext:
|
||||
"""
|
||||
Return an SSL context for verified connections.
|
||||
"""
|
||||
@ -133,7 +133,7 @@ class SSLConfig:
|
||||
"invalid path: {}".format(self.verify)
|
||||
)
|
||||
|
||||
context = self._create_default_ssl_context(http_2=http_2)
|
||||
context = self._create_default_ssl_context(http2=http2)
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.check_hostname = True
|
||||
|
||||
@ -162,7 +162,7 @@ class SSLConfig:
|
||||
|
||||
return context
|
||||
|
||||
def _create_default_ssl_context(self, http_2: bool) -> ssl.SSLContext:
|
||||
def _create_default_ssl_context(self, http2: bool) -> ssl.SSLContext:
|
||||
"""
|
||||
Creates the default SSLContext object that's used for both verified
|
||||
and unverified connections.
|
||||
@ -176,7 +176,7 @@ class SSLConfig:
|
||||
context.set_ciphers(DEFAULT_CIPHERS)
|
||||
|
||||
if ssl.HAS_ALPN:
|
||||
alpn_idents = ["http/1.1", "h2"] if http_2 else ["http/1.1"]
|
||||
alpn_idents = ["http/1.1", "h2"] if http2 else ["http/1.1"]
|
||||
context.set_alpn_protocols(alpn_idents)
|
||||
|
||||
if hasattr(context, "keylog_filename"):
|
||||
|
||||
@ -32,7 +32,7 @@ class HTTPConnection(Dispatcher):
|
||||
cert: CertTypes = None,
|
||||
trust_env: bool = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
http_2: bool = False,
|
||||
http2: bool = False,
|
||||
backend: typing.Union[str, ConcurrencyBackend] = "auto",
|
||||
release_func: typing.Optional[ReleaseCallback] = None,
|
||||
uds: typing.Optional[str] = None,
|
||||
@ -40,7 +40,7 @@ class HTTPConnection(Dispatcher):
|
||||
self.origin = Origin(origin) if isinstance(origin, str) else origin
|
||||
self.ssl = SSLConfig(cert=cert, verify=verify, trust_env=trust_env)
|
||||
self.timeout = TimeoutConfig(timeout)
|
||||
self.http_2 = http_2
|
||||
self.http2 = http2
|
||||
self.backend = lookup_backend(backend)
|
||||
self.release_func = release_func
|
||||
self.uds = uds
|
||||
@ -114,7 +114,7 @@ class HTTPConnection(Dispatcher):
|
||||
return None
|
||||
|
||||
# Run the SSL loading in a threadpool, since it may make disk accesses.
|
||||
return await self.backend.run_in_threadpool(ssl.load_ssl_context, self.http_2)
|
||||
return await self.backend.run_in_threadpool(ssl.load_ssl_context, self.http2)
|
||||
|
||||
async def close(self) -> None:
|
||||
logger.trace("close_connection")
|
||||
|
||||
@ -86,7 +86,7 @@ class ConnectionPool(Dispatcher):
|
||||
trust_env: bool = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
pool_limits: PoolLimits = DEFAULT_POOL_LIMITS,
|
||||
http_2: bool = False,
|
||||
http2: bool = False,
|
||||
backend: typing.Union[str, ConcurrencyBackend] = "auto",
|
||||
uds: typing.Optional[str] = None,
|
||||
):
|
||||
@ -94,7 +94,7 @@ class ConnectionPool(Dispatcher):
|
||||
self.cert = cert
|
||||
self.timeout = TimeoutConfig(timeout)
|
||||
self.pool_limits = pool_limits
|
||||
self.http_2 = http_2
|
||||
self.http2 = http2
|
||||
self.is_closed = False
|
||||
self.trust_env = trust_env
|
||||
self.uds = uds
|
||||
@ -155,7 +155,7 @@ class ConnectionPool(Dispatcher):
|
||||
verify=self.verify,
|
||||
cert=self.cert,
|
||||
timeout=self.timeout,
|
||||
http_2=self.http_2,
|
||||
http2=self.http2,
|
||||
backend=self.backend,
|
||||
release_func=self.release_connection,
|
||||
trust_env=self.trust_env,
|
||||
|
||||
@ -47,7 +47,7 @@ class HTTPProxy(ConnectionPool):
|
||||
trust_env: bool = None,
|
||||
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
|
||||
pool_limits: PoolLimits = DEFAULT_POOL_LIMITS,
|
||||
http_2: bool = False,
|
||||
http2: bool = False,
|
||||
backend: typing.Union[str, ConcurrencyBackend] = "auto",
|
||||
):
|
||||
|
||||
@ -58,7 +58,7 @@ class HTTPProxy(ConnectionPool):
|
||||
pool_limits=pool_limits,
|
||||
backend=backend,
|
||||
trust_env=trust_env,
|
||||
http_2=http_2,
|
||||
http2=http2,
|
||||
)
|
||||
|
||||
self.proxy_url = URL(proxy_url)
|
||||
@ -134,7 +134,7 @@ class HTTPProxy(ConnectionPool):
|
||||
cert=self.cert,
|
||||
timeout=self.timeout,
|
||||
backend=self.backend,
|
||||
http_2=False, # Short-lived 'connection'
|
||||
http2=False, # Short-lived 'connection'
|
||||
trust_env=self.trust_env,
|
||||
release_func=self.release_connection,
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@ def app(request):
|
||||
async def test_http2_get_request():
|
||||
backend = MockHTTP2Backend(app=app)
|
||||
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
response = await client.get("http://example.org")
|
||||
|
||||
assert response.status_code == 200
|
||||
@ -38,7 +38,7 @@ async def test_http2_get_request():
|
||||
async def test_http2_post_request():
|
||||
backend = MockHTTP2Backend(app=app)
|
||||
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
response = await client.post("http://example.org", data=b"<data>")
|
||||
|
||||
assert response.status_code == 200
|
||||
@ -54,7 +54,7 @@ async def test_http2_large_post_request():
|
||||
backend = MockHTTP2Backend(app=app)
|
||||
|
||||
data = b"a" * 100000
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
response = await client.post("http://example.org", data=data)
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content) == {
|
||||
@ -68,7 +68,7 @@ async def test_http2_large_post_request():
|
||||
async def test_http2_multiple_requests():
|
||||
backend = MockHTTP2Backend(app=app)
|
||||
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
response_1 = await client.get("http://example.org/1")
|
||||
response_2 = await client.get("http://example.org/2")
|
||||
response_3 = await client.get("http://example.org/3")
|
||||
@ -91,7 +91,7 @@ async def test_http2_reconnect():
|
||||
"""
|
||||
backend = MockHTTP2Backend(app=app)
|
||||
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
response_1 = await client.get("http://example.org/1")
|
||||
backend.server.close_connection = True
|
||||
response_2 = await client.get("http://example.org/2")
|
||||
@ -106,7 +106,7 @@ async def test_http2_reconnect():
|
||||
async def test_http2_settings_in_handshake(backend):
|
||||
backend = MockHTTP2Backend(app=app, backend=backend)
|
||||
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
await client.get("http://example.org")
|
||||
|
||||
h2_conn = backend.server.conn
|
||||
@ -139,7 +139,7 @@ async def test_http2_settings_in_handshake(backend):
|
||||
|
||||
|
||||
async def test_http2_live_request(backend):
|
||||
async with Client(backend=backend, http_2=True) as client:
|
||||
async with Client(backend=backend, http2=True) as client:
|
||||
try:
|
||||
resp = await client.get("https://nghttp2.org/httpbin/anything")
|
||||
except Timeout:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user