Skip to content

Commit

Permalink
Merge pull request #195 from nekokatt/bugfix/windows-os-error
Browse files Browse the repository at this point in the history
Bugfix/windows os error
  • Loading branch information
Nekokatt authored Sep 16, 2020
2 parents d3ac417 + d0a221f commit 8a2627b
Show file tree
Hide file tree
Showing 9 changed files with 565 additions and 178 deletions.
303 changes: 263 additions & 40 deletions hikari/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
]

import base64
import ssl as ssl_
import typing

import attr
import yarl

from hikari.utilities import attr_extensions
from hikari.utilities import data_binding
Expand All @@ -42,52 +44,136 @@
_PROXY_AUTHENTICATION_HEADER: typing.Final[str] = "Proxy-Authentication"


def _ssl_factory(value: typing.Union[bool, ssl_.SSLContext]) -> ssl_.SSLContext:
if isinstance(value, bool):
ssl = ssl_.create_default_context()
# We can't turn SSL verification off without disabling hostname verification first.
# If we are using verification, this will just leave it enabled, so it is fine.
ssl.check_hostname = value
ssl.verify_mode = ssl_.CERT_REQUIRED if value else ssl_.CERT_NONE
else:
ssl = value
return ssl


@attr_extensions.with_copy
@attr.s(slots=True, kw_only=True, repr=False, weakref_slot=False)
@attr.s(slots=True, kw_only=True, repr=True, weakref_slot=False)
class BasicAuthHeader:
"""An object that can be set as a producer for a basic auth header."""

username: str = attr.ib()
"""Username for the header."""
username: str = attr.ib(validator=attr.validators.instance_of(str))
"""Username for the header.
Returns
-------
builtins.str
The username to use. This must not contain `":"`.
"""

password: str = attr.ib(repr=False, validator=attr.validators.instance_of(str))
"""Password to use.
Returns
-------
builtins.str
The password to use.
"""

charset: str = attr.ib(default="utf-8", validator=attr.validators.instance_of(str))
"""Encoding to use for the username and password.
Default is `"utf-8"`, but you may choose to use something else,
including third-party encodings (e.g. IBM's EBCDIC codepages).
password: str = attr.ib()
"""Password for the header."""
Returns
-------
builtins.str
The encoding to use.
"""

@property
def header(self) -> str:
"""Generate the header value and return it."""
raw_token = f"{self.username}:{self.password}".encode("ascii")
token_part = base64.b64encode(raw_token).decode("ascii")
"""Create the full `Authentication` header value.
Returns
-------
builtins.str
A base64-encoded string containing
`"{username}:{password}`.
"""
raw_token = f"{self.username}:{self.password}".encode(self.charset)
token_part = base64.b64encode(raw_token).decode(self.charset)
return f"{_BASICAUTH_TOKEN_PREFIX} {token_part}"

def __str__(self) -> str:
return self.header

__repr__ = __str__


@attr_extensions.with_copy
@attr.s(slots=True, kw_only=True, weakref_slot=False)
class ProxySettings:
"""The proxy settings to use."""
"""Settings for configuring an HTTP-based proxy."""

auth: typing.Any = attr.ib(default=None)
"""Authentication header value to use.
When cast to a `builtins.str`, this should provide the full value
for the authentication header.
If you are using basic auth, you should consider using the
`BasicAuthHeader` helper object here, as this will provide any
transformations you may require into a base64 string.
auth: typing.Optional[typing.Any] = attr.ib(default=None)
"""An object that when cast to a string, yields the proxy auth header."""
The default is to have this set to `builtins.None`, which will
result in no authentication being provided.
Returns
-------
typing.Any
The value for the `Authentication` header, or `builtins.None`
to disable.
"""

headers: typing.Optional[data_binding.Headers] = attr.ib(default=None)
"""Additional headers to use for requests via a proxy, if required."""

url: typing.Optional[str] = attr.ib(default=None)
"""The URL of the proxy to use."""
url: typing.Union[None, str, yarl.URL] = attr.ib(default=None)
"""Proxy URL to use.
trust_env: bool = attr.ib(default=False)
"""If `builtins.True`, and no proxy info is given, then `HTTP_PROXY` and
`HTTPS_PROXY` will be used from the environment variables if present.
Defaults to `builtins.None` which disables the use of an explicit proxy.
Returns
-------
typing.Union[builtins.None, builtins.str, yarl.URL]
The proxy URL to use, or `builtins.None` to disable it.
"""

@url.validator
def _(self, _: attr.Attribute[typing.Optional[str]], value: typing.Optional[str]) -> None:
if value is not None and not isinstance(value, (str, yarl.URL)):
raise TypeError("ProxySettings.url must be None, a str, or a yarl.URL instance")

trust_env: bool = attr.ib(default=False, validator=attr.validators.instance_of(bool))
"""Toggle whether to look for a `netrc` file or environment variables.
If `builtins.True`, and no `url` is given on this object, then
`HTTP_PROXY` and `HTTPS_PROXY` will be used from the environment
variables, or a `netrc` file may be read to determine credentials.
Any proxy credentials will be read from the user's `netrc` file
(https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html)
If `builtins.False`, then this information is instead ignored.
Defaults to `builtins.False` if unspecified.
Defaults to `builtins.False` to prevent potentially unwanted behavior.
!!! note
For more details of using `netrc`, visit:
https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
Returns
-------
builtins.bool
`builtins.True` if allowing the use of environment variables
and/or `netrc` to determine proxy settings; `builtins.False`
if this should be disabled explicitly.
"""

@property
Expand Down Expand Up @@ -116,45 +202,182 @@ class HTTPTimeoutSettings:
"""Settings to control HTTP request timeouts."""

acquire_and_connect: typing.Optional[float] = attr.ib(default=None)
"""Timeout for `request_socket_connect` PLUS connection acquisition."""
"""Timeout for `request_socket_connect` PLUS connection acquisition.
By default, this has no timeout allocated.
Returns
-------
typing.Optional[builtins.float]
The timeout, or `builtins.None` to disable it.
"""

request_socket_connect: typing.Optional[float] = attr.ib(default=None)
"""Timeout for connecting a socket."""
"""Timeout for connecting a socket.
By default, this has no timeout allocated.
Returns
-------
typing.Optional[builtins.float]
The timeout, or `builtins.None` to disable it.
"""

request_socket_read: typing.Optional[float] = attr.ib(default=None)
"""Timeout for reading a socket."""
"""Timeout for reading a socket.
By default, this has no timeout allocated.
Returns
-------
typing.Optional[builtins.float]
The timeout, or `builtins.None` to disable it.
"""

total: typing.Optional[float] = attr.ib(default=30.0)
"""Total timeout for entire request.
Defaults to 30 seconds.
By default, this has a 30 second timeout allocated.
Returns
-------
typing.Optional[builtins.float]
The timeout, or `builtins.None` to disable it.
"""

@acquire_and_connect.validator
@request_socket_connect.validator
@request_socket_read.validator
@total.validator
def _(self, attrib: attr.Attribute[typing.Optional[float]], value: typing.Optional[float]) -> None:
# This error won't occur until some time in the future where it will be annoying to
# try and determine the root cause, so validate it NOW.
if value is not None and (not isinstance(value, (float, int)) or value <= 0): # type: ignore[unreachable]
raise ValueError(f"HTTPTimeoutSettings.{attrib.name} must be None, or a POSITIVE float/int")


@attr_extensions.with_copy
@attr.s(slots=True, kw_only=True, weakref_slot=False)
class HTTPSettings:
"""Settings to control the HTTP client."""
"""Settings to control HTTP clients."""

enable_cleanup_closed: bool = attr.ib(default=True, validator=attr.validators.instance_of(bool))
"""Toggle whether to clean up closed transports.
allow_redirects: bool = attr.ib(default=False)
"""If `builtins.True`, allow following redirects from `3xx` HTTP responses.
This defaults to `builtins.True` to combat various protocol and asyncio
issues present when using Microsoft Windows. If you are sure you know
what you are doing, you may instead set this to `False` to disable this
behavior internally.
Generally you do not want to enable this unless you have a good reason to.
Returns
-------
builtins.bool
`builtins.True` to enable this behavior, `builtins.False` to disable
it.
"""

max_redirects: int = attr.ib(default=10)
"""The maximum number of redirects to allow.
force_close_transports: bool = attr.ib(default=True, validator=attr.validators.instance_of(bool))
"""Toggle whether to force close transports on shutdown.
If `allow_redirects` is `builtins.False`, then this is ignored.
This defaults to `builtins.True` to combat various protocol and asyncio
issues present when using Microsoft Windows. If you are sure you know
what you are doing, you may instead set this to `False` to disable this
behavior internally.
Returns
-------
builtins.bool
`builtins.True` to enable this behavior, `builtins.False` to disable
it.
"""

timeouts: HTTPTimeoutSettings = attr.ib(factory=HTTPTimeoutSettings)
"""Settings to control HTTP request timeouts."""
max_redirects: typing.Optional[int] = attr.ib(default=10)
"""Behavior for handling redirect HTTP responses.
If a `builtins.int`, allow following redirects from `3xx` HTTP responses
for up to this many redirects. Exceeding this value will raise an
exception.
If `builtins.None`, then disallow any redirects.
The default is to disallow this behavior for security reasons.
Generally, it is safer to keep this disabled. You may find a case in the
future where you need to enable this if Discord change their URL without
warning.
!!! note
This will only apply to the REST API. WebSockets remain unaffected
by any value set here.
Returns
-------
typing.Optional[builtins.int]
The number of redirects to allow at a maximum per request.
`builtins.None` disables the handling
of redirects and will result in exceptions being raised instead
should one occur.
"""

@max_redirects.validator
def _(self, _: attr.Attribute[typing.Optional[int]], value: typing.Optional[int]) -> None:
# This error won't occur until some time in the future where it will be annoying to
# try and determine the root cause, so validate it NOW.
if value is not None and (not isinstance(value, int) or value <= 0): # type: ignore[unreachable]
raise ValueError("http_settings.max_redirects must be None or a POSITIVE integer")

ssl: ssl_.SSLContext = attr.ib(
default=True,
converter=_ssl_factory,
validator=attr.validators.instance_of(ssl_.SSLContext), # type: ignore[assignment,arg-type]
)
"""SSL context to use.
This may be __assigned__ a `builtins.bool` or an `ssl.SSLContext` object.
If assigned to `builtins.True`, a default SSL context is generated by
this class that will enforce SSL verification. This is then stored in
this field.
If `builtins.False`, then a default SSL context is generated by this
class that will **NOT** enforce SSL verification. This is then stored
in this field.
If an instance of `ssl.SSLContext`, then this context will be used.
!!! warning
Setting a custom value here may have security implications, or
may result in the application being unable to connect to Discord
at all.
!!! warning
Disabling SSL verification is almost always unadvised. This
is because your application will no longer check whether you are
connecting to Discord, or to some third party spoof designed
to steal personal credentials such as your application token.
There may be cases where SSL certificates do not get updated,
and in this case, you may find that disabling this explicitly
allows you to work around any issues that are occurring, but
you should immediately seek a better solution where possible
if any form of personal security is in your interest.
Returns
-------
ssl.SSLContext
The SSL context to use for this application.
"""

timeouts: HTTPTimeoutSettings = attr.ib(
factory=HTTPTimeoutSettings, validator=attr.validators.instance_of(HTTPTimeoutSettings)
)
"""Settings to control HTTP request timeouts.
The behaviour if this is not explicitly defined is to use sane
defaults that are most efficient for optimal use of this library.
verify_ssl: bool = attr.ib(default=True)
"""If `builtins.True`, then responses with invalid SSL certificates will be
rejected. Generally you want to keep this enabled unless you have a
problem with SSL and you know exactly what you are doing by disabling
this. Disabling SSL verification can have major security implications.
You turn this off at your own risk.
Returns
-------
HTTPTimeoutSettings
The HTTP timeout settings to use for connection timeouts.
"""
Loading

0 comments on commit 8a2627b

Please sign in to comment.