Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

agriconnect / aiohttp   python

Repository URL to install this package:

/ _http_parser.pyx

#cython: language_level=3
#
# Based on https://github.com/MagicStack/httptools
#
from __future__ import absolute_import, print_function
from cpython.mem cimport PyMem_Malloc, PyMem_Free
from libc.string cimport memcpy
from cpython cimport (PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE,
                      Py_buffer, PyBytes_AsString, PyBytes_AsStringAndSize)

from multidict import (CIMultiDict as _CIMultiDict,
                       CIMultiDictProxy as _CIMultiDictProxy)
from yarl import URL as _URL

from aiohttp import hdrs
from .http_exceptions import (
    BadHttpMessage, BadStatusLine, InvalidHeader, LineTooLong, InvalidURLError,
    PayloadEncodingError, ContentLengthError, TransferEncodingError)
from .http_writer import (HttpVersion as _HttpVersion,
                          HttpVersion10 as _HttpVersion10,
                          HttpVersion11 as _HttpVersion11)
from .http_parser import DeflateBuffer as _DeflateBuffer
from .streams import (EMPTY_PAYLOAD as _EMPTY_PAYLOAD,
                      StreamReader as _StreamReader)

cimport cython
from aiohttp cimport _cparser as cparser

include "_headers.pxi"

from aiohttp cimport _find_header

DEF DEFAULT_FREELIST_SIZE = 250

cdef extern from "Python.h":
    int PyByteArray_Resize(object, Py_ssize_t) except -1
    Py_ssize_t PyByteArray_Size(object) except -1
    char* PyByteArray_AsString(object)

__all__ = ('HttpRequestParser', 'HttpResponseParser',
           'RawRequestMessage', 'RawResponseMessage')

cdef object URL = _URL
cdef object URL_build = URL.build
cdef object CIMultiDict = _CIMultiDict
cdef object CIMultiDictProxy = _CIMultiDictProxy
cdef object HttpVersion = _HttpVersion
cdef object HttpVersion10 = _HttpVersion10
cdef object HttpVersion11 = _HttpVersion11
cdef object SEC_WEBSOCKET_KEY1 = hdrs.SEC_WEBSOCKET_KEY1
cdef object CONTENT_ENCODING = hdrs.CONTENT_ENCODING
cdef object EMPTY_PAYLOAD = _EMPTY_PAYLOAD
cdef object StreamReader = _StreamReader
cdef object DeflateBuffer = _DeflateBuffer


cdef inline object extend(object buf, const char* at, size_t length):
    cdef Py_ssize_t s
    cdef char* ptr
    s = PyByteArray_Size(buf)
    PyByteArray_Resize(buf, s + length)
    ptr = PyByteArray_AsString(buf)
    memcpy(ptr + s, at, length)


DEF METHODS_COUNT = 34;

cdef list _http_method = []

for i in range(METHODS_COUNT):
    _http_method.append(
        cparser.http_method_str(<cparser.http_method> i).decode('ascii'))


cdef inline str http_method_str(int i):
    if i < METHODS_COUNT:
        return <str>_http_method[i]
    else:
        return "<unknown>"

cdef inline object find_header(bytes raw_header):
    cdef Py_ssize_t size
    cdef char *buf
    cdef int idx
    PyBytes_AsStringAndSize(raw_header, &buf, &size)
    idx = _find_header.find_header(buf, size)
    if idx == -1:
        return raw_header.decode('utf-8', 'surrogateescape')
    return headers[idx]


@cython.freelist(DEFAULT_FREELIST_SIZE)
cdef class RawRequestMessage:
    cdef readonly str method
    cdef readonly str path
    cdef readonly object version  # HttpVersion
    cdef readonly object headers  # CIMultiDict
    cdef readonly object raw_headers  # tuple
    cdef readonly object should_close
    cdef readonly object compression
    cdef readonly object upgrade
    cdef readonly object chunked
    cdef readonly object url  # yarl.URL

    def __init__(self, method, path, version, headers, raw_headers,
                 should_close, compression, upgrade, chunked, url):
        self.method = method
        self.path = path
        self.version = version
        self.headers = headers
        self.raw_headers = raw_headers
        self.should_close = should_close
        self.compression = compression
        self.upgrade = upgrade
        self.chunked = chunked
        self.url = url

    def __repr__(self):
        info = []
        info.append(("method", self.method))
        info.append(("path", self.path))
        info.append(("version", self.version))
        info.append(("headers", self.headers))
        info.append(("raw_headers", self.raw_headers))
        info.append(("should_close", self.should_close))
        info.append(("compression", self.compression))
        info.append(("upgrade", self.upgrade))
        info.append(("chunked", self.chunked))
        info.append(("url", self.url))
        sinfo = ', '.join(name + '=' + repr(val) for name, val in info)
        return '<RawRequestMessage(' + sinfo + ')>'

    def _replace(self, **dct):
        cdef RawRequestMessage ret
        ret = _new_request_message(self.method,
                                   self.path,
                                   self.version,
                                   self.headers,
                                   self.raw_headers,
                                   self.should_close,
                                   self.compression,
                                   self.upgrade,
                                   self.chunked,
                                   self.url)
        if "method" in dct:
            ret.method = dct["method"]
        if "path" in dct:
            ret.path = dct["path"]
        if "version" in dct:
            ret.version = dct["version"]
        if "headers" in dct:
            ret.headers = dct["headers"]
        if "raw_headers" in dct:
            ret.raw_headers = dct["raw_headers"]
        if "should_close" in dct:
            ret.should_close = dct["should_close"]
        if "compression" in dct:
            ret.compression = dct["compression"]
        if "upgrade" in dct:
            ret.upgrade = dct["upgrade"]
        if "chunked" in dct:
            ret.chunked = dct["chunked"]
        if "url" in dct:
            ret.url = dct["url"]
        return ret

cdef _new_request_message(str method,
                           str path,
                           object version,
                           object headers,
                           object raw_headers,
                           bint should_close,
                           object compression,
                           bint upgrade,
                           bint chunked,
                           object url):
    cdef RawRequestMessage ret
    ret = RawRequestMessage.__new__(RawRequestMessage)
    ret.method = method
    ret.path = path
    ret.version = version
    ret.headers = headers
    ret.raw_headers = raw_headers
    ret.should_close = should_close
    ret.compression = compression
    ret.upgrade = upgrade
    ret.chunked = chunked
    ret.url = url
    return ret


@cython.freelist(DEFAULT_FREELIST_SIZE)
cdef class RawResponseMessage:
    cdef readonly object version  # HttpVersion
    cdef readonly int code
    cdef readonly str reason
    cdef readonly object headers  # CIMultiDict
    cdef readonly object raw_headers  # tuple
    cdef readonly object should_close
    cdef readonly object compression
    cdef readonly object upgrade
    cdef readonly object chunked

    def __init__(self, version, code, reason, headers, raw_headers,
                 should_close, compression, upgrade, chunked):
        self.version = version
        self.code = code
        self.reason = reason
        self.headers = headers
        self.raw_headers = raw_headers
        self.should_close = should_close
        self.compression = compression
        self.upgrade = upgrade
        self.chunked = chunked

    def __repr__(self):
        info = []
        info.append(("version", self.version))
        info.append(("code", self.code))
        info.append(("reason", self.reason))
        info.append(("headers", self.headers))
        info.append(("raw_headers", self.raw_headers))
        info.append(("should_close", self.should_close))
        info.append(("compression", self.compression))
        info.append(("upgrade", self.upgrade))
        info.append(("chunked", self.chunked))
        sinfo = ', '.join(name + '=' + repr(val) for name, val in info)
        return '<RawResponseMessage(' + sinfo + ')>'


cdef _new_response_message(object version,
                           int code,
                           str reason,
                           object headers,
                           object raw_headers,
                           bint should_close,
                           object compression,
                           bint upgrade,
                           bint chunked):
    cdef RawResponseMessage ret
    ret = RawResponseMessage.__new__(RawResponseMessage)
    ret.version = version
    ret.code = code
    ret.reason = reason
    ret.headers = headers
    ret.raw_headers = raw_headers
    ret.should_close = should_close
    ret.compression = compression
    ret.upgrade = upgrade
    ret.chunked = chunked
    return ret


@cython.internal
cdef class HttpParser:

    cdef:
        cparser.http_parser* _cparser
        cparser.http_parser_settings* _csettings

        bytearray _raw_name
        bytearray _raw_value
        bint      _has_value

        object _protocol
        object _loop
        object _timer

        size_t _max_line_size
        size_t _max_field_size
        size_t _max_headers
        bint _response_with_body

        bint    _started
        object  _url
        bytearray   _buf
        str     _path
        str     _reason
        object  _headers
        list    _raw_headers
        bint    _upgraded
        list    _messages
        object  _payload
        bint    _payload_error
        object  _payload_exception
        object  _last_error
        bint    _auto_decompress

        str     _content_encoding

        Py_buffer py_buf

    def __cinit__(self):
        self._cparser = <cparser.http_parser*> \
                                PyMem_Malloc(sizeof(cparser.http_parser))
        if self._cparser is NULL:
            raise MemoryError()

        self._csettings = <cparser.http_parser_settings*> \
                                PyMem_Malloc(sizeof(cparser.http_parser_settings))
        if self._csettings is NULL:
            raise MemoryError()

    def __dealloc__(self):
        PyMem_Free(self._cparser)
        PyMem_Free(self._csettings)

    cdef _init(self, cparser.http_parser_type mode,
                   object protocol, object loop, object timer=None,
                   size_t max_line_size=8190, size_t max_headers=32768,
                   size_t max_field_size=8190, payload_exception=None,
                   bint response_with_body=True, bint auto_decompress=True):
        cparser.http_parser_init(self._cparser, mode)
        self._cparser.data = <void*>self
        self._cparser.content_length = 0

        cparser.http_parser_settings_init(self._csettings)

        self._protocol = protocol
        self._loop = loop
        self._timer = timer

        self._buf = bytearray()
        self._payload = None
        self._payload_error = 0
        self._payload_exception = payload_exception
        self._messages = []

        self._raw_name = bytearray()
        self._raw_value = bytearray()
        self._has_value = False

        self._max_line_size = max_line_size
        self._max_headers = max_headers
        self._max_field_size = max_field_size
        self._response_with_body = response_with_body
        self._upgraded = False
        self._auto_decompress = auto_decompress
        self._content_encoding = None

        self._csettings.on_url = cb_on_url
        self._csettings.on_status = cb_on_status
        self._csettings.on_header_field = cb_on_header_field
        self._csettings.on_header_value = cb_on_header_value
        self._csettings.on_headers_complete = cb_on_headers_complete
Loading ...