def _handle_header_block(self, headers): """ Handles the logic for receiving a completed headers block. A headers block is an uninterrupted sequence of one HEADERS frame followed by zero or more CONTINUATION frames, and is terminated by a frame bearing the END_HEADERS flag. HTTP/2 allows receipt of up to three such blocks on a stream. The first is optional, and contains a 1XX response. The second is mandatory, and must contain a final response (200 or higher). The third is optional, and may contain 'trailers', headers that are sent after a chunk-encoded body is sent. Here we only process the simple state: no push, one header frame. """ if self.response_headers is None: self.response_headers = HTTPHeaderMap(headers) else: # Received too many headers blocks. raise ProtocolError("Too many header blocks.") status = int(self.response_headers[b':status'][0]) if status in [400, 403]: # xlog.warn("status:%d host:%s", status, self.host) self.connection.close("get 40x") return
def receive_frame(self, frame): """ Handle a frame received on this stream. called by connection. """ # self.logger.debug("stream %d recved frame %r", self.stream_id, frame) if frame.type == WindowUpdateFrame.type: self.remote_window_size += frame.window_increment self.send_left_body() elif frame.type == HeadersFrame.type: # Begin the header block for the response headers. #self.response_header_datas = [frame.data] self.response_header_datas.append(frame.data) elif frame.type == PushPromiseFrame.type: self.logger.error("%s receive PushPromiseFrame:%d", self.ip_str, frame.stream_id) elif frame.type == ContinuationFrame.type: # Continue a header block begun with either HEADERS or PUSH_PROMISE. self.response_header_datas.append(frame.data) elif frame.type == DataFrame.type: # Append the data to the buffer. if not self.task.finished: self.task.put_data(frame.data) if 'END_STREAM' not in frame.flags: # Increase the window size. Only do this if the data frame contains # actual data. # don't do it if stream is closed. size = frame.flow_controlled_length increment = self.receive_window_manager._handle_frame(size) #if increment: # self.logger.debug("stream:%d frame size:%d increase win:%d", self.stream_id, size, increment) #content_len = int(self.request_headers.get("Content-Length")[0]) #self.logger.debug("%s get:%d s:%d", self.ip, self.response_body_len, size) if increment and not self._remote_closed: w = WindowUpdateFrame(self.stream_id) w.window_increment = increment self._send_cb(w) elif frame.type == RstStreamFrame.type: # Rest Frame send from server is not define in RFC inactive_time = time.time() - self.connection.last_recv_time self.logger.debug( "%s Stream %d Rest by server, inactive:%d. error code:%d", self.ip_str, self.stream_id, inactive_time, frame.error_code) self.connection.close("RESET") elif frame.type in FRAMES: # This frame isn't valid at this point. #raise ValueError("Unexpected frame %s." % frame) self.logger.error("%s Unexpected frame %s.", self.ip_str, frame) else: # pragma: no cover # Unknown frames belong to extensions. Just drop it on the # floor, but log so that users know that something happened. self.logger.error("%s Received unknown frame, type %d", self.ip_str, frame.type) pass if 'END_HEADERS' in frame.flags: if self.config.http2_show_debug: self.logger.debug("END_HEADERS") if self.response_headers is not None: raise ProtocolError("Too many header blocks.") # Begin by decoding the header block. If this fails, we need to # tear down the entire connection. if len(self.response_header_datas) == 1: header_data = self.response_header_datas[0] else: header_data = b''.join(self.response_header_datas) try: headers = self._decoder.decode(header_data) except Exception as e: self.logger.exception("decode h2 header %s fail:%r", header_data, e) raise e self.response_headers = HTTPHeaderMap(headers) # We've handled the headers, zero them out. self.response_header_datas = None self.get_head_time = time.time() length = self.response_headers.get("Content-Length", None) if isinstance(length, list): length = int(length[0]) if not self.task.finished: self.task.content_length = length self.task.set_state("h2_get_head") self.send_response() if 'END_STREAM' in frame.flags: if self.config.http2_show_debug: self.logger.debug("%s Closing remote side of stream:%d", self.connection.ssl_sock.ip_str, self.stream_id) time_now = time.time() time_cost = time_now - self.get_head_time if time_cost > 0 and \ isinstance(self.task.content_length, int) and \ not self.task.finished: speed = self.task.content_length / time_cost self.task.set_state("h2_finish[SP:%d]" % speed) self._close_remote() self.close("end stream") if not self.task.finished: self.connection.continue_timeout = 0