diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2022-09-18 14:09:05 +0200 |
---|---|---|
committer | Trygve Aaberge <trygveaa@gmail.com> | 2022-09-18 16:36:55 +0200 |
commit | 5d1e5133e4a368f9b025a7f92f7920920aba3cf1 (patch) | |
tree | 55cee8767a57aaa26306cac0d376c6ff21712aa0 | |
parent | fee2398b6f7a3f66d8f9fb364f05ad1d31e7dc83 (diff) | |
download | wee-slack-5d1e5133e4a368f9b025a7f92f7920920aba3cf1.tar.gz |
Support retrying requests when ratelimited
-rw-r--r-- | _pytest/conftest.py | 3 | ||||
-rw-r--r-- | wee_slack.py | 135 |
2 files changed, 89 insertions, 49 deletions
diff --git a/_pytest/conftest.py b/_pytest/conftest.py index af86b02..90e064e 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -51,7 +51,8 @@ def realish_eventrouter(mock_websocket, mock_weechat): rtmstartdata = rtmstartfile.read().decode("utf-8") else: rtmstartdata = rtmstartfile.read() - e.receive_httprequest_callback(context, "", 0, rtmstartdata, "") + response = "HTTP/2 200\r\n\r\n" + rtmstartdata + e.receive_httprequest_callback(context, "", 0, response, "") while len(e.queue): e.handle_next() for team in e.teams.values(): diff --git a/wee_slack.py b/wee_slack.py index 7b0f38c..00ba638 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -668,6 +668,59 @@ class EventRouter(object): message_json["wee_slack_metadata_team"] = team self.receive(message_json) + def http_check_ratelimited(self, request_metadata, response): + headers_end_index = response.index("\r\n\r\n") + headers = response[:headers_end_index].split("\r\n") + http_status = headers[0].split(" ")[1] + + if http_status == "429": + for header in headers[1:]: + name, value = header.split(":", 1) + if name.lower() == "retry-after": + retry_after = int(value.strip()) + request_metadata.retry_time = time.time() + retry_after + return "", "ratelimited" + + body = response[headers_end_index + 4 :] + return body, "" + + def retry_request(self, request_metadata, data, return_code, err): + self.reply_buffer.pop(request_metadata.response_id, None) + self.delete_context(data) + retry_text = ( + "retrying" + if request_metadata.should_try() + else "will not retry after too many failed attempts" + ) + team = ( + "for team {}".format(request_metadata.team) + if request_metadata.team + else "with token {}".format(token_for_print(request_metadata.token)) + ) + w.prnt( + "", + ( + "Failed requesting {} {}, {}. " + + "If this persists, try increasing slack_timeout. Error (code {}): {}" + ).format( + request_metadata.request, + team, + retry_text, + return_code, + err, + ), + ) + dbg( + "{} failed with return_code {} and error {}. stack:\n{}".format( + request_metadata.request, + return_code, + err, + "".join(traceback.format_stack()), + ), + level=5, + ) + self.receive(request_metadata) + @utf8_decode def receive_httprequest_callback(self, data, command, return_code, out, err): """ @@ -692,25 +745,31 @@ class EventRouter(object): if request_metadata.response_id not in self.reply_buffer: self.reply_buffer[request_metadata.response_id] = StringIO() self.reply_buffer[request_metadata.response_id].write(out) - try: - j = json.loads( - self.reply_buffer[request_metadata.response_id].getvalue() - ) - except: - pass - # dbg("Incomplete json, awaiting more", True) - try: - j["wee_slack_process_method"] = request_metadata.request_normalized - if self.recording: - self.record_event( - j, request_metadata.team, "wee_slack_process_method", "http" - ) - j["wee_slack_request_metadata"] = request_metadata - self.reply_buffer.pop(request_metadata.response_id) - self.receive(j) - self.delete_context(data) - except: - dbg("HTTP REQUEST CALLBACK FAILED", True) + + response = self.reply_buffer[request_metadata.response_id].getvalue() + body, error = self.http_check_ratelimited(request_metadata, response) + if error: + self.retry_request(request_metadata, data, return_code, error) + else: + j = json.loads(body) + + try: + j[ + "wee_slack_process_method" + ] = request_metadata.request_normalized + if self.recording: + self.record_event( + j, + request_metadata.team, + "wee_slack_process_method", + "http", + ) + j["wee_slack_request_metadata"] = request_metadata + self.reply_buffer.pop(request_metadata.response_id) + self.receive(j) + self.delete_context(data) + except: + dbg("HTTP REQUEST CALLBACK FAILED", True) # We got an empty reply and this is weird so just ditch it and retry else: dbg("length was zero, probably a bug..") @@ -721,33 +780,7 @@ class EventRouter(object): self.reply_buffer[request_metadata.response_id] = StringIO() self.reply_buffer[request_metadata.response_id].write(out) else: - self.reply_buffer.pop(request_metadata.response_id, None) - self.delete_context(data) - if request_metadata.request.startswith("rtm."): - retry_text = ( - "retrying" - if request_metadata.should_try() - else "will not retry after too many failed attempts" - ) - w.prnt( - "", - ( - "Failed connecting to slack team with token {}, {}. " - + "If this persists, try increasing slack_timeout. Error (code {}): {}" - ).format( - token_for_print(request_metadata.token), - retry_text, - return_code, - err, - ), - ) - dbg( - "rtm.start failed with return_code {}. stack:\n{}".format( - return_code, "".join(traceback.format_stack()) - ), - level=5, - ) - self.receive(request_metadata) + self.retry_request(request_metadata, data, return_code, err) return w.WEECHAT_RC_OK def receive(self, dataobj, slow=False): @@ -933,10 +966,12 @@ def local_process_async_slack_api_request(request, event_router): ) ) request.tried() + options = request.options() + options["header"] = "1" context = event_router.store_context(request) w.hook_process_hashtable( weechat_request, - request.options(), + options, config.slack_timeout, "receive_httprequest_callback", context, @@ -1448,6 +1483,7 @@ class SlackRequest(object): self.channel = channel self.metadata = metadata if metadata else {} self.retries = retries + self.retry_time = 0 self.token = token if token else team.token self.cookies = cookies or {} if ":" in self.token: @@ -1517,7 +1553,10 @@ class SlackRequest(object): return self.tries < self.retries def retry_ready(self): - return (self.start_time + (self.tries**2)) < time.time() + if self.retry_time: + return time.time() > self.retry_time + else: + return (self.start_time + (self.tries**2)) < time.time() class SlackSubteam(object): |