(self, *, body: Dict[str, Any], headers: Dict[str, str])
| 140 | ) |
| 141 | |
| 142 | def _perform_http_request(self, *, body: Dict[str, Any], headers: Dict[str, str]) -> WebhookResponse: |
| 143 | raw_body = json.dumps(body) |
| 144 | headers["Content-Type"] = "application/json;charset=utf-8" |
| 145 | |
| 146 | if self.logger.level <= logging.DEBUG: |
| 147 | self.logger.debug(f"Sending a request - url: {self.url}, body: {raw_body}, headers: {headers}") |
| 148 | |
| 149 | url = self.url |
| 150 | # NOTE: Intentionally ignore the `http_verb` here |
| 151 | # Slack APIs accepts any API method requests with POST methods |
| 152 | req = Request(method="POST", url=url, data=raw_body.encode("utf-8"), headers=headers) |
| 153 | resp = None |
| 154 | last_error = Exception("undefined internal error") |
| 155 | |
| 156 | retry_state = RetryState() |
| 157 | counter_for_safety = 0 |
| 158 | while counter_for_safety < 100: |
| 159 | counter_for_safety += 1 |
| 160 | # If this is a retry, the next try started here. We can reset the flag. |
| 161 | retry_state.next_attempt_requested = False |
| 162 | |
| 163 | try: |
| 164 | resp = self._perform_http_request_internal(url, req) |
| 165 | # The resp is a 200 OK response |
| 166 | return resp |
| 167 | |
| 168 | except HTTPError as e: |
| 169 | # read the response body here |
| 170 | charset = e.headers.get_content_charset() or "utf-8" |
| 171 | response_body: str = e.read().decode(charset) |
| 172 | # As adding new values to HTTPError#headers can be ignored, building a new dict object here |
| 173 | response_headers = dict(e.headers.items()) |
| 174 | resp = WebhookResponse( |
| 175 | url=url, |
| 176 | status_code=e.code, |
| 177 | body=response_body, |
| 178 | headers=response_headers, |
| 179 | ) |
| 180 | if e.code == 429: |
| 181 | # for backward-compatibility with WebClient (v.2.5.0 or older) |
| 182 | if "retry-after" not in resp.headers and "Retry-After" in resp.headers: |
| 183 | resp.headers["retry-after"] = resp.headers["Retry-After"] |
| 184 | if "Retry-After" not in resp.headers and "retry-after" in resp.headers: |
| 185 | resp.headers["Retry-After"] = resp.headers["retry-after"] |
| 186 | _debug_log_response(self.logger, resp) |
| 187 | |
| 188 | # Try to find a retry handler for this error |
| 189 | retry_request = RetryHttpRequest.from_urllib_http_request(req) |
| 190 | retry_response = RetryHttpResponse( |
| 191 | status_code=e.code, |
| 192 | headers={k: [v] for k, v in e.headers.items()}, |
| 193 | data=response_body.encode("utf-8") if response_body is not None else None, |
| 194 | ) |
| 195 | for handler in self.retry_handlers: |
| 196 | if handler.can_retry( |
| 197 | state=retry_state, |
| 198 | request=retry_request, |
| 199 | response=retry_response, |
no test coverage detected