From 89b428749e4a8d1e7cd59a33c7f05ec5e8fc6443 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Wed, 3 Jul 2013 11:23:30 -0700 Subject: [PATCH 1/2] Adding parsed_body parameter to simplify checks parsed_body attempts to parse the post body if it matches a compatible content type * content is returned unaltered if it's not a compatible type * content parses json if content-type type is application/json * content parse querystring params if content-type is application/x-www-form-urlencoded * if the value cannot be parsed for any reason, it is unaltered --- httpretty/core.py | 14 ++++++++++++ tests/unit/test_httpretty.py | 42 +++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/httpretty/core.py b/httpretty/core.py index d456fb1..ce8dcfe 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -33,6 +33,7 @@ import itertools import warnings import logging import traceback +import json from .compat import ( @@ -108,6 +109,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): self.parse_request() self.method = self.command self.querystring = parse_qs(self.path.split("?", 1)[-1]) + self.parsed_body = self.__parse_body(self.body) def __str__(self): return 'HTTPrettyRequest(headers={0}, body="{1}")'.format( @@ -115,6 +117,18 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): self.body, ) + def __parse_body(self, body): + """ Attempt to parse the post based on the content-type passed. Return the regular body if not """ + return_value = body + try: + if 'content-type' in self.headers.keys(): + if self.headers['content-type'].lower() == 'application/json': + return_value = json.loads(body) + elif self.headers['content-type'].lower() == 'application/x-www-form-urlencoded': + return_value = parse_qs(body) + finally: + return return_value + class EmptyRequestHeaders(dict): pass diff --git a/tests/unit/test_httpretty.py b/tests/unit/test_httpretty.py index 525df6e..1840238 100644 --- a/tests/unit/test_httpretty.py +++ b/tests/unit/test_httpretty.py @@ -25,9 +25,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. from __future__ import unicode_literals +import json from sure import expect from httpretty import HTTPretty, HTTPrettyError, core -from httpretty.core import URIInfo, BaseClass, Entry, FakeSockFile +from httpretty.core import URIInfo, BaseClass, Entry, FakeSockFile, HTTPrettyRequest from httpretty.http import STATUSES try: @@ -35,6 +36,12 @@ try: except ImportError: from unittest.mock import MagicMock +TEST_HEADER = """ +GET /test/test.html HTTP/1.1 +Host: www.host1.com:80 +Content-Type: %(content_type)s +""" + def test_httpretty_should_raise_proper_exception_on_inconsistent_length(): "HTTPretty should raise proper exception on inconsistent Content-Length / "\ @@ -322,3 +329,36 @@ def test_fake_socket_passes_through_shutdown(): s.truesock = MagicMock() expect(s.shutdown).called_with(socket.SHUT_RD).should_not.throw(AttributeError) s.truesock.shutdown.assert_called_with(socket.SHUT_RD) + + +def test_HTTPrettyRequest_json_body(): + """ A content-type of application/json should parse a valid json body """ + header = TEST_HEADER % {'content_type': 'application/json'} + test_dict = {'hello': 'world'} + request = HTTPrettyRequest(header, json.dumps(test_dict)) + expect(request.parsed_body).to.equal(test_dict) + + +def test_HTTPrettyRequest_invalid_json_body(): + """ A content-type of application/json with an invalid json body should return the content unaltered """ + header = TEST_HEADER % {'content_type': 'application/json'} + invalid_json = "{'hello', 'world','thisstringdoesntstops}" + request = HTTPrettyRequest(header, invalid_json) + expect(request.parsed_body).to.equal(invalid_json) + + +def test_HTTPrettyRequest_queryparam(): + """ A content-type of x-www-form-urlencoded with a valid queryparam body should return parsed content """ + header = TEST_HEADER % {'content_type': 'application/x-www-form-urlencoded'} + valid_queryparam = "hello=world&this=isavalidquerystring" + valid_results = {'hello': ['world'], 'this': ['isavalidquerystring']} + request = HTTPrettyRequest(header, valid_queryparam) + expect(request.parsed_body).to.equal(valid_results) + + +def test_HTTPrettyRequest_arbitrarypost(): + """ A non-handled content type request's post body should return the content unaltered """ + header = TEST_HEADER % {'content_type': 'thisis/notarealcontenttype'} + gibberish_body = "1234567890!@#$%^&*()" + request = HTTPrettyRequest(header, gibberish_body) + expect(request.parsed_body).to.equal(gibberish_body) From e888d73babf9a876bfd2ac0c1bc5a71561dde7bd Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Sun, 8 Sep 2013 00:29:34 -0700 Subject: [PATCH 2/2] fixing tests for python3 --- httpretty/core.py | 15 ++++++++------- tests/unit/test_httpretty.py | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/httpretty/core.py b/httpretty/core.py index ce8dcfe..4a831ec 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -102,7 +102,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): def __init__(self, headers, body=''): self.body = utf8(body) self.raw_headers = utf8(headers) - self.rfile = StringIO(b'\r\n\r\n'.join([headers.strip(), body])) + self.rfile = StringIO(b'\r\n\r\n'.join([utf8(headers.strip()), self.body])) self.wfile = StringIO() self.raw_requestline = self.rfile.readline() self.error_code = self.error_message = None @@ -119,13 +119,14 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): def __parse_body(self, body): """ Attempt to parse the post based on the content-type passed. Return the regular body if not """ - return_value = body + return_value = body.decode('utf-8') try: - if 'content-type' in self.headers.keys(): - if self.headers['content-type'].lower() == 'application/json': - return_value = json.loads(body) - elif self.headers['content-type'].lower() == 'application/x-www-form-urlencoded': - return_value = parse_qs(body) + for header in self.headers.keys(): + if header.lower() == 'content-type': + if self.headers['content-type'].lower() == 'application/json': + return_value = json.loads(return_value) + elif self.headers['content-type'].lower() == 'application/x-www-form-urlencoded': + return_value = parse_qs(return_value) finally: return return_value diff --git a/tests/unit/test_httpretty.py b/tests/unit/test_httpretty.py index 1840238..b39ccc6 100644 --- a/tests/unit/test_httpretty.py +++ b/tests/unit/test_httpretty.py @@ -342,7 +342,7 @@ def test_HTTPrettyRequest_json_body(): def test_HTTPrettyRequest_invalid_json_body(): """ A content-type of application/json with an invalid json body should return the content unaltered """ header = TEST_HEADER % {'content_type': 'application/json'} - invalid_json = "{'hello', 'world','thisstringdoesntstops}" + invalid_json = u"{'hello', 'world','thisstringdoesntstops}" request = HTTPrettyRequest(header, invalid_json) expect(request.parsed_body).to.equal(invalid_json) @@ -350,7 +350,7 @@ def test_HTTPrettyRequest_invalid_json_body(): def test_HTTPrettyRequest_queryparam(): """ A content-type of x-www-form-urlencoded with a valid queryparam body should return parsed content """ header = TEST_HEADER % {'content_type': 'application/x-www-form-urlencoded'} - valid_queryparam = "hello=world&this=isavalidquerystring" + valid_queryparam = u"hello=world&this=isavalidquerystring" valid_results = {'hello': ['world'], 'this': ['isavalidquerystring']} request = HTTPrettyRequest(header, valid_queryparam) expect(request.parsed_body).to.equal(valid_results)