Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/abstractions/kiota_abstractions/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ class Method(Enum):
HEAD = "HEAD"
# The HTTP PUT method
PUT = "PUT"
# The HTTP QUERY method
QUERY = "QUERY"
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ def _redirect_method(self, request: httpx.Request, response: httpx.Response) ->
method = "GET"

# Do what the browsers do, despite standards...
# Turn 302s into GETs.
if response.status_code == 302 and method != "HEAD":
# Turn 302s into GETs, except QUERY which must be preserved (RFC 10008).
if response.status_code == 302 and method not in ("HEAD", "QUERY"):
method = "GET"

# If a POST is responded to with a 301, turn it into a GET.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class RetryHandler(BaseMiddleware):
DEFAULT_RETRY_STATUS_CODES: set[int] = {429, 503, 504}

DEFAULT_ALLOWED_METHODS: frozenset[str] = frozenset(
['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']
['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'QUERY']
)

def __init__(self, options: RetryHandlerOption = RetryHandlerOption()) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,27 @@ def request_handler(request: httpx.Request):
assert resp.request.url == REDIRECT_URL


@pytest.mark.asyncio
async def test_query_redirect_with_302_preserves_method_and_body():
"""Test that a QUERY request keeps its method and body on a 302 redirect (RFC 10008)"""

def request_handler(request: httpx.Request):
if request.url == REDIRECT_URL:
return httpx.Response(200, )
return httpx.Response(
FOUND,
headers={LOCATION_HEADER: REDIRECT_URL},
)

handler = RedirectHandler()
request = httpx.Request('QUERY', BASE_URL, content=b'select *')
mock_transport = httpx.MockTransport(request_handler)
resp = await handler.send(request, mock_transport)
assert resp.status_code == 200
assert resp.request.method == 'QUERY'
assert resp.request.read() == b'select *'


@pytest.mark.asyncio
async def test_redirect_to_different_host_removes_auth_header():
"""Test that if a request is redirected to a different host,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_no_config():
assert retry_handler.options.max_retry == options.max_retry
assert retry_handler.options.max_delay == options.max_delay
assert retry_handler.allowed_methods == frozenset(
['HEAD', 'GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS']
['HEAD', 'GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS', 'QUERY']
Comment thread
baywet marked this conversation as resolved.
)
assert retry_handler.respect_retry_after_header

Expand Down Expand Up @@ -205,6 +205,24 @@ def request_handler(request: httpx.Request):
assert resp.request.headers[RETRY_ATTEMPT] == '1'


@pytest.mark.asyncio
async def test_query_request_is_retried():
"""Test that a QUERY request with a body is retried"""

def request_handler(request: httpx.Request):
if RETRY_ATTEMPT in request.headers:
return httpx.Response(200, )
return httpx.Response(SERVICE_UNAVAILABLE, )

handler = RetryHandler()
request = httpx.Request('QUERY', BASE_URL, content=b'select *')
mock_transport = httpx.MockTransport(request_handler)
resp = await handler.send(request, mock_transport)
assert resp.status_code == 200
assert resp.request.method == 'QUERY'
assert resp.request.headers[RETRY_ATTEMPT] == '1'


@pytest.mark.asyncio
async def test_should_retry_false():
"""Test that a request is not retried if should_retry is set to False"""
Expand Down
Loading