Guest User

async_urequests

a guest
Jul 8th, 2024
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.41 KB | Software | 0 0
  1. import uasyncio as asyncio
  2.  
  3.  
  4. class Response:
  5.     def __init__(self, f):
  6.         self.raw = f
  7.         self.encoding = "utf-8"
  8.         self._cached = None
  9.  
  10.     async def close(self):
  11.         if self.raw:
  12.             self.raw.close()
  13.             await self.raw.wait_closed()
  14.             self.raw = None
  15.         self._cached = None
  16.  
  17.     @property
  18.     async def content(self):
  19.         if self._cached is None:
  20.             try:
  21.                 if(self.headers["Content-Length"]):
  22.                     self._cached = await self.raw.read(int(self.headers["Content-Length"]))
  23.                 else:
  24.                     self._cached = await self.raw.read()
  25.             finally:
  26.                 self.raw.close()
  27.                 await self.raw.wait_closed()
  28.                 self.raw = None
  29.         return self._cached
  30.  
  31.     @property
  32.     async def text(self):
  33.         return str(await self.content, self.encoding)
  34.  
  35.     async def json(self):
  36.         import ujson
  37.  
  38.         return ujson.loads(await self.content)
  39.  
  40.  
  41. async def request(
  42.     method,
  43.     url,
  44.     data=None,
  45.     json=None,
  46.     headers={},
  47.     stream=None,
  48.     auth=None,
  49.     hostname=None,
  50.     timeout=None,
  51.     parse_headers=True,
  52. ):
  53.     redirect = None  # redirection url, None means no redirection
  54.     chunked_data = (
  55.         data and getattr(data, "__iter__", None) and not getattr(data, "__len__", None)
  56.     )
  57.  
  58.     if auth is not None:
  59.         import ubinascii
  60.  
  61.         username, password = auth
  62.         formated = b"{}:{}".format(username, password)
  63.         formated = str(ubinascii.b2a_base64(formated)[:-1], "ascii")
  64.         headers["Authorization"] = "Basic {}".format(formated)
  65.  
  66.     try:
  67.         proto, dummy, host, path = url.split("/", 3)
  68.     except ValueError:
  69.         proto, dummy, host = url.split("/", 2)
  70.         path = ""
  71.     if proto == "http:":
  72.         port = 80
  73.     elif proto == "https:":
  74.         port = 443
  75.     else:
  76.         raise ValueError("Unsupported protocol: " + proto)
  77.  
  78.     if ":" in host:
  79.         host, port = host.split(":", 1)
  80.         port = int(port)
  81.  
  82.     # ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM)
  83.     # ai = ai[0]
  84.     # print(proto, host, port)
  85.     resp_d = None
  86.     if parse_headers is not False:
  87.         resp_d = {}
  88.  
  89.     # s = usocket.socket(ai[0], usocket.SOCK_STREAM, ai[2])
  90.  
  91.     # if timeout is not None:
  92.     #     # Note: settimeout is not supported on all platforms, will raise
  93.     #     # an AttributeError if not available.
  94.     #     s.settimeout(timeout)
  95.  
  96.     try:
  97.         # s.connect(ai[-1])
  98.         # host ai[-1][0]
  99.         if not hostname:
  100.             hostname = host
  101.         if proto == "https:":
  102.             raise "https support is broken and I do not care!"
  103.  
  104.         reader, writer = await asyncio.open_connection(host, port)
  105.         writer.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
  106.         await writer.drain()
  107.         if "Host" not in headers:
  108.             writer.write(b"Host: %s\r\n" % host)
  109.             await writer.drain()
  110.         # Iterate over keys to avoid tuple alloc
  111.         for k in headers:
  112.             writer.write(k)
  113.             writer.write(b": ")
  114.             writer.write(headers[k])
  115.             writer.write(b"\r\n")
  116.             await writer.drain()
  117.         if json is not None:
  118.             assert data is None
  119.             import ujson
  120.  
  121.             data = ujson.dumps(json)
  122.             writer.write(b"Content-Type: application/json\r\n")
  123.             await writer.drain()
  124.         if data:
  125.             if chunked_data:
  126.                 writer.write(b"Transfer-Encoding: chunked\r\n")
  127.             else:
  128.                 writer.write(b"Content-Length: %d\r\n" % len(data))
  129.  
  130.             await writer.drain()
  131.         writer.write(b"Connection: close\r\n\r\n")
  132.         await writer.drain()
  133.         if data:
  134.             if chunked_data:
  135.                 for chunk in data:
  136.                     writer.write(b"%x\r\n" % len(chunk))
  137.                     writer.write(chunk)
  138.                     writer.write(b"\r\n")
  139.                     await writer.drain()
  140.                 writer.write("0\r\n\r\n")
  141.                 await writer.drain()
  142.             else:
  143.                 writer.write(data)
  144.                 await writer.drain()
  145.  
  146.         l = await reader.readline()
  147.         # print(l)
  148.         l = l.split(None, 2)
  149.         if len(l) < 2:
  150.             # Invalid response
  151.             raise ValueError("HTTP error: BadStatusLine:\n%s" % l)
  152.         status = int(l[1])
  153.         reason = ""
  154.         if len(l) > 2:
  155.             reason = l[2].rstrip()
  156.         while True:
  157.             l = await reader.readline()
  158.             if not l or l == b"\r\n":
  159.                 break
  160.             # print(l)
  161.             if l.startswith(b"Transfer-Encoding:"):
  162.                 if b"chunked" in l:
  163.                     raise ValueError("Unsupported " + str(l, "utf-8"))
  164.             elif l.startswith(b"Location:") and not 200 <= status <= 299:
  165.                 if status in [301, 302, 303, 307, 308]:
  166.                     redirect = str(l[10:-2], "utf-8")
  167.                 else:
  168.                     raise NotImplementedError("Redirect %d not yet supported" % status)
  169.             if parse_headers is False:
  170.                 pass
  171.             elif parse_headers is True:
  172.                 l = str(l, "utf-8")
  173.                 k, v = l.split(":", 1)
  174.                 resp_d[k] = v.strip()
  175.             else:
  176.                 parse_headers(l, resp_d)
  177.     except OSError:
  178.         writer.close()
  179.         await writer.wait_closed()
  180.         raise
  181.  
  182.     if redirect:
  183.         writer.close()
  184.         await writer.wait_closed()
  185.         if status in [301, 302, 303]:
  186.             return await request("GET", redirect, None, None, headers, stream)
  187.         else:
  188.             return await request(method, redirect, data, json, headers, stream)
  189.     else:
  190.         resp = Response(reader)
  191.         resp.status_code = status
  192.         resp.reason = reason
  193.         if resp_d is not None:
  194.             resp.headers = resp_d
  195.             return resp
  196.  
  197.  
  198. async def head(url, **kw):
  199.     return await request("HEAD", url, **kw)
  200.  
  201.  
  202. async def get(url, **kw):
  203.     return await request("GET", url, **kw)
  204.  
  205.  
  206. async def post(url, **kw):
  207.     return await request("POST", url, **kw)
  208.  
  209.  
  210. async def put(url, **kw):
  211.     return await request("PUT", url, **kw)
  212.  
  213.  
  214. async def patch(url, **kw):
  215.     return await request("PATCH", url, **kw)
  216.  
  217.  
  218. async def delete(url, **kw):
  219.     return await request("DELETE", url, **kw)
  220.  
  221.  
Add Comment
Please, Sign In to add comment