-
-
Notifications
You must be signed in to change notification settings - Fork 857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exception hierarchy #1095
Merged
Merged
Exception hierarchy #1095
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
9b4a00e
Exception heirachy
tomchristie f022092
Exception heirarchy
tomchristie d57f7c8
Formatting tweaks
tomchristie 6f83adb
Merge branch 'master' into exception-heirarchy
florimondmanca d1cece3
Update httpx/_exceptions.py
tomchristie 124bd13
Update httpx/_exceptions.py
tomchristie 4886574
Update httpx/_exceptions.py
tomchristie 63aa028
Update httpx/_exceptions.py
tomchristie 639be5d
Merge branch 'master' into exception-heirarchy
tomchristie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,20 +116,24 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non | |
# need to build an authenticated request. | ||
return | ||
|
||
header = response.headers["www-authenticate"] | ||
challenge = self._parse_challenge(header) | ||
challenge = self._parse_challenge(request, response) | ||
request.headers["Authorization"] = self._build_auth_header(request, challenge) | ||
yield request | ||
|
||
def _parse_challenge(self, header: str) -> "_DigestAuthChallenge": | ||
def _parse_challenge( | ||
self, request: Request, response: Response | ||
) -> "_DigestAuthChallenge": | ||
""" | ||
Returns a challenge from a Digest WWW-Authenticate header. | ||
These take the form of: | ||
`Digest realm="[email protected]",qop="auth,auth-int",nonce="abc",opaque="xyz"` | ||
""" | ||
header = response.headers["www-authenticate"] | ||
|
||
scheme, _, fields = header.partition(" ") | ||
if scheme.lower() != "digest": | ||
raise ProtocolError("Header does not start with 'Digest'") | ||
message = "Header does not start with 'Digest'" | ||
raise ProtocolError(message, request=request) | ||
|
||
header_dict: typing.Dict[str, str] = {} | ||
for field in parse_http_list(fields): | ||
|
@@ -146,7 +150,8 @@ def _parse_challenge(self, header: str) -> "_DigestAuthChallenge": | |
realm=realm, nonce=nonce, qop=qop, opaque=opaque, algorithm=algorithm | ||
) | ||
except KeyError as exc: | ||
raise ProtocolError("Malformed Digest WWW-Authenticate header") from exc | ||
message = "Malformed Digest WWW-Authenticate header" | ||
raise ProtocolError(message, request=request) from exc | ||
|
||
def _build_auth_header( | ||
self, request: Request, challenge: "_DigestAuthChallenge" | ||
|
@@ -171,7 +176,7 @@ def digest(data: bytes) -> bytes: | |
if challenge.algorithm.lower().endswith("-sess"): | ||
HA1 = digest(b":".join((HA1, challenge.nonce, cnonce))) | ||
|
||
qop = self._resolve_qop(challenge.qop) | ||
qop = self._resolve_qop(challenge.qop, request=request) | ||
if qop is None: | ||
digest_data = [HA1, challenge.nonce, HA2] | ||
else: | ||
|
@@ -221,7 +226,9 @@ def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str: | |
|
||
return header_value | ||
|
||
def _resolve_qop(self, qop: typing.Optional[bytes]) -> typing.Optional[bytes]: | ||
def _resolve_qop( | ||
self, qop: typing.Optional[bytes], request: Request | ||
) -> typing.Optional[bytes]: | ||
if qop is None: | ||
return None | ||
qops = re.split(b", ?", qop) | ||
|
@@ -231,7 +238,8 @@ def _resolve_qop(self, qop: typing.Optional[bytes]) -> typing.Optional[bytes]: | |
if qops == [b"auth-int"]: | ||
raise NotImplementedError("Digest auth-int support is not yet implemented") | ||
|
||
raise ProtocolError(f'Unexpected qop value "{qop!r}" in digest auth') | ||
message = f'Unexpected qop value "{qop!r}" in digest auth' | ||
raise ProtocolError(message, request=request) | ||
|
||
|
||
class _DigestAuthChallenge: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? (Applicable to all other decoders here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah a guess a lot of other Python developers would tend always just rely on "hey I'm supposed to call
super
right". Personally, I think explicit is preferable to indirect here. Calling intosuper
isn't always necessary or inherently the "right" thing to do.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, well then I guess
Decoder
would really be better served by aProtocol
, rather than a full-fledged base class… But we only have them on Py38+, which is unfortunate.But maybe we could then also use the
request: Request
annotation on theDecoder
interface, remove its constructor, and add an explicit constructor forIdentityDecoder
(for consistency with other decoder classes)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MultiDecoder
inherits theDecoder
interface, but do not have this propertyFrom my point of view,
MultiDecoder
,TextDecoder
andLineDecoder
should be renamed, since they:decode
andflush
)mypy
pointed it for me), e.g. as they accept different types as args of decode