2021-05-07 01:57:30 +02:00
|
|
|
import os
|
2021-05-07 01:56:31 +02:00
|
|
|
from typing import Any
|
2021-05-07 01:57:30 +02:00
|
|
|
from unittest import mock
|
2021-05-07 01:56:31 +02:00
|
|
|
|
|
|
|
import requests
|
|
|
|
import responses
|
2022-01-13 22:02:54 +01:00
|
|
|
from urllib3.util import Retry
|
2021-05-07 01:56:31 +02:00
|
|
|
|
|
|
|
from zerver.lib.outgoing_http import OutgoingSession
|
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
|
|
|
|
|
|
|
2021-05-07 01:57:30 +02:00
|
|
|
class RequestMockWithProxySupport(responses.RequestsMock):
|
|
|
|
def _on_request(
|
|
|
|
self,
|
|
|
|
adapter: requests.adapters.HTTPAdapter,
|
|
|
|
request: requests.PreparedRequest,
|
|
|
|
**kwargs: Any,
|
|
|
|
) -> requests.Response:
|
|
|
|
if "proxies" in kwargs and request.url:
|
2023-04-08 07:01:50 +02:00
|
|
|
proxy_url = requests.utils.select_proxy(request.url, kwargs["proxies"])
|
|
|
|
if proxy_url is not None:
|
2021-05-07 01:57:30 +02:00
|
|
|
request = requests.Request(
|
|
|
|
method="GET",
|
2023-04-08 07:01:50 +02:00
|
|
|
url=f"{proxy_url}/",
|
|
|
|
headers=adapter.proxy_headers(proxy_url),
|
2021-05-07 01:57:30 +02:00
|
|
|
).prepare()
|
2022-06-01 23:53:07 +02:00
|
|
|
return super()._on_request(adapter, request, **kwargs)
|
2021-05-07 01:57:30 +02:00
|
|
|
|
|
|
|
|
2021-05-07 01:56:31 +02:00
|
|
|
class RequestMockWithTimeoutAsHeader(responses.RequestsMock):
|
|
|
|
def _on_request(
|
|
|
|
self,
|
|
|
|
adapter: requests.adapters.HTTPAdapter,
|
|
|
|
request: requests.PreparedRequest,
|
|
|
|
**kwargs: Any,
|
|
|
|
) -> requests.Response:
|
|
|
|
if kwargs.get("timeout") is not None:
|
|
|
|
request.headers["X-Timeout"] = kwargs["timeout"]
|
2022-06-01 23:53:07 +02:00
|
|
|
return super()._on_request(adapter, request, **kwargs)
|
2021-05-07 01:56:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestOutgoingHttp(ZulipTestCase):
|
2021-05-07 01:57:46 +02:00
|
|
|
def test_headers(self) -> None:
|
|
|
|
with RequestMockWithProxySupport() as mock_requests:
|
|
|
|
mock_requests.add(responses.GET, "http://example.com/")
|
|
|
|
OutgoingSession(role="testing", timeout=1, headers={"X-Foo": "bar"}).get(
|
|
|
|
"http://example.com/"
|
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(mock_requests.calls, 1)
|
2021-05-07 01:57:46 +02:00
|
|
|
headers = mock_requests.calls[0].request.headers
|
|
|
|
# We don't see a proxy header with no proxy set
|
|
|
|
self.assertFalse("X-Smokescreen-Role" in headers)
|
|
|
|
self.assertEqual(headers["X-Foo"], "bar")
|
|
|
|
|
2021-05-07 01:57:30 +02:00
|
|
|
@mock.patch.dict(os.environ, {"http_proxy": "http://localhost:4242"})
|
|
|
|
def test_proxy_headers(self) -> None:
|
|
|
|
with RequestMockWithProxySupport() as mock_requests:
|
|
|
|
mock_requests.add(responses.GET, "http://localhost:4242/")
|
2021-05-07 01:57:46 +02:00
|
|
|
OutgoingSession(role="testing", timeout=1, headers={"X-Foo": "bar"}).get(
|
|
|
|
"http://example.com/"
|
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(mock_requests.calls, 1)
|
2021-05-07 01:57:30 +02:00
|
|
|
headers = mock_requests.calls[0].request.headers
|
|
|
|
self.assertEqual(headers["X-Smokescreen-Role"], "testing")
|
|
|
|
|
2021-05-07 01:57:46 +02:00
|
|
|
# We don't see the request-level headers in the proxy
|
|
|
|
# request. This isn't a _true_ test because we're
|
|
|
|
# fiddling the headers above, instead of urllib3 actually
|
|
|
|
# setting them.
|
|
|
|
self.assertFalse("X-Foo" in headers)
|
|
|
|
|
2021-05-07 01:56:31 +02:00
|
|
|
def test_timeouts(self) -> None:
|
|
|
|
with RequestMockWithTimeoutAsHeader() as mock_requests:
|
|
|
|
mock_requests.add(responses.GET, "http://example.com/")
|
2021-05-07 01:57:30 +02:00
|
|
|
OutgoingSession(role="testing", timeout=17).get("http://example.com/")
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(mock_requests.calls, 1)
|
2021-05-07 01:56:31 +02:00
|
|
|
self.assertEqual(mock_requests.calls[0].request.headers["X-Timeout"], 17)
|
|
|
|
|
|
|
|
with RequestMockWithTimeoutAsHeader() as mock_requests:
|
|
|
|
mock_requests.add(responses.GET, "http://example.com/")
|
2021-05-07 01:57:30 +02:00
|
|
|
OutgoingSession(role="testing", timeout=17).get("http://example.com/", timeout=42)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(mock_requests.calls, 1)
|
2021-05-07 01:56:31 +02:00
|
|
|
self.assertEqual(mock_requests.calls[0].request.headers["X-Timeout"], 42)
|
2021-06-29 20:54:49 +02:00
|
|
|
|
|
|
|
def test_retries(self) -> None:
|
|
|
|
# Responses doesn't support testing the low-level retry
|
|
|
|
# functionality, so we can't test the retry itself easily. :(
|
|
|
|
# https://github.com/getsentry/responses/issues/135
|
|
|
|
|
|
|
|
# Defaults to no retries
|
|
|
|
session = requests.Session()
|
|
|
|
self.assertEqual(session.adapters["http://"].max_retries.total, 0)
|
|
|
|
self.assertEqual(session.adapters["https://"].max_retries.total, 0)
|
|
|
|
|
|
|
|
session = OutgoingSession(role="testing", timeout=1)
|
|
|
|
self.assertEqual(session.adapters["http://"].max_retries.total, 0)
|
|
|
|
self.assertEqual(session.adapters["https://"].max_retries.total, 0)
|
|
|
|
|
|
|
|
session = OutgoingSession(role="testing", timeout=1, max_retries=2)
|
|
|
|
self.assertEqual(session.adapters["http://"].max_retries.total, 2)
|
|
|
|
self.assertEqual(session.adapters["https://"].max_retries.total, 2)
|
|
|
|
|
|
|
|
session = OutgoingSession(role="testing", timeout=1, max_retries=Retry(total=5))
|
|
|
|
self.assertEqual(session.adapters["http://"].max_retries.total, 5)
|
|
|
|
self.assertEqual(session.adapters["https://"].max_retries.total, 5)
|