11import json
22import logging
3+ import re
34from http .client import HTTPResponse
5+ from ssl import SSLContext
46from typing import Dict , Union , List , Optional
57from urllib .error import HTTPError
68from urllib .request import Request , urlopen
@@ -16,13 +18,24 @@ class WebhookClient:
1618 logger = logging .getLogger (__name__ )
1719
1820 def __init__ (
19- self , url : str , default_headers : Dict [str , str ] = {},
21+ self ,
22+ url : str ,
23+ timeout : int = 30 ,
24+ ssl : Optional [SSLContext ] = None ,
25+ proxy : Optional [str ] = None ,
26+ default_headers : Dict [str , str ] = {},
2027 ):
2128 """API client for Incoming Webhooks and response_url
2229 :param url: a complete URL to send data (e.g., https://hooks.slack.com/XXX)
30+ :param timeout: request timeout (in seconds)
31+ :param ssl: ssl.SSLContext to use for requests
32+ :param proxy: proxy URL (e.g., localhost:9000, http://localhost:9000)
2333 :param default_headers: request headers to add to all requests
2434 """
2535 self .url = url
36+ self .timeout = timeout
37+ self .ssl = ssl
38+ self .proxy = proxy
2639 self .default_headers = default_headers
2740
2841 def send (
@@ -65,11 +78,11 @@ def send_dict(
6578 body = convert_bool_to_0_or_1 (body )
6679 self ._parse_web_class_objects (body )
6780 return self ._perform_http_request (
68- url = self . url , body = body , headers = self ._build_request_headers (headers ),
81+ body = body , headers = self ._build_request_headers (headers )
6982 )
7083
7184 def _perform_http_request (
72- self , * , url : str , body : Dict [str , any ], headers : Dict [str , str ]
85+ self , * , body : Dict [str , any ], headers : Dict [str , str ]
7386 ) -> WebhookResponse :
7487 """Performs an HTTP request and parses the response.
7588 :param url: a complete URL to send data (e.g., https://hooks.slack.com/XXX)
@@ -85,19 +98,24 @@ def _perform_http_request(
8598 f"Sending a request - url: { self .url } , body: { body } , headers: { headers } "
8699 )
87100 try :
101+ url = self .url
88102 # for security
89103 if url .lower ().startswith ("http" ):
90104 req = Request (
91105 method = "POST" , url = url , data = body .encode ("utf-8" ), headers = headers
92106 )
107+ if self .proxy is not None :
108+ host = re .sub ("^https?://" , "" , self .proxy )
109+ req .set_proxy (host , "http" )
110+ req .set_proxy (host , "https" )
93111 else :
94112 raise SlackRequestError (f"Invalid URL detected: { url } " )
95113
96- resp : HTTPResponse = urlopen (req )
114+ resp : HTTPResponse = urlopen (req , context = self . ssl , timeout = self . timeout )
97115 charset : str = resp .headers .get_content_charset () or "utf-8"
98116 response_body : str = resp .read ().decode (charset )
99117 resp = WebhookResponse (
100- url = self . url ,
118+ url = url ,
101119 status_code = resp .status ,
102120 body = response_body ,
103121 headers = resp .headers ,
@@ -109,7 +127,7 @@ def _perform_http_request(
109127 charset : str = e .headers .get_content_charset () or "utf-8"
110128 response_body : str = resp .read ().decode (charset )
111129 resp = WebhookResponse (
112- url = self . url , status_code = e .code , body = response_body , headers = e .headers ,
130+ url = url , status_code = e .code , body = response_body , headers = e .headers ,
113131 )
114132 if e .code == 429 :
115133 # for backward-compatibility with WebClient (v.2.5.0 or older)
0 commit comments