aboutsummaryrefslogtreecommitdiffstats
path: root/slack/http.py
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2022-10-24 22:42:24 +0200
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-18 11:32:52 +0100
commit9f938758633224f9ddacdbb730bf877a631fea03 (patch)
tree875f92d13d2d76f0b72644c06f9817f58b96da91 /slack/http.py
parentfed1898bb75f2c3f0297cf7228c9d86adef57b78 (diff)
downloadwee-slack-9f938758633224f9ddacdbb730bf877a631fea03.tar.gz
Rename weechat_http to http
Diffstat (limited to 'slack/http.py')
-rw-r--r--slack/http.py107
1 files changed, 107 insertions, 0 deletions
diff --git a/slack/http.py b/slack/http.py
new file mode 100644
index 0000000..ba30339
--- /dev/null
+++ b/slack/http.py
@@ -0,0 +1,107 @@
+from __future__ import annotations
+
+import os
+import resource
+from io import StringIO
+from typing import Dict
+
+import weechat
+
+from .log import LogLevel, log
+from .task import FutureProcess, sleep, weechat_task_cb
+from .util import get_callback_name
+
+
+class HttpError(Exception):
+ def __init__(self, url: str, return_code: int, http_status: int, error: str):
+ super().__init__()
+ self.url = url
+ self.return_code = return_code
+ self.http_status = http_status
+ self.error = error
+
+
+def available_file_descriptors():
+ num_current_file_descriptors = len(os.listdir("/proc/self/fd/"))
+ max_file_descriptors = min(resource.getrlimit(resource.RLIMIT_NOFILE))
+ return max_file_descriptors - num_current_file_descriptors
+
+
+async def hook_process_hashtable(command: str, options: Dict[str, str], timeout: int):
+ future = FutureProcess()
+ log(
+ LogLevel.DEBUG,
+ f"hook_process_hashtable calling ({future.id}): command: {command}",
+ )
+ while available_file_descriptors() < 10:
+ await sleep(10)
+ weechat.hook_process_hashtable(
+ command, options, timeout, get_callback_name(weechat_task_cb), future.id
+ )
+
+ stdout = StringIO()
+ stderr = StringIO()
+ return_code = -1
+
+ while return_code == -1:
+ _, return_code, out, err = await future
+ log(
+ LogLevel.TRACE,
+ f"hook_process_hashtable intermediary response ({future.id}): command: {command}",
+ )
+ stdout.write(out)
+ stderr.write(err)
+
+ out = stdout.getvalue()
+ err = stderr.getvalue()
+ log(
+ LogLevel.DEBUG,
+ f"hook_process_hashtable response ({future.id}): command: {command}, "
+ f"return_code: {return_code}, response length: {len(out)}"
+ + (f", error: {err}" if err else ""),
+ )
+
+ return command, return_code, out, err
+
+
+async def http_request(
+ url: str, options: Dict[str, str], timeout: int, max_retries: int = 5
+) -> str:
+ options["header"] = "1"
+ _, return_code, out, err = await hook_process_hashtable(
+ f"url:{url}", options, timeout
+ )
+
+ if return_code != 0 or err:
+ if max_retries > 0:
+ log(
+ LogLevel.INFO,
+ f"HTTP error, retrying (max {max_retries} times): "
+ f"return_code: {return_code}, error: {err}, url: {url}",
+ )
+ await sleep(1000)
+ return await http_request(url, options, timeout, max_retries - 1)
+ raise HttpError(url, return_code, 0, err)
+
+ headers_end_index = out.index("\r\n\r\n")
+ headers = out[:headers_end_index].split("\r\n")
+ http_status = int(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())
+ log(
+ LogLevel.INFO,
+ f"HTTP ratelimit, retrying in {retry_after} seconds, url: {url}",
+ )
+ await sleep(retry_after * 1000)
+ return await http_request(url, options, timeout)
+
+ body = out[headers_end_index + 4 :]
+
+ if http_status >= 400:
+ raise HttpError(url, return_code, http_status, body)
+
+ return body