first stable release. pre-1.0.0 verification complete: all surviving MED regressions and
gaps resolved and independently re-fired, tree audited clean across the suite.
Signed-off-by: disqualifier <dev@disqualifier.me>
curl_cffi never populates Response.history (it follows redirects in the native curl layer
and surfaces only the final URL/status), so the v0.1.7 claim that resp.history shows 'the
real hops on either backend' was false for CurlCffi - resp.history/redirect_chain are []
there even after a redirect. README + changelog now document this curl_cffi limitation
instead. Noble threads whatever noble_tls records. No code change (the adapter is correct;
curl_cffi just has no data to map).
Signed-off-by: disqualifier <dev@disqualifier.me>
aioweb's git+ssh pin was stale at v0.1.5 against aioweb's actual latest tag,
v0.1.10 - bumped the pin, no change to aioweb_tls's own version. Also updates the
README's backend-protocol table to state that TLSSession never auto-invokes a
backend's setup(), matching the session.py docstring fix.
Signed-off-by: disqualifier <dev@disqualifier.me>
CurlCffi.raw_request and Noble.raw_request built their Response without
history=, so resp.history/redirect_chain were always empty after a real
redirect despite inheriting aioweb's feature set unchanged. A shared
_history_entries() now maps each client's native history shape (curl_cffi
list[dict], noble_tls list[Response]) into aioweb's (status, url) tuples.
Signed-off-by: disqualifier <dev@disqualifier.me>
Both backends' asyncio.TimeoutError except-branch was dead code (neither curl_cffi
nor noble_tls raises it), so a real wire timeout fell through to the generic OSError
branch and got mislabeled a plain aiohttp.ClientError instead of aioweb's own
ServerTimeoutError contract; now detected via curl_cffi's Timeout type or Go-side
timeout text and re-wrapped correctly. Noble's multi-valued response headers (e.g.
two Set-Cookie lines) arrived as Python lists instead of strings, breaking any
downstream .split()/.lower() call; now comma-joined per RFC 7230. An unknown Noble
client profile string raised a raw AttributeError from the enum lookup; now a
ValueError listing the valid profile names. Also compresses essay-length docstrings
and narrating comments across the module with no behavior change.
Signed-off-by: disqualifier <dev@disqualifier.me>
TLSSession.set_cookie/get_cookies/clear_cookies crashed with AttributeError on
both backends (they reached into self.session.cookie_jar, which curl_cffi and
noble_tls sessions don't have); both backends now route the mutable cookie api
through their own requests-style session.cookies store.
Noble.raw_request now requests is_byte_response=True and decodes the resulting
base64 data-URI body, since noble_tls's default text response silently corrupts
any binary payload (image/zip/pdf) via lossy UTF-8 decoding on the Go side.
CurlCffi/Noble.create_session no longer bake session-default headers into the
underlying client; baking caused clear_headers()/get_headers() to lie about
what's actually still on the wire (a credential-leak divergence from the
aiohttp base, which never bakes). Headers flow through aioweb's per-request
merge only, matching the base's documented contract.
Noble.create_session now applies the same max(1, ceil()) timeout coercion
raw_request already had (extracted into a shared _noble_timeout_seconds
helper) — without it, a sub-second/float session-default timeout made every
request fail Go-side JSON unmarshal.
README corrected: dropped the 'every aioweb feature behaves identically'
overclaim re: cookies, documented the binary-body handling and the
no-header-baking rationale, bumped install pins to v0.1.4.
Signed-off-by: disqualifier <dev@disqualifier.me>
- CurlCffi/Noble raw_request translate backend-native network errors (curl_cffi
RequestException, noble_tls TLSClientException) into aiohttp.ClientError so the bare
request() path gives the same typed-failure contract as the aiohttp backend (L6)
- Noble.setup uses download_if_necessary (the current noble_tls API), with
update_if_necessary only as a fallback; docstring/CLAUDE.md no longer claim the dead
'refreshes an existing one' path (L7)
- pyproject description says composition (one injectable TLSSession), not the old
'ExtendedSession subclasses' (L8).
Signed-off-by: disqualifier <dev@disqualifier.me>
per-call impersonate= is honored only via the low-level request()/_raw_request path
(which forwards **kwargs to the backend), NOT request_with_retries — its inherited
aioweb signature is fixed with no **kwargs and raises TypeError on an extra kwarg.
docs-only across README + backend docstring + CLAUDE.md; for the retrying path, set
the profile on the CurlCffi/Noble instance. no code change.
Signed-off-by: disqualifier <dev@disqualifier.me>
Noble.setup guarded the one-time Go shared-library fetch with a bare 'if self._updated' flag — a TOCTOU race where concurrent first requests both passed the check before either set the flag, running the download multiple times. now guarded by a per-instance asyncio.Lock with a check-lock-recheck. verified under load: 2/10/100/500 concurrent setups run the fetch exactly once each (a no-lock control runs it N times).
Signed-off-by: disqualifier <dev@disqualifier.me>