Skip to content

Commit eef5c29

Browse files
committed
micropython/uaiohttpclient: Add ssl support.
Add ssl support and SSLContext support with ClientSession Signed-off-by: Carlos Gil <carlosgilglez@gmail.com>
1 parent e6b89ea commit eef5c29

File tree

2 files changed

+135
-2
lines changed

2 files changed

+135
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import uaiohttpclient as aiohttp
2+
import asyncio
3+
4+
5+
async def fetch(client):
6+
async with client.get("http://micropython.org") as resp:
7+
assert resp.status == 200
8+
return await resp.text()
9+
10+
11+
async def main():
12+
async with aiohttp.ClientSession() as client:
13+
html = await fetch(client)
14+
print(html)
15+
16+
17+
asyncio.run(main())

micropython/uaiohttpclient/uaiohttpclient.py

+118-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ def __init__(self, reader):
88
def read(self, sz=-1):
99
return (yield from self.content.read(sz))
1010

11+
def text(self, sz=-1):
12+
return self.read(sz=sz)
13+
1114
def __repr__(self):
1215
return "<ClientResponse %d %s>" % (self.status, self.headers)
1316

@@ -40,15 +43,128 @@ def __repr__(self):
4043
return "<ChunkedClientResponse %d %s>" % (self.status, self.headers)
4144

4245

46+
class _RequestContextManager:
47+
def __init__(self, client, request_co):
48+
self.reqco = request_co
49+
self.client = client
50+
51+
async def __aenter__(self):
52+
return await self.reqco
53+
54+
async def __aexit__(self, *args):
55+
await self.client._reader.aclose()
56+
return await asyncio.sleep(0)
57+
58+
59+
class ClientSession:
60+
def __init__(self):
61+
self._reader = None
62+
63+
async def __aenter__(self):
64+
return self
65+
66+
async def __aexit__(self, *args):
67+
await self._reader.aclose()
68+
return await asyncio.sleep(0)
69+
70+
def request(self, method, url, ssl=None):
71+
return _RequestContextManager(self, self._request(method, url, ssl=ssl))
72+
73+
async def _request(self, method, url, ssl=None):
74+
redir_cnt = 0
75+
redir_url = None
76+
while redir_cnt < 2:
77+
reader = yield from self.request_raw(method, url, ssl)
78+
headers = []
79+
sline = yield from reader.readline()
80+
sline = sline.split(None, 2)
81+
status = int(sline[1])
82+
chunked = False
83+
while True:
84+
line = yield from reader.readline()
85+
if not line or line == b"\r\n":
86+
break
87+
headers.append(line)
88+
if line.startswith(b"Transfer-Encoding:"):
89+
if b"chunked" in line:
90+
chunked = True
91+
elif line.startswith(b"Location:"):
92+
url = line.rstrip().split(None, 1)[1].decode("latin-1")
93+
94+
if 301 <= status <= 303:
95+
redir_cnt += 1
96+
yield from reader.aclose()
97+
continue
98+
break
99+
100+
if chunked:
101+
resp = ChunkedClientResponse(reader)
102+
else:
103+
resp = ClientResponse(reader)
104+
resp.status = status
105+
resp.headers = headers
106+
self._reader = reader
107+
return resp
108+
109+
async def request_raw(self, method, url, ssl=None):
110+
try:
111+
proto, dummy, host, path = url.split("/", 3)
112+
except ValueError:
113+
proto, dummy, host = url.split("/", 2)
114+
path = ""
115+
116+
if proto == "http:":
117+
port = 80
118+
elif proto == "https:":
119+
port = 443
120+
if ssl is None:
121+
ssl = True
122+
else:
123+
raise ValueError("Unsupported protocol: " + proto)
124+
125+
if ":" in host:
126+
host, port = host.split(":", 1)
127+
port = int(port)
128+
129+
reader, writer = yield from asyncio.open_connection(host, port, ssl=ssl)
130+
# Use protocol 1.0, because 1.1 always allows to use chunked transfer-encoding
131+
# But explicitly set Connection: close, even though this should be default for 1.0,
132+
# because some servers misbehave w/o it.
133+
query = (
134+
"%s /%s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\nUser-Agent: compat\r\n\r\n"
135+
% (
136+
method,
137+
path,
138+
host,
139+
)
140+
)
141+
yield from writer.awrite(query.encode("latin-1"))
142+
# yield from writer.aclose()
143+
return reader
144+
145+
def get(self, url, ssl=None):
146+
return _RequestContextManager(self, self._request("GET", url, ssl=ssl))
147+
148+
43149
def request_raw(method, url):
44150
try:
45151
proto, dummy, host, path = url.split("/", 3)
46152
except ValueError:
47153
proto, dummy, host = url.split("/", 2)
48154
path = ""
49-
if proto != "http:":
155+
156+
if proto == "http:":
157+
port = 80
158+
elif proto == "https:":
159+
port = 443
160+
else:
50161
raise ValueError("Unsupported protocol: " + proto)
51-
reader, writer = yield from asyncio.open_connection(host, 80)
162+
163+
if ":" in host:
164+
host, port = host.split(":", 1)
165+
port = int(port)
166+
167+
reader, writer = yield from asyncio.open_connection(host, port, ssl=proto == "https:")
52168
# Use protocol 1.0, because 1.1 always allows to use chunked transfer-encoding
53169
# But explicitly set Connection: close, even though this should be default for 1.0,
54170
# because some servers misbehave w/o it.

0 commit comments

Comments
 (0)