aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy')
-rw-r--r--libmproxy/console/common.py17
-rw-r--r--libmproxy/console/flowview.py5
-rw-r--r--libmproxy/dump.py7
-rw-r--r--libmproxy/flow.py5
-rw-r--r--libmproxy/protocol/http.py79
-rw-r--r--libmproxy/protocol/http_wrappers.py142
-rw-r--r--libmproxy/utils.py12
7 files changed, 69 insertions, 198 deletions
diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py
index 90bccfe7..5ce2c0b7 100644
--- a/libmproxy/console/common.py
+++ b/libmproxy/console/common.py
@@ -4,10 +4,13 @@ import urwid
import urwid.util
import os
+from netlib.http.semantics import CONTENT_MISSING
+import netlib.utils
+
from .. import utils
-from ..protocol.http import CONTENT_MISSING, decoded
+from ..protocol.http import decoded
from . import signals
-import netlib.utils
+
try:
import pyperclip
@@ -135,7 +138,7 @@ def raw_format_flow(f, focus, extended, padding):
)
else:
req.append(fcol(">>" if focus else " ", "focus"))
-
+
if f["marked"]:
req.append(fcol(SYMBOL_MARK, "mark"))
@@ -249,7 +252,7 @@ def copy_flow_format_data(part, scope, flow):
return None, "Request content is missing"
with decoded(flow.request):
if part == "h":
- data += flow.request.assemble()
+ data += flow.client_protocol.assemble(flow.request)
elif part == "c":
data += flow.request.content
else:
@@ -262,7 +265,7 @@ def copy_flow_format_data(part, scope, flow):
return None, "Response content is missing"
with decoded(flow.response):
if part == "h":
- data += flow.response.assemble()
+ data += flow.client_protocol.assemble(flow.response)
elif part == "c":
data += flow.response.content
else:
@@ -295,7 +298,7 @@ def copy_flow(part, scope, flow, master, state):
toclip = ""
try:
toclip = data.decode('utf-8')
- except (UnicodeDecodeError):
+ except (UnicodeDecodeError):
toclip = data
try:
@@ -391,7 +394,7 @@ def format_flow(f, focus, extended=False, hostheader=False, padding=2,
err_msg = f.error.msg if f.error else None,
resp_code = f.response.code if f.response else None,
-
+
marked = marked,
)
if f.response:
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index c6c4c10d..1e0f0c17 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -2,11 +2,14 @@ from __future__ import absolute_import
import os
import sys
import urwid
+
from netlib import odict
+from netlib.http.semantics import CONTENT_MISSING
+
from . import common, grideditor, contentview, signals, searchable, tabs
from . import flowdetailview
from .. import utils, controller
-from ..protocol.http import HTTPRequest, HTTPResponse, CONTENT_MISSING, decoded
+from ..protocol.http import HTTPRequest, HTTPResponse, decoded
class SearchError(Exception):
diff --git a/libmproxy/dump.py b/libmproxy/dump.py
index ee8c65a0..bf409803 100644
--- a/libmproxy/dump.py
+++ b/libmproxy/dump.py
@@ -2,7 +2,10 @@ from __future__ import absolute_import, print_function
import json
import sys
import os
+
+from netlib.http.semantics import CONTENT_MISSING
import netlib.utils
+
from . import flow, filt, utils
from .protocol import http
@@ -173,7 +176,7 @@ class DumpMaster(flow.FlowMaster):
if self.o.flow_detail >= 2:
print(self.indent(4, message.headers.format()), file=self.outfile)
if self.o.flow_detail >= 3:
- if message.content == http.CONTENT_MISSING:
+ if message.content == CONTENT_MISSING:
print(self.indent(4, "(content missing)"), file=self.outfile)
elif message.content:
print("", file=self.outfile)
@@ -210,7 +213,7 @@ class DumpMaster(flow.FlowMaster):
self._print_message(f.request)
if f.response:
- if f.response.content == http.CONTENT_MISSING:
+ if f.response.content == CONTENT_MISSING:
sz = "(content missing)"
else:
sz = netlib.utils.pretty_size(len(f.response.content))
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index 4b725ae5..82a25461 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -8,8 +8,11 @@ import Cookie
import cookielib
import os
import re
+
from netlib import odict, wsgi, tcp
+from netlib.http.semantics import CONTENT_MISSING
import netlib.http
+
from . import controller, protocol, tnetstring, filt, script, version
from .onboarding import app
from .protocol import http, handle
@@ -921,7 +924,7 @@ class FlowMaster(controller.Master):
return "Can't replay live request."
if f.intercepted:
return "Can't replay while intercepting..."
- if f.request.content == http.CONTENT_MISSING:
+ if f.request.content == CONTENT_MISSING:
return "Can't replay request with missing content..."
if f.request:
f.backup()
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py
index e0deadd5..7f1aa78b 100644
--- a/libmproxy/protocol/http.py
+++ b/libmproxy/protocol/http.py
@@ -10,6 +10,7 @@ from email.utils import parsedate_tz, formatdate, mktime_tz
import netlib
from netlib import http, tcp, odict, utils
from netlib.http import cookies, http1
+from netlib.http.semantics import CONTENT_MISSING
from .tcp import TCPHandler
from .primitives import KILL, ProtocolHandler, Flow, Error
@@ -20,7 +21,6 @@ from .http_wrappers import decoded, HTTPRequest, HTTPResponse
HDR_FORM_URLENCODED = "application/x-www-form-urlencoded"
HDR_FORM_MULTIPART = "multipart/form-data"
-CONTENT_MISSING = 0
class KillSignal(Exception):
@@ -39,14 +39,14 @@ def send_connect_request(conn, host, port, update_state=True):
odict.ODictCaseless(),
""
)
- conn.send(upstream_request.assemble())
protocol = http.http1.HTTP1Protocol(conn)
+ conn.send(protocol.assemble(upstream_request))
resp = HTTPResponse.from_protocol(protocol, upstream_request.method)
if resp.status_code != 200:
raise proxy.ProxyError(resp.status_code,
"Cannot establish SSL " +
"connection with upstream proxy: \r\n" +
- str(resp.assemble()))
+ repr(resp))
if update_state:
conn.state.append(("http", {
"state": "connect",
@@ -174,16 +174,15 @@ class HTTPHandler(ProtocolHandler):
def get_response_from_server(self, flow):
self.c.establish_server_connection()
- request_raw = flow.request.assemble()
for attempt in (0, 1):
try:
- self.c.server_conn.send(request_raw)
+ flow.server_protocol = http.http1.HTTP1Protocol(self.c.server_conn)
+ self.c.server_conn.send(flow.server_protocol.assemble(flow.request))
# Only get the headers at first...
- protocol = http.http1.HTTP1Protocol(self.c.server_conn)
flow.response = HTTPResponse.from_protocol(
- protocol,
+ flow.server_protocol,
flow.request.method,
body_size_limit=self.c.config.body_size_limit,
include_body=False
@@ -221,8 +220,8 @@ class HTTPHandler(ProtocolHandler):
if flow.response.stream:
flow.response.content = CONTENT_MISSING
else:
- protocol = http1.HTTP1Protocol(self.c.server_conn)
- flow.response.content = protocol.read_http_body(
+ flow.server_protocol = http1.HTTP1Protocol(self.c.server_conn)
+ flow.response.content = flow.server_protocol.read_http_body(
flow.response.headers,
self.c.config.body_size_limit,
flow.request.method,
@@ -235,9 +234,9 @@ class HTTPHandler(ProtocolHandler):
flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.live)
try:
try:
- protocol = http.http1.HTTP1Protocol(self.c.client_conn)
+ flow.client_protocol = http.http1.HTTP1Protocol(self.c.client_conn)
req = HTTPRequest.from_protocol(
- protocol,
+ flow.client_protocol,
body_size_limit=self.c.config.body_size_limit
)
except tcp.NetLibError:
@@ -247,7 +246,7 @@ class HTTPHandler(ProtocolHandler):
self.c.log(
"request",
"debug",
- [req._assemble_first_line(req.form_in)]
+ [repr(req)]
)
ret = self.process_request(flow, req)
if ret is not None:
@@ -276,8 +275,10 @@ class HTTPHandler(ProtocolHandler):
flow.server_conn = self.c.server_conn
self.c.log(
- "response", "debug", [
- flow.response._assemble_first_line()])
+ "response",
+ "debug",
+ [repr(flow.response)]
+ )
response_reply = self.c.channel.ask("response", flow)
if response_reply is None or response_reply == KILL:
raise KillSignal()
@@ -553,30 +554,33 @@ class HTTPHandler(ProtocolHandler):
# no streaming:
# we already received the full response from the server and can
# send it to the client straight away.
- self.c.client_conn.send(flow.response.assemble())
+ self.c.client_conn.send(flow.client_protocol.assemble(flow.response))
else:
+ raise NotImplementedError("HTTP streaming is currently not supported.")
+ # TODO: implement it according to new protocols and messages
+
# streaming:
# First send the headers and then transfer the response
# incrementally:
- h = flow.response._assemble_head(preserve_transfer_encoding=True)
- self.c.client_conn.send(h)
-
- protocol = http1.HTTP1Protocol(rfile=self.c.server_conn.rfile)
- chunks = protocol.read_http_body_chunked(
- flow.response.headers,
- self.c.config.body_size_limit,
- flow.request.method,
- flow.response.code,
- False,
- 4096
- )
- if callable(flow.response.stream):
- chunks = flow.response.stream(chunks)
- for chunk in chunks:
- for part in chunk:
- self.c.client_conn.wfile.write(part)
- self.c.client_conn.wfile.flush()
- flow.response.timestamp_end = utils.timestamp()
+ # h = flow.response._assemble_head(preserve_transfer_encoding=True)
+ # self.c.client_conn.send(h)
+ #
+ # protocol = http1.HTTP1Protocol(rfile=self.c.server_conn.rfile)
+ # chunks = protocol.read_http_body_chunked(
+ # flow.response.headers,
+ # self.c.config.body_size_limit,
+ # flow.request.method,
+ # flow.response.code,
+ # False,
+ # 4096
+ # )
+ # if callable(flow.response.stream):
+ # chunks = flow.response.stream(chunks)
+ # for chunk in chunks:
+ # for part in chunk:
+ # self.c.client_conn.wfile.write(part)
+ # self.c.client_conn.wfile.flush()
+ # flow.response.timestamp_end = utils.timestamp()
def check_close_connection(self, flow):
"""
@@ -726,12 +730,13 @@ class RequestReplayThread(threading.Thread):
sni=self.flow.server_conn.sni
)
r.form_out = "relative"
- server.send(r.assemble())
+
+ server.send(self.flow.server_protocol.assemble(r))
self.flow.server_conn = server
- protocol = http.http1.HTTP1Protocol(server)
+ self.flow.server_protocol = http.http1.HTTP1Protocol(self.flow.server_conn)
self.flow.response = HTTPResponse.from_protocol(
- protocol,
+ self.flow.server_protocol,
r.method,
body_size_limit=self.config.body_size_limit,
)
diff --git a/libmproxy/protocol/http_wrappers.py b/libmproxy/protocol/http_wrappers.py
index 7d3e3706..18a355dc 100644
--- a/libmproxy/protocol/http_wrappers.py
+++ b/libmproxy/protocol/http_wrappers.py
@@ -108,17 +108,6 @@ class MessageMixin(stateobject.StateObject):
self.body = encoding.encode(e, self.body)
self.headers["content-encoding"] = [e]
- def size(self, **kwargs):
- """
- Size in bytes of a fully rendered message, including headers and
- HTTP lead-in.
- """
- hl = len(self._assemble_head(**kwargs))
- if self.body:
- return hl + len(self.body)
- else:
- return hl
-
def copy(self):
c = copy.copy(self)
c.headers = self.headers.copy()
@@ -139,30 +128,6 @@ class MessageMixin(stateobject.StateObject):
c += self.headers.replace(pattern, repl, *args, **kwargs)
return c
- def _assemble_first_line(self):
- """
- Returns the assembled request/response line
- """
- raise NotImplementedError() # pragma: nocover
-
- def _assemble_headers(self):
- """
- Returns the assembled headers
- """
- raise NotImplementedError() # pragma: nocover
-
- def _assemble_head(self):
- """
- Returns the assembled request/response line plus headers
- """
- raise NotImplementedError() # pragma: nocover
-
- def assemble(self):
- """
- Returns the assembled request/response
- """
- raise NotImplementedError() # pragma: nocover
-
class HTTPRequest(MessageMixin, semantics.Request):
"""
@@ -286,7 +251,8 @@ class HTTPRequest(MessageMixin, semantics.Request):
def __repr__(self):
return "<HTTPRequest: {0}>".format(
- self._assemble_first_line(self.form_in)[:-9]
+ # just for visualisation purposes we use HTTP/1 protocol here
+ http.http1.HTTP1Protocol._assemble_request_first_line(self)[:-9]
)
@classmethod
@@ -315,66 +281,6 @@ class HTTPRequest(MessageMixin, semantics.Request):
req.timestamp_end,
)
- def _assemble_first_line(self, form=None):
- form = form or self.form_out
-
- if form == "relative":
- request_line = '%s %s HTTP/%s.%s' % (
- self.method, self.path, self.httpversion[0], self.httpversion[1]
- )
- elif form == "authority":
- request_line = '%s %s:%s HTTP/%s.%s' % (
- self.method, self.host, self.port, self.httpversion[0],
- self.httpversion[1]
- )
- elif form == "absolute":
- request_line = '%s %s://%s:%s%s HTTP/%s.%s' % (
- self.method, self.scheme, self.host,
- self.port, self.path, self.httpversion[0],
- self.httpversion[1]
- )
- else:
- raise http.HttpError(400, "Invalid request form")
- return request_line
-
- def _assemble_headers(self):
- headers = self.headers.copy()
- for k in self._headers_to_strip_off:
- del headers[k]
- if 'host' not in headers and self.scheme and self.host and self.port:
- headers["Host"] = [utils.hostport(self.scheme,
- self.host,
- self.port)]
-
- # If content is defined (i.e. not None or CONTENT_MISSING), we always
- # add a content-length header.
- if self.body or self.body == "":
- headers["Content-Length"] = [str(len(self.body))]
-
- return headers.format()
-
- def _assemble_head(self, form=None):
- return "%s\r\n%s\r\n" % (
- self._assemble_first_line(form), self._assemble_headers()
- )
-
- def assemble(self, form=None):
- """
- Assembles the request for transmission to the server. We make some
- modifications to make sure interception works properly.
-
- Raises an Exception if the request cannot be assembled.
- """
- if self.body == CONTENT_MISSING:
- raise proxy.ProxyError(
- 502,
- "Cannot assemble flow with CONTENT_MISSING"
- )
- head = self._assemble_head(form)
- if self.body:
- return head + self.body
- else:
- return head
def __hash__(self):
return id(self)
@@ -699,50 +605,6 @@ class HTTPResponse(MessageMixin, semantics.Response):
resp.timestamp_end,
)
- def _assemble_first_line(self):
- return 'HTTP/%s.%s %s %s' % \
- (self.httpversion[0], self.httpversion[1], self.code, self.msg)
-
- def _assemble_headers(self, preserve_transfer_encoding=False):
- headers = self.headers.copy()
- for k in self._headers_to_strip_off:
- del headers[k]
- if not preserve_transfer_encoding:
- del headers['Transfer-Encoding']
-
- # If body is defined (i.e. not None or CONTENT_MISSING), we always
- # add a content-length header.
- if self.body or self.body == "":
- headers["Content-Length"] = [str(len(self.body))]
-
- return headers.format()
-
- def _assemble_head(self, preserve_transfer_encoding=False):
- return '%s\r\n%s\r\n' % (
- self._assemble_first_line(),
- self._assemble_headers(
- preserve_transfer_encoding=preserve_transfer_encoding
- )
- )
-
- def assemble(self):
- """
- Assembles the response for transmission to the client. We make some
- modifications to make sure interception works properly.
-
- Raises an Exception if the request cannot be assembled.
- """
- if self.body == CONTENT_MISSING:
- raise proxy.ProxyError(
- 502,
- "Cannot assemble flow with CONTENT_MISSING"
- )
- head = self._assemble_head()
- if self.body:
- return head + self.body
- else:
- return head
-
def _refresh_cookie(self, c, delta):
"""
Takes a cookie string c and a time delta in seconds, and returns
diff --git a/libmproxy/utils.py b/libmproxy/utils.py
index a29a53f5..78f74767 100644
--- a/libmproxy/utils.py
+++ b/libmproxy/utils.py
@@ -8,6 +8,7 @@ import functools
import cgi
import json
+import netlib.utils
def timestamp():
"""
@@ -195,21 +196,12 @@ def parse_content_type(c):
return ts[0].lower(), ts[1].lower(), d
-def hostport(scheme, host, port):
- """
- Returns the host component, with a port specifcation if needed.
- """
- if (port, scheme) in [(80, "http"), (443, "https")]:
- return host
- else:
- return "%s:%s" % (host, port)
-
def unparse_url(scheme, host, port, path=""):
"""
Returns a URL string, constructed from the specified compnents.
"""
- return "%s://%s%s" % (scheme, hostport(scheme, host, port), path)
+ return "%s://%s%s" % (scheme, netlib.utils.hostport(scheme, host, port), path)
def clean_hanging_newline(t):