def test_connection_lost(self): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, stream_id=1, headers=[(':authority', "127.0.0.1:{}".format( self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('foo', 'bar')]) done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False except: break try: client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() except: break if len(self.master.state.flows) == 1: assert self.master.state.flows[0].response is None
def test_connection_lost(self): client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, stream_id=1, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ("foo", "bar"), ], ) done = False while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False except: break try: client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() except: break if len(self.master.state.flows) == 1: assert self.master.state.flows[0].response is None
def test_connection_terminated(self): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) done = False connection_terminated_event = None while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) for event in events: if isinstance(event, h2.events.ConnectionTerminated): connection_terminated_event = event done = True except: break assert len(self.master.state.flows) == 1 assert connection_terminated_event is not None assert connection_terminated_event.error_code == 5 assert connection_terminated_event.last_stream_id == 42 assert connection_terminated_event.additional_data == b'foobar'
def test_connection_terminated(self): client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ], ) done = False connection_terminated_event = None while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) for event in events: if isinstance(event, h2.events.ConnectionTerminated): connection_terminated_event = event done = True except: break assert len(self.master.state.flows) == 1 assert connection_terminated_event is not None assert connection_terminated_event.error_code == 5 assert connection_terminated_event.last_stream_id == 42 assert connection_terminated_event.additional_data == b"foobar"
def test_push_promise(self): client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, stream_id=1, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ("foo", "bar"), ], ) done = False ended_streams = 0 pushed_streams = 0 responses = 0 while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False except: break client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): ended_streams += 1 elif isinstance(event, h2.events.PushedStreamReceived): pushed_streams += 1 elif isinstance(event, h2.events.ResponseReceived): responses += 1 if isinstance(event, h2.events.ConnectionTerminated): done = True if responses == 3 and ended_streams == 3 and pushed_streams == 2: done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert ended_streams == 3 assert pushed_streams == 2 bodies = [flow.response.body for flow in self.master.state.flows] assert len(bodies) == 3 assert b"regular_stream" in bodies assert b"pushed_stream_foo" in bodies assert b"pushed_stream_bar" in bodies
def test_push_promise_reset(self): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, stream_id=1, headers=[(':authority', "127.0.0.1:{}".format( self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('foo', 'bar')]) done = False ended_streams = 0 pushed_streams = 0 responses = 0 while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded) and event.stream_id == 1: ended_streams += 1 elif isinstance(event, h2.events.PushedStreamReceived): pushed_streams += 1 h2_conn.reset_stream(event.pushed_stream_id, error_code=0x8) client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() elif isinstance(event, h2.events.ResponseReceived): responses += 1 if isinstance(event, h2.events.ConnectionTerminated): done = True if responses >= 1 and ended_streams >= 1 and pushed_streams == 2: done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() bodies = [ flow.response.body for flow in self.master.state.flows if flow.response ] assert len(bodies) >= 1 assert b'regular_stream' in bodies
def handle(self): # send magic self.wfile.write(codecs.decode('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a', 'hex_codec')) self.wfile.flush() # send empty settings frame self.wfile.write(codecs.decode('000000040000000000', 'hex_codec')) self.wfile.flush() # check empty settings frame raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec') # check settings acknowledgement raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode('000000040100000000', 'hex_codec') # send settings acknowledgement self.wfile.write(codecs.decode('000000040100000000', 'hex_codec')) self.wfile.flush()
def handle(self): # send magic self.wfile.write( codecs.decode( '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a', 'hex_codec')) self.wfile.flush() # send empty settings frame self.wfile.write(codecs.decode('000000040000000000', 'hex_codec')) self.wfile.flush() # check empty settings frame raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode( '00000c040000000000000200000000000300000001', 'hex_codec') # check settings acknowledgement raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode('000000040100000000', 'hex_codec') # send settings acknowledgement self.wfile.write(codecs.decode('000000040100000000', 'hex_codec')) self.wfile.flush()
def read_frame(self, hide=False): while True: frm = http2.parse_frame(*http2.read_raw_frame(self.tcp_handler.rfile)) if not hide and self.dump_frames: # pragma no cover print(frm.human_readable("<<")) if isinstance(frm, hyperframe.frame.PingFrame): raw_bytes = hyperframe.frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize() self.tcp_handler.wfile.write(raw_bytes) self.tcp_handler.wfile.flush() continue if isinstance(frm, hyperframe.frame.SettingsFrame) and 'ACK' not in frm.flags: self._apply_settings(frm.settings, hide) if isinstance(frm, hyperframe.frame.DataFrame) and frm.flow_controlled_length > 0: self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length) return frm
def test_simple(self): response_body_buffer = b'' client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('ClIeNt-FoO', 'client-bar-1'), ('ClIeNt-FoO', 'client-bar-2'), ], body=b'request body') done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.DataReceived): response_body_buffer += event.data elif isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.master.state.flows[0].response.status_code == 200 assert self.master.state.flows[0].response.headers[ 'server-foo'] == 'server-bar' assert self.master.state.flows[0].response.headers['föo'] == 'bär' assert self.master.state.flows[0].response.body == b'response body' assert self.request_body_buffer == b'request body' assert response_body_buffer == b'response body'
def __call__(self): self._initiate_server_conn() self._complete_handshake() client = self.client_conn.connection server = self.server_conn.connection conns = [client, server] try: while True: r = tcp.ssl_read_select(conns, 1) for conn in r: source_conn = self.client_conn if conn == client else self.server_conn other_conn = self.server_conn if conn == client else self.client_conn is_server = (conn == self.server_conn.connection) with source_conn.h2.lock: try: raw_frame = b''.join( http2.read_raw_frame(source_conn.rfile)) except: # read frame failed: connection closed self._kill_all_streams() return if source_conn.h2.state_machine.state == h2.connection.ConnectionState.CLOSED: self.log( "HTTP/2 connection entered closed state already", "debug") return incoming_events = source_conn.h2.receive_data( raw_frame) source_conn.send(source_conn.h2.data_to_send()) for event in incoming_events: if not self._handle_event(event, source_conn, other_conn, is_server): # connection terminated: GoAway self._kill_all_streams() return self._cleanup_streams() except Exception as e: # pragma: no cover self.log(repr(e), "info") self.log(traceback.format_exc(), "debug") self._kill_all_streams()
def test_simple(self): response_body_buffer = b"" client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ("ClIeNt-FoO", "client-bar-1"), ("ClIeNt-FoO", "client-bar-2"), ], body=b"request body", ) done = False while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.DataReceived): response_body_buffer += event.data elif isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.master.state.flows[0].response.status_code == 200 assert self.master.state.flows[0].response.headers["server-foo"] == "server-bar" assert self.master.state.flows[0].response.headers["föo"] == "bär" assert self.master.state.flows[0].response.body == b"response body" assert self.request_body_buffer == b"request body" assert response_body_buffer == b"response body"
def test_request_with_priority(self): client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], priority_exclusive=True, priority_depends_on=42424242, priority_weight=42, ) done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.master.state.flows[0].response.headers[ 'priority_exclusive'] == 'True' assert self.master.state.flows[0].response.headers[ 'priority_depends_on'] == '42424242' assert self.master.state.flows[0].response.headers[ 'priority_weight'] == '42'
def handle(self): h2_conn = h2.connection.H2Connection(client_side=False, header_encoding=False) preamble = self.rfile.read(24) h2_conn.initiate_connection() h2_conn.receive_data(preamble) self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() if 'h2_server_settings' in self.kwargs: h2_conn.update_settings(self.kwargs['h2_server_settings']) self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False except netlib.exceptions.TcpDisconnect: break except: print(traceback.format_exc()) break self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() for event in events: try: if not self.server.handle_server_event( event, h2_conn, self.rfile, self.wfile): done = True break except netlib.exceptions.TcpDisconnect: done = True except: done = True print(traceback.format_exc()) break
def test_priority_with_existing_stream(self): client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ], end_stream=False, ) h2_conn.prioritize(1, exclusive=True, depends_on=0, weight=42) h2_conn.end_stream(1) client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() done = False while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.priority_data == [(True, 0, 42)]
def __call__(self): self._initiate_server_conn() self._complete_handshake() client = self.client_conn.connection server = self.server_conn.connection conns = [client, server] try: while True: r = tcp.ssl_read_select(conns, 1) for conn in r: source_conn = self.client_conn if conn == client else self.server_conn other_conn = self.server_conn if conn == client else self.client_conn is_server = (conn == self.server_conn.connection) with source_conn.h2.lock: try: raw_frame = b''.join(http2.read_raw_frame(source_conn.rfile)) except: # read frame failed: connection closed self._kill_all_streams() return if source_conn.h2.state_machine.state == h2.connection.ConnectionState.CLOSED: self.log("HTTP/2 connection entered closed state already", "debug") return incoming_events = source_conn.h2.receive_data(raw_frame) source_conn.send(source_conn.h2.data_to_send()) for event in incoming_events: if not self._handle_event(event, source_conn, other_conn, is_server): # connection terminated: GoAway self._kill_all_streams() return self._cleanup_streams() except Exception as e: # pragma: no cover self.log(repr(e), "info") self.log(traceback.format_exc(), "debug") self._kill_all_streams()
def handle(self): h2_conn = h2.connection.H2Connection(client_side=False, header_encoding=False) preamble = self.rfile.read(24) h2_conn.initiate_connection() h2_conn.receive_data(preamble) self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() if "h2_server_settings" in self.kwargs: h2_conn.update_settings(self.kwargs["h2_server_settings"]) self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() done = False while not done: try: raw = b"".join(http2.read_raw_frame(self.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False except netlib.exceptions.TcpDisconnect: break except: print(traceback.format_exc()) break self.wfile.write(h2_conn.data_to_send()) self.wfile.flush() for event in events: try: if not self.server.handle_server_event(event, h2_conn, self.rfile, self.wfile): done = True break except netlib.exceptions.TcpDisconnect: done = True except: done = True print(traceback.format_exc()) break
def test_max_concurrent_streams(self): client, h2_conn = self._setup_connection() new_streams = [1, 3, 5, 7, 9, 11] for stream_id in new_streams: # this will exceed MAX_CONCURRENT_STREAMS on the server connection # and cause mitmproxy to throttle stream creation to the server self._send_request( client.wfile, h2_conn, stream_id=stream_id, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ("X-Stream-ID", str(stream_id)), ], ) ended_streams = 0 while ended_streams != len(new_streams): try: header, body = http2.read_raw_frame(client.rfile) events = h2_conn.receive_data(b"".join([header, body])) except: break client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): ended_streams += 1 h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == len(new_streams) for flow in self.master.state.flows: assert flow.response.status_code == 200 assert b"Stream-ID " in flow.response.body
def test_max_concurrent_streams(self): client, h2_conn = self._setup_connection() new_streams = [1, 3, 5, 7, 9, 11] for stream_id in new_streams: # this will exceed MAX_CONCURRENT_STREAMS on the server connection # and cause mitmproxy to throttle stream creation to the server self._send_request(client.wfile, h2_conn, stream_id=stream_id, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('X-Stream-ID', str(stream_id)), ]) ended_streams = 0 while ended_streams != len(new_streams): try: header, body = http2.read_raw_frame(client.rfile) events = h2_conn.receive_data(b''.join([header, body])) except: break client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): ended_streams += 1 h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == len(new_streams) for flow in self.master.state.flows: assert flow.response.status_code == 200 assert b"Stream-ID " in flow.response.body
def test_body_size_limit(self): self.config.options.body_size_limit = 20 client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], body=b'very long body over 20 characters long', ) done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 0
def read_frame(self, hide=False): while True: frm = http2.parse_frame( *http2.read_raw_frame(self.tcp_handler.rfile)) if not hide and self.dump_frames: # pragma no cover print(frm.human_readable("<<")) if isinstance(frm, hyperframe.frame.PingFrame): raw_bytes = hyperframe.frame.PingFrame( flags=['ACK'], payload=frm.payload).serialize() self.tcp_handler.wfile.write(raw_bytes) self.tcp_handler.wfile.flush() continue if isinstance( frm, hyperframe.frame.SettingsFrame) and 'ACK' not in frm.flags: self._apply_settings(frm.settings, hide) if isinstance(frm, hyperframe.frame.DataFrame ) and frm.flow_controlled_length > 0: self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length) return frm
def test_body_size_limit(self): self.config.options.body_size_limit = 20 client, h2_conn = self._setup_connection() self._send_request( client.wfile, h2_conn, headers=[ (":authority", "127.0.0.1:{}".format(self.server.server.address.port)), (":method", "GET"), (":scheme", "https"), (":path", "/"), ], body=b"very long body over 20 characters long", ) done = False while not done: try: raw = b"".join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) assert False client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): done = True h2_conn.close_connection() client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() assert len(self.master.state.flows) == 0