Skip to content

Commit

Permalink
⚡ Lazy load the OCSP extension in order to improve the import perform…
Browse files Browse the repository at this point in the history
…ance (#124)

3.6.5 (2024-05-22)
------------------

**Fixed**
- Support `localhost` as a valid domain for cookies. The standard
library does not allow this special
domain. Researches showed that a valid domain should have at least two
dots (e.g. abc.com. and xyz.tld. but not com.).
Public suffixes cannot be used as a cookie domain for security reasons,
but as `localhost` isn't one we are explicitly
  allowing it. Reported in httpie/cli#602
`RequestsCookieJar` set a default policy that circumvent that
limitation, if you specified a custom cookie policy then this
  fix won't be applied.

**Changed**
- Lazy load the OCSP extension in order to improve the import
performance.

**Removed**
- Class variable `disable_thread` in `AsyncSession` that is no longer
relevant since the native asyncio implementation. (PR #122)
  • Loading branch information
Ousret authored May 22, 2024
1 parent 5fcfe0c commit 2b0f99d
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 65 deletions.
8 changes: 7 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Release History
===============

3.6.5 (2024-05-??)
3.6.5 (2024-05-22)
------------------

**Fixed**
Expand All @@ -12,6 +12,12 @@ Release History
`RequestsCookieJar` set a default policy that circumvent that limitation, if you specified a custom cookie policy then this
fix won't be applied.

**Changed**
- Lazy load the OCSP extension in order to improve the import performance.

**Removed**
- Class variable `disable_thread` in `AsyncSession` that is no longer relevant since the native asyncio implementation. (PR #122)

3.6.4 (2024-05-16)
------------------

Expand Down
4 changes: 0 additions & 4 deletions src/niquests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@

# urllib3's DependencyWarnings should be silenced.
warnings.simplefilter("ignore", DependencyWarning)
# Commonly happen on Windows due to some legacy root CA in
# their trust store. They are aware of it, we silent the warning
# yield by Cryptography to avoid producing undesired noise to end-users.
warnings.filterwarnings("ignore", "Parsed a negative serial number")

# ruff: noqa: E402
from . import utils
Expand Down
29 changes: 15 additions & 14 deletions src/niquests/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
_swap_context,
_deepcopy_ci,
parse_scheme,
is_ocsp_capable,
)
from .cookies import (
RequestsCookieJar,
Expand All @@ -82,11 +83,6 @@
from .structures import AsyncQuicSharedCache
from .adapters import AsyncBaseAdapter, AsyncHTTPAdapter

try:
from .extensions._async_ocsp import verify as ocsp_verify
except ImportError:
ocsp_verify = None # type: ignore[assignment]

# Preferred clock, based on which one is more accurate on a given system.
if sys.platform == "win32":
preferred_clock = time.perf_counter
Expand Down Expand Up @@ -323,21 +319,26 @@ async def on_post_connection(conn_info: ConnectionInfo) -> None:
if (
ptr_request.url
and ptr_request.url.startswith("https://")
and ocsp_verify is not None
and kwargs["verify"]
and is_ocsp_capable(conn_info)
):
strict_ocsp_enabled: bool = (
os.environ.get("NIQUESTS_STRICT_OCSP", "0") != "0"
)

await ocsp_verify(
ptr_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
resolver=self.resolver,
happy_eyeballs=self._happy_eyeballs,
)
try:
from .extensions._async_ocsp import verify as ocsp_verify
except ImportError:
pass
else:
await ocsp_verify(
ptr_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
resolver=self.resolver,
happy_eyeballs=self._happy_eyeballs,
)

# don't trigger pre_send for redirects
if ptr_request == request:
Expand Down
71 changes: 39 additions & 32 deletions src/niquests/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,9 @@
_swap_context,
_deepcopy_ci,
parse_scheme,
is_ocsp_capable,
)

try:
from .extensions._ocsp import verify as ocsp_verify
from .extensions._async_ocsp import verify as async_ocsp_verify
except ImportError:
ocsp_verify = None # type: ignore[assignment]

try:
if HAS_LEGACY_URLLIB3 is False:
from urllib3.contrib.socks import SOCKSProxyManager, AsyncSOCKSProxyManager
Expand Down Expand Up @@ -1041,23 +1036,28 @@ def on_post_connection(conn_info: ConnectionInfo) -> None:
if (
next_request.url
and next_request.url.startswith("https://")
and ocsp_verify is not None
and kwargs["verify"]
and is_ocsp_capable(conn_info)
):
strict_ocsp_enabled: bool = (
os.environ.get("NIQUESTS_STRICT_OCSP", "0") != "0"
)

ocsp_verify(
next_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
self._resolver
if isinstance(self._resolver, BaseResolver)
else None,
self._happy_eyeballs,
)
try:
from .extensions._ocsp import verify as ocsp_verify
except ImportError:
pass
else:
ocsp_verify(
next_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
self._resolver
if isinstance(self._resolver, BaseResolver)
else None,
self._happy_eyeballs,
)

kwargs["on_post_connection"] = on_post_connection

Expand Down Expand Up @@ -2015,23 +2015,30 @@ async def on_post_connection(conn_info: ConnectionInfo) -> None:
if (
next_request.url
and next_request.url.startswith("https://")
and ocsp_verify is not None
and kwargs["verify"]
and is_ocsp_capable(conn_info)
):
strict_ocsp_enabled: bool = (
os.environ.get("NIQUESTS_STRICT_OCSP", "0") != "0"
)

await async_ocsp_verify(
next_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
self._resolver
if isinstance(self._resolver, AsyncBaseResolver)
else None,
self._happy_eyeballs,
)
try:
from .extensions._async_ocsp import (
verify as async_ocsp_verify,
)
except ImportError:
pass
else:
strict_ocsp_enabled: bool = (
os.environ.get("NIQUESTS_STRICT_OCSP", "0") != "0"
)

await async_ocsp_verify(
next_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
self._resolver
if isinstance(self._resolver, AsyncBaseResolver)
else None,
self._happy_eyeballs,
)

kwargs["on_post_connection"] = on_post_connection

Expand Down
29 changes: 15 additions & 14 deletions src/niquests/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,6 @@
)
from .hooks import HOOKS, default_hooks, dispatch_hook

try:
from .extensions._ocsp import verify as ocsp_verify
except ImportError:
ocsp_verify = None # type: ignore[assignment]

# formerly defined here, reexposed here for backward compatibility
from .models import ( # noqa: F401
DEFAULT_REDIRECT_LIMIT,
Expand All @@ -98,6 +93,7 @@
create_resolver,
_deepcopy_ci,
parse_scheme,
is_ocsp_capable,
)

# Preferred clock, based on which one is more accurate on a given system.
Expand Down Expand Up @@ -1087,21 +1083,26 @@ def on_post_connection(conn_info: ConnectionInfo) -> None:
if (
ptr_request.url
and parse_scheme(ptr_request.url) == "https"
and ocsp_verify is not None
and kwargs["verify"]
and is_ocsp_capable(conn_info)
):
strict_ocsp_enabled: bool = (
os.environ.get("NIQUESTS_STRICT_OCSP", "0") != "0"
)

ocsp_verify(
ptr_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
resolver=self.resolver,
happy_eyeballs=self._happy_eyeballs,
)
try:
from .extensions._ocsp import verify as ocsp_verify
except ImportError:
pass
else:
ocsp_verify(
ptr_request,
strict_ocsp_enabled,
0.2 if not strict_ocsp_enabled else 1.0,
kwargs["proxies"],
resolver=self.resolver,
happy_eyeballs=self._happy_eyeballs,
)

# don't trigger pre_send for redirects
if ptr_request == request:
Expand Down
23 changes: 23 additions & 0 deletions src/niquests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,3 +1173,26 @@ def parse_scheme(url: str, default: str | None = None, max_length: int = 9) -> s
) from e

return scheme.lower()


def is_ocsp_capable(conn_info: ConnectionInfo | None) -> bool:
# we can't do anything in that case.
if (
conn_info is None
or conn_info.certificate_der is None
or conn_info.certificate_dict is None
):
return False

endpoints: list[str] = [ # type: ignore
# exclude non-HTTP endpoint. like ldap.
ep # type: ignore
for ep in list(conn_info.certificate_dict.get("OCSP", [])) # type: ignore
if ep.startswith("http://") # type: ignore
]

# well... not all issued certificate have a OCSP entry. e.g. mkcert.
if not endpoints:
return False

return True

0 comments on commit 2b0f99d

Please sign in to comment.