Пример #1
0
    def __setitem__(self, key: str, value: str) -> None:
        """
        Set header using the bracket syntax. This operation would remove any existing header named after the key.
        Warning, if your value contain comma separated entries, it will split it into multiple Header instance.
        >>> headers = Headers()
        >>> headers.content_type = "application/json"
        >>> len(headers)
        1
        >>> headers.accept = "text/html, application/json;q=1.0"
        >>> len(headers)
        3
        """
        if not isinstance(value, str):
            raise TypeError(
                "Cannot assign header '{key}' using type {type_} to headers.".
                format(key=key, type_=type(value)))
        if key in self:
            del self[key]

        # Permit to detect multiple entries.
        if normalize_str(key) != "subject":

            entries: List[str] = header_content_split(value, ",")

            if len(entries) > 1:

                for entry in entries:
                    self._headers.append(Header(key, entry))

                return

        self._headers.append(Header(key, value))
Пример #2
0
    def test_esc_double_quote(self):

        with self.subTest(
                "Ensure that the double quote character is handled correctly."
        ):
            attributes = Attributes(
                header_content_split(r'text/html; charset="UTF-\"8"', ";"))

            self.assertEqual(attributes["charset"], 'UTF-"8')

            self.assertEqual(str(attributes), r'text/html; charset="UTF-\"8"')
Пример #3
0
 def __contains__(self, item: str) -> bool:
     """
     Verify if a string match a member or an attribute name of an Header.
     """
     if item in self.attrs:
         return True
     item = normalize_str(item)
     for attr in self.attrs:
         target = normalize_str(attr)
         if item == target or item in header_content_split(target, " "):
             return True
     return False
Пример #4
0
    def __init__(self, name: str, content: str):
        """
        :param name: The name of the header, should contain only ASCII characters with no spaces in it.
        :param content: Initial content associated with the header.
        """
        if not is_legal_header_name(name):
            raise ValueError(
                f"'{name}' is not a valid header name. Cannot proceed with it."
            )

        self._name: str = name
        self._normalized_name: str = normalize_str(self._name)
        self._pretty_name: str = prettify_header_name(self._name)
        self._content: str = content

        self._members: List[str] = header_content_split(self._content, ";")

        self._not_valued_attrs: List[str] = list()
        self._valued_attrs: MutableMapping[str, Union[
            str, List[str]]] = CaseInsensitiveDict()

        for member in self._members:
            if member == "":
                continue

            if "=" in member:
                key, value = tuple(member.split("=", maxsplit=1))

                # avoid confusing base64 look alike single value for (key, value)
                if value.count("=") == len(value) or len(
                        value) == 0 or " " in key:
                    self._not_valued_attrs.append(unquote(member))
                    continue

                if key not in self._valued_attrs:
                    self._valued_attrs[key] = value
                else:
                    if isinstance(self._valued_attrs[key], str):
                        self._valued_attrs[key] = [
                            self._valued_attrs[key], value
                        ]  # type: ignore
                    else:
                        self._valued_attrs[key].append(value)  # type: ignore

                continue

            self._not_valued_attrs.append(unquote(member))
Пример #5
0
def parse_it(raw_headers: Any) -> Headers:
    """
    Just decode anything that could contain headers. That simple PERIOD.
    :param raw_headers: Accept bytes, str, fp, dict, email.Message, requests.Response, urllib3.HTTPResponse and httpx.Response.
    :raises:
        TypeError: If passed argument cannot be parsed to extract headers from it.
    """

    headers: Optional[Iterable[Tuple[str, Any]]] = None

    if isinstance(raw_headers, str):
        headers = HeaderParser().parsestr(raw_headers,
                                          headersonly=True).items()
    elif isinstance(raw_headers, bytes) or isinstance(raw_headers, RawIOBase):
        decoded, not_decoded = extract_encoded_headers(
            raw_headers if isinstance(raw_headers, bytes
                                      ) else raw_headers.read() or b"")
        return parse_it(decoded)
    elif isinstance(raw_headers, Mapping) or isinstance(raw_headers, Message):
        headers = raw_headers.items()
    else:
        r = extract_class_name(type(raw_headers))

        if r:
            if r == "requests.models.Response":
                headers = []
                for header_name in raw_headers.raw.headers:
                    for header_content in raw_headers.raw.headers.getlist(
                            header_name):
                        headers.append((header_name, header_content))
            elif r in [
                    "httpx._models.Response", "urllib3.response.HTTPResponse"
            ]:
                headers = raw_headers.headers.items()

    if headers is None:
        raise TypeError(
            "Cannot parse type {type_} as it is not supported by kiss-header.".
            format(type_=type(raw_headers)))

    revised_headers: List[Tuple[str, str]] = decode_partials(headers)

    # Sometime raw content does not begin with headers. If that is the case, search for the next line.
    if (len(revised_headers) == 0 and len(raw_headers) > 0 and
        (isinstance(raw_headers, bytes) or isinstance(raw_headers, str))):
        next_iter = raw_headers.split(
            b"\n" if isinstance(raw_headers, bytes) else "\n",
            maxsplit=1  # type: ignore
        )

        if len(next_iter) >= 2:
            return parse_it(next_iter[-1])

    # Prepare Header objects
    list_of_headers: List[Header] = []

    for head, content in revised_headers:

        # We should ignore when a illegal name is considered as an header. We avoid ValueError (in __init__ of Header)
        if is_legal_header_name(head) is False:
            continue

        entries: List[str] = header_content_split(content, ",")

        # Multiple entries are detected in one content at the only exception that its not IMAP header "Subject".
        if len(entries) > 1 and normalize_str(head) != "subject":
            for entry in entries:
                list_of_headers.append(Header(head, entry))
        else:
            list_of_headers.append(Header(head, content))

    return Headers(*list_of_headers)