mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
implement Http1 and Http2 protocols as layers
This commit is contained in:
parent
c04fa1b233
commit
38c456bb62
@ -9,14 +9,41 @@ from libmproxy.protocol import KILL
|
|||||||
|
|
||||||
from libmproxy.protocol.http import HTTPFlow
|
from libmproxy.protocol.http import HTTPFlow
|
||||||
from libmproxy.protocol.http_wrappers import HTTPResponse, HTTPRequest
|
from libmproxy.protocol.http_wrappers import HTTPResponse, HTTPRequest
|
||||||
from libmproxy.protocol2.http_protocol_mock import HTTP1
|
|
||||||
from libmproxy.protocol2.tls import TlsLayer
|
from libmproxy.protocol2.tls import TlsLayer
|
||||||
from netlib import tcp
|
from netlib import tcp
|
||||||
from netlib.http import status_codes, http1, HttpErrorConnClosed
|
from netlib.http import status_codes, http1, HttpErrorConnClosed
|
||||||
from netlib.http.semantics import CONTENT_MISSING
|
from netlib.http.semantics import CONTENT_MISSING
|
||||||
from netlib import odict
|
from netlib import odict
|
||||||
from netlib.tcp import NetLibError
|
from netlib.tcp import NetLibError
|
||||||
|
from netlib.http.http1 import HTTP1Protocol
|
||||||
|
from netlib.http.http2 import HTTP2Protocol
|
||||||
|
|
||||||
|
class Http1Layer(Layer):
|
||||||
|
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 __call__(self):
|
||||||
|
from .http import HttpLayer
|
||||||
|
layer = HttpLayer(self, self.mode)
|
||||||
|
for message in layer():
|
||||||
|
yield message
|
||||||
|
|
||||||
|
|
||||||
|
class Http2Layer(Layer):
|
||||||
|
def __init__(self, ctx, mode):
|
||||||
|
super(Http2Layer, self).__init__(ctx)
|
||||||
|
self.mode = mode
|
||||||
|
self.client_protocol = HTTP2Protocol(self.client_conn, is_server=True)
|
||||||
|
self.server_protocol = HTTP2Protocol(self.server_conn, is_server=False)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
from .http import HttpLayer
|
||||||
|
layer = HttpLayer(self, self.mode)
|
||||||
|
for message in layer():
|
||||||
|
yield message
|
||||||
|
|
||||||
def make_error_response(status_code, message, headers=None):
|
def make_error_response(status_code, message, headers=None):
|
||||||
response = status_codes.RESPONSES.get(status_code, "Unknown")
|
response = status_codes.RESPONSES.get(status_code, "Unknown")
|
||||||
@ -79,8 +106,8 @@ class HttpLayer(Layer):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
request = HTTP1.read_request(
|
request = HTTPRequest.from_protocol(
|
||||||
self.client_conn,
|
self.client_protocol,
|
||||||
body_size_limit=self.config.body_size_limit
|
body_size_limit=self.config.body_size_limit
|
||||||
)
|
)
|
||||||
except tcp.NetLibError:
|
except tcp.NetLibError:
|
||||||
@ -168,12 +195,12 @@ class HttpLayer(Layer):
|
|||||||
# streaming:
|
# streaming:
|
||||||
# First send the headers and then transfer the response
|
# First send the headers and then transfer the response
|
||||||
# incrementally:
|
# incrementally:
|
||||||
h = HTTP1._assemble_response_first_line(flow.response)
|
h = self.client_protocol._assemble_response_first_line(flow.response)
|
||||||
self.send_to_client(h + "\r\n")
|
self.send_to_client(h + "\r\n")
|
||||||
h = HTTP1._assemble_response_headers(flow.response, preserve_transfer_encoding=True)
|
h = self.client_protocol._assemble_response_headers(flow.response, preserve_transfer_encoding=True)
|
||||||
self.send_to_client(h + "\r\n")
|
self.send_to_client(h + "\r\n")
|
||||||
|
|
||||||
chunks = HTTP1.read_http_body_chunked(
|
chunks = self.client_protocol.read_http_body_chunked(
|
||||||
flow.response.headers,
|
flow.response.headers,
|
||||||
self.config.body_size_limit,
|
self.config.body_size_limit,
|
||||||
flow.request.method,
|
flow.request.method,
|
||||||
@ -196,8 +223,8 @@ class HttpLayer(Layer):
|
|||||||
# TODO: Add second attempt.
|
# TODO: Add second attempt.
|
||||||
self.send_to_server(flow.request)
|
self.send_to_server(flow.request)
|
||||||
|
|
||||||
flow.response = HTTP1.read_response(
|
flow.response = HTTPResponse.from_protocol(
|
||||||
self.server_conn,
|
self.server_protocol,
|
||||||
flow.request.method,
|
flow.request.method,
|
||||||
body_size_limit=self.config.body_size_limit,
|
body_size_limit=self.config.body_size_limit,
|
||||||
include_body=False,
|
include_body=False,
|
||||||
@ -211,8 +238,8 @@ class HttpLayer(Layer):
|
|||||||
|
|
||||||
if flow.response.stream:
|
if flow.response.stream:
|
||||||
flow.response.content = CONTENT_MISSING
|
flow.response.content = CONTENT_MISSING
|
||||||
else:
|
elif isinstance(self.server_protocol, http1.HTTP1Protocol):
|
||||||
flow.response.content = HTTP1.read_http_body(
|
flow.response.content = self.server_protocol.read_http_body(
|
||||||
self.server_conn,
|
self.server_conn,
|
||||||
flow.response.headers,
|
flow.response.headers,
|
||||||
self.config.body_size_limit,
|
self.config.body_size_limit,
|
||||||
@ -329,9 +356,9 @@ class HttpLayer(Layer):
|
|||||||
raise InvalidCredentials("Proxy Authentication Required")
|
raise InvalidCredentials("Proxy Authentication Required")
|
||||||
|
|
||||||
def send_to_server(self, message):
|
def send_to_server(self, message):
|
||||||
self.server_conn.send(HTTP1.assemble(message))
|
self.server_conn.send(self.server_protocol.assemble(message))
|
||||||
|
|
||||||
def send_to_client(self, message):
|
def send_to_client(self, message):
|
||||||
# FIXME
|
# FIXME
|
||||||
# - possibly do some http2 stuff here
|
# - possibly do some http2 stuff here
|
||||||
self.client_conn.send(HTTP1.assemble(message))
|
self.client_conn.send(self.client_protocol.assemble(message))
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
"""
|
|
||||||
Temporary mock to sort out API discrepancies
|
|
||||||
"""
|
|
||||||
from libmproxy.protocol.http_wrappers import HTTPResponse, HTTPRequest
|
|
||||||
from netlib.http.http1 import HTTP1Protocol
|
|
||||||
|
|
||||||
|
|
||||||
class HTTP1(object):
|
|
||||||
@staticmethod
|
|
||||||
def read_request(connection, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
:type connection: object
|
|
||||||
"""
|
|
||||||
return HTTPRequest.from_protocol(HTTP1Protocol(connection), *args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def read_response(connection, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
:type connection: object
|
|
||||||
"""
|
|
||||||
return HTTPResponse.from_protocol(HTTP1Protocol(connection), *args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def read_http_body(connection, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
:type connection: object
|
|
||||||
"""
|
|
||||||
return HTTP1Protocol(connection).read_http_body(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _assemble_response_first_line(*args, **kwargs):
|
|
||||||
return HTTP1Protocol()._assemble_response_first_line(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _assemble_response_headers(*args, **kwargs):
|
|
||||||
return HTTP1Protocol()._assemble_response_headers(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def read_http_body_chunked(connection, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
:type connection: object
|
|
||||||
"""
|
|
||||||
return HTTP1Protocol(connection).read_http_body_chunked(*args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def assemble(*args, **kwargs):
|
|
||||||
return HTTP1Protocol().assemble(*args, **kwargs)
|
|
@ -1,12 +1,12 @@
|
|||||||
from __future__ import (absolute_import, print_function, division)
|
from __future__ import (absolute_import, print_function, division)
|
||||||
|
|
||||||
from .layer import Layer, ServerConnectionMixin
|
from .layer import Layer, ServerConnectionMixin
|
||||||
from .http import HttpLayer
|
from .http import Http1Layer, HttpLayer
|
||||||
|
|
||||||
|
|
||||||
class HttpProxy(Layer, ServerConnectionMixin):
|
class HttpProxy(Layer, ServerConnectionMixin):
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
layer = HttpLayer(self, "regular")
|
layer = Http1Layer(self, "regular")
|
||||||
for message in layer():
|
for message in layer():
|
||||||
if not self._handle_server_message(message):
|
if not self._handle_server_message(message):
|
||||||
yield message
|
yield message
|
||||||
@ -18,7 +18,7 @@ class HttpUpstreamProxy(Layer, ServerConnectionMixin):
|
|||||||
self.server_address = server_address
|
self.server_address = server_address
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
layer = HttpLayer(self, "upstream")
|
layer = Http1Layer(self, "upstream")
|
||||||
for message in layer():
|
for message in layer():
|
||||||
if not self._handle_server_message(message):
|
if not self._handle_server_message(message):
|
||||||
yield message
|
yield message
|
||||||
|
@ -64,6 +64,7 @@ class Layer(_LayerCodeCompletion):
|
|||||||
"""
|
"""
|
||||||
super(Layer, self).__init__()
|
super(Layer, self).__init__()
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
|
print("%s -> %s" % (repr(ctx), repr(self)))
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import (absolute_import, print_function, division)
|
|||||||
|
|
||||||
from .rawtcp import RawTcpLayer
|
from .rawtcp import RawTcpLayer
|
||||||
from .tls import TlsLayer
|
from .tls import TlsLayer
|
||||||
from .http import HttpLayer
|
from .http import Http1Layer, Http2Layer, HttpLayer
|
||||||
|
|
||||||
class RootContext(object):
|
class RootContext(object):
|
||||||
"""
|
"""
|
||||||
@ -34,13 +34,20 @@ class RootContext(object):
|
|||||||
d[2] in ('\x00', '\x01', '\x02', '\x03')
|
d[2] in ('\x00', '\x01', '\x02', '\x03')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: build is_http2_magic check here, maybe this is an easy way to detect h2c
|
||||||
|
|
||||||
if not d:
|
if not d:
|
||||||
return
|
return
|
||||||
|
|
||||||
if is_tls_client_hello:
|
if is_tls_client_hello:
|
||||||
return TlsLayer(top_layer, True, True)
|
return TlsLayer(top_layer, True, True)
|
||||||
elif isinstance(top_layer, TlsLayer) and isinstance(top_layer.ctx, HttpLayer):
|
elif isinstance(top_layer, TlsLayer):
|
||||||
return HttpLayer(top_layer, "transparent")
|
if top_layer.client_conn.get_alpn_proto_negotiated() == 'h2':
|
||||||
|
return Http2Layer(top_layer, 'regular') # TODO: regular correct here?
|
||||||
|
else:
|
||||||
|
return Http1Layer(top_layer, 'regular') # TODO: regular correct here?
|
||||||
|
elif isinstance(top_layer, TlsLayer) and isinstance(top_layer.ctx, Http1Layer):
|
||||||
|
return Http1Layer(top_layer, "transparent")
|
||||||
else:
|
else:
|
||||||
return RawTcpLayer(top_layer)
|
return RawTcpLayer(top_layer)
|
||||||
|
|
||||||
|
@ -157,7 +157,6 @@ class TlsLayer(Layer):
|
|||||||
self.yield_from_callback(Reconnect())
|
self.yield_from_callback(Reconnect())
|
||||||
|
|
||||||
self.client_alpn_protos = options
|
self.client_alpn_protos = options
|
||||||
print("foo: %s" % options)
|
|
||||||
|
|
||||||
if alpn_preference in options:
|
if alpn_preference in options:
|
||||||
return bytes(alpn_preference)
|
return bytes(alpn_preference)
|
||||||
|
Loading…
Reference in New Issue
Block a user