Пример #1
0
class Rederict(BaseSiteModule):
    def __init__(self,
                 location=None,
                 code=302,
                 headers=None,
                 methods=None,
                 url_modifier=None):
        if not location and not url_modifier:
            raise ValueError('must be passed "location" or "url_modifier"')
        self._code = 302
        self._headers = AutoCFG(headers or {})
        self._url_modifier = url_modifier
        if not url_modifier:
            self._headers.update(location=location)
        for method in (methods or HTTP_METHODS):
            setattr(self, method.lower(), self._req_handler)

    async def _req_handler(self, req):
        headers = dict(self._headers)
        if self._url_modifier:
            headers.update(location=self._url_modifier(req))
        return Response(
            code=self._code,
            headers=headers,
        )
Пример #2
0
    def __init__(self,
                 data=None,
                 headers=None,
                 code=200,
                 cookies=None,
                 http_version='HTTP/1.1'):
        if data is not None and not isinstance(data, (str, bytes)):
            raise TypeError('data must be str or bytes')

        if headers is not None and not isinstance(headers, dict):
            raise TypeError('headers must be dict')

        if not isinstance(code, int):
            raise TypeError('code must be int')

        # response data
        self._data = data or b''
        # dict of headers: {'Content-Type': 'text/html'}
        self._headers = AutoCFG(headers or {},
                                key_modifier=lambda x: x.lower())
        # HTTP status code: 200
        self._code = code
        # dict of dicts for Set-Cookie header:
        # {'uid': {'value': '123', 'flags':['HttpOnly'], 'properties': {'Path': '/'}}
        self._cookies = AutoCFG()
        if cookies:
            for ck in cookies:
                self.add_cookie(**ck)
        self._http_version = http_version
Пример #3
0
Файл: parser.py Проект: moff4/k2
async def parse_data(reader, **kwargs):
    """
        take io stream and cfg and parse HTTP headers
        return dict of attributes/headers/url/args
    """
    cfg = AutoCFG(PARSE_DATA_DEFAULTS).update_fields(kwargs)
    req = AutoCFG({
        'url': b'',
        'args': {},
        'method': b'',
        'http_version': b'',
        'headers': AutoCFG(key_modifier=lambda x: x.lower()),
        'data': b'',
    })
    if reader.at_eof():
        raise RuntimeError('socket was closed')
    if not (st := (await readln(
            reader,
            max_len=cfg.max_uri_length + 12,
            ignore_zeros=True,
            exception=AeonResponse('URI too long', code=414),
    )).strip()):
        raise AeonResponse('empty string',
                           code=400,
                           close_conn=True,
                           silent=True)
Пример #4
0
 def __init__(self,
              location=None,
              code=302,
              headers=None,
              methods=None,
              url_modifier=None):
     if not location and not url_modifier:
         raise ValueError('must be passed "location" or "url_modifier"')
     self._code = 302
     self._headers = AutoCFG(headers or {})
     self._url_modifier = url_modifier
     if not url_modifier:
         self._headers.update(location=location)
     for method in (methods or HTTP_METHODS):
         setattr(self, method.lower(), self._req_handler)
Пример #5
0
    def __init__(self, addr, reader, writer, **kwargs):
        self.__start_time = time.time()
        self._initialized = False
        self.cfg = AutoCFG(self.defaults).update_fields(kwargs)
        self.logger = logger.new_channel(f'{addr[0]}:{addr[1]}', parent='aeon')
        self._addr = addr
        self._reader = reader
        self._writer = writer
        self._source_ip = self._addr[0]
        self.port = self._addr[1]
        self.keep_alive = True

        self._real_ip = self._addr
        self._url = None
        self._args = {}
        self._method = None
        self._http_version = None
        self._headers = {}
        self._data = b''
        self._ssl = kwargs.get('ssl', False)
        self._send = False
        self._callback = {
            'before_send': [],
            'after_send': [],
        }
Пример #6
0
Файл: aeon.py Проект: moff4/k2
 async def emulate_request(
     self,
     url: str,
     headers: Dict[str, str] = None,
     args: Dict[str, str] = None,
     data: Optional[bytes] = None,
     method: str = 'GET',
     http_version: str = 'HTTP/1.1',
     _run_ware: bool = False,
 ) -> Response:
     """
         imulate incoming request
         usefull for testing
     """
     request = Request(
         addr=('127.0.0.1', 0),
         reader=None,
         writer=None,
         **self._request_prop,
     )
     request.init_from_dict(
         AutoCFG({
             'url': url,
             'headers': headers or {},
             'args': args or {},
             'method': method,
             'http_version': http_version,
             'data': data or b'',
         }))
     return await self._handle_request(request, _run_ware=_run_ware)
Пример #7
0
async def _request(method,
                   url,
                   params=None,
                   data=None,
                   json=None,
                   headers=None,
                   *a,
                   **b):
    url = urlparse(url)
    host, port = (url.netloc.split(':') if ':' in url.netloc else (
        url.netloc,
        80 if url.scheme == 'http' else 443,
    ))
    headers = AutoCFG(headers or {}).update_missing({
        'Connection': 'close',
    })
    async with ClientSession(
            host=host,
            port=int(port),
            ssl=url.scheme == 'https',
            *a,
            **b,
    ) as session:
        b.pop('timeout', None)
        return await session._request(
            method=method,
            url=url.path or '/',
            params=params,
            data=data,
            json=json,
            headers=headers,
            *a,
            **b,
        )
Пример #8
0
 def __init__(self, **kwargs):
     self.cfg = AutoCFG(
         {k: kwargs.get(k, v)
          for k, v in ABSTRACT_AEON_DEFAULTS.items()})
     if self.cfg.loop is None:
         self.cfg.loop = asyncio.get_event_loop()
     self._contexts = {}
     if self.cfg.ssl is None and self.cfg.certs:
         for host in self.cfg.certs:
             context = ssl.create_default_context(
                 purpose=ssl.Purpose.CLIENT_AUTH,
                 cafile=self.cfg.ca_cert,
             )
             context.load_cert_chain(
                 certfile=self.cfg.certs[host]['certfile'],
                 keyfile=self.cfg.certs[host]['keyfile'],
                 password=self.cfg.certs[host].get('keypassword', None),
             )
             self._contexts[host] = context
         self.cfg.ssl = ssl.create_default_context(
             purpose=ssl.Purpose.CLIENT_AUTH,
             cafile=self.cfg.ca_cert,
         )
         self.cfg.ssl.set_servername_callback(self.servername_callback)
     self._task = None
     self._server = None
     self._logger = logger.new_channel(
         key='aeon',
         parent=logger.get_channel('base_logger'),
         **self.cfg.logger,
     )
     self.running_tasks = []
Пример #9
0
 def __init__(self, request, **kwargs):
     super().__init__()
     self.cfg = AutoCFG(STATIC_RESPONSE_DEFAULTS).update_fields(kwargs)
     self.content_mod = None
     self.vars = dict({
         'req': request,
     }, **(kwargs.get('vars') or {}))
     self.req = request
     self._data = ''
     self._cached = False
Пример #10
0
 def add_cookie(self, name: str, value: str, *options, **kwoptions):
     """
         name - cookie name
         value - value of cookie
         options - boolean properties like HttpOnly and Secure
         kwoptions - kev-value properties like Max-Age and Domain
     """
     self._cookies[name] = {
         'value': value,
         'flags': set(options),
         'properties': AutoCFG(kwoptions)
     }
Пример #11
0
class NameSpace:
    TYPE_LEAFE = 0
    TYPE_SUBTREE = 1

    def __init__(self, tree=None) -> None:
        self._keys = AutoCFG()
        if tree:
            self.create_tree(tree)

    def __contains__(self, key) -> bool:
        return key in self._keys

    def __getitem__(self, key) -> Tuple[str, str]:
        return self._keys[key]['value'], self._keys[key]['type']

    def __setitem__(self, key: str, value) -> None:
        try:
            err = (not isinstance(
                value,
                (BaseSiteModule, WSHandler, NameSpace, Response, dict))) and (
                    not issubclass(value, WSHandler))
        except TypeError:
            err = True
        if err:
            raise TypeError(
                'value "{}" for key "{}" must be NameSpace, dict, BaseSiteModule, WSHandler or Response'
                .format(
                    value,
                    key,
                ))
        self._keys[key] = {
            'value':
            value,
            'type':
            (self.TYPE_SUBTREE if isinstance(value,
                                             (NameSpace,
                                              dict)) else self.TYPE_LEAFE)
        }

    def items(self):
        yield from self._keys.items()

    def find_best(
        self,
        name,
        args: Optional[Dict[str, Any]] = None
    ) -> Tuple[Optional[str], Optional[Dict[str, str]]]:
        args = args or {}
        if name in self._keys and self._keys[name]['type'] == self.TYPE_LEAFE:
            return self._keys[name]['value'], args

        if not (az := [(key, m)
                       for key in self._keys if (m := re.match(key, name))]):
Пример #12
0
Файл: task.py Проект: moff4/k2
 def __init__(self, target, **kwargs):
     if not (callable(target) or asyncio.iscoroutinefunction(target)):
         raise TypeError('target must be callable or async coroutine')
     self._cfg = AutoCFG(TASK_DEFAULTS).deep_update_fields(kwargs)
     self._target = target
     self._offset = (self._cfg.offset['hour'] * 60 +
                     self._cfg.offset['min']) * 60 + self._cfg.offset['sec']
     self._inter = (self._cfg.interval['hour'] * 60 + self._cfg.
                    interval['min']) * 60 + self._cfg.interval['sec']
     self.logger = logger.new_channel('planner_task_{}'.format(
         self._cfg.key),
                                      parent='planner')
     self._shedule = []
Пример #13
0
 def __init__(self, req, **kwargs):
     self.req = req
     self.alive = True
     self.rfile, self.wfile = req.get_rw()
     self.args = AutoCFG(kwargs)
     self.handler_map = {
         self.OPCODE_TEXT: self.handle_incoming_msg,
         self.OPCODE_BINARY: self.handle_incoming_bin,
         self.OPCODE_PING: self.send_pong,
         self.OPCODE_PONG: lambda msg: msg,
     }
     self.keep_alive = True
     self.handshake_done = False
     self.valid_client = False
Пример #14
0
Файл: parser.py Проект: moff4/k2
async def parse_response_data(reader, **kwargs):
    """
        take io stream and cfg and parse HTTP headers
        return dict of attributes/headers/url/args
    """
    cfg = AutoCFG(PARSE_RESPONSE_DEFAULTS).update_fields(kwargs)
    if reader.at_eof():
        raise ValueError('socket was closed')

    if not (st := (await readln(
            reader,
            max_len=cfg.max_status_length,
            ignore_zeros=True,
    )).decode()) or not st.startswith('HTTP/'):
        raise ValueError('Invalid protocol')
Пример #15
0
Файл: base.py Проект: moff4/k2
    def __init__(self, host, port, ssl=False, limit=None, loop=None, **kwargs):
        self._conn_args = {
            'host': host,
            'port': port,
            'ssl': ssl,
            **{k: v
               for k, v in kwargs.items() if k not in {'timeout'}},
        }
        if limit:
            self._conn_args['limit'] = limit
        if loop:
            self._conn_args['loop'] = loop

        self._rd = None
        self._wr = None
        self.cfg = AutoCFG(kwargs).update_missing(self.defaults)
        self._logger = None
Пример #16
0
    def __init__(self, **kwargs) -> None:
        self.cfg = AutoCFG(CHANNEL_DEFAULTS).update_fields(kwargs)
        if self.cfg.key is not None:
            if any((re.match(f'--stdout=.*{self.cfg.key}.*', x) is not None
                    for x in sys.argv)):
                self.cfg.stdout = True
            if any((re.match(f'--debug=.*{self.cfg.key}.*', x) is not None
                    for x in sys.argv)):
                self.cfg.log_levels.add('debug')
            if any((re.match(f'--no-log=.*{self.cfg.key}.*', x) is not None
                    for x in sys.argv)):
                self.cfg.autosave = False

        if self.cfg.autosave and self.cfg.log_file not in Locks:
            Locks[self.cfg.log_file] = Lock()
        self.parents = []  # type: List[Dict[str, Union[Channel, Iterable]]]
        self._logs = []  # type: List[Dict[str, Union[str, int]]]
        self._t = 0
        self._logger = logging.Logger(self.cfg.key)
Пример #17
0
Файл: parser.py Проект: moff4/k2
            max_len=cfg.max_status_length,
            ignore_zeros=True,
    )).decode()) or not st.startswith('HTTP/'):
        raise ValueError('Invalid protocol')

    version, code, *_ = st.split()

    if version not in cfg.expected_http_version:
        raise ValueError('unsupported protocol version "{}"'.format(version))

    if not code.isdigit():
        raise ValueError('status code is not integer "{}"'.format(code))

    if not (100 <= (code := int(code)) < 600):
        raise ValueError('Invalid status code')
    headers = AutoCFG(key_modifier=lambda x: x.lower())

    for _ in range(cfg.max_header_count):
        if not (st := (await readln(
                reader, max_len=cfg.max_header_length)).decode().strip()):
            break

        key, *value = st.split(':')
        if not value:
            raise ValueError('Invalid headers format')
        value = unquote(':'.join(value).strip())
        if (key := key.lower()) in headers:
            headers[key] = ', '.join([
                headers[key],
                value,
            ], )
Пример #18
0
Файл: base.py Проект: moff4/k2
 async def _request(self,
                    method,
                    url,
                    params=None,
                    data=None,
                    json=None,
                    headers=None,
                    **kwargs):
     params = params or {}
     (headers := AutoCFG(
         headers or {},
         key_modifier=lambda x: x.lower(),
     )).update_missing({
         'Host': self._conn_args['host'],
         'User-Agent': 'AeonClient/1.0',
         # FIXME
         # Cannot read data as Content-Length not passed
         # 'Accept-Encoding': 'gzip',
     })
     if not data and json:
         data = dumps(json)
         headers.update_missing({
             'Content-Type': 'application/json',
         })
     elif not data and method in {'POST', 'PUT', 'DELETE'} and params:
         data = urlencode(params)
         params = {}
     if data:
         if isinstance(data, str):
             data = data.encode()
         headers.update_missing({
             'Content-Length': len(data),
         })
     await self._logger.debug(
         'making request: {method} {host}:{port}{url} ? {params}',
         method=method,
         host=self._conn_args['host'],
         port=self._conn_args['port'],
         url=url,
         params=params,
     )
     self._wr.write(b'\r\n'.join([
         '{method} {url}{params} HTTP/1.1'.format(
             method=method,
             url=url,
             params=(''.join([
                 '?',
                 urlencode(params),
             ]) if params else ''),
         ).encode(),
     ] + ([
         f'{k}: {quote_from_bytes(v) if isinstance(v, bytes) else quote(str(v))}'
         .encode() for k, v in headers.items()
     ] if headers else []) + ([
         b'',
         data,
     ] if data else [
         b'',
         b'',
     ])))
     await self._wr.drain()
     return await asyncio.wait_for(
         self._read_answer(),
         timeout=kwargs.get('timeout') or self.cfg.timeout,
     )
Пример #19
0
 def __init__(self, tree=None) -> None:
     self._keys = AutoCFG()
     if tree:
         self.create_tree(tree)
Пример #20
0
 def __init__(self, **kwargs):
     self.cfg = AutoCFG(kwargs).update_missing(STATS_CGI_DEFAULTS)
Пример #21
0
class Response:
    """
        basic class for response
    """
    def __init__(self,
                 data=None,
                 headers=None,
                 code=200,
                 cookies=None,
                 http_version='HTTP/1.1'):
        if data is not None and not isinstance(data, (str, bytes)):
            raise TypeError('data must be str or bytes')

        if headers is not None and not isinstance(headers, dict):
            raise TypeError('headers must be dict')

        if not isinstance(code, int):
            raise TypeError('code must be int')

        # response data
        self._data = data or b''
        # dict of headers: {'Content-Type': 'text/html'}
        self._headers = AutoCFG(headers or {},
                                key_modifier=lambda x: x.lower())
        # HTTP status code: 200
        self._code = code
        # dict of dicts for Set-Cookie header:
        # {'uid': {'value': '123', 'flags':['HttpOnly'], 'properties': {'Path': '/'}}
        self._cookies = AutoCFG()
        if cookies:
            for ck in cookies:
                self.add_cookie(**ck)
        self._http_version = http_version

    async def _extra_prepare_data(self):
        return self.data

    async def _cache_n_zip(self, data):
        return data

    def __str__(self):
        return f'<Response: {self._code} {HTTP_CODE_MSG[self._code]}>'

    @property
    def code(self):
        return self._code

    @code.setter
    def code(self, code):
        if code not in HTTP_CODE_MSG:
            raise ValueError('Code must be in k2.utils.http.HTTP_CODE_MSG')
        self._code = code

    @property
    def http_version(self) -> str:
        return self._http_version

    @property
    def data(self):
        return self._data

    @property
    def cookies(self) -> AutoCFG:
        return self._cookies

    @property
    def headers(self) -> AutoCFG:
        return self._headers

    @data.setter
    def data(self, d):
        self._data = b'' if d is None else (
            d.encode() if isinstance(d, str) else d)

    def add_headers(self, *args, **kwargs):
        if any(not isinstance(i, (dict, tuple)) for i in args):
            raise TypeError('HTTP-header must be tuple of dict')

        if any(not isinstance(kwargs[i], str) for i in kwargs):
            raise TypeError('HTTP-header value must be string')

        self._headers.update(*args, **kwargs)

    def add_cookie(self, name: str, value: str, *options, **kwoptions):
        """
            name - cookie name
            value - value of cookie
            options - boolean properties like HttpOnly and Secure
            kwoptions - kev-value properties like Max-Age and Domain
        """
        self._cookies[name] = {
            'value': value,
            'flags': set(options),
            'properties': AutoCFG(kwoptions)
        }

    async def export(self) -> bytes:
        data = await self._cache_n_zip(data.encode() if isinstance(
            data := await self._extra_prepare_data(), str) else data)
        headers = self._headers.update_missing(STANDART_HEADERS)
        headers.update({'Content-Length': len(data)})
        return b''.join([
            '\r\n'.join([
                f'{self.http_version} '
                f'{204 if len(data) <= 0 and self.code in {200, 201} else self.code} '
                f'{HTTP_CODE_MSG[self.code]}',
                *[
                    ''.join([key, ': ', str(value)])
                    for key, value in headers.items() if key and value
                ],
                *[
                    'Set-Cookie: {}'.format('; '.join([
                        f'''{name}={quote(self._cookies[name]['value'])}''', *[
                            f'{key}={quote(str(val))}' for key, val in
                            self._cookies[name]['properties'].items()
                        ], *list(self._cookies[name]['flags'])
                    ])) for name in self._cookies
                ],
                '\r\n',
            ]).encode(),
            data,
        ])