Skip to content

Commit 717e57f

Browse files
committed
Add, test support for chunk extensions
The chunk line is read up to the terminating "\r\n", then any extensions are ignored by only using the string up to the first ";" (if any) when determining the length of the chunk. Resources: https://tools.ietf.org/html/rfc7230#section-4.1.1 https://www.boost.org/doc/libs/1_74_0/libs/beast/doc/html/beast/using_http/chunked_encoding.html
1 parent 0106c75 commit 717e57f

File tree

2 files changed

+52
-46
lines changed

2 files changed

+52
-46
lines changed

adafruit_requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def _readinto(self, buf):
212212
# Consume trailing \r\n for chunks 2+
213213
if self._remaining == 0:
214214
self._throw_away(2)
215-
chunk_header = self._readto(b";", b"\r\n")
215+
chunk_header = self._readto(b"\r\n").split(b";", 1)[0]
216216
http_chunk_size = int(bytes(chunk_header), 16)
217217
if http_chunk_size == 0:
218218
self._chunked = False
@@ -253,7 +253,7 @@ def close(self):
253253
self._throw_away(self._remaining)
254254
elif self._chunked:
255255
while True:
256-
chunk_header = self._readto(b";", b"\r\n")
256+
chunk_header = self._readto(b"\r\n").split(b";", 1)[0]
257257
chunk_size = int(bytes(chunk_header), 16)
258258
if chunk_size == 0:
259259
break

tests/chunk_test.py

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
headers = b"HTTP/1.0 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
1010

1111

12-
def _chunk(response, split):
12+
def _chunk(response, split, extra=b""):
1313
i = 0
1414
chunked = b""
1515
while i < len(response):
@@ -19,7 +19,11 @@ def _chunk(response, split):
1919
chunk_size = remaining
2020
new_i = i + chunk_size
2121
chunked += (
22-
hex(chunk_size)[2:].encode("ascii") + b"\r\n" + response[i:new_i] + b"\r\n"
22+
hex(chunk_size)[2:].encode("ascii")
23+
+ extra
24+
+ b"\r\n"
25+
+ response[i:new_i]
26+
+ b"\r\n"
2327
)
2428
i = new_i
2529
# The final chunk is zero length.
@@ -28,56 +32,58 @@ def _chunk(response, split):
2832

2933

3034
def test_get_text():
31-
pool = mocket.MocketPool()
32-
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
33-
c = _chunk(text, 33)
34-
print(c)
35-
sock = mocket.Mocket(headers + c)
36-
pool.socket.return_value = sock
35+
for extra in (b"", b";blahblah; blah"):
36+
pool = mocket.MocketPool()
37+
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
38+
c = _chunk(text, 33, extra)
39+
print(c)
40+
sock = mocket.Mocket(headers + c)
41+
pool.socket.return_value = sock
3742

38-
s = adafruit_requests.Session(pool)
39-
r = s.get("http://" + host + path)
43+
s = adafruit_requests.Session(pool)
44+
r = s.get("http://" + host + path)
4045

41-
sock.connect.assert_called_once_with((ip, 80))
46+
sock.connect.assert_called_once_with((ip, 80))
4247

43-
sock.send.assert_has_calls(
44-
[
45-
mock.call(b"GET"),
46-
mock.call(b" /"),
47-
mock.call(b"testwifi/index.html"),
48-
mock.call(b" HTTP/1.1\r\n"),
49-
]
50-
)
51-
sock.send.assert_has_calls(
52-
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
53-
)
54-
assert r.text == str(text, "utf-8")
48+
sock.send.assert_has_calls(
49+
[
50+
mock.call(b"GET"),
51+
mock.call(b" /"),
52+
mock.call(b"testwifi/index.html"),
53+
mock.call(b" HTTP/1.1\r\n"),
54+
]
55+
)
56+
sock.send.assert_has_calls(
57+
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
58+
)
59+
assert r.text == str(text, "utf-8")
5560

5661

5762
def test_close_flush():
5863
"""Test that a chunked response can be closed even when the request contents were not accessed."""
59-
pool = mocket.MocketPool()
60-
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
61-
c = _chunk(text, 33)
62-
print(c)
63-
sock = mocket.Mocket(headers + c)
64-
pool.socket.return_value = sock
64+
for extra in (b"", b";blahblah; blah"):
65+
pool = mocket.MocketPool()
66+
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
67+
c = _chunk(text, 33, extra)
68+
print(c)
69+
sock = mocket.Mocket(headers + c)
70+
pool.socket.return_value = sock
6571

66-
s = adafruit_requests.Session(pool)
67-
r = s.get("http://" + host + path)
72+
s = adafruit_requests.Session(pool)
73+
r = s.get("http://" + host + path)
6874

69-
sock.connect.assert_called_once_with((ip, 80))
75+
sock.connect.assert_called_once_with((ip, 80))
7076

71-
sock.send.assert_has_calls(
72-
[
73-
mock.call(b"GET"),
74-
mock.call(b" /"),
75-
mock.call(b"testwifi/index.html"),
76-
mock.call(b" HTTP/1.1\r\n"),
77-
]
78-
)
79-
sock.send.assert_has_calls(
80-
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
81-
)
77+
sock.send.assert_has_calls(
78+
[
79+
mock.call(b"GET"),
80+
mock.call(b" /"),
81+
mock.call(b"testwifi/index.html"),
82+
mock.call(b" HTTP/1.1\r\n"),
83+
]
84+
)
85+
sock.send.assert_has_calls(
86+
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
87+
)
8288

83-
r.close()
89+
r.close()

0 commit comments

Comments
 (0)