aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/dev/models.rst2
-rw-r--r--examples/redirect_requests.py7
-rw-r--r--mitmproxy/console/flowview.py5
-rw-r--r--netlib/http/response.py54
-rw-r--r--test/netlib/http/test_response.py20
5 files changed, 73 insertions, 15 deletions
diff --git a/docs/dev/models.rst b/docs/dev/models.rst
index 7260f1f7..a333fb06 100644
--- a/docs/dev/models.rst
+++ b/docs/dev/models.rst
@@ -39,6 +39,8 @@ Datastructures
.. autoclass:: Response
+ .. automethod:: make
+
.. rubric:: Data
.. autoattribute:: http_version
.. autoattribute:: status_code
diff --git a/examples/redirect_requests.py b/examples/redirect_requests.py
index 36594bcd..8cde1bfd 100644
--- a/examples/redirect_requests.py
+++ b/examples/redirect_requests.py
@@ -2,7 +2,6 @@
This example shows two ways to redirect flows to other destinations.
"""
from mitmproxy.models import HTTPResponse
-from netlib.http import Headers
def request(flow):
@@ -12,11 +11,7 @@ def request(flow):
# Method 1: Answer with a locally generated response
if flow.request.pretty_host.endswith("example.com"):
- resp = HTTPResponse(
- b"HTTP/1.1", 200, b"OK",
- Headers(Content_Type="text/html"),
- b"helloworld"
- )
+ resp = HTTPResponse.make(200, b"Hello World", {"Content-Type": "text/html"})
flow.reply.send(resp)
# Method 2: Redirect the request to a different server
diff --git a/mitmproxy/console/flowview.py b/mitmproxy/console/flowview.py
index 6d74be65..15be379b 100644
--- a/mitmproxy/console/flowview.py
+++ b/mitmproxy/console/flowview.py
@@ -374,10 +374,7 @@ class FlowView(tabs.Tabs):
message = self.flow.request
else:
if not self.flow.response:
- self.flow.response = models.HTTPResponse(
- self.flow.request.http_version,
- 200, b"OK", Headers(), b""
- )
+ self.flow.response = models.HTTPResponse.make(200, b"")
message = self.flow.response
self.flow.backup()
diff --git a/netlib/http/response.py b/netlib/http/response.py
index 85f54940..7866c142 100644
--- a/netlib/http/response.py
+++ b/netlib/http/response.py
@@ -1,14 +1,19 @@
from __future__ import absolute_import, print_function, division
-from email.utils import parsedate_tz, formatdate, mktime_tz
-import time
import six
-
+import time
+from email.utils import parsedate_tz, formatdate, mktime_tz
+from netlib import human
+from netlib import multidict
from netlib.http import cookies
from netlib.http import headers as nheaders
from netlib.http import message
-from netlib import multidict
-from netlib import human
+from netlib.http import status_codes
+from typing import AnyStr # noqa
+from typing import Dict # noqa
+from typing import Iterable # noqa
+from typing import Tuple # noqa
+from typing import Union # noqa
class ResponseData(message.MessageData):
@@ -54,6 +59,45 @@ class Response(message.Message):
details=details
)
+ @classmethod
+ def make(
+ cls,
+ status_code=200, # type: int
+ content=b"", # type: AnyStr
+ headers=() # type: Union[Dict[AnyStr, AnyStr], Iterable[Tuple[bytes, bytes]]]
+ ):
+ """
+ Simplified API for creating response objects.
+ """
+ resp = cls(
+ b"HTTP/1.1",
+ status_code,
+ status_codes.RESPONSES.get(status_code, "").encode(),
+ (),
+ None
+ )
+ # Assign this manually to update the content-length header.
+ if isinstance(content, bytes):
+ resp.content = content
+ elif isinstance(content, str):
+ resp.text = content
+ else:
+ raise TypeError("Expected content to be str or bytes, but is {}.".format(
+ type(content).__name__
+ ))
+
+ # Headers can be list or dict, we differentiate here.
+ if isinstance(headers, dict):
+ resp.headers = nheaders.Headers(**headers)
+ elif isinstance(headers, Iterable):
+ resp.headers = nheaders.Headers(headers)
+ else:
+ raise TypeError("Expected headers to be an iterable or dict, but is {}.".format(
+ type(headers).__name__
+ ))
+
+ return resp
+
@property
def status_code(self):
"""
diff --git a/test/netlib/http/test_response.py b/test/netlib/http/test_response.py
index b3c2f736..c7b1b646 100644
--- a/test/netlib/http/test_response.py
+++ b/test/netlib/http/test_response.py
@@ -5,6 +5,7 @@ import email
import time
from netlib.http import Headers
+from netlib.http import Response
from netlib.http.cookies import CookieAttrs
from netlib.tutils import raises, tresp
from .test_message import _test_passthrough_attr, _test_decoded_attr
@@ -28,6 +29,25 @@ class TestResponseCore(object):
response.content = None
assert repr(response) == "Response(200 OK, no content)"
+ def test_make(self):
+ r = Response.make()
+ assert r.status_code == 200
+ assert r.content == b""
+
+ Response.make(content=b"foo")
+ Response.make(content="foo")
+ with raises(TypeError):
+ Response.make(content=42)
+
+ r = Response.make(headers=[(b"foo", b"bar")])
+ assert r.headers["foo"] == "bar"
+
+ r = Response.make(headers=({"foo": "baz"}))
+ assert r.headers["foo"] == "baz"
+
+ with raises(TypeError):
+ Response.make(headers=42)
+
def test_status_code(self):
_test_passthrough_attr(tresp(), "status_code")