micropython-lib/python-ecosys/requests/requests/__init__.py

220 wiersze
5.7 KiB
Python

import socket
class Response:
def __init__(self, f):
self.raw = f
self.encoding = "utf-8"
self._cached = None
def close(self):
if self.raw:
self.raw.close()
self.raw = None
self._cached = None
@property
def content(self):
if self._cached is None:
try:
self._cached = self.raw.read()
finally:
self.raw.close()
self.raw = None
return self._cached
@property
def text(self):
return str(self.content, self.encoding)
def json(self):
import json
return json.loads(self.content)
def request(
method,
url,
data=None,
json=None,
headers=None,
stream=None,
auth=None,
timeout=None,
parse_headers=True,
):
if headers is None:
headers = {}
else:
headers = headers.copy()
redirect = None # redirection url, None means no redirection
chunked_data = data and getattr(data, "__next__", None) and not getattr(data, "__len__", None)
if auth is not None:
import binascii
username, password = auth
formated = b"{}:{}".format(username, password)
formated = str(binascii.b2a_base64(formated)[:-1], "ascii")
headers["Authorization"] = "Basic {}".format(formated)
try:
proto, dummy, host, path = url.split("/", 3)
except ValueError:
proto, dummy, host = url.split("/", 2)
path = ""
if proto == "http:":
port = 80
elif proto == "https:":
import tls
port = 443
else:
raise ValueError("Unsupported protocol: " + proto)
if ":" in host:
host, port = host.split(":", 1)
port = int(port)
ai = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
ai = ai[0]
resp_d = None
if parse_headers is not False:
resp_d = {}
s = socket.socket(ai[0], socket.SOCK_STREAM, ai[2])
if timeout is not None:
# Note: settimeout is not supported on all platforms, will raise
# an AttributeError if not available.
s.settimeout(timeout)
try:
s.connect(ai[-1])
if proto == "https:":
context = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
context.verify_mode = tls.CERT_NONE
s = context.wrap_socket(s, server_hostname=host)
s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
if "Host" not in headers:
headers["Host"] = host
if json is not None:
assert data is None
from json import dumps
data = dumps(json)
if "Content-Type" not in headers:
headers["Content-Type"] = "application/json"
if data:
if chunked_data:
if "Transfer-Encoding" not in headers and "Content-Length" not in headers:
headers["Transfer-Encoding"] = "chunked"
elif "Content-Length" not in headers:
headers["Content-Length"] = str(len(data))
if "Connection" not in headers:
headers["Connection"] = "close"
# Iterate over keys to avoid tuple alloc
for k in headers:
s.write(k)
s.write(b": ")
s.write(headers[k])
s.write(b"\r\n")
s.write(b"\r\n")
if data:
if chunked_data:
if headers.get("Transfer-Encoding", None) == "chunked":
for chunk in data:
s.write(b"%x\r\n" % len(chunk))
s.write(chunk)
s.write(b"\r\n")
s.write("0\r\n\r\n")
else:
for chunk in data:
s.write(chunk)
else:
s.write(data)
l = s.readline()
# print(l)
l = l.split(None, 2)
if len(l) < 2:
# Invalid response
raise ValueError("HTTP error: BadStatusLine:\n%s" % l)
status = int(l[1])
reason = ""
if len(l) > 2:
reason = l[2].rstrip()
while True:
l = s.readline()
if not l or l == b"\r\n":
break
# print(l)
if l.startswith(b"Transfer-Encoding:"):
if b"chunked" in l:
raise ValueError("Unsupported " + str(l, "utf-8"))
elif l.startswith(b"Location:") and not 200 <= status <= 299:
if status in [301, 302, 303, 307, 308]:
redirect = str(l[10:-2], "utf-8")
else:
raise NotImplementedError("Redirect %d not yet supported" % status)
if parse_headers is False:
pass
elif parse_headers is True:
l = str(l, "utf-8")
k, v = l.split(":", 1)
resp_d[k] = v.strip()
else:
parse_headers(l, resp_d)
except OSError:
s.close()
raise
if redirect:
s.close()
if status in [301, 302, 303]:
return request("GET", redirect, None, None, headers, stream)
else:
return request(method, redirect, data, json, headers, stream)
else:
resp = Response(s)
resp.status_code = status
resp.reason = reason
if resp_d is not None:
resp.headers = resp_d
return resp
def head(url, **kw):
return request("HEAD", url, **kw)
def get(url, **kw):
return request("GET", url, **kw)
def post(url, **kw):
return request("POST", url, **kw)
def put(url, **kw):
return request("PUT", url, **kw)
def patch(url, **kw):
return request("PATCH", url, **kw)
def delete(url, **kw):
return request("DELETE", url, **kw)