Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit a4e9128

Browse files
author
Aviv Cohn
committed
Raising TypeError in HTTP11Connection._send_body on invalid body type
Added tests for the case when the error may be raised. Also testing the other cases when errors regarding the type of `body` argument may be raised.
1 parent 74fc0e5 commit a4e9128

File tree

2 files changed

+49
-12
lines changed

2 files changed

+49
-12
lines changed

hyper/http11/connection.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from collections import Iterable, Mapping
1414

15+
import collections
1516
from hyperframe.frame import SettingsFrame
1617

1718
from .response import HTTP11Response
@@ -141,7 +142,7 @@ def request(self, method, url, body=None, headers={}):
141142
142143
:param method: The request method, e.g. ``'GET'``.
143144
:param url: The URL to contact, e.g. ``'/path/segment'``.
144-
:param body: (optional) The request body to send. Must be a bytestring
145+
:param body: (optional) The request body to send. Must be a bytestring, an iterable of bytestring
145146
or a file-like object.
146147
:param headers: (optional) The headers to send on the request.
147148
:returns: Nothing.
@@ -282,9 +283,7 @@ def _send_body(self, body, body_type):
282283
try:
283284
self._sock.send(block)
284285
except TypeError:
285-
raise ValueError(
286-
"File objects must return bytestrings"
287-
)
286+
raise ValueError("File-like bodies must return bytestrings. Got: {}".format(type(block)))
288287

289288
return
290289

@@ -295,15 +294,19 @@ def _send_body(self, body, body_type):
295294
return
296295

297296
# Iterables that set a specific content length.
298-
else:
297+
elif isinstance(body, collections.Iterable):
299298
for item in body:
300299
try:
301300
self._sock.send(item)
302301
except TypeError:
303-
raise ValueError("Body must be a bytestring")
304-
302+
raise ValueError("Elements in iterable body must be bytestrings. "
303+
"Illegal element: {}".format(item))
305304
return
306305

306+
else:
307+
raise ValueError('Request body must be a bytestring, a file-like object returning bytestrings '
308+
'or an iterable of bytestrings. Got: {}'.format(type(body)))
309+
307310
# Chunked! For chunked bodies we don't special-case, we just iterate
308311
# over what we have and send stuff out.
309312
for chunk in body:
@@ -317,9 +320,7 @@ def _send_body(self, body, body_type):
317320
self._sock.send(chunk)
318321
self._sock.send(b'\r\n')
319322
except TypeError:
320-
raise ValueError(
321-
"Iterable bodies must always iterate in bytestrings"
322-
)
323+
raise ValueError("Iterable bodies must always iterate in bytestrings")
323324

324325
self._sock.send(b'0\r\n\r\n')
325326
return

test/test_http11.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,14 @@ def body():
269269
def test_content_length_overrides_generator(self):
270270
c = HTTP11Connection('httpbin.org')
271271
c._sock = sock = DummySocket()
272+
272273
def body():
273274
yield b'hi'
274275
yield b'there'
275276
yield b'sir'
276277

277278
c.request(
278-
'POST', '/post', headers={b'content-length': b'10'}, body=body()
279+
'POST', '/post', body=body(), headers={b'content-length': b'10'}
279280
)
280281

281282
expected = (
@@ -288,8 +289,8 @@ def body():
288289
b"\r\n"
289290
b"hitheresir"
290291
)
291-
received = b''.join(sock.queue)
292292

293+
received = b''.join(sock.queue)
293294
assert received == expected
294295

295296
def test_chunked_overrides_body(self):
@@ -414,6 +415,7 @@ def body():
414415
def test_content_length_overrides_generator_unicode(self):
415416
c = HTTP11Connection('httpbin.org')
416417
c._sock = DummySocket()
418+
417419
def body():
418420
yield u'hi'
419421
yield u'there'
@@ -446,6 +448,40 @@ def test_http_upgrade_headers_only_sent_once(self):
446448

447449
assert received == expected
448450

451+
def test_exception_raised_for_illegal_body_type(self):
452+
c = HTTP11Connection('httpbin.org')
453+
454+
with pytest.raises(ValueError) as exc_info:
455+
body = 1234
456+
# content-length set so body type is set to BODY_FLAT. value doesn't matter
457+
c.request('GET', '/get', body=body, headers={'content-length': str(len(str(body)))})
458+
assert 'Request body must be a bytestring, a file-like object returning bytestrings ' \
459+
'or an iterable of bytestrings. Got: {}'.format(type(body)) in str(exc_info)
460+
461+
def test_exception_raised_for_illegal_elements_in_iterable_body(self):
462+
c = HTTP11Connection('httpbin.org')
463+
464+
rogue_element = 123
465+
with pytest.raises(ValueError) as exc_info:
466+
# content-length set so body type is set to BODY_FLAT. value doesn't matter
467+
body = ['legal1', 'legal2', rogue_element]
468+
c.request('GET', '/get', body=body, headers={'content-length': str(len(map(str, body)))})
469+
assert 'Elements in iterable body must be bytestrings. Illegal element: {}'.format(rogue_element)\
470+
in str(exc_info)
471+
472+
def test_exception_raised_for_filelike_body_not_returning_bytes(self):
473+
c = HTTP11Connection('httpbin.org')
474+
475+
class RogueFile(object):
476+
def read(self, size):
477+
return 42
478+
479+
with pytest.raises(ValueError) as exc_info:
480+
# content-length set so body type is BODY_FLAT. value doesn't matter
481+
c.request('GET', '/get', body=RogueFile(), headers={'content-length': str(10)})
482+
assert 'File-like bodies must return bytestrings. Got: {}'.format(int) in str(exc_info)
483+
484+
449485
class TestHTTP11Response(object):
450486
def test_short_circuit_read(self):
451487
r = HTTP11Response(200, 'OK', {b'content-length': [b'0']}, None, None)

0 commit comments

Comments
 (0)