Skip to content

Commit

Permalink
🔖 Release 2.3.900 (#45)
Browse files Browse the repository at this point in the history
2.3.900 (2023-11-18)
====================

- Disabled unsafe renegotiation option with TLS by default where
applicable.
- Added fallback package ``urllib3_future`` in addition to ``urllib3``.
This became increasingly needed as a significant number of projects
require ``urllib3`` and accidentally override this fork.
  • Loading branch information
Ousret authored Nov 18, 2023
1 parent c9145e7 commit 1eb7ea8
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 15 deletions.
10 changes: 5 additions & 5 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Restrict all files related to deploying to
# require lead maintainer approval.

.github/workflows/ @sethmlarson @pquentin @shazow
.github/CODEOWNERS @sethmlarson @pquentin @shazow
src/urllib3/_version.py @sethmlarson @pquentin @shazow
pyproject.toml @sethmlarson @pquentin @shazow
ci/ @sethmlarson @pquentin @shazow
.github/workflows/ @Ousret
.github/CODEOWNERS @Ousret
src/urllib3/_version.py @Ousret
pyproject.toml @Ousret
ci/ @Ousret
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2.3.900 (2023-11-18)
====================

- Disabled unsafe renegotiation option with TLS by default where applicable.
- Added fallback package ``urllib3_future`` in addition to ``urllib3``.
This became increasingly needed as a significant number of projects requires ``urllib3`` and
accidentally override this fork.

2.2.907 (2023-11-11)
====================

Expand Down
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<br><small>urllib3.future is as BoringSSL is to OpenSSL but to urllib3 (except support is available!)</small>
</p>

⚡ urllib3.future is a powerful, *user-friendly* HTTP client for Python.
⚡ urllib3.future goes beyond supported features while remaining compatible.
⚡ urllib3.future is a powerful, *user-friendly* HTTP client for Python.<br>
⚡ urllib3.future goes beyond supported features while remaining compatible.<br>
⚡ urllib3.future brings many critical features that are missing from the Python standard libraries:

- Thread safety.
Expand All @@ -21,9 +21,9 @@
- Helpers for retrying requests and dealing with HTTP redirects.
- Support for gzip, deflate, brotli, and zstd encoding.
- HTTP/1.1, HTTP/2 and HTTP/3 support.
- Multiplexed connection.
- Proxy support for HTTP and SOCKS.
- 100% test coverage.
- Multiplexed connection.
- 93% test coverage.

urllib3.future is powerful and easy to use:

Expand All @@ -46,12 +46,40 @@ urllib3.future can be installed with [pip](https://pip.pypa.io):
$ python -m pip install urllib3.future
```

You either do

```python
import urllib3
```

Or...

```python
import urllib3_future
```

Doing `import urllib3_future` is the safest option for you as there is a significant number of projects that
require `urllib3`.

## Notes

- **It's a fork**

⚠️ Installing urllib3.future shadows the actual urllib3 package (_depending on installation order_).
The semver will always be like _MAJOR.MINOR.9PP_ like 2.0.941, the patch node is always greater or equal to 900.

Support for bugs or improvements is served in this repository. We regularly sync this fork
with the main branch of urllib3/urllib3 against bugfixes and security patches if applicable.

- **OS Package Managers**

Fellow OS package maintainers, you cannot _just_ build and ship this package to your package registry.
As it override `urllib3` and due to its current criticality, you'll have to set:

`URLLIB3_NO_OVERRIDE=true python -m build`. Set `URLLIB3_NO_OVERRIDE` variable with "**true**" in it.

It will prevent the override.

## Compatibility with downstream

You should _always_ install the downstream project prior to this fork.
Expand All @@ -63,7 +91,8 @@ python -m pip install requests
python -m pip install urllib3.future
```

We suggest using the package **Niquests** as replacement for **Requests**. It leverage urllib3.future capabilities.
We suggest using the package [**Niquests**](https://github.com/jawah/niquests) as a drop-in replacement for **Requests**.
It leverages urllib3.future capabilities.

## Documentation

Expand Down
34 changes: 34 additions & 0 deletions hatch_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from os import environ, path
from shutil import copytree, rmtree
from typing import Any

from hatchling.builders.hooks.plugin.interface import BuildHookInterface

SHOULD_PREVENT_FORK_OVERRIDE = environ.get("URLLIB3_NO_OVERRIDE", None) == "true"


class CustomBuildHook(BuildHookInterface):
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
#: Clean-up in case of previously missed proper exit
if path.exists("./src/urllib3_future"):
rmtree("./src/urllib3_future")

#: Copying the main package and duplicate it. Provide an escape hatch.
copytree("./src/urllib3", "./src/urllib3_future")

#: Aimed at OS package manager, so that they don't override accidentally urllib3.
if SHOULD_PREVENT_FORK_OVERRIDE and path.exists("./src/urllib3"):
rmtree("./src/urllib3")

def finalize(
self, version: str, build_data: dict[str, Any], artifact_path: str
) -> None:
#: We shall restore the original package before exiting
if SHOULD_PREVENT_FORK_OVERRIDE and not path.exists("./src/urllib3"):
copytree("./src/urllib3_future", "./src/urllib3")

#: Removing the temporary duplicate
if path.exists("./src/urllib3_future"):
rmtree("./src/urllib3_future")
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ include = [
[tool.hatch.build.targets.wheel]
packages = [
"src/urllib3",
"src/urllib3_future",
]

[tool.hatch.build.hooks.custom]

[tool.pytest.ini_options]
xfail_strict = true
python_classes = ["Test", "*TestCase"]
Expand Down
2 changes: 1 addition & 1 deletion src/urllib3/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file is protected via CODEOWNERS
from __future__ import annotations

__version__ = "2.2.907"
__version__ = "2.3.900"
16 changes: 12 additions & 4 deletions src/urllib3/contrib/hface/protocols/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,20 @@ def has(
type_protocol != HTTPProtocol
), "HTTPProtocol is ambiguous and cannot be requested in the factory."

package_name: str = __name__.split(".")[0]

version_target: str = "".join(
c for c in str(type_protocol).replace("urllib3", "") if c.isdigit()
c for c in str(type_protocol).replace(package_name, "") if c.isdigit()
)
module_expr: str = f".protocols.http{version_target}"

if implementation:
module_expr += f"._{implementation.lower()}"

try:
http_module = importlib.import_module(module_expr, "urllib3.contrib.hface")
http_module = importlib.import_module(
module_expr, f"{package_name}.contrib.hface"
)
except ImportError:
return False

Expand Down Expand Up @@ -81,16 +85,20 @@ def new(
type_protocol != HTTPProtocol
), "HTTPProtocol is ambiguous and cannot be requested in the factory."

package_name: str = __name__.split(".")[0]

version_target: str = "".join(
c for c in str(type_protocol).replace("urllib3", "") if c.isdigit()
c for c in str(type_protocol).replace(package_name, "") if c.isdigit()
)
module_expr: str = f".protocols.http{version_target}"

if implementation:
module_expr += f"._{implementation.lower()}"

try:
http_module = importlib.import_module(module_expr, "urllib3.contrib.hface")
http_module = importlib.import_module(
module_expr, f"{package_name}.contrib.hface"
)
except ImportError as e:
raise NotImplementedError(
f"{type_protocol} cannot be loaded. Tried to import '{module_expr}'."
Expand Down
12 changes: 12 additions & 0 deletions src/urllib3/util/ssl_.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ def _is_has_never_check_common_name_reliable(
TLSVersion,
)

try:
from ssl import OP_NO_RENEGOTIATION
except ImportError:
OP_NO_RENEGOTIATION = None # type: ignore[assignment]

PROTOCOL_SSLv23 = PROTOCOL_TLS

# Setting SSLContext.hostname_checks_common_name = False didn't work before CPython
Expand Down Expand Up @@ -134,6 +139,7 @@ def _is_has_never_check_common_name_reliable(
OP_NO_SSLv3 = 0x2000000 # type: ignore[assignment]
PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 # type: ignore[assignment]
PROTOCOL_TLS_CLIENT = 16 # type: ignore[assignment]
OP_NO_RENEGOTIATION = None # type: ignore[assignment]


def assert_fingerprint(cert: bytes | None, fingerprint: str) -> None:
Expand Down Expand Up @@ -301,6 +307,12 @@ def create_urllib3_context(
# if the server is not rotating its ticketing keys properly.
options |= OP_NO_TICKET

# Disable renegotiation as it was proven to be weak and dangerous.
if (
OP_NO_RENEGOTIATION is not None
): # Not always available depending on your interpreter.
options |= OP_NO_RENEGOTIATION

context.options |= options

# Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is
Expand Down
22 changes: 22 additions & 0 deletions test/with_dummyserver/test_poolmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ def test_redirect(self) -> None:
assert r.status == 200
assert r.data == b"Dummy server!"

def test_redirect_with_alt_top_level(self) -> None:
from urllib3_future import PoolManager as APM # type: ignore[import-not-found]

with APM() as http:
r = http.request(
"GET",
f"{self.base_url}/redirect",
fields={"target": f"{self.base_url}/"},
redirect=False,
)

assert r.status == 303

r = http.request(
"GET",
f"{self.base_url}/redirect",
fields={"target": f"{self.base_url}/"},
)

assert r.status == 200
assert r.data == b"Dummy server!"

def test_redirect_twice(self) -> None:
with PoolManager() as http:
r = http.request(
Expand Down

0 comments on commit 1eb7ea8

Please sign in to comment.