diff --git a/httpretty/core.py b/httpretty/core.py index 10d4090..31c5fb2 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 ( @@ -102,13 +103,14 @@ 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 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( @@ -116,6 +118,19 @@ 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.decode('utf-8') + try: + 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 + class EmptyRequestHeaders(dict): pass diff --git a/tests/unit/test_httpretty.py b/tests/unit/test_httpretty.py index 21d74fd..420aaee 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 / "\ @@ -344,3 +351,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 = u"{'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 = u"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)