outgoing_http: Factor out outgoing HTTP session with timeout.

This commit is contained in:
Alex Vandiver 2021-05-06 16:56:31 -07:00 committed by Tim Abbott
parent 2dc6df33ae
commit b88d7a741e
3 changed files with 68 additions and 17 deletions

View File

@ -0,0 +1,25 @@
from typing import Any, Dict, Optional
import requests
from urllib3 import HTTPResponse
class OutgoingSession(requests.Session):
def __init__(self, timeout: int, headers: Optional[Dict[str, str]] = None) -> None:
super().__init__()
outgoing_adapter = OutgoingHTTPAdapter(timeout=timeout)
self.mount("http://", outgoing_adapter)
self.mount("https://", outgoing_adapter)
class OutgoingHTTPAdapter(requests.adapters.HTTPAdapter):
timeout: int
def __init__(self, timeout: int, *args: Any, **kwargs: Any) -> None:
self.timeout = timeout
super().__init__(*args, *kwargs)
def send(self, *args: Any, **kwargs: Any) -> HTTPResponse:
if kwargs.get("timeout") is None:
kwargs["timeout"] = self.timeout
return super().send(*args, **kwargs)

View File

@ -7,13 +7,13 @@ from typing import Any, AnyStr, Dict, Optional
import requests
from django.conf import settings
from django.utils.translation import gettext as _
from requests import Response, Session
from urllib3 import HTTPResponse
from requests import Response
from version import ZULIP_VERSION
from zerver.decorator import JsonableError
from zerver.lib.actions import check_send_message
from zerver.lib.message import MessageDict
from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.queue import retry_event
from zerver.lib.topic import get_topic_from_message_info
from zerver.lib.url_encoding import near_message_url
@ -28,26 +28,14 @@ from zerver.models import (
)
class TimeoutAdapter(requests.adapters.HTTPAdapter):
def __init__(self, timeout: Optional[int] = None, *args: Any, **kwargs: Any) -> None:
self.timeout = timeout
super().__init__(*args, *kwargs)
def send(self, *args: Any, **kwargs: Any) -> HTTPResponse:
if kwargs.get("timeout") is None:
kwargs["timeout"] = self.timeout
return super().send(*args, **kwargs)
class OutgoingWebhookServiceInterface(metaclass=abc.ABCMeta):
def __init__(self, token: str, user_profile: UserProfile, service_name: str) -> None:
self.token: str = token
self.user_profile: UserProfile = user_profile
self.service_name: str = service_name
self.session: Session = Session()
timeout_adapter = TimeoutAdapter(timeout=settings.OUTGOING_WEBHOOK_TIMEOUT_SECONDS)
self.session.mount("http://", timeout_adapter)
self.session.mount("https://", timeout_adapter)
self.session: requests.Session = OutgoingSession(
timeout=10,
)
self.session.headers.update(
{
"X-Smokescreen-Role": "webhook",

View File

@ -0,0 +1,38 @@
from typing import Any
import requests
import responses
from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.test_classes import ZulipTestCase
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"]
return super()._on_request( # type: ignore[misc] # This is an undocumented internal API
adapter,
request,
**kwargs,
)
class TestOutgoingHttp(ZulipTestCase):
def test_timeouts(self) -> None:
with RequestMockWithTimeoutAsHeader() as mock_requests:
mock_requests.add(responses.GET, "http://example.com/")
OutgoingSession(timeout=17).get("http://example.com/")
self.assertEqual(len(mock_requests.calls), 1)
self.assertEqual(mock_requests.calls[0].request.headers["X-Timeout"], 17)
with RequestMockWithTimeoutAsHeader() as mock_requests:
mock_requests.add(responses.GET, "http://example.com/")
OutgoingSession(timeout=17).get("http://example.com/", timeout=42)
self.assertEqual(len(mock_requests.calls), 1)
self.assertEqual(mock_requests.calls[0].request.headers["X-Timeout"], 42)