Metadata-Version: 2.4
Name: modern-httplib2
Version: 1.32.0.post1
Summary: A modern drop-in replacement for httplib2 powered by httpx
Author-email: Jay Lee <jay0lee@gmail.com>
License-Expression: Apache-2.0 AND MIT
Project-URL: Homepage, https://github.com/GAM-team/modern-httplib2
Project-URL: Repository, https://github.com/GAM-team/modern-httplib2
Project-URL: Issues, https://github.com/GAM-team/modern-httplib2/issues
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyparsing>=3.0.0
Requires-Dist: httpx[http2]>=0.28.0
Provides-Extra: socks
Requires-Dist: PySocks>=1.7.1; extra == "socks"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-timeout; extra == "test"
Requires-Dist: PySocks>=1.7.1; extra == "test"
Requires-Dist: cryptography; extra == "test"
Requires-Dist: google-auth; extra == "test"
Requires-Dist: google-auth-httplib2; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-timeout; extra == "dev"
Requires-Dist: PySocks>=1.7.1; extra == "dev"
Requires-Dist: cryptography; extra == "dev"
Requires-Dist: google-auth; extra == "dev"
Requires-Dist: google-auth-httplib2; extra == "dev"
Dynamic: license-file

# modern-httplib2

A 100% compatible, zero-code-change drop-in replacement for Python's [`httplib2`](https://github.com/httplib2/httplib2) library, powered under the hood by [`httpx`](https://www.python-httpx.org/) and [`httpcore`](https://www.encode.io/httpcore/).

## Why `modern-httplib2`?

Legacy `httplib2` is deeply embedded across the Python ecosystem (notably via `google-api-python-client`, `google-auth-httplib2`, and multi-threaded tools like GAM), but its original transport layer is built directly on Python's low-level `http.client` with single-socket state per `Http()` instance.

`modern-httplib2` keeps the exact `httplib2` public API, module layout, exception hierarchy, RFC 2616 caching, authentication schemes (`Basic`, `Digest`, `WSSE`, `GoogleLogin`, `HmacDigest`), and `ProxyInfo` semantics while replacing the underlying transport engine with **`httpx` (`httpcore` + `h2`)**:

- **Python Package & PyPI Name**: Published as [`modern-httplib2`](https://pypi.org/project/modern-httplib2/), importing as `modern_httplib2`.
- **Transparent Interceptor / Auto-Patching**: Providing `import modern_httplib2.auto` or `modern_httplib2.patch()` which mounts `modern_httplib2` into `sys.modules["httplib2"]`. Existing dependencies like `google-api-python-client` and `google-auth-httplib2` automatically and transparently use `modern_httplib2` without any code changes to Google's libraries.
- **Direct Import Support**: Use directly via `import modern_httplib2` or `import modern_httplib2 as httplib2`.
- **Modern `httpx` / `httpcore` Transport**: Uses `httpx`'s `httpcore` connection pool and `h11`/`h2` protocol state machines.
- **True Multi-Threaded HTTP/2 Multiplexing**: Multiple threads sharing a single `Http()` instance multiplex concurrent requests over a **single TCP+TLS HTTP/2 connection** (up to 100 concurrent streams per connection), even on cold start.
- **Thread-Safe Connection Pooling**: Unlike upstream `httplib2.Http()` (which is notorious for thread-safety crashes when shared across threads), `modern-httplib2` uses thread-local active request dispatch backed by a thread-safe per-origin `httpcore.ConnectionPool` (`pool_maxsize=16` by default).
- **Transparent HTTP/2 over TLS (ALPN `h2`)**: Automatically negotiates HTTP/2 on HTTPS connections when the server advertises `h2` via TLS ALPN, with seamless fallback to HTTP/1.1.
- **Python 3.11+ Native**: Built exclusively for supported Python versions (3.11+), free of legacy Python 2 / early Python 3 compatibility shims.
- **100% Upstream Test Suite Compatibility**: Passes all 526+ upstream `httplib2` unit and integration tests.

---

## Installation

Install from PyPI:

```bash
pip install modern-httplib2
```

---

## Usage

### 1. Transparent Interception in Existing Projects (e.g. GAM)

To automatically redirect all `import httplib2` calls across your application and its dependencies (like `google-api-python-client` and `google-auth-httplib2`) to `modern_httplib2`, import `modern_httplib2.auto` at your application entrypoint:

```python
try:
    import modern_httplib2.auto
except ImportError:
    pass

# All downstream imports now transparently use modern_httplib2!
import google_auth_httplib2
import googleapiclient.discovery
import httplib2

http = httplib2.Http()
```

### 2. Direct Import Usage

```python
import modern_httplib2 as httplib2

http = httplib2.Http(".cache")
resp, content = http.request("https://httpbin.org/get", "GET")
print(resp.status, resp.version)  # e.g. 200, 20 (HTTP/2) or 11 (HTTP/1.1)
```

### 2. Thread-Safe Concurrent Requests

You can safely share a single `httplib2.Http` instance across multiple threads:

```python
from concurrent.futures import ThreadPoolExecutor
import httplib2

http = httplib2.Http(pool_maxsize=16)

def fetch(url):
    resp, body = http.request(url)
    return resp.status, len(body)

with ThreadPoolExecutor(max_workers=8) as pool:
    results = list(pool.map(fetch, ["https://example.com"] * 32))
```

### 3. Controlling HTTP/2 Negotiation & Corporate Firewall Downgrade

HTTP/2 over TLS is enabled by default (`enable_http2=True`). For environments where corporate firewalls, deep packet inspection (DPI) middleboxes, or legacy proxies reject, reset, or misbehave with HTTP/2 traffic, `modern-httplib2` provides multiple ways to force HTTP/1.1:

#### Automatic Firewall Fallback (Enabled by Default)
By default (`auto_downgrade_http2=True`), if an HTTP/2 connection fails due to a network or protocol reset (e.g. proxy RST on HTTP/2 preface, stream errors, or closed connections), `modern-httplib2` will:
1. Automatically downgrade the connection to HTTP/1.1.
2. Remember that host for subsequent requests in the session.
3. Transparently retry the request without raising an exception to the caller.

#### Global / Application-Wide Toggle
Disable HTTP/2 globally in your application (for example, when a user sets `force_http11 = true` in their configuration file):
```python
import httplib2

# Any of these can be called at startup:
httplib2.force_http11()
# or:
httplib2.ENABLE_HTTP2 = False
# or:
httplib2.set_enable_http2(False)
```

#### Environment Variable (Zero Code Change)
Set the environment variable in your shell or configuration:
```bash
export MODERN_HTTPLIB2_ENABLE_HTTP2=0
# or
export HTTPLIB2_ENABLE_HTTP2=0
```

#### Per-Instance Control
```python
import httplib2

http_v1 = httplib2.Http(enable_http2=False)
# or dynamically on an existing instance:
http_v1.force_http11()

resp, content = http_v1.request("https://example.com")
assert resp.version == 11
```

### 4. Using with `google-auth-httplib2`

```python
import google.auth
import google_auth_httplib2
import httplib2

credentials, project = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
authed_http = google_auth_httplib2.AuthorizedHttp(credentials, http=httplib2.Http())
resp, content = authed_http.request("https://www.googleapis.com/oauth2/v1/ tokeninfo")
```

---

## Running Tests

```bash
.venv/bin/pytest
```
