text: from urllib.parse import urlparse, ParseResult
  from castle.errors import ConfigurationError
  from castle.headers.format import HeadersFormat
  from castle.failover.strategy import FailoverStrategy
  
  DEFAULT_ALLOWLIST = [
      "Accept",
      "Accept-Charset",
      "Accept-Datetime",
      "Accept-Encoding",
      "Accept-Language",
      "Cache-Control",
      "Connection",
      "Content-Length",
      "Content-Type",
      "Dnt",
      "Host",
      "Origin",
      "Pragma",
      "Referer",
      "Sec-Fetch-Dest",
      "Sec-Fetch-Mode",
      "Sec-Fetch-Site",
      "Sec-Fetch-User",
      "TE",
      "Upgrade-Insecure-Requests",
      "User-Agent",
      "X-Castle-Client-Id",
      "X-Requested-With",
  ]
  
  # API endpoint
  BASE_URL = 'https://api.castle.io/v1'
  FAILOVER_STRATEGY = FailoverStrategy.ALLOW.value
  # 1000 milliseconds
  REQUEST_TIMEOUT = 1000
  FAILOVER_STRATEGIES = FailoverStrategy.list()
  # regexp of trusted proxies which is always appended to the trusted proxy list
  TRUSTED_PROXIES = [r"""
          \A127\.0\.0\.1\Z|
          \A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|
          \A::1\Z|\Afd[0-9a-f]{2}:.+|
          \Alocalhost\Z|
          \Aunix\Z|
          \Aunix:"""]
  
  
  class Configuration(object):
      def __init__(self):
          self.request_timeout = REQUEST_TIMEOUT
          self.failover_strategy = FAILOVER_STRATEGY
          self.base_url = urlparse(BASE_URL)
          self.allowlisted = []
          self.denylisted = []
          self.api_secret = None
          self.ip_headers = []
          self.trusted_proxies = []
          self.trust_proxy_chain = False
          self.trusted_proxy_depth = None
          self.logger = None
  
      def isValid(self):
          return self.api_secret and self.base_url.hostname
  
      @property
      def request_timeout(self):
          return self.__request_timeout
  
      @request_timeout.setter
      def request_timeout(self, value):
          self.__request_timeout = value
  
      @property
      def failover_strategy(self):
          return self.__failover_strategy
  
      @failover_strategy.setter
      def failover_strategy(self, value):
          if value in FAILOVER_STRATEGIES:
              self.__failover_strategy = value
          else:
              raise ConfigurationError
  
      @property
      def base_url(self):
          return self.__base_url
  
      @base_url.setter
      def base_url(self, value):
          if isinstance(value, ParseResult):
              self.__base_url = value
          else:
              self.__base_url = urlparse(value)
  
      @property
      def allowlisted(self):
          return self.__allowlisted
  
      @allowlisted.setter
      def allowlisted(self, value):
          if value:
              self.__allowlisted = [HeadersFormat.call(v) for v in value]
          else:
              self.__allowlisted = []
  
      @property
      def denylisted(self):
          return self.__denylisted
  
      @denylisted.setter
      def denylisted(self, value):
          if value:
              self.__denylisted = [HeadersFormat.call(v) for v in value]
          else:
              self.__denylisted = []
  
      @property
      def api_secret(self):
          return self.__api_secret
  
      @api_secret.setter
      def api_secret(self, value):
          self.__api_secret = value
  
      @property
      def ip_headers(self):
          return self.__ip_headers
  
      @ip_headers.setter
      def ip_headers(self, value):
          if isinstance(value, list):
              self.__ip_headers = [HeadersFormat.call(v) for v in value]
          else:
              raise ConfigurationError
  
      @property
      def trusted_proxies(self):
          return self.__trusted_proxies
  
      @trusted_proxies.setter
      def trusted_proxies(self, value):
          if isinstance(value, list):
              self.__trusted_proxies = value
          else:
              raise ConfigurationError
  
      @property
      def trust_proxy_chain(self):
          return self.__trust_proxy_chain
  
      @trust_proxy_chain.setter
      def trust_proxy_chain(self, value):
          if isinstance(value, bool):
              self.__trust_proxy_chain = value
          else:
              raise ConfigurationError
  
      @property
      def trusted_proxy_depth(self):
          return self.__trusted_proxy_depth
  
      @trusted_proxy_depth.setter
      def trusted_proxy_depth(self, value):
          if isinstance(value, (int, type(None))):
              self.__trusted_proxy_depth = int(0 if value is None else value)
          else:
              raise ConfigurationError
  
      @property
      def logger(self):
          return self.__logger
  
      @logger.setter
      def logger(self, value):
          self.__logger = value
  
  
  class SingletonConfiguration(Configuration):
  
      instance = None
  
      def __new__(cls, *args, **kwargs):
          if not SingletonConfiguration.instance:
              SingletonConfiguration.instance = super().__new__(cls, *args, **kwargs)
          return SingletonConfiguration.instance
  
  
  configuration = SingletonConfiguration()
  