def _request( self, method: bytes, url: typing.Tuple[bytes, bytes, int, bytes], headers: typing.List[typing.Tuple[bytes, bytes]], stream: ContentStream, timeout: typing.Mapping[str, typing.Optional[float]] = None, ) -> typing.Tuple[bytes, int, bytes, typing.List[typing.Tuple[ bytes, bytes]], ContentStream]: scheme, host, port, path = url if scheme not in (b"http", b"https"): raise httpcore.UnsupportedProtocol( f"Scheme {scheme!r} not supported.") path, _, query = path.partition(b"?") if path == b"/no_redirect": return b"HTTP/1.1", codes.OK, b"OK", [], ByteStream(b"") elif path == b"/redirect_301": async def body(): yield b"<a href='https://example.org/'>here</a>" status_code = codes.MOVED_PERMANENTLY headers = [(b"location", b"https://example.org/")] stream = AsyncIteratorStream(aiterator=body()) return b"HTTP/1.1", status_code, b"Moved Permanently", headers, stream elif path == b"/redirect_302": status_code = codes.FOUND headers = [(b"location", b"https://example.org/")] return b"HTTP/1.1", status_code, b"Found", headers, ByteStream(b"") elif path == b"/redirect_303": status_code = codes.SEE_OTHER headers = [(b"location", b"https://example.org/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/relative_redirect": status_code = codes.SEE_OTHER headers = [(b"location", b"/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/malformed_redirect": status_code = codes.SEE_OTHER headers = [(b"location", b"https://:443/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/invalid_redirect": status_code = codes.SEE_OTHER headers = [(b"location", "https://😇/".encode("utf-8"))] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/no_scheme_redirect": status_code = codes.SEE_OTHER headers = [(b"location", b"//example.org/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/multiple_redirects": params = parse_qs(query.decode("ascii")) count = int(params.get("count", "0")[0]) redirect_count = count - 1 code = codes.SEE_OTHER if count else codes.OK phrase = b"See Other" if count else b"OK" location = b"/multiple_redirects" if redirect_count: location += b"?count=" + str(redirect_count).encode("ascii") headers = [(b"location", location)] if count else [] return b"HTTP/1.1", code, phrase, headers, ByteStream(b"") if path == b"/redirect_loop": code = codes.SEE_OTHER headers = [(b"location", b"/redirect_loop")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/cross_domain": code = codes.SEE_OTHER headers = [(b"location", b"https://example.org/cross_domain_target")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/cross_domain_target": headers_dict = { key.decode("ascii"): value.decode("ascii") for key, value in headers } stream = ByteStream(json.dumps({"headers": headers_dict}).encode()) return b"HTTP/1.1", 200, b"OK", [], stream elif path == b"/redirect_body": code = codes.PERMANENT_REDIRECT headers = [(b"location", b"/redirect_body_target")] return b"HTTP/1.1", code, b"Permanent Redirect", headers, ByteStream( b"") elif path == b"/redirect_no_body": code = codes.SEE_OTHER headers = [(b"location", b"/redirect_body_target")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/redirect_body_target": content = b"".join(stream) headers_dict = { key.decode("ascii"): value.decode("ascii") for key, value in headers } stream = ByteStream( json.dumps({ "body": content.decode(), "headers": headers_dict }).encode()) return b"HTTP/1.1", 200, b"OK", [], stream elif path == b"/cross_subdomain": host = get_header_value(headers, "host") if host != "www.example.org": headers = [(b"location", b"https://www.example.org/cross_subdomain")] return ( b"HTTP/1.1", codes.PERMANENT_REDIRECT, b"Permanent Redirect", headers, ByteStream(b""), ) else: return b"HTTP/1.1", 200, b"OK", [], ByteStream( b"Hello, world!") elif path == b"/redirect_custom_scheme": status_code = codes.MOVED_PERMANENTLY headers = [(b"location", b"market://details?id=42")] return ( b"HTTP/1.1", status_code, b"Moved Permanently", headers, ByteStream(b""), ) return b"HTTP/1.1", 200, b"OK", [], ByteStream(b"Hello, world!")
def redirects(request: httpx.Request) -> httpx.Response: if request.url.scheme not in ("http", "https"): raise httpcore.UnsupportedProtocol( f"Scheme {request.url.scheme!r} not supported.") if request.url.path == "/no_redirect": return httpx.Response(200) elif request.url.path == "/redirect_301": status_code = httpx.codes.MOVED_PERMANENTLY content = b"<a href='https://example.org/'>here</a>" headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers, content=content) elif request.url.path == "/redirect_302": status_code = httpx.codes.FOUND headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_303": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/relative_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/malformed_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://:443/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/invalid_redirect": status_code = httpx.codes.SEE_OTHER raw_headers = [(b"location", "https://😇/".encode("utf-8"))] return httpx.Response(status_code, headers=raw_headers) elif request.url.path == "/no_scheme_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "//example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/multiple_redirects": params = httpx.QueryParams(request.url.query) count = int(params.get("count", "0")) redirect_count = count - 1 status_code = httpx.codes.SEE_OTHER if count else httpx.codes.OK if count: location = "/multiple_redirects" if redirect_count: location += f"?count={redirect_count}" headers = {"location": location} else: headers = {} return httpx.Response(status_code, headers=headers) if request.url.path == "/redirect_loop": status_code = httpx.codes.SEE_OTHER headers = {"location": "/redirect_loop"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/cross_domain": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://example.org/cross_domain_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/cross_domain_target": status_code = httpx.codes.OK data = { "body": request.content.decode("ascii"), "headers": dict(request.headers), } return httpx.Response(status_code, json=data) elif request.url.path == "/redirect_body": status_code = httpx.codes.PERMANENT_REDIRECT headers = {"location": "/redirect_body_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_no_body": status_code = httpx.codes.SEE_OTHER headers = {"location": "/redirect_body_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_body_target": data = { "body": request.content.decode("ascii"), "headers": dict(request.headers), } return httpx.Response(200, json=data) elif request.url.path == "/cross_subdomain": if request.headers["Host"] != "www.example.org": status_code = httpx.codes.PERMANENT_REDIRECT headers = {"location": "https://www.example.org/cross_subdomain"} return httpx.Response(status_code, headers=headers) else: return httpx.Response(200, text="Hello, world!") elif request.url.path == "/redirect_custom_scheme": status_code = httpx.codes.MOVED_PERMANENTLY headers = {"location": "market://details?id=42"} return httpx.Response(status_code, headers=headers) if request.method == "HEAD": return httpx.Response(200) return httpx.Response(200, html="<html><body>Hello, world!</body></html>")
async def arequest(self, method, url, headers=None, stream=None, ext=None): raise httpcore.UnsupportedProtocol("HTTP protocol is disabled")