diff --git a/python-ecosys/urequests/metadata.txt b/python-ecosys/urequests/metadata.txt index 8cd156c39..04861d96f 100644 --- a/python-ecosys/urequests/metadata.txt +++ b/python-ecosys/urequests/metadata.txt @@ -1,4 +1,3 @@ srctype = micropython-lib type = module -version = 0.6 -author = Paul Sokolovsky +version = 0.7.0 diff --git a/python-ecosys/urequests/setup.py b/python-ecosys/urequests/setup.py index 65ee5da8b..51f1158e0 100644 --- a/python-ecosys/urequests/setup.py +++ b/python-ecosys/urequests/setup.py @@ -10,11 +10,11 @@ setup( name="micropython-urequests", - version="0.6", + version="0.7", description="urequests module for MicroPython", long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.", url="https://github.com/micropython/micropython-lib", - author="Paul Sokolovsky", + author="micropython-lib Developers", author_email="micro-python@googlegroups.com", maintainer="micropython-lib Developers", maintainer_email="micro-python@googlegroups.com", diff --git a/python-ecosys/urequests/urequests.py b/python-ecosys/urequests/urequests.py index 75a145702..e1998711d 100644 --- a/python-ecosys/urequests/urequests.py +++ b/python-ecosys/urequests/urequests.py @@ -33,7 +33,28 @@ def json(self): return ujson.loads(self.content) -def request(method, url, data=None, json=None, headers={}, stream=None): +def request( + method, + url, + data=None, + json=None, + headers={}, + stream=None, + auth=None, + timeout=None, + parse_headers=True, +): + redirect = None # redirection url, None means no redirection + chunked_data = data and getattr(data, "__iter__", None) and not getattr(data, "__len__", None) + + if auth is not None: + import ubinascii + + username, password = auth + formated = b"{}:{}".format(username, password) + formated = str(ubinascii.b2a_base64(formated)[:-1], "ascii") + headers["Authorization"] = "Basic {}".format(formated) + try: proto, dummy, host, path = url.split("/", 3) except ValueError: @@ -55,7 +76,17 @@ def request(method, url, data=None, json=None, headers={}, stream=None): ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) ai = ai[0] - s = usocket.socket(ai[0], ai[1], ai[2]) + resp_d = None + if parse_headers is not False: + resp_d = {} + + s = usocket.socket(ai[0], usocket.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:": @@ -76,14 +107,27 @@ def request(method, url, data=None, json=None, headers={}, stream=None): data = ujson.dumps(json) s.write(b"Content-Type: application/json\r\n") if data: - s.write(b"Content-Length: %d\r\n" % len(data)) - s.write(b"\r\n") + if chunked_data: + s.write(b"Transfer-Encoding: chunked\r\n") + else: + s.write(b"Content-Length: %d\r\n" % len(data)) + s.write(b"Connection: close\r\n\r\n") if data: - s.write(data) + if chunked_data: + 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: + 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: @@ -95,17 +139,37 @@ def request(method, url, data=None, json=None, headers={}, stream=None): # print(l) if l.startswith(b"Transfer-Encoding:"): if b"chunked" in l: - raise ValueError("Unsupported " + l) + raise ValueError("Unsupported " + str(l, "utf-8")) elif l.startswith(b"Location:") and not 200 <= status <= 299: - raise NotImplementedError("Redirects not yet supported") + 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 - resp = Response(s) - resp.status_code = status - resp.reason = reason - return resp + 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): diff --git a/python-stdlib/binascii/binascii.py b/python-stdlib/binascii/binascii.py index 430b1132b..f2ec39e84 100644 --- a/python-stdlib/binascii/binascii.py +++ b/python-stdlib/binascii/binascii.py @@ -331,7 +331,7 @@ def a2b_base64(ascii): table_b2a_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -def b2a_base64(bin): +def b2a_base64(bin, newline=True): "Base64-code line of data." newlength = (len(bin) + 2) // 3 @@ -357,5 +357,6 @@ def b2a_base64(bin): elif leftbits == 4: res.append(table_b2a_base64[(leftchar & 0xF) << 2]) res.append(PAD) - res.append("\n") + if newline: + res.append("\n") return "".join(res).encode("ascii")