Skip to content

Commit 26a0445

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 26a0445

File tree

2 files changed

+48
-46
lines changed

2 files changed

+48
-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: 46 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,7 @@ 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") + extra + b"\r\n" + response[i:new_i] + b"\r\n"
2323
)
2424
i = new_i
2525
# The final chunk is zero length.
@@ -28,56 +28,58 @@ def _chunk(response, split):
2828

2929

3030
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
31+
for extra in (b'', b';blahblah; blah'):
32+
pool = mocket.MocketPool()
33+
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
34+
c = _chunk(text, 33, extra)
35+
print(c)
36+
sock = mocket.Mocket(headers + c)
37+
pool.socket.return_value = sock
3738

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

41-
sock.connect.assert_called_once_with((ip, 80))
42+
sock.connect.assert_called_once_with((ip, 80))
4243

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")
44+
sock.send.assert_has_calls(
45+
[
46+
mock.call(b"GET"),
47+
mock.call(b" /"),
48+
mock.call(b"testwifi/index.html"),
49+
mock.call(b" HTTP/1.1\r\n"),
50+
]
51+
)
52+
sock.send.assert_has_calls(
53+
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
54+
)
55+
assert r.text == str(text, "utf-8")
5556

5657

5758
def test_close_flush():
5859
"""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
60+
for extra in (b'', b';blahblah; blah'):
61+
pool = mocket.MocketPool()
62+
pool.getaddrinfo.return_value = ((None, None, None, None, (ip, 80)),)
63+
c = _chunk(text, 33, extra)
64+
print(c)
65+
sock = mocket.Mocket(headers + c)
66+
pool.socket.return_value = sock
6567

66-
s = adafruit_requests.Session(pool)
67-
r = s.get("http://" + host + path)
68+
s = adafruit_requests.Session(pool)
69+
r = s.get("http://" + host + path)
6870

69-
sock.connect.assert_called_once_with((ip, 80))
71+
sock.connect.assert_called_once_with((ip, 80))
7072

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-
)
73+
sock.send.assert_has_calls(
74+
[
75+
mock.call(b"GET"),
76+
mock.call(b" /"),
77+
mock.call(b"testwifi/index.html"),
78+
mock.call(b" HTTP/1.1\r\n"),
79+
]
80+
)
81+
sock.send.assert_has_calls(
82+
[mock.call(b"Host: "), mock.call(b"wifitest.adafruit.com"),]
83+
)
8284

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

0 commit comments

Comments
 (0)