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

Commit

Permalink
Merge pull request #223 from AvivC/validate-http11-body-type
Browse files Browse the repository at this point in the history
Validate http11 body type
  • Loading branch information
Lukasa committed Apr 19, 2016
2 parents db66a7a + a4e9128 commit 08b0c98
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 12 deletions.
21 changes: 11 additions & 10 deletions hyper/http11/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from collections import Iterable, Mapping

import collections
from hyperframe.frame import SettingsFrame

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

return

Expand All @@ -299,15 +298,19 @@ def _send_body(self, body, body_type):
return

# Iterables that set a specific content length.
else:
elif isinstance(body, collections.Iterable):
for item in body:
try:
self._sock.send(item)
except TypeError:
raise ValueError("Body must be a bytestring")

raise ValueError("Elements in iterable body must be bytestrings. "
"Illegal element: {}".format(item))
return

else:
raise ValueError('Request body must be a bytestring, a file-like object returning bytestrings '
'or an iterable of bytestrings. Got: {}'.format(type(body)))

# Chunked! For chunked bodies we don't special-case, we just iterate
# over what we have and send stuff out.
for chunk in body:
Expand All @@ -321,9 +324,7 @@ def _send_body(self, body, body_type):
self._sock.send(chunk)
self._sock.send(b'\r\n')
except TypeError:
raise ValueError(
"Iterable bodies must always iterate in bytestrings"
)
raise ValueError("Iterable bodies must always iterate in bytestrings")

self._sock.send(b'0\r\n\r\n')
return
Expand Down
40 changes: 38 additions & 2 deletions test/test_http11.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,14 @@ def body():
def test_content_length_overrides_generator(self):
c = HTTP11Connection('httpbin.org')
c._sock = sock = DummySocket()

def body():
yield b'hi'
yield b'there'
yield b'sir'

c.request(
'POST', '/post', headers={b'content-length': b'10'}, body=body()
'POST', '/post', body=body(), headers={b'content-length': b'10'}
)

expected = (
Expand All @@ -288,8 +289,8 @@ def body():
b"\r\n"
b"hitheresir"
)
received = b''.join(sock.queue)

received = b''.join(sock.queue)
assert received == expected

def test_chunked_overrides_body(self):
Expand Down Expand Up @@ -414,6 +415,7 @@ def body():
def test_content_length_overrides_generator_unicode(self):
c = HTTP11Connection('httpbin.org')
c._sock = DummySocket()

def body():
yield u'hi'
yield u'there'
Expand Down Expand Up @@ -446,6 +448,40 @@ def test_http_upgrade_headers_only_sent_once(self):

assert received == expected

def test_exception_raised_for_illegal_body_type(self):
c = HTTP11Connection('httpbin.org')

with pytest.raises(ValueError) as exc_info:
body = 1234
# content-length set so body type is set to BODY_FLAT. value doesn't matter
c.request('GET', '/get', body=body, headers={'content-length': str(len(str(body)))})
assert 'Request body must be a bytestring, a file-like object returning bytestrings ' \
'or an iterable of bytestrings. Got: {}'.format(type(body)) in str(exc_info)

def test_exception_raised_for_illegal_elements_in_iterable_body(self):
c = HTTP11Connection('httpbin.org')

rogue_element = 123
with pytest.raises(ValueError) as exc_info:
# content-length set so body type is set to BODY_FLAT. value doesn't matter
body = ['legal1', 'legal2', rogue_element]
c.request('GET', '/get', body=body, headers={'content-length': str(len(map(str, body)))})
assert 'Elements in iterable body must be bytestrings. Illegal element: {}'.format(rogue_element)\
in str(exc_info)

def test_exception_raised_for_filelike_body_not_returning_bytes(self):
c = HTTP11Connection('httpbin.org')

class RogueFile(object):
def read(self, size):
return 42

with pytest.raises(ValueError) as exc_info:
# content-length set so body type is BODY_FLAT. value doesn't matter
c.request('GET', '/get', body=RogueFile(), headers={'content-length': str(10)})
assert 'File-like bodies must return bytestrings. Got: {}'.format(int) in str(exc_info)


class TestHTTP11Response(object):
def test_short_circuit_read(self):
r = HTTP11Response(200, 'OK', {b'content-length': [b'0']}, None, None)
Expand Down

0 comments on commit 08b0c98

Please sign in to comment.