aboutsummaryrefslogtreecommitdiffstats
path: root/netlib
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2015-09-26 17:39:50 +0200
committerMaximilian Hils <git@maximilianhils.com>2015-09-26 17:39:50 +0200
commit49ea8fc0ebcfe4861f099200044a553f092faec7 (patch)
tree8b5d9db62503a6e0125d0e6dbd708c6482aa5bf4 /netlib
parent106f7046d3862cb0e3cbb4f38335af0330b4e7e3 (diff)
downloadmitmproxy-49ea8fc0ebcfe4861f099200044a553f092faec7.tar.gz
mitmproxy-49ea8fc0ebcfe4861f099200044a553f092faec7.tar.bz2
mitmproxy-49ea8fc0ebcfe4861f099200044a553f092faec7.zip
refactor response model
Diffstat (limited to 'netlib')
-rw-r--r--netlib/http/__init__.py15
-rw-r--r--netlib/http/headers.py26
-rw-r--r--netlib/http/http1/assemble.py16
-rw-r--r--netlib/http/http1/read.py2
-rw-r--r--netlib/http/http2/connections.py4
-rw-r--r--netlib/http/http2/frame.py3
-rw-r--r--netlib/http/message.py64
-rw-r--r--netlib/http/models.py112
-rw-r--r--netlib/http/request.py155
-rw-r--r--netlib/http/response.py124
-rw-r--r--netlib/tutils.py6
-rw-r--r--netlib/wsgi.py6
12 files changed, 277 insertions, 256 deletions
diff --git a/netlib/http/__init__.py b/netlib/http/__init__.py
index e8c7ba20..fd632cd5 100644
--- a/netlib/http/__init__.py
+++ b/netlib/http/__init__.py
@@ -1,17 +1,14 @@
from __future__ import absolute_import, print_function, division
-from .headers import Headers
-from .message import decoded
from .request import Request
-from .models import Response
-from .models import ALPN_PROTO_HTTP1, ALPN_PROTO_H2
-from .models import HDR_FORM_MULTIPART, HDR_FORM_URLENCODED, CONTENT_MISSING
+from .response import Response
+from .headers import Headers
+from .message import decoded, CONTENT_MISSING
from . import http1, http2
__all__ = [
+ "Request",
+ "Response",
"Headers",
- "decoded",
- "Request", "Response",
- "ALPN_PROTO_HTTP1", "ALPN_PROTO_H2",
- "HDR_FORM_MULTIPART", "HDR_FORM_URLENCODED", "CONTENT_MISSING",
+ "decoded", "CONTENT_MISSING",
"http1", "http2",
]
diff --git a/netlib/http/headers.py b/netlib/http/headers.py
index 47ea923b..c79c3344 100644
--- a/netlib/http/headers.py
+++ b/netlib/http/headers.py
@@ -36,12 +36,8 @@ class Headers(MutableMapping):
.. code-block:: python
- # Create header from a list of (header_name, header_value) tuples
- >>> h = Headers([
- ["Host","example.com"],
- ["Accept","text/html"],
- ["accept","application/xml"]
- ])
+ # Create headers with keyword arguments
+ >>> h = Headers(host="example.com", content_type="application/xml")
# Headers mostly behave like a normal dict.
>>> h["Host"]
@@ -51,6 +47,13 @@ class Headers(MutableMapping):
>>> h["host"]
"example.com"
+ # Headers can also be creatd from a list of raw (header_name, header_value) byte tuples
+ >>> h = Headers([
+ [b"Host",b"example.com"],
+ [b"Accept",b"text/html"],
+ [b"accept",b"application/xml"]
+ ])
+
# Multiple headers are folded into a single header as per RFC7230
>>> h["Accept"]
"text/html, application/xml"
@@ -60,17 +63,14 @@ class Headers(MutableMapping):
>>> h["Accept"]
"application/text"
- # str(h) returns a HTTP1 header block.
- >>> print(h)
+ # bytes(h) returns a HTTP1 header block.
+ >>> print(bytes(h))
Host: example.com
Accept: application/text
# For full control, the raw header fields can be accessed
>>> h.fields
- # Headers can also be crated from keyword arguments
- >>> h = Headers(host="example.com", content_type="application/xml")
-
Caveats:
For use with the "Set-Cookie" header, see :py:meth:`get_all`.
"""
@@ -79,8 +79,8 @@ class Headers(MutableMapping):
def __init__(self, fields=None, **headers):
"""
Args:
- fields: (optional) list of ``(name, value)`` header tuples,
- e.g. ``[("Host","example.com")]``. All names and values must be bytes.
+ fields: (optional) list of ``(name, value)`` header byte tuples,
+ e.g. ``[(b"Host", b"example.com")]``. All names and values must be bytes.
**headers: Additional headers to set. Will overwrite existing values from `fields`.
For convenience, underscores in header names will be transformed to dashes -
this behaviour does not extend to other methods.
diff --git a/netlib/http/http1/assemble.py b/netlib/http/http1/assemble.py
index 864f6017..785ee8d3 100644
--- a/netlib/http/http1/assemble.py
+++ b/netlib/http/http1/assemble.py
@@ -10,7 +10,7 @@ def assemble_request(request):
if request.content == CONTENT_MISSING:
raise HttpException("Cannot assemble flow with CONTENT_MISSING")
head = assemble_request_head(request)
- body = b"".join(assemble_body(request.headers, [request.data.content]))
+ body = b"".join(assemble_body(request.data.headers, [request.data.content]))
return head + body
@@ -24,13 +24,13 @@ def assemble_response(response):
if response.content == CONTENT_MISSING:
raise HttpException("Cannot assemble flow with CONTENT_MISSING")
head = assemble_response_head(response)
- body = b"".join(assemble_body(response.headers, [response.content]))
+ body = b"".join(assemble_body(response.data.headers, [response.data.content]))
return head + body
def assemble_response_head(response):
- first_line = _assemble_response_line(response)
- headers = _assemble_response_headers(response)
+ first_line = _assemble_response_line(response.data)
+ headers = _assemble_response_headers(response.data)
return b"%s\r\n%s\r\n" % (first_line, headers)
@@ -92,11 +92,11 @@ def _assemble_request_headers(request_data):
return bytes(headers)
-def _assemble_response_line(response):
+def _assemble_response_line(response_data):
return b"%s %d %s" % (
- response.http_version,
- response.status_code,
- response.msg,
+ response_data.http_version,
+ response_data.status_code,
+ response_data.reason,
)
diff --git a/netlib/http/http1/read.py b/netlib/http/http1/read.py
index 76721e06..0d5e7f4b 100644
--- a/netlib/http/http1/read.py
+++ b/netlib/http/http1/read.py
@@ -50,7 +50,7 @@ def read_request_head(rfile):
def read_response(rfile, request, body_size_limit=None):
response = read_response_head(rfile)
expected_body_size = expected_http_body_size(request, response)
- response._body = b"".join(read_body(rfile, expected_body_size, body_size_limit))
+ response.data.content = b"".join(read_body(rfile, expected_body_size, body_size_limit))
response.timestamp_end = time.time()
return response
diff --git a/netlib/http/http2/connections.py b/netlib/http/http2/connections.py
index 5220d5d2..c493abe6 100644
--- a/netlib/http/http2/connections.py
+++ b/netlib/http/http2/connections.py
@@ -4,7 +4,7 @@ import time
from hpack.hpack import Encoder, Decoder
from ... import utils
-from .. import Headers, Response, Request, ALPN_PROTO_H2
+from .. import Headers, Response, Request
from . import frame
@@ -283,7 +283,7 @@ class HTTP2Protocol(object):
def check_alpn(self):
alp = self.tcp_handler.get_alpn_proto_negotiated()
- if alp != ALPN_PROTO_H2:
+ if alp != b'h2':
raise NotImplementedError(
"HTTP2Protocol can not handle unknown ALP: %s" % alp)
return True
diff --git a/netlib/http/http2/frame.py b/netlib/http/http2/frame.py
index cb2cde99..188629d4 100644
--- a/netlib/http/http2/frame.py
+++ b/netlib/http/http2/frame.py
@@ -25,9 +25,6 @@ ERROR_CODES = BiDi(
CLIENT_CONNECTION_PREFACE = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
-ALPN_PROTO_H2 = b'h2'
-
-
class Frame(object):
"""
diff --git a/netlib/http/message.py b/netlib/http/message.py
index 20497bd5..ee138746 100644
--- a/netlib/http/message.py
+++ b/netlib/http/message.py
@@ -6,11 +6,14 @@ import six
from .. import encoding, utils
+
+CONTENT_MISSING = 0
+
if six.PY2:
_native = lambda x: x
_always_bytes = lambda x: x
else:
- # While headers _should_ be ASCII, it's not uncommon for certain headers to be utf-8 encoded.
+ # While the HTTP head _should_ be ASCII, it's not uncommon for certain headers to be utf-8 encoded.
_native = lambda x: x.decode("utf-8", "surrogateescape")
_always_bytes = lambda x: utils.always_bytes(x, "utf-8", "surrogateescape")
@@ -28,17 +31,6 @@ class Message(object):
return not self.__eq__(other)
@property
- def http_version(self):
- """
- Version string, e.g. "HTTP/1.1"
- """
- return _native(self.data.http_version)
-
- @http_version.setter
- def http_version(self, http_version):
- self.data.http_version = _always_bytes(http_version)
-
- @property
def headers(self):
"""
Message headers object
@@ -53,6 +45,32 @@ class Message(object):
self.data.headers = h
@property
+ def content(self):
+ """
+ The raw (encoded) HTTP message body
+
+ See also: :py:attr:`text`
+ """
+ return self.data.content
+
+ @content.setter
+ def content(self, content):
+ self.data.content = content
+ if isinstance(content, bytes):
+ self.headers["content-length"] = str(len(content))
+
+ @property
+ def http_version(self):
+ """
+ Version string, e.g. "HTTP/1.1"
+ """
+ return _native(self.data.http_version)
+
+ @http_version.setter
+ def http_version(self, http_version):
+ self.data.http_version = _always_bytes(http_version)
+
+ @property
def timestamp_start(self):
"""
First byte timestamp
@@ -75,25 +93,13 @@ class Message(object):
self.data.timestamp_end = timestamp_end
@property
- def content(self):
- """
- The raw (encoded) HTTP message body
-
- See also: :py:attr:`text`
- """
- return self.data.content
-
- @content.setter
- def content(self, content):
- self.data.content = content
- if isinstance(content, bytes):
- self.headers["content-length"] = str(len(content))
-
- @property
def text(self):
"""
The decoded HTTP message body.
- Decoded contents are not cached, so this method is relatively expensive to call.
+ Decoded contents are not cached, so accessing this attribute repeatedly is relatively expensive.
+
+ .. note::
+ This is not implemented yet.
See also: :py:attr:`content`, :py:class:`decoded`
"""
@@ -104,6 +110,8 @@ class Message(object):
def text(self, text):
raise NotImplementedError()
+ # Legacy
+
@property
def body(self):
warnings.warn(".body is deprecated, use .content instead.", DeprecationWarning)
diff --git a/netlib/http/models.py b/netlib/http/models.py
deleted file mode 100644
index 40f6e98c..00000000
--- a/netlib/http/models.py
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
-from ..odict import ODict
-from .. import utils, encoding
-from ..utils import always_bytes, native
-from . import cookies
-from .headers import Headers
-
-from six.moves import urllib
-
-# TODO: Move somewhere else?
-ALPN_PROTO_HTTP1 = b'http/1.1'
-ALPN_PROTO_H2 = b'h2'
-HDR_FORM_URLENCODED = "application/x-www-form-urlencoded"
-HDR_FORM_MULTIPART = "multipart/form-data"
-
-CONTENT_MISSING = 0
-
-
-class Message(object):
- def __init__(self, http_version, headers, body, timestamp_start, timestamp_end):
- self.http_version = http_version
- if not headers:
- headers = Headers()
- assert isinstance(headers, Headers)
- self.headers = headers
-
- self._body = body
- self.timestamp_start = timestamp_start
- self.timestamp_end = timestamp_end
-
- @property
- def body(self):
- return self._body
-
- @body.setter
- def body(self, body):
- self._body = body
- if isinstance(body, bytes):
- self.headers["content-length"] = str(len(body)).encode()
-
- content = body
-
- def __eq__(self, other):
- if isinstance(other, Message):
- return self.__dict__ == other.__dict__
- return False
-
-
-class Response(Message):
- def __init__(
- self,
- http_version,
- status_code,
- msg=None,
- headers=None,
- body=None,
- timestamp_start=None,
- timestamp_end=None,
- ):
- super(Response, self).__init__(http_version, headers, body, timestamp_start, timestamp_end)
- self.status_code = status_code
- self.msg = msg
-
- def __repr__(self):
- # return "Response(%s - %s)" % (self.status_code, self.msg)
-
- if self.body:
- size = utils.pretty_size(len(self.body))
- else:
- size = "content missing"
- # TODO: Remove "(unknown content type, content missing)" edge-case
- return "<Response: {status_code} {msg} ({contenttype}, {size})>".format(
- status_code=self.status_code,
- msg=self.msg,
- contenttype=self.headers.get("content-type", "unknown content type"),
- size=size)
-
- def get_cookies(self):
- """
- Get the contents of all Set-Cookie headers.
-
- Returns a possibly empty ODict, where keys are cookie name strings,
- and values are [value, attr] lists. Value is a string, and attr is
- an ODictCaseless containing cookie attributes. Within attrs, unary
- attributes (e.g. HTTPOnly) are indicated by a Null value.
- """
- ret = []
- for header in self.headers.get_all("set-cookie"):
- v = cookies.parse_set_cookie_header(header)
- if v:
- name, value, attrs = v
- ret.append([name, [value, attrs]])
- return ODict(ret)
-
- def set_cookies(self, odict):
- """
- Set the Set-Cookie headers on this response, over-writing existing
- headers.
-
- Accepts an ODict of the same format as that returned by get_cookies.
- """
- values = []
- for i in odict.lst:
- values.append(
- cookies.format_set_cookie_header(
- i[0],
- i[1][0],
- i[1][1]
- )
- )
- self.headers.set_all("set-cookie", values)
diff --git a/netlib/http/request.py b/netlib/http/request.py
index 6830ca40..f8a3b5b9 100644
--- a/netlib/http/request.py
+++ b/netlib/http/request.py
@@ -55,7 +55,7 @@ class Request(Message):
else:
hostport = ""
path = self.path or ""
- return "HTTPRequest({} {}{})".format(
+ return "Request({} {}{})".format(
self.method, hostport, path
)
@@ -97,7 +97,8 @@ class Request(Message):
@property
def host(self):
"""
- Target host for the request. This may be directly taken in the request (e.g. "GET http://example.com/ HTTP/1.1")
+ Target host. This may be parsed from the raw request
+ (e.g. from a ``GET http://example.com/ HTTP/1.1`` request line)
or inferred from the proxy mode (e.g. an IP in transparent mode).
"""
@@ -154,6 +155,83 @@ class Request(Message):
def path(self, path):
self.data.path = _always_bytes(path)
+ @property
+ def url(self):
+ """
+ The URL string, constructed from the request's URL components
+ """
+ return utils.unparse_url(self.scheme, self.host, self.port, self.path)
+
+ @url.setter
+ def url(self, url):
+ self.scheme, self.host, self.port, self.path = utils.parse_url(url)
+
+ @property
+ def pretty_host(self):
+ """
+ Similar to :py:attr:`host`, but using the Host headers as an additional preferred data source.
+ This is useful in transparent mode where :py:attr:`host` is only an IP address,
+ but may not reflect the actual destination as the Host header could be spoofed.
+ """
+ return self.headers.get("host", self.host)
+
+ @property
+ def pretty_url(self):
+ """
+ Like :py:attr:`url`, but using :py:attr:`pretty_host` instead of :py:attr:`host`.
+ """
+ if self.first_line_format == "authority":
+ return "%s:%d" % (self.pretty_host, self.port)
+ return utils.unparse_url(self.scheme, self.pretty_host, self.port, self.path)
+
+ @property
+ def query(self):
+ """
+ The request query string as an :py:class:`ODict` object.
+ None, if there is no query.
+ """
+ _, _, _, _, query, _ = urllib.parse.urlparse(self.url)
+ if query:
+ return ODict(utils.urldecode(query))
+ return None
+
+ @query.setter
+ def query(self, odict):
+ query = utils.urlencode(odict.lst)
+ scheme, netloc, path, params, _, fragment = urllib.parse.urlparse(self.url)
+ self.url = urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment])
+
+ @property
+ def cookies(self):
+ """
+ The request cookies.
+ An empty :py:class:`ODict` object if the cookie monster ate them all.
+ """
+ ret = ODict()
+ for i in self.headers.get_all("Cookie"):
+ ret.extend(cookies.parse_cookie_header(i))
+ return ret
+
+ @cookies.setter
+ def cookies(self, odict):
+ self.headers["cookie"] = cookies.format_cookie_header(odict)
+
+ @property
+ def path_components(self):
+ """
+ The URL's path components as a list of strings.
+ Components are unquoted.
+ """
+ _, _, path, _, _, _ = urllib.parse.urlparse(self.url)
+ return [urllib.parse.unquote(i) for i in path.split("/") if i]
+
+ @path_components.setter
+ def path_components(self, components):
+ components = map(lambda x: urllib.parse.quote(x, safe=""), components)
+ path = "/" + "/".join(components)
+ scheme, netloc, _, params, query, fragment = urllib.parse.urlparse(self.url)
+ self.url = urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment])
+
def anticache(self):
"""
Modifies this request to remove headers that might produce a cached
@@ -191,7 +269,7 @@ class Request(Message):
@property
def urlencoded_form(self):
"""
- The URL-encoded form data as an ODict object.
+ The URL-encoded form data as an :py:class:`ODict` object.
None if there is no data or the content-type indicates non-form data.
"""
is_valid_content_type = "application/x-www-form-urlencoded" in self.headers.get("content-type", "").lower()
@@ -211,7 +289,7 @@ class Request(Message):
@property
def multipart_form(self):
"""
- The multipart form data as an ODict object.
+ The multipart form data as an :py:class:`ODict` object.
None if there is no data or the content-type indicates non-form data.
"""
is_valid_content_type = "multipart/form-data" in self.headers.get("content-type", "").lower()
@@ -223,75 +301,6 @@ class Request(Message):
def multipart_form(self):
raise NotImplementedError()
- @property
- def path_components(self):
- """
- The URL's path components as a list of strings.
- Components are unquoted.
- """
- _, _, path, _, _, _ = urllib.parse.urlparse(self.url)
- return [urllib.parse.unquote(i) for i in path.split("/") if i]
-
- @path_components.setter
- def path_components(self, components):
- components = map(lambda x: urllib.parse.quote(x, safe=""), components)
- path = "/" + "/".join(components)
- scheme, netloc, _, params, query, fragment = urllib.parse.urlparse(self.url)
- self.url = urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment])
-
- @property
- def query(self):
- """
- The request query string as an ODict object.
- None, if there is no query.
- """
- _, _, _, _, query, _ = urllib.parse.urlparse(self.url)
- if query:
- return ODict(utils.urldecode(query))
- return None
-
- @query.setter
- def query(self, odict):
- query = utils.urlencode(odict.lst)
- scheme, netloc, path, params, _, fragment = urllib.parse.urlparse(self.url)
- self.url = urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment])
-
- @property
- def cookies(self):
- """
- The request cookies.
- An empty ODict object if the cookie monster ate them all.
- """
- ret = ODict()
- for i in self.headers.get_all("Cookie"):
- ret.extend(cookies.parse_cookie_header(i))
- return ret
-
- @cookies.setter
- def cookies(self, odict):
- self.headers["cookie"] = cookies.format_cookie_header(odict)
-
- @property
- def url(self):
- """
- The URL string, constructed from the request's URL components
- """
- return utils.unparse_url(self.scheme, self.host, self.port, self.path)
-
- @url.setter
- def url(self, url):
- self.scheme, self.host, self.port, self.path = utils.parse_url(url)
-
- @property
- def pretty_host(self):
- return self.headers.get("host", self.host)
-
- @property
- def pretty_url(self):
- if self.first_line_format == "authority":
- return "%s:%d" % (self.pretty_host, self.port)
- return utils.unparse_url(self.scheme, self.pretty_host, self.port, self.path)
-
# Legacy
def get_cookies(self):
diff --git a/netlib/http/response.py b/netlib/http/response.py
index 02fac3df..7d64243d 100644
--- a/netlib/http/response.py
+++ b/netlib/http/response.py
@@ -1,3 +1,125 @@
from __future__ import absolute_import, print_function, division
-# TODO \ No newline at end of file
+import warnings
+
+from . import cookies
+from .headers import Headers
+from .message import Message, _native, _always_bytes
+from .. import utils
+from ..odict import ODict
+
+
+class ResponseData(object):
+ def __init__(self, http_version, status_code, reason=None, headers=None, content=None,
+ timestamp_start=None, timestamp_end=None):
+ if not headers:
+ headers = Headers()
+ assert isinstance(headers, Headers)
+
+ self.http_version = http_version
+ self.status_code = status_code
+ self.reason = reason
+ self.headers = headers
+ self.content = content
+ self.timestamp_start = timestamp_start
+ self.timestamp_end = timestamp_end
+
+ def __eq__(self, other):
+ if isinstance(other, ResponseData):
+ return self.__dict__ == other.__dict__
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+class Response(Message):
+ """
+ An HTTP response.
+ """
+ def __init__(self, *args, **kwargs):
+ data = ResponseData(*args, **kwargs)
+ super(Response, self).__init__(data)
+
+ def __repr__(self):
+ if self.content:
+ details = "{}, {}".format(
+ self.headers.get("content-type", "unknown content type"),
+ utils.pretty_size(len(self.content))
+ )
+ else:
+ details = "content missing"
+ return "Response({status_code} {reason}, {details})".format(
+ status_code=self.status_code,
+ reason=self.reason,
+ details=details
+ )
+
+ @property
+ def status_code(self):
+ """
+ HTTP Status Code, e.g. ``200``.
+ """
+ return self.data.status_code
+
+ @status_code.setter
+ def status_code(self, status_code):
+ self.data.status_code = status_code
+
+ @property
+ def reason(self):
+ """
+ HTTP Reason Phrase, e.g. "Not Found".
+ This is always :py:obj:`None` for HTTP2 requests, because HTTP2 responses do not contain a reason phrase.
+ """
+ return _native(self.data.reason)
+
+ @reason.setter
+ def reason(self, reason):
+ self.data.reason = _always_bytes(reason)
+
+ @property
+ def cookies(self):
+ """
+ Get the contents of all Set-Cookie headers.
+
+ A possibly empty :py:class:`ODict`, where keys are cookie name strings,
+ and values are [value, attr] lists. Value is a string, and attr is
+ an ODictCaseless containing cookie attributes. Within attrs, unary
+ attributes (e.g. HTTPOnly) are indicated by a Null value.
+ """
+ ret = []
+ for header in self.headers.get_all("set-cookie"):
+ v = cookies.parse_set_cookie_header(header)
+ if v:
+ name, value, attrs = v
+ ret.append([name, [value, attrs]])
+ return ODict(ret)
+
+ @cookies.setter
+ def cookies(self, odict):
+ values = []
+ for i in odict.lst:
+ header = cookies.format_set_cookie_header(i[0], i[1][0], i[1][1])
+ values.append(header)
+ self.headers.set_all("set-cookie", values)
+
+ # Legacy
+
+ def get_cookies(self):
+ warnings.warn(".get_cookies is deprecated, use .cookies instead.", DeprecationWarning)
+ return self.cookies
+
+ def set_cookies(self, odict):
+ warnings.warn(".set_cookies is deprecated, use .cookies instead.", DeprecationWarning)
+ self.cookies = odict
+
+ @property
+ def msg(self):
+ warnings.warn(".msg is deprecated, use .reason instead.", DeprecationWarning)
+ return self.reason
+
+ @msg.setter
+ def msg(self, reason):
+ warnings.warn(".msg is deprecated, use .reason instead.", DeprecationWarning)
+ self.reason = reason
diff --git a/netlib/tutils.py b/netlib/tutils.py
index ff63c33c..e16f1a76 100644
--- a/netlib/tutils.py
+++ b/netlib/tutils.py
@@ -120,9 +120,9 @@ def tresp(**kwargs):
default = dict(
http_version=b"HTTP/1.1",
status_code=200,
- msg=b"OK",
- headers=Headers(header_response=b"svalue"),
- body=b"message",
+ reason=b"OK",
+ headers=Headers(header_response="svalue"),
+ content=b"message",
timestamp_start=time.time(),
timestamp_end=time.time(),
)
diff --git a/netlib/wsgi.py b/netlib/wsgi.py
index 4fcd5178..df248a19 100644
--- a/netlib/wsgi.py
+++ b/netlib/wsgi.py
@@ -25,9 +25,9 @@ class Flow(object):
class Request(object):
- def __init__(self, scheme, method, path, http_version, headers, body):
+ def __init__(self, scheme, method, path, http_version, headers, content):
self.scheme, self.method, self.path = scheme, method, path
- self.headers, self.body = headers, body
+ self.headers, self.content = headers, content
self.http_version = http_version
@@ -64,7 +64,7 @@ class WSGIAdaptor(object):
environ = {
'wsgi.version': (1, 0),
'wsgi.url_scheme': native(flow.request.scheme, "latin-1"),
- 'wsgi.input': BytesIO(flow.request.body or b""),
+ 'wsgi.input': BytesIO(flow.request.content or b""),
'wsgi.errors': errsoc,
'wsgi.multithread': True,
'wsgi.multiprocess': False,