aboutsummaryrefslogtreecommitdiffstats
path: root/netlib/http.py
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@nullcube.com>2015-04-21 11:05:12 +1200
committerAldo Cortesi <aldo@nullcube.com>2015-04-21 11:05:12 +1200
commit2c660d76337b11eb438a2978ec3bda3ac10babd5 (patch)
tree3c3cef787b9575c60de911a76dfd55e1074eea92 /netlib/http.py
parent0141629c0863c583f6c57965220be57f81a6ce17 (diff)
downloadmitmproxy-2c660d76337b11eb438a2978ec3bda3ac10babd5.tar.gz
mitmproxy-2c660d76337b11eb438a2978ec3bda3ac10babd5.tar.bz2
mitmproxy-2c660d76337b11eb438a2978ec3bda3ac10babd5.zip
Migrate requeset reading from mitmproxy to netlib
Diffstat (limited to 'netlib/http.py')
-rw-r--r--netlib/http.py124
1 files changed, 122 insertions, 2 deletions
diff --git a/netlib/http.py b/netlib/http.py
index 26438863..aacdd1d4 100644
--- a/netlib/http.py
+++ b/netlib/http.py
@@ -1,7 +1,10 @@
from __future__ import (absolute_import, print_function, division)
-import string, urlparse, binascii
+import collections
+import string
+import urlparse
+import binascii
import sys
-from . import odict, utils
+from . import odict, utils, tcp
class HttpError(Exception):
@@ -30,6 +33,19 @@ def _is_valid_host(host):
return True
+def get_line(fp):
+ """
+ Get a line, possibly preceded by a blank.
+ """
+ line = fp.readline()
+ if line == "\r\n" or line == "\n":
+ # Possible leftover from previous message
+ line = fp.readline()
+ if line == "":
+ raise tcp.NetLibDisconnect()
+ return line
+
+
def parse_url(url):
"""
Returns a (scheme, host, port, path) tuple, or None on error.
@@ -436,3 +452,107 @@ def expected_http_body_size(headers, is_request, request_method, response_code):
if is_request:
return 0
return -1
+
+
+Request = collections.namedtuple(
+ "Request",
+ [
+ "form_in",
+ "method",
+ "scheme",
+ "host",
+ "port",
+ "path",
+ "httpversion",
+ "headers",
+ "content"
+ ]
+)
+
+
+def read_request(rfile, include_body=True, body_size_limit=None, wfile=None):
+ """
+ Parse an HTTP request from a file stream
+
+ Args:
+ rfile (file): Input file to read from
+ include_body (bool): Read response body as well
+ body_size_limit (bool): Maximum body size
+ wfile (file): If specified, HTTP Expect headers are handled
+ automatically, by writing a HTTP 100 CONTINUE response to the stream.
+
+ Returns:
+ Request: The HTTP request
+
+ Raises:
+ HttpError: If the input is invalid.
+ """
+ httpversion, host, port, scheme, method, path, headers, content = (
+ None, None, None, None, None, None, None, None)
+
+ request_line = get_line(rfile)
+
+ request_line_parts = parse_init(request_line)
+ if not request_line_parts:
+ raise HttpError(
+ 400,
+ "Bad HTTP request line: %s" % repr(request_line)
+ )
+ method, path, httpversion = request_line_parts
+
+ if path == '*' or path.startswith("/"):
+ form_in = "relative"
+ if not utils.isascii(path):
+ raise HttpError(
+ 400,
+ "Bad HTTP request line: %s" % repr(request_line)
+ )
+ elif method.upper() == 'CONNECT':
+ form_in = "authority"
+ r = parse_init_connect(request_line)
+ if not r:
+ raise HttpError(
+ 400,
+ "Bad HTTP request line: %s" % repr(request_line)
+ )
+ host, port, _ = r
+ path = None
+ else:
+ form_in = "absolute"
+ r = parse_init_proxy(request_line)
+ if not r:
+ raise HttpError(
+ 400,
+ "Bad HTTP request line: %s" % repr(request_line)
+ )
+ _, scheme, host, port, path, _ = r
+
+ headers = read_headers(rfile)
+ if headers is None:
+ raise HttpError(400, "Invalid headers")
+
+ expect_header = headers.get_first("expect")
+ if expect_header and expect_header.lower() == "100-continue" and httpversion >= (1, 1):
+ wfile.write(
+ 'HTTP/1.1 100 Continue\r\n'
+ '\r\n'
+ )
+ wfile.flush()
+ del headers['expect']
+
+ if include_body:
+ content = read_http_body(
+ rfile, headers, body_size_limit, method, None, True
+ )
+
+ return Request(
+ form_in,
+ method,
+ scheme,
+ host,
+ port,
+ path,
+ httpversion,
+ headers,
+ content
+ )