def parse_body(self):
        if self.content_length is None and self.request.method in NO_SEMANTICS:
            self.on_body(self.request)
            return 0
        elif self.content_length == 0:
            self.request.body = b""
            self.on_body(self.request)
            return 0
        elif self.content_length is not None:
            if self.content_length > len(self.buffer):
                return -2

            self.request.body = bytes(self.buffer[:self.content_length])
            self.on_body(self.request)
            self.buffer = self.buffer[self.content_length:]

            result = self.content_length

            return result
        elif self.transfer == 'identity':
            return -2
        elif self.transfer == 'chunked':
            if not self.chunked_decoder:
                self.chunked_decoder = ffi.new('struct phr_chunked_decoder*')
                self.chunked_decoder.consume_trailer = b'\x01'
                self.chunked_offset = ffi.new('size_t*')

            chunked_offset_start = self.chunked_offset[0]
            self.chunked_offset[0] = len(self.buffer) - self.chunked_offset[0]
            result = lib.phr_decode_chunked(
                self.chunked_decoder,
                ffi.from_buffer(self.buffer) + chunked_offset_start,
                self.chunked_offset)
            self.chunked_offset[0] = self.chunked_offset[0] + chunked_offset_start

            if result == -2:
                self.buffer = self.buffer[:self.chunked_offset[0]]
                return result
            elif result == -1:
                self.on_error('malformed_body')
                self._reset_state()
                self.buffer = bytearray()

                return result

            self.request.body = bytes(self.buffer[:self.chunked_offset[0]])
            self.on_body(self.request)
            self.buffer = self.buffer[
                self.chunked_offset[0]:self.chunked_offset[0] + result]

            return result
    def parse_headers(self, data):
        c_method = ffi.new('char **')
        method_len = ffi.new('size_t *')
        c_path = ffi.new('char **')
        path_len = ffi.new('size_t *')
        minor_version = ffi.new('int *')
        c_headers = ffi.new('struct phr_header[10]')
        num_headers = ffi.new('size_t *')
        num_headers[0] = 10

        result = lib.phr_parse_request(self.buffer, self.buffer_len, c_method,
                                       method_len, c_path, path_len,
                                       minor_version, c_headers, num_headers,
                                       0)
        print('parse_headers:result: {}'.format(
            result))  # parse_headers:result: 679

        if result == -2:
            if self.buffer == data:
                self.buffer = ffi.new('char[8192]')
                self.buffer_owned = True
                ffi.memmove(self.buffer, data, len(data))

            return result
        if result == -1:
            self.on_error()
            self._reset()

            return result

        self.buffer_consumed += result

        method = ffi.string(c_method[0], method_len[0]).decode('ascii')
        path = ffi.string(c_path[0], path_len[0]).decode('ascii')
        version = "1." + str(minor_version[0])

        headers = {}
        for idx in range(num_headers[0]):
            header = c_headers[idx]
            name = ffi.string(header.name, header.name_len).decode('ascii')
            value = ffi.string(header.value, header.value_len).decode('latin1')
            headers[name] = value

        self.request = HttpRequest(method, path, version, headers)

        self.on_headers(self.request)

        return result
    def __init__(self, on_headers, on_body, on_error):
        self.on_headers = on_headers
        self.on_body = on_body
        self.on_error = on_error

        self.c_method = ffi.new('char **')
        self.method_len = ffi.new('size_t *')
        self.c_path = ffi.new('char **')
        self.path_len = ffi.new('size_t *')
        self.minor_version = ffi.new('int *')
        self.c_headers = ffi.new('struct phr_header[10]')
        self.num_headers = ffi.new('size_t *')
        self.chunked_offset = ffi.new('size_t*')

        self.buffer = bytearray()
        self._reset_state()