Skip to content

Commit 53e8608

Browse files
committed
micropython/uaiohttpclient: Add ssl support.
Add ssl support and SSLContext support with ClientSession Signed-off-by: Carlos Gil <[email protected]>
1 parent e6b89ea commit 53e8608

File tree

2 files changed

+143
-6
lines changed

2 files changed

+143
-6
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

+126-6
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,22 +43,139 @@ 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+
return await asyncio.sleep(0)
68+
69+
def request(self, method, url, ssl=None):
70+
return _RequestContextManager(self, self._request(method, url, ssl=ssl))
71+
72+
async def _request(self, method, url, ssl=None):
73+
redir_cnt = 0
74+
redir_url = None
75+
while redir_cnt < 2:
76+
reader = yield from self.request_raw(method, url, ssl)
77+
headers = []
78+
sline = yield from reader.readline()
79+
sline = sline.split(None, 2)
80+
status = int(sline[1])
81+
chunked = False
82+
while True:
83+
line = yield from reader.readline()
84+
if not line or line == b"\r\n":
85+
break
86+
headers.append(line)
87+
if line.startswith(b"Transfer-Encoding:"):
88+
if b"chunked" in line:
89+
chunked = True
90+
elif line.startswith(b"Location:"):
91+
url = line.rstrip().split(None, 1)[1].decode("latin-1")
92+
93+
if 301 <= status <= 303:
94+
redir_cnt += 1
95+
yield from reader.aclose()
96+
continue
97+
break
98+
99+
if chunked:
100+
resp = ChunkedClientResponse(reader)
101+
else:
102+
resp = ClientResponse(reader)
103+
resp.status = status
104+
resp.headers = headers
105+
self._reader = reader
106+
return resp
107+
108+
async def request_raw(self, method, url, ssl=None):
109+
try:
110+
proto, dummy, host, path = url.split("/", 3)
111+
except ValueError:
112+
proto, dummy, host = url.split("/", 2)
113+
path = ""
114+
115+
if proto == "http:":
116+
port = 80
117+
elif proto == "https:":
118+
port = 443
119+
if ssl is None:
120+
ssl = True
121+
else:
122+
raise ValueError("Unsupported protocol: " + proto)
123+
124+
if ":" in host:
125+
host, port = host.split(":", 1)
126+
port = int(port)
127+
128+
reader, writer = yield from asyncio.open_connection(host, port, ssl=ssl)
129+
# Use protocol 1.0, because 1.1 always allows to use chunked transfer-encoding
130+
# But explicitly set Connection: close, even though this should be default for 1.0,
131+
# because some servers misbehave w/o it.
132+
query = (
133+
"%s /%s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\nUser-Agent: compat\r\n\r\n"
134+
% (
135+
method,
136+
path,
137+
host,
138+
)
139+
)
140+
yield from writer.awrite(query.encode("latin-1"))
141+
# yield from writer.aclose()
142+
return reader
143+
144+
def get(self, url, ssl=None):
145+
return _RequestContextManager(self, self._request("GET", url, ssl=ssl))
146+
147+
43148
def request_raw(method, url):
44149
try:
45150
proto, dummy, host, path = url.split("/", 3)
46151
except ValueError:
47152
proto, dummy, host = url.split("/", 2)
48153
path = ""
49-
if proto != "http:":
154+
155+
if proto == "http:":
156+
port = 80
157+
elif proto == "https:":
158+
port = 443
159+
else:
50160
raise ValueError("Unsupported protocol: " + proto)
51-
reader, writer = yield from asyncio.open_connection(host, 80)
161+
162+
if ":" in host:
163+
host, port = host.split(":", 1)
164+
port = int(port)
165+
166+
reader, writer = yield from asyncio.open_connection(
167+
host, port, ssl=proto == "https:"
168+
)
52169
# Use protocol 1.0, because 1.1 always allows to use chunked transfer-encoding
53170
# But explicitly set Connection: close, even though this should be default for 1.0,
54171
# because some servers misbehave w/o it.
55-
query = "%s /%s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\nUser-Agent: compat\r\n\r\n" % (
56-
method,
57-
path,
58-
host,
172+
query = (
173+
"%s /%s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\nUser-Agent: compat\r\n\r\n"
174+
% (
175+
method,
176+
path,
177+
host,
178+
)
59179
)
60180
yield from writer.awrite(query.encode("latin-1"))
61181
# yield from writer.aclose()

0 commit comments

Comments
 (0)