def write(self, data, end_stream=False): data_len = len(data) left_len = self.send_window_size - self.send_size logger.debug('send window size: %d left length: %d', self.send_window_size, left_len) if data_len > left_len: # send data data_frame = DataFrame(self.stream_id) data_frame.data = data[:left_len] self.conn.write(data_frame.get_frame_bin()) self.conn.flush() # re-initialize self.send_size += left_len data = data[left_len:] data_len = len(data) logger.debug('wait for flow control') while self.send_size + data_len > self.send_window_size: sleep(0) # wait for get flow control data_frame = DataFrame(self.stream_id, end_stream) data_frame.data = data self.conn.write(data_frame.get_frame_bin()) self.send_size += data_len
def recv_frame(self, frame): if isinstance(frame, DataFrame): self.recv_data(frame.data) if frame.is_end_stream: self.end_stream() elif isinstance(frame, HeaderFrame): self.recv_header(frame.get_all()) if frame.is_end_header: self.is_recv_end_header = True if frame.is_end_stream: self.end_stream() elif isinstance(frame, PingFrame): if not frame.ack: ping = PingFrame(frame.opaque, True) self.conn.write(ping.get_frame_bin()) elif isinstance(frame, WindowUpdateFrame): logger.debug('window update %d in stream %d', frame.window_size, self.stream_id) if self.stream_id == StreamHTTP2.CONNECTION_STREAM_ID: self.conn.send_initial_window_size += frame.window_size self.send_window_size += frame.window_size elif isinstance(frame, SettingFrame): for setting in frame.setting_list: if setting[0] == SettingFrame.SETTINGS_MAX_FRAME_SIZE: self.conn.send_initial_window_size = setting[1] elif isinstance(frame, RSTFrame): self.close() logger.error('user reset stream %s' % error_codes[frame.error_code])
def flush_data(self, results): logger.debug('flush data in %d' % self.stream_id) if results is not None: for result in results: self.write(result) self.write(b'', end_stream=True) if self.is_wait_res: self.conn.flush() self.state = 'half-closed(remote)'
def promise(self, push_headers): push_promise = PushPromiseFrame(self.stream_id, push_headers) push_stream = self.conn.create_stream() push_promise.promised_stream_id = push_stream.stream_id promise = push_promise.get_frame_bin() logger.debug('send promise stream\n%s' % push_promise) self.conn.write(promise) # promise push self.conn.flush() spawn(push_stream.push, push_headers)
def load(cls, frame, header, **kargs): logger.debug('recv settings frame %s', frame) # frame length, type, flag, id frm_len, frm_type, frm_flag, frm_id = header if frm_id is not 0x0: # protocol error raise ProtocolError("'frm_id must be 0x0") if frm_type is not FrameType.SETTINGS: raise ValueError("frame is not type of SETTINGS type") if frm_flag == SettingFrame.ACK_FLAG and frm_len is not 0: # protocol error raise ProtocolError("Frame is ACK frame but frame length is not 0") setting_frame = cls() # check frame flag if frm_flag == SettingFrame.ACK_FLAG: setting_frame.is_ack = True if frm_len is not 0: # parsing setting list index = 0 # first frame payload index try: # check for out of index while index < frm_len: # index default value was 9 setting_id = frame[index] << 8 setting_id += frame[index + 1] # read setting id 16 bit # read setting value 32 bit setting_value = frame[index + 2] << 24 setting_value += frame[index + 3] << 16 setting_value += frame[index + 4] << 8 setting_value += frame[index + 5] if setting_id in SettingFrame.SETTINGS_RANGE: # ignore if setting id is not in range setting_frame._setting_list.append((setting_id, setting_value)) index += 6 # read 6 byte more for next reading except IndexError: raise ProtocolError("frame payload is out of format") return setting_frame
def run(self): while True: raw_frame_header = self.rfile.read( HTTP2Connection.STREAM_HEADER_SIZE ) if len(raw_frame_header) == 0: # user close connection logger.debug('user close connection') return try: frame_header = Frame.parse_header( raw_frame_header[:HTTP2Connection.STREAM_HEADER_SIZE] ) (frame_len, frame_type, frame_flag, frame_id) = frame_header try: target_stream = self.get_stream(frame_id) except StreamClosedError: # if closed error if not (frame_type == FrameType.WINDOW_UPDATE or frame_type == FrameType.RST_STREAM or frame_type == FrameType.PRIORITY): logger.debug('User send frame in closed stream(frame_type: %d)', frame_type) raise StreamClosedError('User send frame in closed stream(frame_type: %d)' % frame_type) # close connection if not target_stream.run_stream(self.rfile, frame_header): break sleep(0) except HTTP2Error as e: if self.server_setting['debug']: import traceback traceback.format_exc() logger.error('Goaway id %d debug data: %s', e.code, e.debug_data) goaway = GoawayFrame(frame_id, e.code, e.debug_data) self.write(goaway.get_frame_bin()) print("close connection") return
def run_app_in_spawn(self, app, environ): try: data = app(environ, self.start_response) self.flush_data(data) except OSError: logger.debug('user close connection')
def run(self): if self.use_ssl: if hasattr(ssl, 'SSLContext'): if hasattr(ssl, 'PROTOCOL_TLSv1_2'): # TODO: it will not work in below 2.7.9 and 3.2 ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) else: ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) if self.setting['use_http2']: protocol_nego = None if hasattr(ssl, 'HAS_NPN'): if ssl.HAS_NPN: protocol_nego = 'NPN' ssl_context.set_npn_protocols(['h2']) if hasattr(ssl, 'HAS_ALPN'): if ssl.HAS_ALPN: protocol_nego = 'ALPN' ssl_context.set_alpn_protocols(['h2']) if protocol_nego is None: logger.info('Unsupport NPN or ALPN') ssl_context.load_cert_chain( certfile=self.setting['crt_file'], keyfile=self.setting['key_file'] ) self.listen_sock = ssl_context.wrap_socket( socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_side=True ) else: logger.info('Unsupport NPN or ALPN') self.listen_sock = ssl.wrap_socket( socket.socket(socket.AF_INET, socket.SOCK_STREAM), certfile=self.setting['crt_file'], keyfile=self.setting['key_file'], ssl_version=ssl.PROTOCOL_TLSv1, server_side=True ) else: self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.listen_sock.bind((self.host, self.port)) self.listen_sock.listen(5) # create workers which process connections self.create_workers() while True: try: conn, addr = self.listen_sock.accept() if conn is None: continue self.connection_queue.put((conn, addr)) except ssl.SSLError: logger.debug('user access in tls connection without ssl cert')
def process_tcp_connection(self): while True: is_http2 = False tcp_connection, remote_addr = self.connection_queue.get() tcp_connection.settimeout(self.setting['keep_alive_timeout']) rfile = tcp_connection.makefile('rb', self.setting['input_buffer_size']) wfile = tcp_connection.makefile('wb', self.setting['output_buffer_size']) try: preface = rfile.peek(PREFACE_SIZE) except socket.timeout: self.close_connection(tcp_connection) continue # close connection except OSError: logger.debug('user close connection') continue is_http2 = (preface[:PREFACE_SIZE] == PREFACE_CODE) if is_http2: rfile.read(PREFACE_SIZE) # clean buffer frame = self.default_setting() wfile.write(frame.get_frame_bin()) wfile.write(SERVER_PREFACE) wfile.flush() connection = HTTP2Connection( is_http2, remote_addr, self.setting, self.app_list, rfile, wfile ) else: connection = HTTP1Connection( is_http2, remote_addr, self.setting, self.app_list, rfile, wfile ) if not self.setting['debug']: try: connection.run() except socket.timeout: pass except ssl.SSLError: pass except: pass # TODO : stack error log else: try: connection.run() except ssl.SSLError: pass except socket.timeout: pass except OSError: logger.debug('user close connection') self.close_connection(tcp_connection) # close connection