implement Http1 and Http2 protocols as layers

This commit is contained in:
Thomas Kriechbaumer 2015-08-16 15:19:11 +02:00
parent c04fa1b233
commit 38c456bb62
6 changed files with 54 additions and 70 deletions

View File

@ -9,14 +9,41 @@ from libmproxy.protocol import KILL
from libmproxy.protocol.http import HTTPFlow
from libmproxy.protocol.http_wrappers import HTTPResponse, HTTPRequest
from libmproxy.protocol2.http_protocol_mock import HTTP1
from libmproxy.protocol2.tls import TlsLayer
from netlib import tcp
from netlib.http import status_codes, http1, HttpErrorConnClosed
from netlib.http.semantics import CONTENT_MISSING
from netlib import odict
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):
response = status_codes.RESPONSES.get(status_code, "Unknown")
@ -79,8 +106,8 @@ class HttpLayer(Layer):
while True:
try:
try:
request = HTTP1.read_request(
self.client_conn,
request = HTTPRequest.from_protocol(
self.client_protocol,
body_size_limit=self.config.body_size_limit
)
except tcp.NetLibError:
@ -168,12 +195,12 @@ class HttpLayer(Layer):
# streaming:
# First send the headers and then transfer the response
# 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")
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")
chunks = HTTP1.read_http_body_chunked(
chunks = self.client_protocol.read_http_body_chunked(
flow.response.headers,
self.config.body_size_limit,
flow.request.method,
@ -196,8 +223,8 @@ class HttpLayer(Layer):
# TODO: Add second attempt.
self.send_to_server(flow.request)
flow.response = HTTP1.read_response(
self.server_conn,
flow.response = HTTPResponse.from_protocol(
self.server_protocol,
flow.request.method,
body_size_limit=self.config.body_size_limit,
include_body=False,
@ -211,8 +238,8 @@ class HttpLayer(Layer):
if flow.response.stream:
flow.response.content = CONTENT_MISSING
else:
flow.response.content = HTTP1.read_http_body(
elif isinstance(self.server_protocol, http1.HTTP1Protocol):
flow.response.content = self.server_protocol.read_http_body(
self.server_conn,
flow.response.headers,
self.config.body_size_limit,
@ -329,9 +356,9 @@ class HttpLayer(Layer):
raise InvalidCredentials("Proxy Authentication Required")
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):
# FIXME
# - possibly do some http2 stuff here
self.client_conn.send(HTTP1.assemble(message))
self.client_conn.send(self.client_protocol.assemble(message))

View File

@ -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)

View File

@ -1,12 +1,12 @@
from __future__ import (absolute_import, print_function, division)
from .layer import Layer, ServerConnectionMixin
from .http import HttpLayer
from .http import Http1Layer, HttpLayer
class HttpProxy(Layer, ServerConnectionMixin):
def __call__(self):
layer = HttpLayer(self, "regular")
layer = Http1Layer(self, "regular")
for message in layer():
if not self._handle_server_message(message):
yield message
@ -18,7 +18,7 @@ class HttpUpstreamProxy(Layer, ServerConnectionMixin):
self.server_address = server_address
def __call__(self):
layer = HttpLayer(self, "upstream")
layer = Http1Layer(self, "upstream")
for message in layer():
if not self._handle_server_message(message):
yield message
yield message

View File

@ -64,6 +64,7 @@ class Layer(_LayerCodeCompletion):
"""
super(Layer, self).__init__()
self.ctx = ctx
print("%s -> %s" % (repr(ctx), repr(self)))
def __call__(self):
"""

View File

@ -2,7 +2,7 @@ from __future__ import (absolute_import, print_function, division)
from .rawtcp import RawTcpLayer
from .tls import TlsLayer
from .http import HttpLayer
from .http import Http1Layer, Http2Layer, HttpLayer
class RootContext(object):
"""
@ -34,13 +34,20 @@ class RootContext(object):
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:
return
if is_tls_client_hello:
return TlsLayer(top_layer, True, True)
elif isinstance(top_layer, TlsLayer) and isinstance(top_layer.ctx, HttpLayer):
return HttpLayer(top_layer, "transparent")
elif isinstance(top_layer, TlsLayer):
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:
return RawTcpLayer(top_layer)

View File

@ -157,7 +157,6 @@ class TlsLayer(Layer):
self.yield_from_callback(Reconnect())
self.client_alpn_protos = options
print("foo: %s" % options)
if alpn_preference in options:
return bytes(alpn_preference)