Exemple #1
0
    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
Exemple #2
0
    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])
Exemple #3
0
    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)'
Exemple #4
0
    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)
Exemple #5
0
    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
Exemple #6
0
    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
Exemple #7
0
 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')
Exemple #8
0
    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')
Exemple #9
0
    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