Skip to content

Commit b78516a

Browse files
committed
Add timeout arg to WebhookClient
1 parent 3609cbc commit b78516a

File tree

3 files changed

+19
-7
lines changed

3 files changed

+19
-7
lines changed

slack/webhook/client.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ class WebhookClient:
1616
logger = logging.getLogger(__name__)
1717

1818
def __init__(
19-
self, url: str, default_headers: Dict[str, str] = {},
19+
self, url: str, timeout: int = 30, default_headers: Dict[str, str] = {}
2020
):
2121
"""API client for Incoming Webhooks and response_url
2222
:param url: a complete URL to send data (e.g., https://hooks.slack.com/XXX)
23+
:param timeout: request timeout (in seconds)
2324
:param default_headers: request headers to add to all requests
2425
"""
2526
self.url = url
27+
self.timeout = timeout
2628
self.default_headers = default_headers
2729

2830
def send(
@@ -65,11 +67,11 @@ def send_dict(
6567
body = convert_bool_to_0_or_1(body)
6668
self._parse_web_class_objects(body)
6769
return self._perform_http_request(
68-
url=self.url, body=body, headers=self._build_request_headers(headers),
70+
body=body, headers=self._build_request_headers(headers)
6971
)
7072

7173
def _perform_http_request(
72-
self, *, url: str, body: Dict[str, any], headers: Dict[str, str]
74+
self, *, body: Dict[str, any], headers: Dict[str, str]
7375
) -> WebhookResponse:
7476
"""Performs an HTTP request and parses the response.
7577
:param url: a complete URL to send data (e.g., https://hooks.slack.com/XXX)
@@ -85,6 +87,7 @@ def _perform_http_request(
8587
f"Sending a request - url: {self.url}, body: {body}, headers: {headers}"
8688
)
8789
try:
90+
url = self.url
8891
# for security
8992
if url.lower().startswith("http"):
9093
req = Request(
@@ -93,11 +96,11 @@ def _perform_http_request(
9396
else:
9497
raise SlackRequestError(f"Invalid URL detected: {url}")
9598

96-
resp: HTTPResponse = urlopen(req)
99+
resp: HTTPResponse = urlopen(req, timeout=self.timeout)
97100
charset: str = resp.headers.get_content_charset() or "utf-8"
98101
response_body: str = resp.read().decode(charset)
99102
resp = WebhookResponse(
100-
url=self.url,
103+
url=url,
101104
status_code=resp.status,
102105
body=response_body,
103106
headers=resp.headers,
@@ -109,7 +112,7 @@ def _perform_http_request(
109112
charset: str = e.headers.get_content_charset() or "utf-8"
110113
response_body: str = resp.read().decode(charset)
111114
resp = WebhookResponse(
112-
url=self.url, status_code=e.code, body=response_body, headers=e.headers,
115+
url=url, status_code=e.code, body=response_body, headers=e.headers,
113116
)
114117
if e.code == 429:
115118
# for backward-compatibility with WebClient (v.2.5.0 or older)

tests/webhook/mock_web_api_server.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import json
21
import logging
32
import re
43
import threading
4+
import time
55
from http import HTTPStatus
66
from http.server import HTTPServer, SimpleHTTPRequestHandler
77
from typing import Type
@@ -28,6 +28,9 @@ def set_common_headers(self):
2828

2929
def do_POST(self):
3030
try:
31+
if self.path == "/timeout":
32+
time.sleep(2)
33+
3134
body = "ok"
3235

3336
self.send_response(HTTPStatus.OK)

tests/webhook/test_webhook.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
import socket
23

34
from slack.web.classes.attachments import Attachment, AttachmentField
45
from slack.web.classes.blocks import SectionBlock, ImageBlock
@@ -154,3 +155,8 @@ def test_send_dict(self):
154155
resp: WebhookResponse = client.send_dict({"text": "hello!"})
155156
self.assertEqual(200, resp.status_code)
156157
self.assertEqual("ok", resp.body)
158+
159+
def test_timeout_issue_712(self):
160+
client = WebhookClient(url="http://localhost:8888/timeout", timeout=1)
161+
with self.assertRaises(socket.timeout):
162+
client.send_dict({"text": "hello!"})

0 commit comments

Comments
 (0)