mitmproxy/test/pathod/protocols/test_http2.py

501 lines
17 KiB
Python
Raw Normal View History

from unittest import mock
import hyperframe
import pytest
from mitmproxy import exceptions
from mitmproxy.net import http, tcp
from mitmproxy.net.http import http2
from pathod.protocols.http2 import HTTP2StateProtocol, TCPHandler
from ...mitmproxy.net import tservers as net_tservers
2016-01-31 13:16:03 +00:00
2015-08-05 19:32:53 +00:00
class TestTCPHandlerWrapper:
def test_wrapped(self):
2015-09-15 22:04:23 +00:00
h = TCPHandler(rfile='foo', wfile='bar')
p = HTTP2StateProtocol(h)
2015-08-05 19:32:53 +00:00
assert p.tcp_handler.rfile == 'foo'
assert p.tcp_handler.wfile == 'bar'
def test_direct(self):
p = HTTP2StateProtocol(rfile='foo', wfile='bar')
2015-09-15 22:04:23 +00:00
assert isinstance(p.tcp_handler, TCPHandler)
2015-08-05 19:32:53 +00:00
assert p.tcp_handler.rfile == 'foo'
assert p.tcp_handler.wfile == 'bar'
2015-06-05 11:28:09 +00:00
class EchoHandler(tcp.BaseHandler):
sni = None
def handle(self):
2015-06-05 18:49:03 +00:00
while True:
v = self.rfile.safe_read(1)
self.wfile.write(v)
self.wfile.flush()
2015-06-05 11:28:09 +00:00
2015-08-05 19:32:53 +00:00
class TestProtocol:
@mock.patch("pathod.protocols.http2.HTTP2StateProtocol.perform_server_connection_preface")
@mock.patch("pathod.protocols.http2.HTTP2StateProtocol.perform_client_connection_preface")
2015-08-05 19:32:53 +00:00
def test_perform_connection_preface(self, mock_client_method, mock_server_method):
protocol = HTTP2StateProtocol(is_server=False)
2015-08-05 19:32:53 +00:00
protocol.connection_preface_performed = True
protocol.perform_connection_preface()
assert not mock_client_method.called
assert not mock_server_method.called
protocol.perform_connection_preface(force=True)
assert mock_client_method.called
assert not mock_server_method.called
@mock.patch("pathod.protocols.http2.HTTP2StateProtocol.perform_server_connection_preface")
@mock.patch("pathod.protocols.http2.HTTP2StateProtocol.perform_client_connection_preface")
2015-08-05 19:32:53 +00:00
def test_perform_connection_preface_server(self, mock_client_method, mock_server_method):
protocol = HTTP2StateProtocol(is_server=True)
2015-08-05 19:32:53 +00:00
protocol.connection_preface_performed = True
protocol.perform_connection_preface()
assert not mock_client_method.called
assert not mock_server_method.called
protocol.perform_connection_preface(force=True)
assert not mock_client_method.called
assert mock_server_method.called
class TestCheckALPNMatch(net_tservers.ServerTestBase):
2015-06-05 11:28:09 +00:00
handler = EchoHandler
ssl = dict(
2015-09-26 15:39:50 +00:00
alpn_select=b'h2',
2015-06-05 11:28:09 +00:00
)
2016-12-01 09:36:18 +00:00
def test_check_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
with c.connect():
c.convert_to_tls(alpn_protos=[b'h2'])
2016-12-01 09:36:18 +00:00
protocol = HTTP2StateProtocol(c)
assert protocol.check_alpn()
2015-06-05 11:28:09 +00:00
class TestCheckALPNMismatch(net_tservers.ServerTestBase):
2015-06-05 11:28:09 +00:00
handler = EchoHandler
ssl = dict(
alpn_select=None,
)
2016-12-01 09:36:18 +00:00
def test_check_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
with c.connect():
c.convert_to_tls(alpn_protos=[b'h2'])
2016-12-01 09:36:18 +00:00
protocol = HTTP2StateProtocol(c)
with pytest.raises(NotImplementedError):
2016-12-01 09:36:18 +00:00
protocol.check_alpn()
2015-06-05 11:28:09 +00:00
class TestPerformServerConnectionPreface(net_tservers.ServerTestBase):
2015-06-11 13:38:32 +00:00
class handler(tcp.BaseHandler):
def handle(self):
2016-01-31 13:16:03 +00:00
# send magic
self.wfile.write(bytes.fromhex("505249202a20485454502f322e300d0a0d0a534d0d0a0d0a"))
2016-01-31 13:16:03 +00:00
self.wfile.flush()
2015-06-11 13:38:32 +00:00
2016-01-31 13:16:03 +00:00
# send empty settings frame
self.wfile.write(bytes.fromhex("000000040000000000"))
2016-01-31 13:16:03 +00:00
self.wfile.flush()
2015-06-11 13:38:32 +00:00
2016-01-31 13:16:03 +00:00
# check empty settings frame
2016-08-23 17:29:24 +00:00
raw = http2.read_raw_frame(self.rfile)
assert raw == bytes.fromhex("00000c040000000000000200000000000300000001")
2015-06-11 13:38:32 +00:00
2016-01-31 13:16:03 +00:00
# check settings acknowledgement
2016-08-23 17:29:24 +00:00
raw = http2.read_raw_frame(self.rfile)
assert raw == bytes.fromhex("000000040100000000")
2015-06-11 13:38:32 +00:00
2016-01-31 13:16:03 +00:00
# send settings acknowledgement
self.wfile.write(bytes.fromhex("000000040100000000"))
2016-01-31 13:16:03 +00:00
self.wfile.flush()
2015-06-11 13:38:32 +00:00
def test_perform_server_connection_preface(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
protocol = HTTP2StateProtocol(c)
2015-08-05 19:32:53 +00:00
2016-06-14 00:09:13 +00:00
assert not protocol.connection_preface_performed
protocol.perform_server_connection_preface()
assert protocol.connection_preface_performed
2015-08-05 19:32:53 +00:00
with pytest.raises(exceptions.TcpDisconnect):
2016-06-14 00:09:13 +00:00
protocol.perform_server_connection_preface(force=True)
2015-06-11 13:38:32 +00:00
class TestPerformClientConnectionPreface(net_tservers.ServerTestBase):
2015-06-05 11:28:09 +00:00
class handler(tcp.BaseHandler):
def handle(self):
# check magic
assert self.rfile.read(24) == HTTP2StateProtocol.CLIENT_CONNECTION_PREFACE
2015-06-05 11:28:09 +00:00
# check empty settings frame
assert self.rfile.read(9) ==\
bytes.fromhex("000000040000000000")
2015-06-05 11:28:09 +00:00
# send empty settings frame
self.wfile.write(bytes.fromhex("000000040000000000"))
2015-06-05 11:28:09 +00:00
self.wfile.flush()
# check settings acknowledgement
assert self.rfile.read(9) == \
bytes.fromhex("000000040100000000")
2015-06-05 11:28:09 +00:00
# send settings acknowledgement
self.wfile.write(bytes.fromhex("000000040100000000"))
2015-06-05 11:28:09 +00:00
self.wfile.flush()
2015-06-11 13:38:32 +00:00
def test_perform_client_connection_preface(self):
2015-06-05 11:28:09 +00:00
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
protocol = HTTP2StateProtocol(c)
2015-08-05 19:32:53 +00:00
2016-06-14 00:09:13 +00:00
assert not protocol.connection_preface_performed
protocol.perform_client_connection_preface()
assert protocol.connection_preface_performed
2015-06-05 11:28:09 +00:00
2016-10-17 04:29:45 +00:00
class TestClientStreamIds:
2015-06-05 11:28:09 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = HTTP2StateProtocol(c)
2015-06-05 11:28:09 +00:00
2015-06-12 13:21:23 +00:00
def test_client_stream_ids(self):
2015-06-05 11:28:09 +00:00
assert self.protocol.current_stream_id is None
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 1
2015-06-05 11:28:09 +00:00
assert self.protocol.current_stream_id == 1
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 3
2015-06-05 11:28:09 +00:00
assert self.protocol.current_stream_id == 3
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 5
2015-06-05 11:28:09 +00:00
assert self.protocol.current_stream_id == 5
2016-10-17 04:29:45 +00:00
class TestserverstreamIds:
2015-06-12 13:21:23 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = HTTP2StateProtocol(c, is_server=True)
2015-06-12 13:21:23 +00:00
def test_server_stream_ids(self):
assert self.protocol.current_stream_id is None
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 2
2015-06-12 13:21:23 +00:00
assert self.protocol.current_stream_id == 2
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 4
2015-06-12 13:21:23 +00:00
assert self.protocol.current_stream_id == 4
2015-07-29 09:27:43 +00:00
assert self.protocol._next_stream_id() == 6
2015-06-12 13:21:23 +00:00
assert self.protocol.current_stream_id == 6
class TestApplySettings(net_tservers.ServerTestBase):
2015-06-05 11:28:09 +00:00
class handler(tcp.BaseHandler):
def handle(self):
# check settings acknowledgement
assert self.rfile.read(9) == bytes.fromhex("000000040100000000")
2017-05-24 12:09:41 +00:00
self.wfile.write(b"OK")
2015-06-05 11:28:09 +00:00
self.wfile.flush()
2015-08-21 07:18:14 +00:00
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
2015-06-05 11:28:09 +00:00
ssl = True
def test_apply_settings(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
c.convert_to_tls()
protocol = HTTP2StateProtocol(c)
2015-06-05 11:28:09 +00:00
2016-06-14 00:09:13 +00:00
protocol._apply_settings({
hyperframe.frame.SettingsFrame.ENABLE_PUSH: 'foo',
hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 'bar',
hyperframe.frame.SettingsFrame.INITIAL_WINDOW_SIZE: 'deadbeef',
})
2015-06-05 11:28:09 +00:00
2016-06-14 00:09:13 +00:00
assert c.rfile.safe_read(2) == b"OK"
2015-06-05 11:28:09 +00:00
2016-06-14 00:09:13 +00:00
assert protocol.http2_settings[
hyperframe.frame.SettingsFrame.ENABLE_PUSH] == 'foo'
assert protocol.http2_settings[
hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS] == 'bar'
assert protocol.http2_settings[
hyperframe.frame.SettingsFrame.INITIAL_WINDOW_SIZE] == 'deadbeef'
2015-06-05 11:28:09 +00:00
2016-10-17 04:29:45 +00:00
class TestCreateHeaders:
2015-06-05 11:28:09 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_headers(self):
2015-09-05 16:15:47 +00:00
headers = http.Headers([
2015-06-05 11:28:09 +00:00
(b':method', b'GET'),
(b':path', b'index.html'),
(b':scheme', b'https'),
2015-09-05 16:15:47 +00:00
(b'foo', b'bar')])
2015-06-05 11:28:09 +00:00
data = HTTP2StateProtocol(self.c)._create_headers(
2015-06-05 11:28:09 +00:00
headers, 1, end_stream=True)
assert b''.join(data) == bytes.fromhex("000014010500000001824488355217caf3a69a3f87408294e7838c767f")
2015-06-05 11:28:09 +00:00
data = HTTP2StateProtocol(self.c)._create_headers(
2015-06-05 11:28:09 +00:00
headers, 1, end_stream=False)
assert b''.join(data) == bytes.fromhex("000014010400000001824488355217caf3a69a3f87408294e7838c767f")
2015-06-05 11:28:09 +00:00
def test_create_headers_multiple_frames(self):
2015-09-05 16:15:47 +00:00
headers = http.Headers([
(b':method', b'GET'),
(b':path', b'/'),
(b':scheme', b'https'),
(b'foo', b'bar'),
2015-09-05 16:15:47 +00:00
(b'server', b'version')])
protocol = HTTP2StateProtocol(self.c)
protocol.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE] = 8
data = protocol._create_headers(headers, 1, end_stream=True)
assert len(data) == 3
assert data[0] == bytes.fromhex("000008010100000001828487408294e783")
assert data[1] == bytes.fromhex("0000080900000000018c767f7685ee5b10")
assert data[2] == bytes.fromhex("00000209040000000163d5")
2015-06-05 11:28:09 +00:00
2016-10-17 04:29:45 +00:00
class TestCreateBody:
2015-06-05 11:28:09 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_body_empty(self):
protocol = HTTP2StateProtocol(self.c)
bytes = protocol._create_body(b'', 1)
2016-01-31 13:16:03 +00:00
assert b''.join(bytes) == b''
2015-06-05 11:28:09 +00:00
def test_create_body_single_frame(self):
protocol = HTTP2StateProtocol(self.c)
data = protocol._create_body(b'foobar', 1)
assert b''.join(data) == bytes.fromhex("000006000100000001666f6f626172")
2015-06-05 11:28:09 +00:00
def test_create_body_multiple_frames(self):
protocol = HTTP2StateProtocol(self.c)
protocol.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE] = 5
data = protocol._create_body(b'foobarmehm42', 1)
assert len(data) == 3
assert data[0] == bytes.fromhex("000005000000000001666f6f6261")
assert data[1] == bytes.fromhex("000005000000000001726d65686d")
assert data[2] == bytes.fromhex("0000020001000000013432")
2015-06-05 11:28:09 +00:00
class TestReadRequest(net_tservers.ServerTestBase):
2015-08-05 19:32:53 +00:00
class handler(tcp.BaseHandler):
2016-01-31 13:16:03 +00:00
2015-08-05 19:32:53 +00:00
def handle(self):
self.wfile.write(
bytes.fromhex("000003010400000001828487"))
2015-08-05 19:32:53 +00:00
self.wfile.write(
bytes.fromhex("000006000100000001666f6f626172"))
2015-08-05 19:32:53 +00:00
self.wfile.flush()
2015-08-21 07:18:14 +00:00
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
2015-06-05 11:28:09 +00:00
2015-08-05 19:32:53 +00:00
ssl = True
def test_read_request(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
c.convert_to_tls()
protocol = HTTP2StateProtocol(c, is_server=True)
2016-06-14 00:09:13 +00:00
protocol.connection_preface_performed = True
2015-08-05 19:32:53 +00:00
2016-06-14 00:09:13 +00:00
req = protocol.read_request(NotImplemented)
2015-08-05 19:32:53 +00:00
2016-06-14 00:09:13 +00:00
assert req.stream_id
assert req.headers.fields == ()
assert req.method == "GET"
assert req.path == "/"
assert req.scheme == "https"
assert req.content == b'foobar'
2015-08-16 18:02:18 +00:00
class TestReadRequestRelative(net_tservers.ServerTestBase):
2015-08-16 18:02:18 +00:00
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
bytes.fromhex("00000c0105000000014287d5af7e4d5a777f4481f9"))
2015-08-16 18:02:18 +00:00
self.wfile.flush()
ssl = True
2016-04-02 19:57:35 +00:00
def test_asterisk_form(self):
2015-08-16 18:02:18 +00:00
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
c.convert_to_tls()
protocol = HTTP2StateProtocol(c, is_server=True)
2016-06-14 00:09:13 +00:00
protocol.connection_preface_performed = True
2015-08-16 18:02:18 +00:00
2016-06-14 00:09:13 +00:00
req = protocol.read_request(NotImplemented)
2015-08-16 18:02:18 +00:00
2016-06-14 00:09:13 +00:00
assert req.first_line_format == "relative"
assert req.method == "OPTIONS"
assert req.path == "*"
2015-08-16 18:02:18 +00:00
class TestReadResponse(net_tservers.ServerTestBase):
2015-06-05 11:28:09 +00:00
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
bytes.fromhex("00000801040000002a88628594e78c767f"))
2015-06-05 11:28:09 +00:00
self.wfile.write(
bytes.fromhex("00000600010000002a666f6f626172"))
2015-06-05 11:28:09 +00:00
self.wfile.flush()
2015-08-21 07:18:14 +00:00
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
2015-06-05 11:28:09 +00:00
ssl = True
def test_read_response(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
c.convert_to_tls()
protocol = HTTP2StateProtocol(c)
2016-06-14 00:09:13 +00:00
protocol.connection_preface_performed = True
2015-06-05 11:28:09 +00:00
2016-06-14 00:09:13 +00:00
resp = protocol.read_response(NotImplemented, stream_id=42)
2015-06-05 11:28:09 +00:00
2016-06-14 00:09:13 +00:00
assert resp.http_version == "HTTP/2.0"
assert resp.status_code == 200
assert resp.reason == ''
assert resp.headers.fields == ((b':status', b'200'), (b'etag', b'foobar'))
assert resp.content == b'foobar'
assert resp.timestamp_end
2015-08-05 19:32:53 +00:00
2015-06-12 13:21:23 +00:00
class TestReadEmptyResponse(net_tservers.ServerTestBase):
2015-06-12 13:21:23 +00:00
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
bytes.fromhex("00000801050000002a88628594e78c767f"))
2015-06-12 13:21:23 +00:00
self.wfile.flush()
ssl = True
def test_read_empty_response(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
2016-06-14 00:09:13 +00:00
with c.connect():
c.convert_to_tls()
protocol = HTTP2StateProtocol(c)
2016-06-14 00:09:13 +00:00
protocol.connection_preface_performed = True
2015-06-12 13:21:23 +00:00
2016-06-14 00:09:13 +00:00
resp = protocol.read_response(NotImplemented, stream_id=42)
2015-06-12 13:21:23 +00:00
2016-06-14 00:09:13 +00:00
assert resp.stream_id == 42
assert resp.http_version == "HTTP/2.0"
assert resp.status_code == 200
assert resp.reason == ''
assert resp.headers.fields == ((b':status', b'200'), (b'etag', b'foobar'))
assert resp.content == b''
2015-06-12 13:21:23 +00:00
2016-10-17 04:29:45 +00:00
class TestAssembleRequest:
2015-08-05 19:32:53 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
2015-06-12 13:21:23 +00:00
2015-08-05 19:32:53 +00:00
def test_request_simple(self):
data = HTTP2StateProtocol(self.c).assemble_request(http.Request(
host="",
port=0,
method=b'GET',
scheme=b'https',
authority=b'',
path=b'/',
http_version=b"HTTP/2.0",
headers=(),
content=None,
trailers=None,
timestamp_start=0,
timestamp_end=0
2015-08-05 19:32:53 +00:00
))
assert len(data) == 1
assert data[0] == bytes.fromhex('00000d0105000000018284874188089d5c0b8170dc07')
2015-06-12 13:21:23 +00:00
2015-08-05 19:32:53 +00:00
def test_request_with_stream_id(self):
req = http.Request(
host="",
port=0,
method=b'GET',
scheme=b'https',
authority=b'',
path=b'/',
http_version=b"HTTP/2.0",
headers=(),
content=None,
trailers=None,
timestamp_start=0,
timestamp_end=0
2015-08-05 19:32:53 +00:00
)
req.stream_id = 0x42
data = HTTP2StateProtocol(self.c).assemble_request(req)
assert len(data) == 1
assert data[0] == bytes.fromhex('00000d0105000000428284874188089d5c0b8170dc07')
2015-06-12 13:21:23 +00:00
2015-08-05 19:32:53 +00:00
def test_request_with_body(self):
data = HTTP2StateProtocol(self.c).assemble_request(http.Request(
host="",
port=0,
method=b'GET',
scheme=b'https',
authority=b'',
path=b'/',
http_version=b"HTTP/2.0",
headers=http.Headers([(b'foo', b'bar')]),
content=b'foobar',
trailers=None,
timestamp_start=0,
timestamp_end=None,
2015-08-05 19:32:53 +00:00
))
assert len(data) == 2
assert data[0] == bytes.fromhex("0000150104000000018284874188089d5c0b8170dc07408294e7838c767f")
assert data[1] == bytes.fromhex("000006000100000001666f6f626172")
2015-06-12 13:21:23 +00:00
2016-10-17 04:29:45 +00:00
class TestAssembleResponse:
2015-06-12 13:21:23 +00:00
c = tcp.TCPClient(("127.0.0.1", 0))
2015-08-05 19:32:53 +00:00
def test_simple(self):
data = HTTP2StateProtocol(self.c, is_server=True).assemble_response(http.Response(
http_version=b"HTTP/2.0",
status_code=200,
reason=b"",
headers=(),
content=b"",
trailers=None,
timestamp_start=0,
timestamp_end=0,
2015-07-29 09:27:43 +00:00
))
assert len(data) == 1
assert data[0] == bytes.fromhex("00000101050000000288")
2015-06-12 13:21:23 +00:00
2015-08-05 19:32:53 +00:00
def test_with_stream_id(self):
resp = http.Response(
http_version=b"HTTP/2.0",
status_code=200,
reason=b"",
headers=(),
content=b"",
trailers=None,
timestamp_start=0,
timestamp_end=0,
2015-08-05 19:32:53 +00:00
)
resp.stream_id = 0x42
data = HTTP2StateProtocol(self.c, is_server=True).assemble_response(resp)
assert len(data) == 1
assert data[0] == bytes.fromhex("00000101050000004288")
2015-08-05 19:32:53 +00:00
def test_with_body(self):
data = HTTP2StateProtocol(self.c, is_server=True).assemble_response(http.Response(
http_version=b"HTTP/2.0",
status_code=200,
reason=b'',
headers=http.Headers(foo=b"bar"),
content=b'foobar',
trailers=None,
timestamp_start=0,
timestamp_end=0,
2015-07-29 09:27:43 +00:00
))
assert len(data) == 2
assert data[0] == bytes.fromhex("00000901040000000288408294e7838c767f")
assert data[1] == bytes.fromhex("000006000100000002666f6f626172")