def test_on_settings(self): client_conn = Connection( self.client_stream, client_side=True, on_unhandled=mock.Mock()) client_conn.initiate_connection() server_conn = Connection( self.server_stream, client_side=False, on_settings=self.on_settings) server_conn.initiate_connection() yield server_conn.read_bytes() # NOTE: h11 initiate_connection will send default settings self.assertIsNotNone(self.settings) self.settings = None client_conn.send_update_settings({ 4: 11111, 5: 22222}) yield server_conn.read_bytes() self.assertIsNotNone(self.settings) new_settings = {id: cs.new_value for (id, cs) in self.settings.iteritems()} self.assertEqual(new_settings, {4: 11111, 5: 22222})
class Http2Layer(ApplicationLayer): ''' Http2Layer: Responsible for handling the http2 request and response. ''' def __init__(self, server_state, context): super(Http2Layer, self).__init__(server_state, context) self.src_conn = Connection( self.src_stream, client_side=False, conn_type="source", on_request=self.on_request, on_settings=self.on_src_settings, on_window_updates=self.on_src_window_updates, on_priority_updates=self.on_src_priority_updates, on_reset=self.on_src_reset, on_terminate=self.on_src_terminate, readonly=(context.mode == "replay")) self.dest_conn = Connection( self.dest_stream, client_side=True, conn_type="destination", on_response=self.on_response, on_push=self.on_push, on_settings=self.on_dest_settings, on_window_updates=self.on_dest_window_updates, on_terminate=self.on_dest_terminate, on_reset=self.on_dest_reset) self.streams = dict() self.src_to_dest_ids = dict([(0, 0)]) self.dest_to_src_ids = dict([(0, 0)]) self._future = concurrent.Future() @gen.coroutine def process_and_return_context(self): yield self._init_h2_connection() self.src_stream.read_until_close( streaming_callback=self.src_conn.receive) self.src_stream.set_close_callback(self.on_src_close) self.dest_stream.read_until_close( streaming_callback=self.dest_conn.receive) self.dest_stream.set_close_callback(self.on_dest_close) result = yield self._future raise gen.Return(result) @gen.coroutine def _init_h2_connection(self): self.dest_conn.initiate_connection() yield self.dest_conn.flush() self.src_conn.initiate_connection() yield self.src_conn.flush() def on_src_close(self): logger.debug("{0}: src stream closed".format(self)) self.dest_stream.close() self.layer_finish() def on_dest_close(self): logger.debug("{0}: dest stream closed".format(self)) self.src_stream.close() self.layer_finish() def layer_finish(self): if self._future.running(): self._future.set_result(self.context) def update_ids(self, src_stream_id, dest_stream_id): self.src_to_dest_ids[src_stream_id] = dest_stream_id self.dest_to_src_ids[dest_stream_id] = src_stream_id def on_request(self, stream_id, request, priority_updated): dest_stream_id = self.dest_conn.get_next_available_stream_id() self.update_ids(stream_id, dest_stream_id) if priority_updated: priority_weight = priority_updated.weight priority_exclusive = priority_updated.exclusive priority_depends_on = self.safe_mapping_id( self.src_to_dest_ids, priority_updated.depends_on) else: priority_weight = None priority_exclusive = None priority_depends_on = None stream = Stream(self, self.context, stream_id, dest_stream_id) stream.on_request( request, priority_weight=priority_weight, priority_exclusive=priority_exclusive, priority_depends_on=priority_depends_on) self.streams[stream_id] = stream def on_push(self, pushed_stream_id, parent_stream_id, request): self.update_ids(pushed_stream_id, pushed_stream_id) target_parent_stream_id = self.dest_to_src_ids[parent_stream_id] stream = Stream(self, self.context, pushed_stream_id, pushed_stream_id) stream.on_push(request, target_parent_stream_id) self.streams[pushed_stream_id] = stream def on_response(self, stream_id, response): src_stream_id = self.dest_to_src_ids[stream_id] self.streams[src_stream_id].on_response(response) self.on_finish(src_stream_id) def on_finish(self, src_stream_id): stream = self.streams[src_stream_id] self.interceptor.publish( layer_context=self.context, request=stream.request, response=stream.response) del self.streams[src_stream_id] if self.context.mode == "replay": self.src_stream.close() self.dest_stream.close() def on_src_settings(self, changed_settings): new_settings = { id: cs.new_value for (id, cs) in changed_settings.iteritems() } self.dest_conn.send_update_settings(new_settings) def on_dest_settings(self, changed_settings): new_settings = { id: cs.new_value for (id, cs) in changed_settings.iteritems() } self.src_conn.send_update_settings(new_settings) def on_src_window_updates(self, stream_id, delta): target_stream_id = self.safe_mapping_id(self.src_to_dest_ids, stream_id) self.dest_conn.send_window_updates(target_stream_id, delta) def on_dest_window_updates(self, stream_id, delta): target_stream_id = self.safe_mapping_id(self.dest_to_src_ids, stream_id) self.src_conn.send_window_updates(target_stream_id, delta) def on_src_priority_updates(self, stream_id, depends_on, weight, exclusive): target_stream_id = self.safe_mapping_id( self.src_to_dest_ids, stream_id) target_depends_on = self.safe_mapping_id( self.src_to_dest_ids, depends_on) if target_stream_id: self.dest_conn.send_priority_updates( target_stream_id, target_depends_on, weight, exclusive) def safe_mapping_id(self, ids, stream_id): if stream_id in ids: return ids[stream_id] return 0 def on_src_reset(self, stream_id, error_code): target_stream_id = self.src_to_dest_ids[stream_id] self.dest_conn.send_reset(target_stream_id, error_code) def on_dest_reset(self, stream_id, error_code): target_stream_id = self.dest_to_src_ids[stream_id] self.src_conn.send_reset(target_stream_id, error_code) def on_src_terminate(self, additional_data, error_code, last_stream_id): self.dest_conn.send_terminate( error_code=error_code, additional_data=additional_data, last_stream_id=last_stream_id) def on_dest_terminate(self, additional_data, error_code, last_stream_id): self.src_conn.send_terminate( error_code=error_code, additional_data=additional_data, last_stream_id=last_stream_id)