예제 #1
0
파일: stream.py 프로젝트: Luavis/test-http2
    def __init__(self, connection, wfile, stream_id=0x0):

        self.state = StreamState.IDLE

        # If8 connection control stream id must 0x0
        self.connection = connection
        self._stream_id = stream_id
        self._wfile = wfile
        self._client_headers = []
        self._server_headers = []
        self._last_header_raw = bytearray(0)  # empty header raw
        self.is_end_header = False
        self.req_stream_io = DataFrameIO()
예제 #2
0
파일: stream.py 프로젝트: Luavis/test-http2
class Stream(object):

    CRLF = '\r\n'

    def __init__(self, connection, wfile, stream_id=0x0):

        self.state = StreamState.IDLE

        # If8 connection control stream id must 0x0
        self.connection = connection
        self._stream_id = stream_id
        self._wfile = wfile
        self._client_headers = []
        self._server_headers = []
        self._last_header_raw = bytearray(0)  # empty header raw
        self.is_end_header = False
        self.req_stream_io = DataFrameIO()

    @property
    def id(self):
        return self._stream_id

    @property
    def is_connection_stream(self):
        return self._stream_id == 0x0

    @property
    def client_headers(self):
        return self._client_headers

    @property
    def server_headers(self):
        return self._server_headers

    @property
    def method(self):

        if self._client_headers:
            for header in self._client_headers:
                if header[0] == ':method':
                    return header[1]

        else:  # stream didn't end
            return None

        return 'GET'  # default method

    @property
    def path(self):

        if self._client_headers:
            for header in self._client_headers:
                if header[0] == ':path':
                    return header[1]

        else:  # stream didn't end
            return None

        return '/'  # default path

    @property
    def is_closed(self):
        return self.state == StreamState.CLOSED

    @property
    def is_wait_for_res(self):
        # self.is_end_header
        return self.state == StreamState.HALF_CLOSED_REMOTE

    def receive_frame(self, frame_header, frame_raw):

        try:
            frame = Frame.load(frame_raw, frame_header, decoder=self.connection.decoder)

            if isinstance(frame, HeaderFrame):
                self._last_header_raw = frame.data  # get payload of frame raw
                self._client_headers = frame.get_all()
                self.state = StreamState.OPEN  # if header recv, open stream

                if frame.is_end_header:
                    self.is_end_header = frame.is_end_header
                print('is_end_stream ', frame.is_end_stream)
            elif isinstance(frame, DataFrame):
                # TODO : need test
                self.req_stream_io.write(frame.data)
                print('is_end_stream ', frame.is_end_stream)

            else:
                # ignore unknow frame
                return False

            if not isinstance(frame, PriorityFrame):  # priority frame always be able to recieved

                if self.state == StreamState.HALF_CLOSED_REMOTE:
                    if not(isinstance(frame.RSTFrame)):  # or TODO: WINDOW_UPDATE
                        raise ProtocolError()
                elif self.state == StreamState.CLOSED:
                    if not(isinstance(frame.RSTFrame)):  # or TODO: WINDOW_UPDATE
                        raise ProtocolError()
                    elif time() - self._closed_time > 1:  # if these streams are recv after 1 sec
                        raise ProtocolError()

            if hasattr(frame, 'is_end_stream') and frame.is_end_stream:  # stream that can change state

                if self.state == StreamState.HALF_CLOSED_LOCAL:
                    self.close()
                elif self.state == StreamState.OPEN:
                    self.state = StreamState.HALF_CLOSED_REMOTE
                else:  # it would not be occured
                    raise ProtocolError()

            if isinstance(frame, RSTFrame):
                self.close()

        except:
            raise ProtocolError()  # unknow exception occur protocol error
            return False

        return True

    def send_header(self, headers, end_stream=False):  # TODO : if header block is bigger than stream size make it sep

        self._server_headers = headers
        header_frame = HeaderFrame(id=self._stream_id, header_list=headers, end_stream=end_stream)

        self.send_frame(header_frame)

    def send_data(self, data, end_stream=False):

        data_frame = DataFrame(id=self._stream_id, end_stream=end_stream)
        data_frame.data = data

        self.send_frame(data_frame)

    def promise(self, promise_headers=[]):

        # TODO: end headers when it can contain all headers in PP Frame

        promise = PushPromiseFrame(self.id, promise_headers, end_header=True)

        push_stream = self.connection.create_stream()

        promise.promised_stream_id = push_stream.id

        self.send_frame(promise)  # send push promise

        return push_stream

    def send_frame(self, frame):
        frame_bin = frame.get_frame_bin()

        self._wfile.write(frame_bin)

    def close(self):
        self.state = StreamState.CLOSED
        self._closed_time = time()