Skip to content

Commit 7671786

Browse files
committed
add minimal test web server, just for tests
1 parent 78eb770 commit 7671786

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

helpers/minimal_webserver.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import gc
2+
import sys
3+
4+
import uasyncio as asyncio
5+
6+
_HEX = '0123456789ABCDEF'
7+
8+
_HTML_PREFIX = b"""
9+
<html>
10+
<head><title>Minimal MicroPython Webserver</title></head>
11+
<body>
12+
<h1>Minimal MicroPython Webserver</h1>
13+
<pre>
14+
"""
15+
_HTML_SUFFIX = b"""
16+
</pre>
17+
<hr>
18+
<h2>POST test form:</h2>
19+
<form action="/test/post/" method="post">
20+
<textarea name="text" rows="4" cols="20">POST text
21+
from textarea!</textarea>
22+
<p>
23+
<input type="checkbox" id="c1" name="c1" checked><label for="c1">c1</label>
24+
<input type="checkbox" id="c2" name="c2"><label for="c2">c2</label>
25+
</p>
26+
<input type="submit">
27+
</form>
28+
<hr>
29+
<h2>GET test form:</h2>
30+
<form action="/test/get/" method="get">
31+
<textarea name="text" rows="4" cols="20">GET text
32+
from textarea!</textarea>
33+
<p>
34+
<input type="checkbox" id="c1" name="c1"><label for="c1">c1</label>
35+
<input type="checkbox" id="c2" name="c2" checked><label for="c2">c2</label>
36+
</p>
37+
<input type="submit">
38+
</form>
39+
<hr>
40+
<p>
41+
"""
42+
_HTML_FOOTER = """
43+
</p>
44+
</body>"""
45+
46+
47+
def unquote(string):
48+
string = string.replace('+', ' ')
49+
if '%' not in string:
50+
return string
51+
52+
bits = string.split('%')
53+
if len(bits) == 1:
54+
return string
55+
56+
res = [bits[0]]
57+
for item in bits[1:]:
58+
if len(item) >= 2:
59+
a, b = item[:2].upper()
60+
if a in _HEX and b in _HEX:
61+
res.append(chr(int(a + b, 16)))
62+
res.append(item[2:])
63+
continue
64+
65+
res.append('%')
66+
res.append(item)
67+
68+
return ''.join(res)
69+
70+
71+
def parse_qsl(qs):
72+
if qs is None:
73+
return ()
74+
qs = str(qs)
75+
pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
76+
res = []
77+
for name_value in pairs:
78+
try:
79+
name, value = name_value.split('=', 1)
80+
except ValueError:
81+
res.append((unquote(name_value), ''))
82+
else:
83+
res.append((unquote(name), unquote(value)))
84+
return res
85+
86+
87+
def request_query2dict(qs):
88+
return dict(parse_qsl(qs))
89+
90+
91+
class WebServer:
92+
async def parse_request(self, reader):
93+
method, url, http_version = (await reader.readline()).decode().strip().split()
94+
print(http_version)
95+
96+
if '?' in url:
97+
url, querystring = url.split('?', 1)
98+
else:
99+
querystring = None
100+
101+
# Consume all headers but use only content-length
102+
content_length = None
103+
while True:
104+
line = await reader.readline()
105+
if line == b'\r\n':
106+
break # header ends
107+
108+
try:
109+
header, value = line.split(b':', 1)
110+
except ValueError:
111+
break
112+
113+
value = value.strip()
114+
115+
if header == b'Content-Length':
116+
content_length = int(value.decode())
117+
118+
print(header, value)
119+
120+
print('content length:', content_length)
121+
122+
# get body
123+
if content_length:
124+
body = await reader.read(content_length)
125+
else:
126+
body = None
127+
128+
return method, url, querystring, body
129+
130+
async def send_response(self, reader, writer):
131+
peername = writer.get_extra_info('peername')
132+
print('\nRequest from:', peername)
133+
await writer.awrite(b'HTTP/1.0 200 OK\r\n')
134+
await writer.awrite(b'Content-type: text/html; charset=utf-8\r\n\r\n')
135+
136+
await writer.awrite(_HTML_PREFIX)
137+
138+
await writer.awrite(b'Your IP: %s port:%s\n' % peername)
139+
140+
await writer.awrite(b'\n')
141+
142+
method, url, querystring, body = await self.parse_request(reader)
143+
144+
await writer.awrite(b'Method: %s\n' % method)
145+
await writer.awrite(b'URL: %s\n' % url)
146+
await writer.awrite(b'querystring: %s\n' % querystring)
147+
await writer.awrite(b'parsed querystring: %s\n' % request_query2dict(querystring))
148+
await writer.awrite(b'body: %s\n' % body)
149+
await writer.awrite(b'parsed body: %s\n' % request_query2dict(body))
150+
151+
await writer.awrite(_HTML_SUFFIX)
152+
153+
alloc = gc.mem_alloc() / 1024
154+
free = gc.mem_free() / 1024
155+
156+
await writer.awrite(
157+
b'RAM total: {total:.2f} KB, used: {alloc:.2f} KB, free: {free:.2f} KB'.format(
158+
total=alloc + free,
159+
alloc=alloc,
160+
free=free
161+
)
162+
)
163+
164+
await writer.awrite(_HTML_FOOTER)
165+
await writer.aclose()
166+
167+
async def request_handler(self, reader, writer):
168+
await self.send_response(reader, writer)
169+
gc.collect()
170+
171+
def run(self):
172+
loop = asyncio.get_event_loop()
173+
loop.create_task(asyncio.start_server(self.request_handler, '0.0.0.0', 80))
174+
print('\nWeb server started...')
175+
loop.run_forever()
176+
177+
178+
def main():
179+
from wifi import WiFi
180+
wifi = WiFi()
181+
if not wifi.is_connected:
182+
wifi.ensure_connection()
183+
del wifi
184+
del WiFi
185+
del sys.modules['wifi']
186+
gc.collect()
187+
188+
server = WebServer()
189+
server.run()
190+
191+
192+
if __name__ == '__main__':
193+
try:
194+
main()
195+
except Exception as e:
196+
sys.print_exception(e)
197+
198+
print('Hard reset !')
199+
200+
import machine
201+
machine.reset()
202+
203+
import utime
204+
utime.sleep(1)
205+
206+
print('sys.exit()')
207+
import sys
208+
sys.exit()

0 commit comments

Comments
 (0)