forgot to add misc.py
This commit is contained in:
parent
e7d2b87a45
commit
5f79d2796f
300
izzylib/http_server_async/misc.py
Normal file
300
izzylib/http_server_async/misc.py
Normal file
|
@ -0,0 +1,300 @@
|
|||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from .. import logging, boolean
|
||||
from ..dotdict import DotDict
|
||||
from ..path import Path
|
||||
|
||||
|
||||
UtcTime = timezone.utc
|
||||
LocalTime = datetime.now(UtcTime).astimezone().tzinfo
|
||||
|
||||
cookie_params = {
|
||||
'Expires': 'expires',
|
||||
'Max-Age': 'maxage',
|
||||
'Domain': 'domain',
|
||||
'Path': 'path',
|
||||
'SameSite': 'samesite',
|
||||
'Secure': 'secure',
|
||||
'HttpOnly': 'httponly'
|
||||
}
|
||||
|
||||
|
||||
class Cookies(DotDict):
|
||||
def __setitem__(self, key, value):
|
||||
if type(value) != CookieItem:
|
||||
value = CookieItem(key, value)
|
||||
|
||||
super().__setitem__(key, value)
|
||||
|
||||
|
||||
class Headers(DotDict):
|
||||
def __getattr__(self, key):
|
||||
return self[key.replace('_', '-')]
|
||||
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self[key.replace('_', '-')] = value
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
return super().__getitem__(key.title())
|
||||
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = key.title()
|
||||
|
||||
if key in ['Cookie', 'Set-Cookie']:
|
||||
logging.warning('Do not set the "Cookie" or "Set-Cookie" headers')
|
||||
return
|
||||
|
||||
try:
|
||||
self[key].append(value)
|
||||
|
||||
except KeyError:
|
||||
super().__setitem__(key, HeaderItem(key, value))
|
||||
|
||||
|
||||
def getone(self, key, default=None):
|
||||
try:
|
||||
return self[key].one()
|
||||
|
||||
except (KeyError, IndexError):
|
||||
return default
|
||||
|
||||
|
||||
def setall(self, key, value):
|
||||
self[key].set(value)
|
||||
|
||||
|
||||
#def update(self, data):
|
||||
#for k,v in data.items():
|
||||
#self.__setitem__(k,v)
|
||||
|
||||
|
||||
class CookieItem:
|
||||
def __init__(self, key, value, **kwargs):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.args = DotDict()
|
||||
|
||||
for k,v in kwargs.items():
|
||||
if k not in cookie_params.values():
|
||||
raise AttributeError(f'Not a valid cookie parameter: {key}')
|
||||
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
text = f'{self.key}={self.value}'
|
||||
|
||||
if self.expires:
|
||||
text += f'; Expires={self.expires.strftime("%a, %d %b %Y %H:%M:%S GMT")}'
|
||||
|
||||
if self.maxage != None:
|
||||
text += f'; Max-Age={self.maxage}'
|
||||
|
||||
if self.domain:
|
||||
text += f'; Domain={self.domain}'
|
||||
|
||||
if self.path:
|
||||
text += f'; Path={self.path}'
|
||||
|
||||
if self.samesite:
|
||||
text += f'; SameSite={self.samesite}'
|
||||
|
||||
if self.secure:
|
||||
text += f'; Secure'
|
||||
|
||||
if self.httponly:
|
||||
text += f'; HttpOnly'
|
||||
|
||||
return text
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, data):
|
||||
kwargs = {}
|
||||
|
||||
for idx, pairs in enumerate(data.split(';')):
|
||||
try:
|
||||
k, v = pairs.split('=', 1)
|
||||
v = v.strip()
|
||||
|
||||
except:
|
||||
k, v = pairs, True
|
||||
|
||||
k = k.replace(' ', '')
|
||||
|
||||
if isinstance(v, str) and v.startswith('"') and v.endswith('"'):
|
||||
v = v[1:-1]
|
||||
|
||||
if idx == 0:
|
||||
key = k
|
||||
value = v
|
||||
|
||||
elif k in cookie_params.keys():
|
||||
kwargs[cookie_params[k]] = v
|
||||
|
||||
else:
|
||||
logging.info(f'Invalid key/value pair for cookie: {k} = {v}')
|
||||
|
||||
return cls(key, value, **kwargs)
|
||||
|
||||
|
||||
@property
|
||||
def expires(self):
|
||||
return self.args.get('Expires')
|
||||
|
||||
|
||||
@expires.setter
|
||||
def expires(self, data):
|
||||
if isinstance(data, str):
|
||||
data = datetime.strptime(data, '%a, %d %b %Y %H:%M:%S GMT').replace(tzinfo=UtcTime)
|
||||
|
||||
elif isinstance(data, int) or isinstance(data, float):
|
||||
data = datetime.fromtimestamp(data).replace(tzinfo=UtcTime)
|
||||
|
||||
elif isinstance(data, datetime):
|
||||
if not data.tzinfo:
|
||||
data = data.replace(tzinfo=UtcTime)
|
||||
|
||||
elif isinstance(data, timedelta):
|
||||
data = datetime.now(UtcTime) + data
|
||||
|
||||
else:
|
||||
raise TypeError(f'Expires must be a http date string, timestamp, or datetime object, not {data.__class__.__name__}')
|
||||
|
||||
self.args['Expires'] = data
|
||||
|
||||
|
||||
@property
|
||||
def maxage(self):
|
||||
return self.args.get('Max-Age')
|
||||
|
||||
|
||||
@maxage.setter
|
||||
def maxage(self, data):
|
||||
if isinstance(data, int):
|
||||
pass
|
||||
|
||||
elif isinstance(date, timedelta):
|
||||
data = data.seconds
|
||||
|
||||
elif isinstance(date, datetime):
|
||||
data = (datetime.now() - date).seconds
|
||||
|
||||
else:
|
||||
raise TypeError(f'Max-Age must be an integer, timedelta object, or datetime object, not {data.__class__.__name__}')
|
||||
|
||||
self.args['Max-Age'] = data
|
||||
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return self.args.get('Domain')
|
||||
|
||||
|
||||
@domain.setter
|
||||
def domain(self, data):
|
||||
if not isinstance(data, str):
|
||||
raise ValueError(f'Domain must be a string, not {data.__class__.__name__}')
|
||||
|
||||
self.args['Domain'] = data
|
||||
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.args.get('Path')
|
||||
|
||||
|
||||
@path.setter
|
||||
def path(self, data):
|
||||
if not isinstance(data, str):
|
||||
raise ValueError(f'Path must be a string or izzylib.Path object, not {data.__class__.__name__}')
|
||||
|
||||
self.args['Path'] = Path(data)
|
||||
|
||||
|
||||
@property
|
||||
def secure(self):
|
||||
return self.args.get('Secure')
|
||||
|
||||
|
||||
@secure.setter
|
||||
def secure(self, data):
|
||||
self.args['Secure'] = boolean(data)
|
||||
|
||||
|
||||
@property
|
||||
def httponly(self):
|
||||
return self.args.get('HttpOnly')
|
||||
|
||||
|
||||
@httponly.setter
|
||||
def httponly(self, data):
|
||||
self.args['HttpOnly'] = boolean(data)
|
||||
|
||||
|
||||
@property
|
||||
def samesite(self):
|
||||
return self.args.get('SameSite')
|
||||
|
||||
|
||||
@samesite.setter
|
||||
def samesite(self, data):
|
||||
if isinstance(data, bool):
|
||||
data = 'Strict' if data else 'None'
|
||||
|
||||
elif isinstance(data, str) and data.title() in ['Strict', 'Lax', 'None']:
|
||||
data = data.title()
|
||||
|
||||
else:
|
||||
raise TypeError(f'SameSite must be a boolean or one of Strict, Lax, or None, not {data.__class__.__name__}')
|
||||
|
||||
self.args['SameSite'] = data
|
||||
self.args['Secure'] = True
|
||||
|
||||
|
||||
def as_dict(self):
|
||||
data = DotDict({self.key: self.value})
|
||||
data.update(self.args)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def set_defaults(self):
|
||||
for key in list(self.args.keys()):
|
||||
del self.args[key]
|
||||
|
||||
|
||||
def set_delete(self):
|
||||
self.args.pop('Expires', None)
|
||||
self.maxage = 0
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class HeaderItem(list):
|
||||
def __init__(self, key, values):
|
||||
super().__init__()
|
||||
self.update(values)
|
||||
|
||||
self.key = key
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return ','.join(str(v) for v in self)
|
||||
|
||||
|
||||
def set(self, *value):
|
||||
self.clear()
|
||||
self.append(value)
|
||||
|
||||
|
||||
def one(self):
|
||||
return self[0]
|
||||
|
||||
|
||||
def update(self, *items):
|
||||
for item in items:
|
||||
self.append(item)
|
Loading…
Reference in a new issue