From 0af060897854e0e0fc8207af02d22bec9eacab12 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Wed, 16 Sep 2015 18:45:22 +0200 Subject: adjust to netlib changes --- libmproxy/cmdline.py | 8 +- libmproxy/console/common.py | 2 +- libmproxy/console/flowview.py | 2 +- libmproxy/dump.py | 2 +- libmproxy/exceptions.py | 8 +- libmproxy/flow.py | 12 +-- libmproxy/models/http.py | 25 +++-- libmproxy/protocol/http.py | 191 ++++++++++++++++---------------------- libmproxy/protocol/http_replay.py | 31 ++++--- libmproxy/protocol/tls.py | 6 +- libmproxy/proxy/root_context.py | 7 +- libmproxy/proxy/server.py | 4 +- libmproxy/web/app.py | 6 +- 13 files changed, 137 insertions(+), 167 deletions(-) (limited to 'libmproxy') diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py index 3779953f..16678486 100644 --- a/libmproxy/cmdline.py +++ b/libmproxy/cmdline.py @@ -103,11 +103,15 @@ def parse_setheader(s): def parse_server_spec(url): - p = netlib.utils.parse_url(url) - if not p or not p[1] or p[0] not in ("http", "https"): + try: + p = netlib.utils.parse_url(url) + if p[0] not in ("http", "https"): + raise ValueError() + except ValueError: raise configargparse.ArgumentTypeError( "Invalid server specification: %s" % url ) + address = Address(p[1:3]) scheme = p[0].lower() return config.ServerSpec(scheme, address) diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py index ae3dd61e..13374b25 100644 --- a/libmproxy/console/common.py +++ b/libmproxy/console/common.py @@ -4,7 +4,7 @@ import urwid import urwid.util import os -from netlib.http.semantics import CONTENT_MISSING +from netlib.http import CONTENT_MISSING import netlib.utils from .. import utils diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py index 3e13fab4..8220c1e7 100644 --- a/libmproxy/console/flowview.py +++ b/libmproxy/console/flowview.py @@ -6,7 +6,7 @@ import sys import urwid from netlib import odict -from netlib.http.semantics import CONTENT_MISSING, Headers +from netlib.http import CONTENT_MISSING, Headers from . import common, grideditor, signals, searchable, tabs from . import flowdetailview from .. import utils, controller, contentviews diff --git a/libmproxy/dump.py b/libmproxy/dump.py index 9fc9e1b8..3915d4c8 100644 --- a/libmproxy/dump.py +++ b/libmproxy/dump.py @@ -6,7 +6,7 @@ import traceback import click import itertools -from netlib.http.semantics import CONTENT_MISSING +from netlib.http import CONTENT_MISSING import netlib.utils from . import flow, filt, contentviews from .exceptions import ContentViewException diff --git a/libmproxy/exceptions.py b/libmproxy/exceptions.py index d916f457..b55201be 100644 --- a/libmproxy/exceptions.py +++ b/libmproxy/exceptions.py @@ -34,17 +34,17 @@ class Socks5Exception(ProtocolException): pass -class HttpException(ProtocolException): +class HttpProtocolException(ProtocolException): pass -class InvalidCredentials(HttpException): +class ServerException(ProxyException): pass -class ServerException(ProxyException): +class ContentViewException(ProxyException): pass -class ContentViewException(ProxyException): +class ReplayException(ProxyException): pass diff --git a/libmproxy/flow.py b/libmproxy/flow.py index d037d36e..d735b9ec 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -12,7 +12,8 @@ import urlparse from netlib import wsgi -from netlib.http.semantics import CONTENT_MISSING, Headers +from netlib.exceptions import HttpException +from netlib.http import CONTENT_MISSING, Headers, http1 import netlib.http from . import controller, tnetstring, filt, script, version from .onboarding import app @@ -161,9 +162,8 @@ class StreamLargeBodies(object): def run(self, flow, is_request): r = flow.request if is_request else flow.response - code = flow.response.code if flow.response else None - expected_size = netlib.http.http1.HTTP1Protocol.expected_http_body_size( - r.headers, is_request, flow.request.method, code + expected_size = http1.expected_http_body_size( + flow.request, flow.response if not is_request else None ) if not (0 <= expected_size <= self.max_size): # r.stream may already be a callable, which we want to preserve. @@ -842,7 +842,7 @@ class FlowMaster(controller.Master): host, port, path, - (1, 1), + b"HTTP/1.1", headers, None, None, @@ -1000,7 +1000,7 @@ class FlowMaster(controller.Master): try: if self.stream_large_bodies: self.stream_large_bodies.run(f, False) - except netlib.http.HttpError: + except HttpException: f.reply(Kill) return diff --git a/libmproxy/models/http.py b/libmproxy/models/http.py index 0d5e53b5..0769d2d0 100644 --- a/libmproxy/models/http.py +++ b/libmproxy/models/http.py @@ -6,18 +6,17 @@ import time from libmproxy import utils from netlib import encoding -from netlib.http import status_codes, Headers +from netlib.http import status_codes, Headers, Request, Response, CONTENT_MISSING from netlib.tcp import Address -from netlib.http.semantics import Request, Response, CONTENT_MISSING from .. import version, stateobject from .flow import Flow class MessageMixin(stateobject.StateObject): _stateobject_attributes = dict( - httpversion=tuple, + httpversion=bytes, headers=Headers, - body=str, + body=bytes, timestamp_start=float, timestamp_end=float ) @@ -120,7 +119,7 @@ class HTTPRequest(MessageMixin, Request): path: Path portion of the URL (not present in authority-form) - httpversion: HTTP version tuple, e.g. (1,1) + httpversion: HTTP version, e.g. "HTTP/1.1" headers: Headers object @@ -186,11 +185,11 @@ class HTTPRequest(MessageMixin, Request): _stateobject_attributes = MessageMixin._stateobject_attributes.copy() _stateobject_attributes.update( form_in=str, - method=str, - scheme=str, - host=str, + method=bytes, + scheme=bytes, + host=bytes, port=int, - path=str, + path=bytes, form_out=str, is_replay=bool ) @@ -267,7 +266,7 @@ class HTTPResponse(MessageMixin, Response): Exposes the following attributes: - httpversion: HTTP version tuple, e.g. (1, 0), (1, 1), or (2, 0) + httpversion: HTTP version, e.g. "HTTP/1.1" status_code: HTTP response status code @@ -312,7 +311,7 @@ class HTTPResponse(MessageMixin, Response): _stateobject_attributes = MessageMixin._stateobject_attributes.copy() _stateobject_attributes.update( status_code=int, - msg=str + msg=bytes ) @classmethod @@ -532,7 +531,7 @@ def make_error_response(status_code, message, headers=None): ) return HTTPResponse( - (1, 1), # FIXME: Should be a string. + b"HTTP/1.1", status_code, response, headers, @@ -543,7 +542,7 @@ def make_error_response(status_code, message, headers=None): def make_connect_request(address): address = Address.wrap(address) return HTTPRequest( - "authority", "CONNECT", None, address.host, address.port, None, (1, 1), + "authority", "CONNECT", None, address.host, address.port, None, b"HTTP/1.1", Headers(), "" ) diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index 230f2be9..a876df41 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -6,14 +6,14 @@ import traceback import six from netlib import tcp -from netlib.http import http1, HttpErrorConnClosed, HttpError, Headers -from netlib.http.semantics import CONTENT_MISSING +from netlib.exceptions import HttpException, HttpReadDisconnect +from netlib.http import http1, Headers +from netlib.http import CONTENT_MISSING from netlib.tcp import NetLibError, Address -from netlib.http.http1 import HTTP1Protocol -from netlib.http.http2 import HTTP2Protocol +from netlib.http.http2.connections import HTTP2Protocol from netlib.http.http2.frame import GoAwayFrame, PriorityFrame, WindowUpdateFrame from .. import utils -from ..exceptions import InvalidCredentials, HttpException, ProtocolException +from ..exceptions import HttpProtocolException, ProtocolException from ..models import ( HTTPFlow, HTTPRequest, HTTPResponse, make_error_response, make_connect_response, Error ) @@ -45,14 +45,14 @@ class _StreamingHttpLayer(_HttpLayer): def read_response_headers(self): raise NotImplementedError - def read_response_body(self, headers, request_method, response_code, max_chunk_size=None): + def read_response_body(self, request, response): raise NotImplementedError() yield "this is a generator" # pragma: no cover - def read_response(self, request_method): + def read_response(self, request): response = self.read_response_headers() - response.body = "".join( - self.read_response_body(response.headers, request_method, response.code) + response.body = b"".join( + self.read_response_body(request, response) ) return response @@ -64,7 +64,7 @@ class _StreamingHttpLayer(_HttpLayer): def send_response(self, response): if response.body == CONTENT_MISSING: - raise HttpError(502, "Cannot assemble flow with CONTENT_MISSING") + raise HttpException("Cannot assemble flow with CONTENT_MISSING") self.send_response_headers(response) self.send_response_body(response, [response.body]) @@ -73,48 +73,31 @@ class Http1Layer(_StreamingHttpLayer): def __init__(self, ctx, mode): super(Http1Layer, self).__init__(ctx) self.mode = mode - self.client_protocol = HTTP1Protocol(self.client_conn) - self.server_protocol = HTTP1Protocol(self.server_conn) def read_request(self): - return HTTPRequest.from_protocol( - self.client_protocol, - body_size_limit=self.config.body_size_limit - ) + req = http1.read_request(self.client_conn.rfile, body_size_limit=self.config.body_size_limit) + return HTTPRequest.wrap(req) def send_request(self, request): - self.server_conn.send(self.server_protocol.assemble(request)) + self.server_conn.wfile.write(http1.assemble_request(request)) + self.server_conn.wfile.flush() def read_response_headers(self): - return HTTPResponse.from_protocol( - self.server_protocol, - request_method=None, # does not matter if we don't read the body. - body_size_limit=self.config.body_size_limit, - include_body=False - ) + resp = http1.read_response_head(self.server_conn.rfile) + return HTTPResponse.wrap(resp) - def read_response_body(self, headers, request_method, response_code, max_chunk_size=None): - return self.server_protocol.read_http_body_chunked( - headers, - self.config.body_size_limit, - request_method, - response_code, - False, - max_chunk_size - ) + def read_response_body(self, request, response): + expected_size = http1.expected_http_body_size(request, response) + return http1.read_body(self.server_conn.rfile, expected_size, self.config.body_size_limit) def send_response_headers(self, response): - h = self.client_protocol._assemble_response_first_line(response) - self.client_conn.wfile.write(h + "\r\n") - h = self.client_protocol._assemble_response_headers( - response, - preserve_transfer_encoding=True - ) - self.client_conn.wfile.write(h + "\r\n") + raw = http1.assemble_response_head(response, preserve_transfer_encoding=True) + self.client_conn.wfile.write(raw) self.client_conn.wfile.flush() def send_response_body(self, response, chunks): - if self.client_protocol.has_chunked_encoding(response.headers): + if b"chunked" in response.headers.get(b"transfer-encoding", b"").lower(): + # TODO: Move this into netlib.http.http1 chunks = itertools.chain( ( "{:x}\r\n{}\r\n".format(len(chunk), chunk) @@ -127,36 +110,24 @@ class Http1Layer(_StreamingHttpLayer): self.client_conn.wfile.flush() def check_close_connection(self, flow): - close_connection = ( - http1.HTTP1Protocol.connection_close( - flow.request.httpversion, - flow.request.headers - ) or http1.HTTP1Protocol.connection_close( - flow.response.httpversion, - flow.response.headers - ) or http1.HTTP1Protocol.expected_http_body_size( - flow.response.headers, - False, - flow.request.method, - flow.response.code) == -1 + request_close = http1.connection_close( + flow.request.httpversion, + flow.request.headers ) + response_close = http1.connection_close( + flow.response.httpversion, + flow.response.headers + ) + read_until_eof = http1.expected_http_body_size(flow.request, flow.response) == -1 + close_connection = request_close or response_close or read_until_eof if flow.request.form_in == "authority" and flow.response.code == 200: - # Workaround for - # https://github.com/mitmproxy/mitmproxy/issues/313: Some - # proxies (e.g. Charles) send a CONNECT response with HTTP/1.0 + # Workaround for https://github.com/mitmproxy/mitmproxy/issues/313: + # Charles Proxy sends a CONNECT response with HTTP/1.0 # and no Content-Length header return False return close_connection - def connect(self): - self.ctx.connect() - self.server_protocol = HTTP1Protocol(self.server_conn) - - def set_server(self, *args, **kwargs): - self.ctx.set_server(*args, **kwargs) - self.server_protocol = HTTP1Protocol(self.server_conn) - def __call__(self): layer = HttpLayer(self, self.mode) layer() @@ -184,10 +155,10 @@ class Http2Layer(_HttpLayer): # TODO: implement flow control and WINDOW_UPDATE frames self.server_conn.send(self.server_protocol.assemble(message)) - def read_response(self, request_method): + def read_response(self, request): return HTTPResponse.from_protocol( self.server_protocol, - request_method=request_method, + request_method=request.method, body_size_limit=self.config.body_size_limit, include_body=True, stream_id=self._stream_id @@ -295,7 +266,7 @@ class UpstreamConnectLayer(Layer): def _send_connect_request(self): self.send_request(self.connect_request) - resp = self.read_response("CONNECT") + resp = self.read_response(self.connect_request) if resp.code != 200: raise ProtocolException("Reconnect: Upstream server refuses CONNECT request") @@ -337,28 +308,31 @@ class HttpLayer(Layer): self.__original_server_conn = self.server_conn while True: try: - flow = HTTPFlow(self.client_conn, self.server_conn, live=self) + request = self.read_request() + self.log("request", "debug", [repr(request)]) - try: - request = self.read_request() - except tcp.NetLibError: - # don't throw an error for disconnects that happen - # before/between requests. + # Handle Proxy Authentication + if not self.authenticate(request): return - self.log("request", "debug", [repr(request)]) + # Make sure that the incoming request matches our expectations + self.validate_request(request) - # Handle Proxy Authentication - self.authenticate(request) + except HttpReadDisconnect: + # don't throw an error for disconnects that happen before/between requests. + return + except (HttpException, NetLibError) as e: + self.send_error_response(400, repr(e)) + six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) + + try: + flow = HTTPFlow(self.client_conn, self.server_conn, live=self) # Regular Proxy Mode: Handle CONNECT if self.mode == "regular" and request.form_in == "authority": self.handle_regular_mode_connect(request) return - # Make sure that the incoming request matches our expectations - self.validate_request(request) - flow.request = request self.process_request_hook(flow) @@ -384,30 +358,26 @@ class HttpLayer(Layer): self.handle_upstream_mode_connect(flow.request.copy()) return - except (HttpErrorConnClosed, NetLibError, HttpError, ProtocolException) as e: - error_propagated = False - if flow.request and not flow.response: + except (HttpException, NetLibError) as e: + self.send_error_response(502, repr(e)) + + if not flow.response: flow.error = Error(str(e)) self.channel.ask("error", flow) self.log(traceback.format_exc(), "debug") - error_propagated = True - - try: - self.send_response(make_error_response( - getattr(e, "code", 502), - repr(e) - )) - except NetLibError: - pass - - if not error_propagated: - if isinstance(e, ProtocolException): - six.reraise(ProtocolException, e, sys.exc_info()[2]) - else: - six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) + return + else: + six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) finally: flow.live = False + def send_error_response(self, code, message): + try: + response = make_error_response(code, message) + self.send_response(response) + except NetLibError: + pass + def change_upstream_proxy_server(self, address): # Make set_upstream_proxy_server always available, # even if there's no UpstreamConnectLayer @@ -435,10 +405,8 @@ class HttpLayer(Layer): # First send the headers and then transfer the response incrementally self.send_response_headers(flow.response) chunks = self.read_response_body( - flow.response.headers, - flow.request.method, - flow.response.code, - max_chunk_size=4096 + flow.request, + flow.response ) if callable(flow.response.stream): chunks = flow.response.stream(chunks) @@ -451,11 +419,11 @@ class HttpLayer(Layer): if self.supports_streaming: flow.response = self.read_response_headers() else: - flow.response = self.read_response(flow.request.method) + flow.response = self.read_response(flow.request) try: get_response() - except (tcp.NetLibError, HttpErrorConnClosed) as v: + except (tcp.NetLibError, HttpException) as v: self.log( "server communication error: %s" % repr(v), level="debug" @@ -485,10 +453,9 @@ class HttpLayer(Layer): if flow.response.stream: flow.response.content = CONTENT_MISSING else: - flow.response.content = "".join(self.read_response_body( - flow.response.headers, - flow.request.method, - flow.response.code + flow.response.content = b"".join(self.read_response_body( + flow.request, + flow.response )) flow.response.timestamp_end = utils.timestamp() @@ -543,7 +510,7 @@ class HttpLayer(Layer): if not self.server_conn: self.connect() if tls: - raise HttpException("Cannot change scheme in upstream proxy mode.") + raise HttpProtocolException("Cannot change scheme in upstream proxy mode.") """ # This is a very ugly (untested) workaround to solve a very ugly problem. if self.server_conn and self.server_conn.tls_established and not ssl: @@ -561,12 +528,10 @@ class HttpLayer(Layer): def validate_request(self, request): if request.form_in == "absolute" and request.scheme != "http": - self.send_response( - make_error_response(400, "Invalid request scheme: %s" % request.scheme)) raise HttpException("Invalid request scheme: %s" % request.scheme) expected_request_forms = { - "regular": ("absolute",), # an authority request would already be handled. + "regular": ("authority", "absolute",), "upstream": ("authority", "absolute"), "transparent": ("relative",) } @@ -576,10 +541,9 @@ class HttpLayer(Layer): err_message = "Invalid HTTP request form (expected: %s, got: %s)" % ( " or ".join(allowed_request_forms), request.form_in ) - self.send_response(make_error_response(400, err_message)) raise HttpException(err_message) - if self.mode == "regular": + if self.mode == "regular" and request.form_in == "absolute": request.form_out = "relative" def authenticate(self, request): @@ -592,4 +556,5 @@ class HttpLayer(Layer): "Proxy Authentication Required", Headers(**self.config.authenticator.auth_challenge_headers()) )) - raise InvalidCredentials("Proxy Authentication Required") + return False + return True diff --git a/libmproxy/protocol/http_replay.py b/libmproxy/protocol/http_replay.py index a9ee5506..9d61d75c 100644 --- a/libmproxy/protocol/http_replay.py +++ b/libmproxy/protocol/http_replay.py @@ -1,8 +1,9 @@ from __future__ import (absolute_import, print_function, division) import threading +from libmproxy.exceptions import ReplayException +from netlib.exceptions import HttpException +from netlib.http import http1 -from netlib.http import HttpError -from netlib.http.http1 import HTTP1Protocol from netlib.tcp import NetLibError from ..controller import Channel from ..models import Error, HTTPResponse, ServerConnection, make_connect_request @@ -47,13 +48,17 @@ class RequestReplayThread(threading.Thread): server_address = self.config.upstream_server.address server = ServerConnection(server_address) server.connect() - protocol = HTTP1Protocol(server) if r.scheme == "https": connect_request = make_connect_request((r.host, r.port)) - server.send(protocol.assemble(connect_request)) - resp = protocol.read_response("CONNECT") + server.wfile.write(http1.assemble_request(connect_request)) + server.wfile.flush() + resp = http1.read_response( + server.rfile, + connect_request, + body_size_limit=self.config.body_size_limit + ) if resp.code != 200: - raise HttpError(502, "Upstream server refuses CONNECT request") + raise ReplayException("Upstream server refuses CONNECT request") server.establish_ssl( self.config.clientcerts, sni=self.flow.server_conn.sni @@ -65,7 +70,6 @@ class RequestReplayThread(threading.Thread): server_address = (r.host, r.port) server = ServerConnection(server_address) server.connect() - protocol = HTTP1Protocol(server) if r.scheme == "https": server.establish_ssl( self.config.clientcerts, @@ -73,18 +77,19 @@ class RequestReplayThread(threading.Thread): ) r.form_out = "relative" - server.send(protocol.assemble(r)) + server.wfile.write(http1.assemble_request(r)) + server.wfile.flush() self.flow.server_conn = server - self.flow.response = HTTPResponse.from_protocol( - protocol, - r.method, - body_size_limit=self.config.body_size_limit, + self.flow.response = http1.read_response( + server.rfile, + r, + body_size_limit=self.config.body_size_limit ) if self.channel: response_reply = self.channel.ask("response", self.flow) if response_reply == Kill: raise Kill() - except (HttpError, NetLibError) as v: + except (ReplayException, HttpException, NetLibError) as v: self.flow.error = Error(repr(v)) if self.channel: self.channel.ask("error", self.flow) diff --git a/libmproxy/protocol/tls.py b/libmproxy/protocol/tls.py index 2935ca9f..cf303ca1 100644 --- a/libmproxy/protocol/tls.py +++ b/libmproxy/protocol/tls.py @@ -7,7 +7,7 @@ from construct import ConstructError import six from netlib.tcp import NetLibError, NetLibInvalidCertificateError -from netlib.http.http1 import HTTP1Protocol +from netlib.http import ALPN_PROTO_HTTP1 from ..contrib.tls._constructs import ClientHello from ..exceptions import ProtocolException, TlsException, ClientHandshakeException from .base import Layer @@ -367,8 +367,8 @@ class TlsLayer(Layer): """ # This gets triggered if we haven't established an upstream connection yet. - default_alpn = HTTP1Protocol.ALPN_PROTO_HTTP1 - # alpn_preference = netlib.http.http2.HTTP2Protocol.ALPN_PROTO_H2 + default_alpn = ALPN_PROTO_HTTP1 + # alpn_preference = ALPN_PROTO_H2 if self.alpn_for_client_connection in options: choice = bytes(self.alpn_for_client_connection) diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index 54bea1db..72243c59 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -5,8 +5,7 @@ import sys import six from libmproxy.exceptions import ProtocolException -from netlib.http.http1 import HTTP1Protocol -from netlib.http.http2 import HTTP2Protocol +from netlib.http import ALPN_PROTO_H2, ALPN_PROTO_HTTP1 from netlib.tcp import NetLibError from ..protocol import ( RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin @@ -85,9 +84,9 @@ class RootContext(object): # 5. Check for TLS ALPN (HTTP1/HTTP2) if isinstance(top_layer, TlsLayer): alpn = top_layer.client_conn.get_alpn_proto_negotiated() - if alpn == HTTP2Protocol.ALPN_PROTO_H2: + if alpn == ALPN_PROTO_H2: return Http2Layer(top_layer, 'transparent') - if alpn == HTTP1Protocol.ALPN_PROTO_HTTP1: + if alpn == ALPN_PROTO_HTTP1: return Http1Layer(top_layer, 'transparent') # 6. Check for raw tcp mode diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 88448172..8b286458 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -6,7 +6,7 @@ import socket import six from netlib import tcp -from netlib.http.http1 import HTTP1Protocol +from netlib.http.http1 import assemble_response from netlib.tcp import NetLibError from ..exceptions import ProtocolException, ServerException, ClientHandshakeException from ..protocol import Kill @@ -138,7 +138,7 @@ class ConnectionHandler(object): # understandable by HTTP clients and humans. try: error_response = make_error_response(502, repr(e)) - self.client_conn.send(HTTP1Protocol().assemble(error_response)) + self.client_conn.send(assemble_response(error_response)) except NetLibError: pass except Exception: diff --git a/libmproxy/web/app.py b/libmproxy/web/app.py index 2517e7ad..8eee6dce 100644 --- a/libmproxy/web/app.py +++ b/libmproxy/web/app.py @@ -128,12 +128,10 @@ class FlowHandler(RequestHandler): if a == "request": request = flow.request for k, v in b.iteritems(): - if k in ["method", "scheme", "host", "path"]: + if k in ["method", "scheme", "host", "path", "httpversion"]: setattr(request, k, str(v)) elif k == "port": request.port = int(v) - elif k == "httpversion": - request.httpversion = tuple(int(x) for x in v) elif k == "headers": request.headers.load_state(v) else: @@ -147,7 +145,7 @@ class FlowHandler(RequestHandler): elif k == "code": response.code = int(v) elif k == "httpversion": - response.httpversion = tuple(int(x) for x in v) + response.httpversion = str(v) elif k == "headers": response.headers.load_state(v) else: -- cgit v1.2.3