diff --git a/httpretty/core.py b/httpretty/core.py index 5e42c49..df6b15b 100644 --- a/httpretty/core.py +++ b/httpretty/core.py @@ -166,6 +166,10 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): # Now 2 convenient attributes for the HTTPretty API: # `querystring` holds a dictionary with the parsed query string + try: + self.path = self.path.encode('iso-8859-1') + except UnicodeDecodeError: + pass self.path = decode_utf8(self.path) qstring = self.path.split("?", 1)[-1] @@ -187,7 +191,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): parsed = parse_qs(expanded) result = {} for k in parsed: - result[k] = map(decode_utf8, parsed[k]) + result[k] = list(map(decode_utf8, parsed[k])) return result @@ -204,6 +208,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass): content_type = self.headers.get('content-type', '') do_parse = PARSING_FUNCTIONS.get(content_type, FALLBACK_FUNCTION) + body = decode_utf8(body) try: return do_parse(body) except: @@ -343,8 +348,8 @@ class fakesock(object): self._sent_data.append(data) try: - requestline, _ = data.split('\r\n', 1) - method, path, version = parse_requestline(requestline) + requestline, _ = data.split(b'\r\n', 1) + method, path, version = parse_requestline(decode_utf8(requestline)) is_parsing_headers = True except ValueError: is_parsing_headers = False @@ -358,10 +363,10 @@ class fakesock(object): if not is_parsing_headers: if len(self._sent_data) > 1: headers = utf8(last_requestline(self._sent_data)) - meta = dict(self._entry.request.headers) + meta = self._entry.request.headers body = utf8(self._sent_data[-1]) if meta.get('transfer-encoding', '') == 'chunked': - if not body.isdigit() and body != '\r\n' and body != '0\r\n\r\n': + if not body.isdigit() and body != b'\r\n' and body != b'0\r\n\r\n': self._entry.request.body += body else: self._entry.request.body += body @@ -372,7 +377,7 @@ class fakesock(object): # path might come with s = urlsplit(path) POTENTIAL_HTTP_PORTS.add(int(s.port or 80)) - headers, body = map(utf8, data.split('\r\n\r\n', 1)) + headers, body = list(map(utf8, data.split(b'\r\n\r\n', 1))) request = httpretty.historify_request(headers, body) @@ -393,7 +398,7 @@ class fakesock(object): def debug(self, func, *a, **kw): if self.is_http: frame = inspect.stack()[0][0] - lines = map(utf8, traceback.format_stack(frame)) + lines = list(map(utf8, traceback.format_stack(frame))) message = [ "HTTPretty intercepted and unexpected socket method call.", @@ -807,12 +812,12 @@ class httpretty(HttpBaseClass): 'uri': uri, 'method': request.method, 'headers': dict(request.headers), - 'body': request.body, + 'body': decode_utf8(request.body), 'querystring': request.querystring }, 'response': { 'status': response.status, - 'body': response.data, + 'body': decode_utf8(response.data), 'headers': dict(response.headers) } }) diff --git a/httpretty/http.py b/httpretty/http.py index 388a73a..7e9a568 100644 --- a/httpretty/http.py +++ b/httpretty/http.py @@ -27,6 +27,7 @@ from __future__ import unicode_literals import re from .compat import BaseClass +from .utils import decode_utf8 STATUSES = { @@ -109,14 +110,14 @@ STATUSES = { class HttpBaseClass(BaseClass): - GET = b'GET' - PUT = b'PUT' - POST = b'POST' - DELETE = b'DELETE' - HEAD = b'HEAD' - PATCH = b'PATCH' - OPTIONS = b'OPTIONS' - CONNECT = b'CONNECT' + GET = 'GET' + PUT = 'PUT' + POST = 'POST' + DELETE = 'DELETE' + HEAD = 'HEAD' + PATCH = 'PATCH' + OPTIONS = 'OPTIONS' + CONNECT = 'CONNECT' METHODS = (GET, PUT, POST, DELETE, HEAD, PATCH, OPTIONS, CONNECT) @@ -133,8 +134,8 @@ def parse_requestline(s): ... ValueError: Not a Request-Line """ - methods = b'|'.join(HttpBaseClass.METHODS) - m = re.match(br'(' + methods + b')\s+(.*)\s+HTTP/(1.[0|1])', s, re.I) + methods = '|'.join(HttpBaseClass.METHODS) + m = re.match(r'(' + methods + ')\s+(.*)\s+HTTP/(1.[0|1])', s, re.I) if m: return m.group(1).upper(), m.group(2), m.group(3) else: @@ -147,7 +148,7 @@ def last_requestline(sent_data): """ for line in reversed(sent_data): try: - parse_requestline(line) + parse_requestline(decode_utf8(line)) except ValueError: pass else: diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py index de0e118..daada92 100644 --- a/tests/functional/test_requests.py +++ b/tests/functional/test_requests.py @@ -32,10 +32,12 @@ import re import json import requests from sure import within, microseconds, expect +from tornado import version as tornado_version from httpretty import HTTPretty, httprettified +from httpretty.compat import text_type from httpretty.core import decode_utf8 -from base import FIXTURE_FILE, use_tornado_server +from .base import FIXTURE_FILE, use_tornado_server from tornado import version as tornado_version try: @@ -573,7 +575,7 @@ def test_httpretty_should_allow_registering_regexes_with_streaming_responses(): os.environ['DEBUG'] = 'true' def my_callback(request, url, headers): - request.body.should.equal('hithere') + request.body.should.equal(b'hithere') return 200, headers, "Received" HTTPretty.register_uri( @@ -590,7 +592,7 @@ def test_httpretty_should_allow_registering_regexes_with_streaming_responses(): 'https://api.yipit.com/v1/deal;brand=gap?first_name=chuck&last_name=norris', data=gen(), ) - expect(response.content).to.equal("Received") + expect(response.content).to.equal(b"Received") expect(HTTPretty.last_request.method).to.equal('POST') expect(HTTPretty.last_request.path).to.equal('/v1/deal;brand=gap?first_name=chuck&last_name=norris') @@ -697,7 +699,7 @@ def test_recording_calls(): ] }) response['response'].should.have.key("status").being.equal(200) - response['response'].should.have.key("body").being.an(unicode) + response['response'].should.have.key("body").being.an(text_type) response['response'].should.have.key("headers").being.a(dict) response['response']["headers"].should.have.key("server").being.equal("TornadoServer/" + tornado_version) diff --git a/tests/unit/test_core.py b/tests/unit/test_core.py index e2551fb..faed4cd 100644 --- a/tests/unit/test_core.py +++ b/tests/unit/test_core.py @@ -8,6 +8,7 @@ from datetime import datetime from mock import Mock, patch, call from sure import expect +from httpretty.compat import StringIO from httpretty.core import HTTPrettyRequest, FakeSSLSocket, fakesock, httpretty @@ -23,11 +24,11 @@ def test_request_stubs_internals(): # Given a valid HTTP request header string headers = "\r\n".join([ 'POST /somewhere/?name=foo&age=bar HTTP/1.1', - 'Accept-Encoding: identity', - 'Host: github.com', - 'Content-Type: application/json', - 'Connection: close', - 'User-Agent: Python-urllib/2.7', + 'accept-encoding: identity', + 'host: github.com', + 'content-type: application/json', + 'connection: close', + 'user-agent: Python-urllib/2.7', ]) # When I create a HTTPrettyRequest with an empty body @@ -43,10 +44,12 @@ def test_request_stubs_internals(): }) # And the `rfile` should be a StringIO - request.should.have.property('rfile').being.a('StringIO.StringIO') + type_as_str = StringIO.__module__ + '.' + StringIO.__name__ + + request.should.have.property('rfile').being.a(type_as_str) # And the `wfile` should be a StringIO - request.should.have.property('wfile').being.a('StringIO.StringIO') + request.should.have.property('wfile').being.a(type_as_str) # And the `method` should be available request.should.have.property('method').being.equal('POST') @@ -301,16 +304,16 @@ def test_fakesock_socket_real_sendall(old_socket): # Background: the real socket will stop returning bytes after the # first call real_socket = old_socket.return_value - real_socket.recv.side_effect = ['response from server', ""] + real_socket.recv.side_effect = [b'response from server', b""] # Given a fake socket socket = fakesock.socket() # When I call real_sendall with data, some args and kwargs - socket.real_sendall("SOMEDATA", 'some extra args...', foo='bar') + socket.real_sendall(b"SOMEDATA", b'some extra args...', foo=b'bar') # Then it should have called sendall in the real socket - real_socket.sendall.assert_called_once_with("SOMEDATA", 'some extra args...', foo='bar') + real_socket.sendall.assert_called_once_with(b"SOMEDATA", b'some extra args...', foo=b'bar') # And the timeout was set to 0 real_socket.settimeout.assert_called_once_with(0) @@ -322,7 +325,7 @@ def test_fakesock_socket_real_sendall(old_socket): ]) # And the buffer should contain the data from the server - socket.fd.getvalue().should.equal("response from server") + socket.fd.getvalue().should.equal(b"response from server") # And connect was never called real_socket.connect.called.should.be.false @@ -336,17 +339,17 @@ def test_fakesock_socket_real_sendall_continue_eagain(socket, old_socket): # Background: the real socket will stop returning bytes after the # first call real_socket = old_socket.return_value - real_socket.recv.side_effect = [SocketErrorStub(errno.EAGAIN), 'after error', ""] + real_socket.recv.side_effect = [SocketErrorStub(errno.EAGAIN), b'after error', b""] # Given a fake socket socket = fakesock.socket() # When I call real_sendall with data, some args and kwargs - socket.real_sendall("SOMEDATA", 'some extra args...', foo='bar') + socket.real_sendall(b"SOMEDATA", b'some extra args...', foo=b'bar') # Then it should have called sendall in the real socket - real_socket.sendall.assert_called_once_with("SOMEDATA", 'some extra args...', foo='bar') + real_socket.sendall.assert_called_once_with(b"SOMEDATA", b'some extra args...', foo=b'bar') # And the timeout was set to 0 real_socket.settimeout.assert_called_once_with(0) @@ -358,7 +361,7 @@ def test_fakesock_socket_real_sendall_continue_eagain(socket, old_socket): ]) # And the buffer should contain the data from the server - socket.fd.getvalue().should.equal("after error") + socket.fd.getvalue().should.equal(b"after error") # And connect was never called real_socket.connect.called.should.be.false @@ -372,16 +375,16 @@ def test_fakesock_socket_real_sendall_socket_error(socket, old_socket): # Background: the real socket will stop returning bytes after the # first call real_socket = old_socket.return_value - real_socket.recv.side_effect = [SocketErrorStub(42), 'after error', ""] + real_socket.recv.side_effect = [SocketErrorStub(42), b'after error', ""] # Given a fake socket socket = fakesock.socket() # When I call real_sendall with data, some args and kwargs - socket.real_sendall("SOMEDATA", 'some extra args...', foo='bar') + socket.real_sendall(b"SOMEDATA", b'some extra args...', foo=b'bar') # Then it should have called sendall in the real socket - real_socket.sendall.assert_called_once_with("SOMEDATA", 'some extra args...', foo='bar') + real_socket.sendall.assert_called_once_with(b"SOMEDATA", b'some extra args...', foo=b'bar') # And the timeout was set to 0 real_socket.settimeout.assert_called_once_with(0) @@ -390,7 +393,7 @@ def test_fakesock_socket_real_sendall_socket_error(socket, old_socket): real_socket.recv.assert_called_once_with(16) # And the buffer should contain the data from the server - socket.fd.getvalue().should.equal("") + socket.fd.getvalue().should.equal(b"") # And connect was never called real_socket.connect.called.should.be.false @@ -403,7 +406,7 @@ def test_fakesock_socket_real_sendall_when_http(POTENTIAL_HTTP_PORTS, old_socket # Background: the real socket will stop returning bytes after the # first call real_socket = old_socket.return_value - real_socket.recv.side_effect = ['response from foobar :)', ""] + real_socket.recv.side_effect = [b'response from foobar :)', b""] # And the potential http port is 4000 POTENTIAL_HTTP_PORTS.__contains__.side_effect = lambda other: int(other) == 4000 @@ -415,7 +418,7 @@ def test_fakesock_socket_real_sendall_when_http(POTENTIAL_HTTP_PORTS, old_socket socket.connect(('foobar.com', 4000)) # And send some data - socket.real_sendall("SOMEDATA") + socket.real_sendall(b"SOMEDATA") # Then connect should have been called real_socket.connect.assert_called_once_with(('foobar.com', 4000)) @@ -430,7 +433,7 @@ def test_fakesock_socket_real_sendall_when_http(POTENTIAL_HTTP_PORTS, old_socket ]) # And the buffer should contain the data from the server - socket.fd.getvalue().should.equal("response from foobar :)") + socket.fd.getvalue().should.equal(b"response from foobar :)") @patch('httpretty.core.old_socket') @@ -456,7 +459,7 @@ def test_fakesock_socket_sendall_with_valid_requestline(POTENTIAL_HTTP_PORTS, ht socket.connect(('foo.com', 80)) # When I try to send data - socket.sendall("GET /foobar HTTP/1.1\r\nContent-Type: application/json\r\n\r\n") + socket.sendall(b"GET /foobar HTTP/1.1\r\nContent-Type: application/json\r\n\r\n") @patch('httpretty.core.old_socket') @@ -482,7 +485,7 @@ def test_fakesock_socket_sendall_with_valid_requestline(POTENTIAL_HTTP_PORTS, ht socket.connect(('foo.com', 80)) # When I try to send data - socket.sendall("GET /foobar HTTP/1.1\r\nContent-Type: application/json\r\n\r\n") + socket.sendall(b"GET /foobar HTTP/1.1\r\nContent-Type: application/json\r\n\r\n") @patch('httpretty.core.old_socket') @@ -493,7 +496,7 @@ def test_fakesock_socket_sendall_with_body_data_no_entry(POTENTIAL_HTTP_PORTS, o # Using a subclass of socket that mocks out real_sendall class MySocket(fakesock.socket): def real_sendall(self, data): - data.should.equal('BLABLABLABLA') + data.should.equal(b'BLABLABLABLA') return 'cool' # Given an instance of that socket @@ -504,7 +507,7 @@ def test_fakesock_socket_sendall_with_body_data_no_entry(POTENTIAL_HTTP_PORTS, o socket.connect(('foo.com', 80)) # When I try to send data - result = socket.sendall("BLABLABLABLA") + result = socket.sendall(b"BLABLABLABLA") # Then the result should be the return value from real_sendall result.should.equal('cool') @@ -522,7 +525,7 @@ def test_fakesock_socket_sendall_with_body_data_with_entry(POTENTIAL_HTTP_PORTS, # Using a mocked entry entry = Mock() entry.request.headers = {} - entry.request.body = '' + entry.request.body = b'' # Given an instance of that socket socket = MySocket() @@ -533,10 +536,10 @@ def test_fakesock_socket_sendall_with_body_data_with_entry(POTENTIAL_HTTP_PORTS, socket.connect(('foo.com', 80)) # When I try to send data - socket.sendall("BLABLABLABLA") + socket.sendall(b"BLABLABLABLA") # Then the entry should have that body - entry.request.body.should.equal('BLABLABLABLA') + entry.request.body.should.equal(b'BLABLABLABLA') @patch('httpretty.core.old_socket') @@ -553,7 +556,7 @@ def test_fakesock_socket_sendall_with_body_data_with_chunked_entry(POTENTIAL_HTT entry.request.headers = { 'transfer-encoding': 'chunked', } - entry.request.body = '' + entry.request.body = b'' # Given an instance of that socket socket = MySocket() @@ -563,7 +566,7 @@ def test_fakesock_socket_sendall_with_body_data_with_chunked_entry(POTENTIAL_HTT socket.connect(('foo.com', 80)) # When I try to send data - socket.sendall("BLABLABLABLA") + socket.sendall(b"BLABLABLABLA") # Then the entry should have that body - httpretty.last_request.body.should.equal('BLABLABLABLA') + httpretty.last_request.body.should.equal(b'BLABLABLABLA')