def test_read_raw_frame_failed(): raw = codecs.decode('485454000000000000', 'hex_codec') bio = BytesIO(raw) bio.safe_read = bio.read with pytest.raises(exceptions.HttpException): read_raw_frame(bio)
def test_connection_terminated(self): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) done = False connection_terminated_event = None while not done: try: raw = b''.join(http2.read_raw_frame(self.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_response_streaming(self, streaming): class Stream: def responseheaders(self, f): f.response.stream = streaming self.master.addons.add(Stream()) h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) done = False self.client.rfile.o.settimeout(2) data = None while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) for event in events: if isinstance(event, h2.events.DataReceived): data = event.data done = True except: break if streaming: assert data else: assert data is None
def test_connection_lost(self): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[(':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('foo', 'bar')]) done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False except: break try: self.client.wfile.write(h2_conn.data_to_send()) self.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): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) done = False connection_terminated_event = None while not done: try: raw = b''.join(http2.read_raw_frame(self.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_lost(self): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('foo', 'bar') ]) done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False except: break try: self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() except: break if len(self.master.state.flows) == 1: assert self.master.state.flows[0].response is None
def test_priority(self, prioritize_before, http2_priority_enabled, priority, expected_priority): self.options.http2_priority = http2_priority_enabled self.__class__.priority_data = [] h2_conn = self.setup_connection() if prioritize_before: h2_conn.prioritize(1, exclusive=priority[0], depends_on=priority[1], weight=priority[2]) self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], end_stream=prioritize_before, ) if not prioritize_before: h2_conn.prioritize(1, exclusive=priority[0], depends_on=priority[1], weight=priority[2]) h2_conn.end_stream(1) self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.priority_data == expected_priority
def test_read_raw_frame(): raw = codecs.decode('000006000101234567666f6f626172', 'hex_codec') bio = BytesIO(raw) bio.safe_read = bio.read header, body = read_raw_frame(bio) assert header assert body
def test_push_promise(self): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[(':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':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(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False except: break self.client.wfile.write(h2_conn.data_to_send()) self.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() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert ended_streams == 3 assert pushed_streams == 2 bodies = [flow.response.content 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 pushed_flows = [ flow for flow in self.master.state.flows if 'h2-pushed-stream' in flow.metadata ] assert len(pushed_flows) == 2
def test_push_promise(self): h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':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(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False except: break self.client.wfile.write(h2_conn.data_to_send()) self.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() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert ended_streams == 3 assert pushed_streams == 2 bodies = [flow.response.content 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 pushed_flows = [flow for flow in self.master.state.flows if 'h2-pushed-stream' in flow.metadata] assert len(pushed_flows) == 2
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 exceptions.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.content 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 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 exceptions.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.content for flow in self.master.state.flows if flow.response] assert len(bodies) >= 1 assert b'regular_stream' in bodies # the other two bodies might not be transmitted before the reset pushed_flows = [flow for flow in self.master.state.flows if 'h2-pushed-stream' in flow.metadata] assert len(pushed_flows) == 2
def test_trailers(self, announce, body): h2_conn = self.setup_connection() stream_id = 1 headers = [ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ] if announce: headers.append(('trailer', 'x-my-trailers')) h2_conn.send_headers( stream_id=stream_id, headers=headers, ) if body: h2_conn.send_data(stream_id, body) # send trailers h2_conn.send_headers(stream_id, [('x-my-trailers', 'foobar')], end_stream=True) self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.master.state.flows[0].request.trailers[ 'x-my-trailers'] == 'foobar' assert self.master.state.flows[0].response.status_code == 200 assert self.master.state.flows[0].response.headers[ 'x-my-trailer-request-received'] == 'success'
def test_request_with_priority(self, http2_priority_enabled, priority, expected_priority): self.options.http2_priority = http2_priority_enabled h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], priority_exclusive=priority[0], priority_depends_on=priority[1], priority_weight=priority[2], ) done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 1 resp = self.master.state.flows[0].response assert resp.headers.get('priority_exclusive', None) == expected_priority[0] assert resp.headers.get('priority_depends_on', None) == expected_priority[1] assert resp.headers.get('priority_weight', None) == expected_priority[2]
def handle(self): # send magic self.wfile.write( bytes.fromhex( "505249202a20485454502f322e300d0a0d0a534d0d0a0d0a")) self.wfile.flush() # send empty settings frame self.wfile.write(bytes.fromhex("000000040000000000")) self.wfile.flush() # check empty settings frame raw = http2.read_raw_frame(self.rfile) assert raw == bytes.fromhex( "00000c040000000000000200000000000300000001") # check settings acknowledgement raw = http2.read_raw_frame(self.rfile) assert raw == bytes.fromhex("000000040100000000") # send settings acknowledgement self.wfile.write(bytes.fromhex("000000040100000000")) 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("<< " + repr(frm)) 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 __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'' h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('self.client-FoO', 'self.client-bar-1'), ('self.client-FoO', 'self.client-bar-2'), ], body=b'request body') done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.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() self.client.wfile.write(h2_conn.data_to_send()) self.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.content == b'response body' assert self.request_body_buffer == b'request body' assert response_body_buffer == b'response body'
def test_trailers(self, announce): response_body_buffer = b'' h2_conn = self.setup_connection() self._send_request(self.client.wfile, h2_conn, stream_id=(1 if announce else 3), headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) trailers_buffer = None done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.DataReceived): response_body_buffer += event.data elif isinstance(event, h2.events.TrailersReceived): trailers_buffer = event.headers elif isinstance(event, h2.events.StreamEnded): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.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.content == b'response body' assert response_body_buffer == b'response body' assert self.master.state.flows[0].response.trailers[ 'x-my-trailers'] == 'foobar' assert trailers_buffer == [(b'x-my-trailers', b'foobar')]
def test_simple(self): response_body_buffer = b'' h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ('self.client-FoO', 'self.client-bar-1'), ('self.client-FoO', 'self.client-bar-2'), ], body=b'request body') done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.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() self.client.wfile.write(h2_conn.data_to_send()) self.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.content == b'response body' assert self.request_body_buffer == b'request body' assert response_body_buffer == b'response body'
def handle(self): config = h2.config.H2Configuration(client_side=False, validate_outbound_headers=False, validate_inbound_headers=False) h2_conn = h2.connection.H2Connection(config) 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 exceptions.HttpException: print(traceback.format_exc()) assert False except 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 exceptions.TcpDisconnect: done = True except: done = True print(traceback.format_exc()) break
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 exceptions.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): config = h2.config.H2Configuration( client_side=False, validate_outbound_headers=False, validate_inbound_headers=False) h2_conn = h2.connection.H2Connection(config) 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 exceptions.HttpException: print(traceback.format_exc()) assert False except 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 exceptions.TcpDisconnect: done = True except: done = True print(traceback.format_exc()) break
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_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 exceptions.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 test_body_size_limit(self): self.options.body_size_limit = "20" # FIXME: This should not be required? self.options._processed["body_size_limit"] = 20 h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':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(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 0
def test_request_streaming(self, streaming): class Stream: def requestheaders(self, f): f.request.stream = streaming self.master.addons.add(Stream()) h2_conn = self.setup_connection() body = generators.RandomGenerator("bytes", 100)[:] self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], body=body, streaming=True ) done = False connection_terminated_event = None self.client.rfile.o.settimeout(2) while not done: try: raw = b''.join(http2.read_raw_frame(self.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 if streaming: assert connection_terminated_event.additional_data == body else: assert connection_terminated_event is None
def test_max_concurrent_streams(self): 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(self.client.wfile, h2_conn, stream_id=stream_id, headers=[ (':authority', "127.0.0.1:{}".format( self.server.server.address[1])), (':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(self.client.rfile) events = h2_conn.receive_data(b''.join([header, body])) except: break self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): ended_streams += 1 h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.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.content
def test_forbidden_headers(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[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ]) done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except exceptions.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.ResponseReceived): assert 'keep-alive' not in event.headers 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[ 'keep-alive'] == 'foobar'
def test_body_size_limit(self): self.options.body_size_limit = "20" h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':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(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 0
def test_response_streaming(self, streaming): class Stream: def responseheaders(self, f): f.response.stream = streaming self.master.addons.add(Stream()) h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ] ) done = False self.client.rfile.o.settimeout(2) data = None while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) for event in events: if isinstance(event, h2.events.DataReceived): data = event.data done = True except: break if streaming: assert data else: assert data is None
def test_request_with_priority(self): h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], ) done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() assert len(self.master.state.flows) == 1 assert self.master.state.flows[0].response is None
def run_test_for_stream_reset(self): h2_conn = self.setup_connection() self._send_request( self.client.wfile, h2_conn, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], ) self.client.rfile.o.settimeout(1) done = False while not done: try: raw = b''.join(http2.read_raw_frame(self.client.rfile)) events = h2_conn.receive_data(raw) except exceptions.HttpException: print(traceback.format_exc()) assert False self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamReset): assert event.error_code == int(self.current_error_code) done = True h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush()
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', '/'), ], ) done = False while not done: try: raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except exceptions.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) == 1 assert self.master.state.flows[0].response is None
def test_max_concurrent_streams(self): 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(self.client.wfile, h2_conn, stream_id=stream_id, headers=[ (':authority', "127.0.0.1:{}".format(self.server.server.address[1])), (':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(self.client.rfile) events = h2_conn.receive_data(b''.join([header, body])) except: break self.client.wfile.write(h2_conn.data_to_send()) self.client.wfile.flush() for event in events: if isinstance(event, h2.events.StreamEnded): ended_streams += 1 h2_conn.close_connection() self.client.wfile.write(h2_conn.data_to_send()) self.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.content