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