Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f7efcc3f8 | ||
|
|
0f278321c8 | ||
|
|
acf20cc20c | ||
|
|
453e133cc2 | ||
|
|
29a9f6f46c | ||
|
|
9d3222ae4b |
@@ -1,5 +1,13 @@
|
|||||||
# Microdot change log
|
# Microdot change log
|
||||||
|
|
||||||
|
**Release 0.8.2** - 2022-04-20
|
||||||
|
|
||||||
|
- Remove debugging print statement [#38](https://github.com/miguelgrinberg/microdot/issues/38) ([commit](https://github.com/miguelgrinberg/microdot/commit/0f278321c8bd65c5cb67425eb837e6581cbb0054)) (thanks **Mark Blakeney**!)
|
||||||
|
|
||||||
|
**Release 0.8.1** - 2022-03-18
|
||||||
|
|
||||||
|
- Optimizations for request streams and bodies ([commit](https://github.com/miguelgrinberg/microdot/commit/29a9f6f46c737aa0fd452766c23bd83008594ac4))
|
||||||
|
|
||||||
**Release 0.8.0** - 2022-02-18
|
**Release 0.8.0** - 2022-02-18
|
||||||
|
|
||||||
- Support streamed request payloads [#26](https://github.com/miguelgrinberg/microdot/issues/26) ([commit](https://github.com/miguelgrinberg/microdot/commit/992fa722c1312c0ac0ee9fbd5e23ad7b52d3caca))
|
- Support streamed request payloads [#26](https://github.com/miguelgrinberg/microdot/issues/26) ([commit](https://github.com/miguelgrinberg/microdot/commit/992fa722c1312c0ac0ee9fbd5e23ad7b52d3caca))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = microdot
|
name = microdot
|
||||||
version = 0.8.0
|
version = 0.8.2
|
||||||
author = Miguel Grinberg
|
author = Miguel Grinberg
|
||||||
author_email = miguel.grinberg@gmail.com
|
author_email = miguel.grinberg@gmail.com
|
||||||
description = The impossibly small web framework for MicroPython
|
description = The impossibly small web framework for MicroPython
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import errno
|
import errno
|
||||||
|
|
||||||
try:
|
|
||||||
import uio as io
|
|
||||||
except ImportError:
|
|
||||||
import io
|
|
||||||
|
|
||||||
concurrency_mode = 'threaded'
|
concurrency_mode = 'threaded'
|
||||||
|
|
||||||
try: # pragma: no cover
|
try: # pragma: no cover
|
||||||
@@ -228,7 +223,7 @@ class Request():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, app, client_addr, method, url, http_version, headers,
|
def __init__(self, app, client_addr, method, url, http_version, headers,
|
||||||
body, stream):
|
body=None, stream=None):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.client_addr = client_addr
|
self.client_addr = client_addr
|
||||||
self.method = method
|
self.method = method
|
||||||
@@ -254,8 +249,10 @@ class Request():
|
|||||||
for cookie in value.split(';'):
|
for cookie in value.split(';'):
|
||||||
name, value = cookie.strip().split('=', 1)
|
name, value = cookie.strip().split('=', 1)
|
||||||
self.cookies[name] = value
|
self.cookies[name] = value
|
||||||
self.body = body
|
self._body = body
|
||||||
self.stream = stream
|
self.body_used = False
|
||||||
|
self._stream = stream
|
||||||
|
self.stream_used = False
|
||||||
self._json = None
|
self._json = None
|
||||||
self._form = None
|
self._form = None
|
||||||
self.g = Request.G()
|
self.g = Request.G()
|
||||||
@@ -280,7 +277,6 @@ class Request():
|
|||||||
|
|
||||||
# headers
|
# headers
|
||||||
headers = {}
|
headers = {}
|
||||||
content_length = 0
|
|
||||||
while True:
|
while True:
|
||||||
line = Request._safe_readline(client_stream).strip().decode()
|
line = Request._safe_readline(client_stream).strip().decode()
|
||||||
if line == '':
|
if line == '':
|
||||||
@@ -288,23 +284,9 @@ class Request():
|
|||||||
header, value = line.split(':', 1)
|
header, value = line.split(':', 1)
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
headers[header] = value
|
headers[header] = value
|
||||||
if header.lower() == 'content-length':
|
|
||||||
content_length = int(value)
|
|
||||||
|
|
||||||
# body
|
|
||||||
body = b''
|
|
||||||
if content_length and content_length <= Request.max_body_length:
|
|
||||||
while len(body) < content_length:
|
|
||||||
data = client_stream.read(content_length - len(body))
|
|
||||||
if len(data) == 0: # pragma: no cover
|
|
||||||
raise EOFError()
|
|
||||||
body += data
|
|
||||||
stream = io.BytesIO(body)
|
|
||||||
else:
|
|
||||||
stream = client_stream
|
|
||||||
|
|
||||||
return Request(app, client_addr, method, url, http_version, headers,
|
return Request(app, client_addr, method, url, http_version, headers,
|
||||||
body, stream)
|
stream=client_stream)
|
||||||
|
|
||||||
def _parse_urlencoded(self, urlencoded):
|
def _parse_urlencoded(self, urlencoded):
|
||||||
data = MultiDict()
|
data = MultiDict()
|
||||||
@@ -312,6 +294,30 @@ class Request():
|
|||||||
data[urldecode(k)] = urldecode(v)
|
data[urldecode(k)] = urldecode(v)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def body(self):
|
||||||
|
if self.stream_used:
|
||||||
|
raise RuntimeError('Cannot use both stream and body')
|
||||||
|
if self._body is None:
|
||||||
|
self._body = b''
|
||||||
|
if self.content_length and \
|
||||||
|
self.content_length <= Request.max_body_length:
|
||||||
|
while len(self._body) < self.content_length:
|
||||||
|
data = self._stream.read(
|
||||||
|
self.content_length - len(self._body))
|
||||||
|
if len(data) == 0: # pragma: no cover
|
||||||
|
raise EOFError()
|
||||||
|
self._body += data
|
||||||
|
self.body_used = True
|
||||||
|
return self._body
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stream(self):
|
||||||
|
if self.body_used:
|
||||||
|
raise RuntimeError('Cannot use both stream and body')
|
||||||
|
self.stream_used = True
|
||||||
|
return self._stream
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def json(self):
|
def json(self):
|
||||||
if self._json is None:
|
if self._json is None:
|
||||||
|
|||||||
@@ -81,12 +81,19 @@ class Request(BaseRequest):
|
|||||||
body = b''
|
body = b''
|
||||||
if content_length and content_length <= Request.max_body_length:
|
if content_length and content_length <= Request.max_body_length:
|
||||||
body = await client_stream.readexactly(content_length)
|
body = await client_stream.readexactly(content_length)
|
||||||
stream = _AsyncBytesIO(body)
|
stream = None
|
||||||
else:
|
else:
|
||||||
|
body = b''
|
||||||
stream = client_stream
|
stream = client_stream
|
||||||
|
|
||||||
return Request(app, client_addr, method, url, http_version, headers,
|
return Request(app, client_addr, method, url, http_version, headers,
|
||||||
body, stream)
|
body=body, stream=stream)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stream(self):
|
||||||
|
if self._stream is None:
|
||||||
|
self._stream = _AsyncBytesIO(self._body)
|
||||||
|
return self._stream
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _safe_readline(stream):
|
async def _safe_readline(stream):
|
||||||
|
|||||||
@@ -93,11 +93,23 @@ class TestRequest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_stream(self):
|
def test_stream(self):
|
||||||
fd = get_request_fd('GET', '/foo', headers={
|
fd = get_request_fd('GET', '/foo', headers={
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'},
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Content-Length': '19'},
|
||||||
|
body='foo=bar&abc=def&x=y')
|
||||||
|
req = Request.create('app', fd, 'addr')
|
||||||
|
self.assertEqual(req.stream.read(), b'foo=bar&abc=def&x=y')
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
req.body
|
||||||
|
|
||||||
|
def test_body(self):
|
||||||
|
fd = get_request_fd('GET', '/foo', headers={
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Content-Length': '19'},
|
||||||
body='foo=bar&abc=def&x=y')
|
body='foo=bar&abc=def&x=y')
|
||||||
req = Request.create('app', fd, 'addr')
|
req = Request.create('app', fd, 'addr')
|
||||||
self.assertEqual(req.body, b'foo=bar&abc=def&x=y')
|
self.assertEqual(req.body, b'foo=bar&abc=def&x=y')
|
||||||
self.assertEqual(req.stream.read(), b'foo=bar&abc=def&x=y')
|
with self.assertRaises(RuntimeError):
|
||||||
|
req.stream
|
||||||
|
|
||||||
def test_large_payload(self):
|
def test_large_payload(self):
|
||||||
saved_max_content_length = Request.max_content_length
|
saved_max_content_length = Request.max_content_length
|
||||||
|
|||||||
Reference in New Issue
Block a user