a bunch
This commit is contained in:
parent
00a5b03995
commit
b4615af139
|
@ -17,7 +17,7 @@ izzylog = logging.logger['IzzyLib']
|
||||||
from .path import Path
|
from .path import Path
|
||||||
from .dotdict import DotDict, LowerDotDict, DefaultDotDict, MultiDotDict, JsonEncoder
|
from .dotdict import DotDict, LowerDotDict, DefaultDotDict, MultiDotDict, JsonEncoder
|
||||||
from .misc import *
|
from .misc import *
|
||||||
from .cache import LruCache, TtlCache
|
from .cache import CacheDecorator, LruCache, TtlCache
|
||||||
from .connection import Connection
|
from .connection import Connection
|
||||||
|
|
||||||
from .http_urllib_client import HttpUrllibClient, HttpUrllibResponse
|
from .http_urllib_client import HttpUrllibClient, HttpUrllibResponse
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import re
|
import json, re
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from functools import wraps
|
from functools import update_wrapper
|
||||||
|
from hashlib import sha1
|
||||||
|
|
||||||
from . import DotDict
|
from . import DotDict
|
||||||
|
|
||||||
|
@ -43,17 +44,24 @@ def parse_ttl(ttl):
|
||||||
|
|
||||||
class BaseCache(OrderedDict):
|
class BaseCache(OrderedDict):
|
||||||
_get = OrderedDict.get
|
_get = OrderedDict.get
|
||||||
|
_items = OrderedDict.items
|
||||||
def __init__(self, maxsize=1024, ttl=None):
|
def __init__(self, maxsize=1024, ttl=None):
|
||||||
self.ttl = parse_ttl(ttl)
|
self.ttl = parse_ttl(ttl)
|
||||||
self.maxsize = maxsize
|
self.maxsize = maxsize
|
||||||
self.set = self.store
|
self.set = self.store
|
||||||
|
|
||||||
|
self.deco = lambda *args: CacheDecorator(self, *args)
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
data = ', '.join([f'{k}="{v["data"]}"' for k,v in self.items()])
|
data = ', '.join([f'{k}="{v}"' for k,v in self.items()])
|
||||||
return f'BaseCache({data})'
|
return f'BaseCache({data})'
|
||||||
|
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return [[k, v.data] for k,v in self._items()]
|
||||||
|
|
||||||
|
|
||||||
def get(self, key):
|
def get(self, key):
|
||||||
while len(self) >= self.maxsize and self.maxsize > 0:
|
while len(self) >= self.maxsize and self.maxsize > 0:
|
||||||
self.popitem(last=False)
|
self.popitem(last=False)
|
||||||
|
@ -109,24 +117,29 @@ class BaseCache(OrderedDict):
|
||||||
self[key]['timestamp'] = timestamp + self.ttl
|
self[key]['timestamp'] = timestamp + self.ttl
|
||||||
|
|
||||||
self.move_to_end(key)
|
self.move_to_end(key)
|
||||||
return self[key].data
|
return item.data
|
||||||
|
|
||||||
|
|
||||||
## Was gonna use this for db stuff, but I need to plan it out better
|
## This doesn't work for some reason
|
||||||
def decorator(function, key, arg=0):
|
def CacheDecorator(cache):
|
||||||
@wraps(function)
|
def decorator(func):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(cls, *args, **kwargs):
|
||||||
cached = self.fetch(key)
|
key = sha1(json.dumps(args).encode() + json.dumps(kwargs).encode()).hexdigest()
|
||||||
|
cached = cache.fetch(key)
|
||||||
|
print(cached)
|
||||||
|
|
||||||
if cached:
|
if cached != None:
|
||||||
|
print('Returning cached value:', cache)
|
||||||
return cached
|
return cached
|
||||||
|
|
||||||
result = function(*args, **kwargs)
|
result = func(cls, *args, **kwargs)
|
||||||
|
|
||||||
self.store(key, args[arg] if type(arg) == int else kwargs[arg])
|
cache.store(key, result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class TtlCache(BaseCache):
|
class TtlCache(BaseCache):
|
||||||
|
|
|
@ -95,7 +95,7 @@ def catch_kb_interrupt(function, *args, **kwargs):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
function(*args, **kwargs)
|
return function(*args, **kwargs)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
izzylog.verbose('Bye! UvU')
|
izzylog.verbose('Bye! UvU')
|
||||||
|
|
1
hasher/izzylib/hasher/__init__.py
Normal file
1
hasher/izzylib/hasher/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .hasher import PasswordHasher
|
|
@ -54,7 +54,7 @@ class PasswordHasher:
|
||||||
|
|
||||||
|
|
||||||
def hash(self, password: str):
|
def hash(self, password: str):
|
||||||
return super().hash(password)
|
return self.hasher.hash(password)
|
||||||
|
|
||||||
|
|
||||||
def verify(self, passhash: str, password: str):
|
def verify(self, passhash: str, password: str):
|
|
@ -3,18 +3,14 @@ from setuptools import setup, find_namespace_packages
|
||||||
|
|
||||||
|
|
||||||
requires = [
|
requires = [
|
||||||
'argon2-cffi==20.1.0',
|
'argon2-cffi==20.1.0'
|
||||||
'pillow==8.2.0',
|
|
||||||
'pycryptodome==3.10.1',
|
|
||||||
'sanic==21.2.4',
|
|
||||||
'Sanic-Cors==1.0.0'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="IzzyLib HTTP Server",
|
name="IzzyLib Password Hasher",
|
||||||
version='0.6.0',
|
version='0.6.0',
|
||||||
packages=find_namespace_packages(include=['izzylib.http_server']),
|
packages=find_namespace_packages(include=['izzylib.hasher']),
|
||||||
python_requires='>=3.7.0',
|
python_requires='>=3.7.0',
|
||||||
install_requires=requires,
|
install_requires=requires,
|
||||||
include_package_data=False,
|
include_package_data=False,
|
|
@ -1,2 +0,0 @@
|
||||||
from .hasher import PasswordHasher
|
|
||||||
from .server import HttpServer, HttpServerRequest, HttpServerResponse
|
|
|
@ -1,331 +0,0 @@
|
||||||
# probably gonna remove this since I'll be using my asgi framework
|
|
||||||
import multiprocessing, sanic, signal, traceback
|
|
||||||
import logging as pylog
|
|
||||||
|
|
||||||
from jinja2.exceptions import TemplateNotFound
|
|
||||||
from multidict import CIMultiDict
|
|
||||||
from multiprocessing import cpu_count, current_process
|
|
||||||
from sanic.views import HTTPMethodView
|
|
||||||
from urllib.parse import parse_qsl, urlparse
|
|
||||||
|
|
||||||
from . import http, izzylog
|
|
||||||
from .misc import DotDict, DefaultDict, LowerDotDict
|
|
||||||
from .template import Template
|
|
||||||
|
|
||||||
|
|
||||||
log_path_ignore = [
|
|
||||||
'/media',
|
|
||||||
'/static'
|
|
||||||
]
|
|
||||||
|
|
||||||
log_ext_ignore = [
|
|
||||||
'js', 'ttf', 'woff2',
|
|
||||||
'ac3', 'aiff', 'flac', 'm4a', 'mp3', 'ogg', 'wav', 'wma',
|
|
||||||
'apng', 'ico', 'jpeg', 'jpg', 'png', 'svg',
|
|
||||||
'divx', 'mov', 'mp4', 'webm', 'wmv'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class HttpServer(sanic.Sanic):
|
|
||||||
def __init__(self, name='sanic', host='0.0.0.0', port='4080', **kwargs):
|
|
||||||
self.host = host
|
|
||||||
self.port = int(port)
|
|
||||||
self.workers = int(kwargs.get('workers', cpu_count()))
|
|
||||||
self.sig_handler = kwargs.get('sig_handler')
|
|
||||||
|
|
||||||
super().__init__(name, request_class=kwargs.get('request_class', HttpServerRequest))
|
|
||||||
|
|
||||||
for log in ['sanic.root', 'sanic.access']:
|
|
||||||
pylog.getLogger(log).setLevel(pylog.ERROR)
|
|
||||||
|
|
||||||
self.template = Template(
|
|
||||||
kwargs.get('tpl_search', []),
|
|
||||||
kwargs.get('tpl_globals', {}),
|
|
||||||
kwargs.get('tpl_context'),
|
|
||||||
kwargs.get('tpl_autoescape', True)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.template.addEnv('app', self)
|
|
||||||
|
|
||||||
self.error_handler.add(TemplateNotFound, NoTemplateError)
|
|
||||||
self.error_handler.add(Exception, kwargs.get('error_handler', GenericError))
|
|
||||||
self.register_middleware(MiddlewareAccessLog, attach_to='response')
|
|
||||||
|
|
||||||
signal.signal(signal.SIGHUP, self.finish)
|
|
||||||
signal.signal(signal.SIGINT, self.finish)
|
|
||||||
signal.signal(signal.SIGQUIT, self.finish)
|
|
||||||
signal.signal(signal.SIGTERM, self.finish)
|
|
||||||
|
|
||||||
|
|
||||||
## Sanic spits out a warning, so this is the workaround to stop it
|
|
||||||
def __setattr__(self, key, value):
|
|
||||||
object.__setattr__(self, key, value)
|
|
||||||
|
|
||||||
|
|
||||||
def add_method_route(self, method, *routes):
|
|
||||||
for route in routes:
|
|
||||||
self.add_route(method.as_view(), route)
|
|
||||||
|
|
||||||
|
|
||||||
def add_method_routes(self, routes: list):
|
|
||||||
for route in routes:
|
|
||||||
self.add_method_route(*route)
|
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
options = {
|
|
||||||
'host': self.host,
|
|
||||||
'port': self.port,
|
|
||||||
'workers': self.workers,
|
|
||||||
'access_log': False,
|
|
||||||
'debug': False
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = f'Starting {self.name} at {self.host}:{self.port}'
|
|
||||||
|
|
||||||
if self.workers > 1:
|
|
||||||
msg += f' with {self.workers} workers'
|
|
||||||
|
|
||||||
izzylog.info(msg)
|
|
||||||
self.run(**options)
|
|
||||||
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
if self.sig_handler:
|
|
||||||
self.sig_handler()
|
|
||||||
|
|
||||||
print('stopping.....')
|
|
||||||
self.stop()
|
|
||||||
izzylog.info('Bye! :3')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
class HttpServerRequest(sanic.request.Request):
|
|
||||||
def __init__(self, url_bytes, headers, version, method, transport, app):
|
|
||||||
super().__init__(url_bytes, headers, version, method, transport, app)
|
|
||||||
|
|
||||||
self.Headers = HttpHeaders(headers)
|
|
||||||
self.Data = HttpData(self)
|
|
||||||
self.template = self.app.template
|
|
||||||
self.__setup_defaults()
|
|
||||||
self.__parse_path()
|
|
||||||
|
|
||||||
#if self.paths.media:
|
|
||||||
#return
|
|
||||||
|
|
||||||
self.__parse_signature()
|
|
||||||
self.Run()
|
|
||||||
|
|
||||||
|
|
||||||
def Run(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def response(self, tpl, *args, **kwargs):
|
|
||||||
return self.template.response(self, tpl, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def alldata(self):
|
|
||||||
return self.__combine_dicts(self.content.json, self.data.query, self.data.form)
|
|
||||||
|
|
||||||
|
|
||||||
def verify(self, actor=None):
|
|
||||||
self.ap.valid = http.VerifyHeaders(self.headers, self.method, self.path, actor, self.body)
|
|
||||||
return self.ap.valid
|
|
||||||
|
|
||||||
|
|
||||||
def __combine_dicts(self, *dicts):
|
|
||||||
data = DotDict()
|
|
||||||
|
|
||||||
for item in dicts:
|
|
||||||
data.update(item)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def __setup_defaults(self):
|
|
||||||
self.paths = DotDict({'media': False, 'json': False, 'ap': False, 'cookie': False})
|
|
||||||
self.ap = DotDict({'valid': False, 'signature': {}, 'actor': None, 'inbox': None, 'domain': None})
|
|
||||||
|
|
||||||
|
|
||||||
def __parse_path(self):
|
|
||||||
self.paths.media = any(map(self.path.startswith, log_path_ignore)) or any(map(self.path.startswith, log_ext_ignore))
|
|
||||||
self.paths.json = self.__json_check()
|
|
||||||
|
|
||||||
|
|
||||||
def __parse_signature(self):
|
|
||||||
sig = self.headers.getone('signature', None)
|
|
||||||
|
|
||||||
if sig:
|
|
||||||
self.ap.signature = http.ParseSig(sig)
|
|
||||||
|
|
||||||
if self.ap.signature:
|
|
||||||
self.ap.actor = self.ap.signature.get('keyid', '').split('#', 1)[0]
|
|
||||||
self.ap.domain = urlparse(self.ap.actor).netloc
|
|
||||||
|
|
||||||
|
|
||||||
def __json_check(self):
|
|
||||||
if self.path.endswith('.json'):
|
|
||||||
return True
|
|
||||||
|
|
||||||
accept = self.headers.getone('Accept', None)
|
|
||||||
|
|
||||||
if accept:
|
|
||||||
mimes = [v.strip() for v in accept.split(',')]
|
|
||||||
|
|
||||||
if any(mime in ['application/json', 'application/activity+json'] for mime in mimes):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class HttpServerResponse:
|
|
||||||
Text = sanic.response.text
|
|
||||||
Html = sanic.response.html
|
|
||||||
Json = sanic.response.json
|
|
||||||
Redir = sanic.response.redirect
|
|
||||||
|
|
||||||
|
|
||||||
def Css(*args, headers={}, **kwargs):
|
|
||||||
ReplaceHeader(headers, 'content-type', 'text/css')
|
|
||||||
return sanic.response.text(*args, headers=headers, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def Js(*args, headers={}, **kwargs):
|
|
||||||
ReplaceHeader(headers, 'content-type', 'application/javascript')
|
|
||||||
return sanic.response.text(*args, headers=headers, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def Ap(*args, headers={}, **kwargs):
|
|
||||||
ReplaceHeader(headers, 'content-type', 'application/activity+json')
|
|
||||||
return sanic.response.json(*args, headers=headers, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def Jrd(*args, headers={}, **kwargs):
|
|
||||||
ReplaceHeader(headers, 'content-type', 'application/jrd+json')
|
|
||||||
return sanic.response.json(*args, headers=headers, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class HttpHeaders(LowerDotDict):
|
|
||||||
def __init__(self, headers):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
for k,v in headers.items():
|
|
||||||
if not self.get(k):
|
|
||||||
self[k] = []
|
|
||||||
|
|
||||||
self[k].append(v)
|
|
||||||
|
|
||||||
|
|
||||||
def getone(self, key, default=None):
|
|
||||||
value = self.get(key)
|
|
||||||
|
|
||||||
if not value:
|
|
||||||
return default
|
|
||||||
|
|
||||||
return value[0]
|
|
||||||
|
|
||||||
|
|
||||||
def getall(self, key, default=[]):
|
|
||||||
return self.get(key.lower(), default)
|
|
||||||
|
|
||||||
|
|
||||||
class HttpData(object):
|
|
||||||
def __init__(self, request):
|
|
||||||
self.request = request
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def combined(self):
|
|
||||||
return DotDict(**self.form.asDict(), **self.query.asDict(), **self.json.asDict())
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def query(self):
|
|
||||||
data = {k: v for k,v in parse_qsl(self.request.query_string)}
|
|
||||||
return DotDict(data)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def form(self):
|
|
||||||
data = {k: v[0] for k,v in self.request.form.items()}
|
|
||||||
return DotDict(data)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def files(self):
|
|
||||||
return DotDict({k:v[0] for k,v in self.request.files.items()})
|
|
||||||
|
|
||||||
|
|
||||||
### body functions
|
|
||||||
@property
|
|
||||||
def raw(self):
|
|
||||||
try:
|
|
||||||
return self.request.body
|
|
||||||
except Exception as e:
|
|
||||||
izzylog.verbose('IzzyLib.http_server.Data.raw: failed to get body')
|
|
||||||
izzylog.debug(f'{e.__class__.__name__}: {e}')
|
|
||||||
return b''
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def text(self):
|
|
||||||
try:
|
|
||||||
return self.raw.decode()
|
|
||||||
except Exception as e:
|
|
||||||
izzylog.verbose('IzzyLib.http_server.Data.text: failed to get body')
|
|
||||||
izzylog.debug(f'{e.__class__.__name__}: {e}')
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def json(self):
|
|
||||||
try:
|
|
||||||
return DotDict(self.text)
|
|
||||||
except Exception as e:
|
|
||||||
izzylog.verbose('IzzyLib.http_server.Data.json: failed to get body')
|
|
||||||
izzylog.debug(f'{e.__class__.__name__}: {e}')
|
|
||||||
data = '{}'
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
async def MiddlewareAccessLog(request, response):
|
|
||||||
if request.paths.media:
|
|
||||||
return
|
|
||||||
|
|
||||||
uagent = request.headers.get('user-agent')
|
|
||||||
address = request.headers.get('x-real-ip', request.forwarded.get('for', request.remote_addr))
|
|
||||||
|
|
||||||
izzylog.info(f'({multiprocessing.current_process().name}) {address} {request.method} {request.path} {response.status} "{uagent}"')
|
|
||||||
|
|
||||||
|
|
||||||
def GenericError(request, exception):
|
|
||||||
try:
|
|
||||||
status = exception.status_code
|
|
||||||
except:
|
|
||||||
status = 500
|
|
||||||
|
|
||||||
if status not in range(200, 499):
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
msg = f'{exception.__class__.__name__}: {str(exception)}'
|
|
||||||
|
|
||||||
if request.paths.json:
|
|
||||||
return sanic.response.json({'error': {'status': status, 'message': msg}})
|
|
||||||
|
|
||||||
try:
|
|
||||||
return request.response('server_error.haml', status=status, context={'status': str(status), 'error': msg})
|
|
||||||
|
|
||||||
except TemplateNotFound:
|
|
||||||
return sanic.response.text(f'Error {status}: {msg}')
|
|
||||||
|
|
||||||
|
|
||||||
def NoTemplateError(request, exception):
|
|
||||||
izzylog.error('TEMPLATE_ERROR:', f'{exception.__class__.__name__}: {str(exception)}')
|
|
||||||
return sanic.response.html('I\'m a dumbass and forgot to create a template for this page', 500)
|
|
||||||
|
|
||||||
|
|
||||||
def ReplaceHeader(headers, key, value):
|
|
||||||
for k,v in headers.items():
|
|
||||||
if k.lower() == header.lower():
|
|
||||||
del headers[k]
|
|
|
@ -7,8 +7,9 @@ from .client import (
|
||||||
verify_headers,
|
verify_headers,
|
||||||
parse_signature,
|
parse_signature,
|
||||||
fetch_actor,
|
fetch_actor,
|
||||||
fetch_webfinger_account,
|
fetch_instance,
|
||||||
fetch_nodeinfo,
|
fetch_nodeinfo,
|
||||||
|
fetch_webfinger_account,
|
||||||
set_requests_client,
|
set_requests_client,
|
||||||
generate_rsa_key
|
generate_rsa_key
|
||||||
)
|
)
|
||||||
|
@ -26,8 +27,9 @@ __all__ = [
|
||||||
'HttpRequestsResponse',
|
'HttpRequestsResponse',
|
||||||
'SigningError',
|
'SigningError',
|
||||||
'fetch_actor',
|
'fetch_actor',
|
||||||
'fetch_webfinger_account',
|
'fetch_instance',
|
||||||
'fetch_nodeinfo',
|
'fetch_nodeinfo',
|
||||||
|
'fetch_webfinger_account',
|
||||||
'generate_rsa_key',
|
'generate_rsa_key',
|
||||||
'parse_signature',
|
'parse_signature',
|
||||||
'set_requests_client',
|
'set_requests_client',
|
||||||
|
|
|
@ -46,10 +46,6 @@ class HttpRequestsClient(object):
|
||||||
|
|
||||||
|
|
||||||
def __sign_request(self, request, privkey, keyid):
|
def __sign_request(self, request, privkey, keyid):
|
||||||
if not crypto_enabled:
|
|
||||||
izzylog.error('Crypto functions disabled')
|
|
||||||
return
|
|
||||||
|
|
||||||
request.add_header('(request-target)', f'{request.method.lower()} {request.path}')
|
request.add_header('(request-target)', f'{request.method.lower()} {request.path}')
|
||||||
request.add_header('host', request.host)
|
request.add_header('host', request.host)
|
||||||
request.add_header('date', datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'))
|
request.add_header('date', datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'))
|
||||||
|
@ -63,7 +59,7 @@ class HttpRequestsClient(object):
|
||||||
'keyId': keyid,
|
'keyId': keyid,
|
||||||
'algorithm': 'rsa-sha256',
|
'algorithm': 'rsa-sha256',
|
||||||
'headers': ' '.join([k.lower() for k in request.headers.keys()]),
|
'headers': ' '.join([k.lower() for k in request.headers.keys()]),
|
||||||
'signature': b64encode(PkcsHeaders(privkey, request.headers)).decode('UTF-8')
|
'signature': b64encode(sign_pkcs_headers(privkey, request.headers)).decode('UTF-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
sig_items = [f'{k}="{v}"' for k,v in sig.items()]
|
sig_items = [f'{k}="{v}"' for k,v in sig.items()]
|
||||||
|
@ -83,6 +79,12 @@ class HttpRequestsClient(object):
|
||||||
return HttpRequestsResponse(request.send())
|
return HttpRequestsResponse(request.send())
|
||||||
|
|
||||||
|
|
||||||
|
def signed_request(self, privkey, keyid, *args, **kwargs):
|
||||||
|
request = HttpRequestsRequest(self, *args, **kwargs)
|
||||||
|
self.__sign_request(request, privkey, keyid)
|
||||||
|
return HttpRequestsResponse(request.send())
|
||||||
|
|
||||||
|
|
||||||
def download(self, url, filepath, *args, filename=None, **kwargs):
|
def download(self, url, filepath, *args, filename=None, **kwargs):
|
||||||
resp = self.request(url, *args, **kwargs)
|
resp = self.request(url, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -130,18 +132,15 @@ class HttpRequestsClient(object):
|
||||||
return self.request(*args, headers=headers, **kwargs)
|
return self.request(*args, headers=headers, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def signed_request(self, privkey, keyid, *args, **kwargs):
|
|
||||||
request = HttpRequestsRequest(self, *args, **kwargs)
|
|
||||||
self.__sign_request(request, privkey, keyid)
|
|
||||||
return HttpRequestsResponse(request.send())
|
|
||||||
|
|
||||||
|
|
||||||
class HttpRequestsRequest(object):
|
class HttpRequestsRequest(object):
|
||||||
def __init__(self, client, url, data=None, headers={}, query={}, method='get'):
|
def __init__(self, client, url, data=None, headers={}, query={}, method='get'):
|
||||||
|
parsed = urlparse(url)
|
||||||
self.args = [url]
|
self.args = [url]
|
||||||
self.kwargs = {'params': query}
|
self.kwargs = DotDict({'params': query})
|
||||||
self.method = method.lower()
|
self.method = method.lower()
|
||||||
self.client = client
|
self.client = client
|
||||||
|
self.path = parsed.path
|
||||||
|
self.host = parsed.netloc
|
||||||
|
|
||||||
new_headers = client.headers.copy()
|
new_headers = client.headers.copy()
|
||||||
new_headers.update(headers)
|
new_headers.update(headers)
|
||||||
|
@ -151,11 +150,34 @@ class HttpRequestsRequest(object):
|
||||||
if not parsed_headers.get('user-agent'):
|
if not parsed_headers.get('user-agent'):
|
||||||
parsed_headers['user-agent'] = client.agent
|
parsed_headers['user-agent'] = client.agent
|
||||||
|
|
||||||
self.kwargs['headers'] = new_headers
|
self.kwargs['headers'] = DotDict(new_headers)
|
||||||
self.kwargs['data'] = data
|
self.kwargs['data'] = data
|
||||||
|
|
||||||
if client.proxy.enabled:
|
if client.proxy.enabled:
|
||||||
self.kwargs['proxies'] = {self.proxy.ptype: f'{self.proxy.ptype}://{self.proxy.host}:{self.proxy.port}'}
|
self.kwargs['proxies'] = DotDict({self.proxy.ptype: f'{self.proxy.ptype}://{self.proxy.host}:{self.proxy.port}'})
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def body(self):
|
||||||
|
return self.kwargs.data
|
||||||
|
|
||||||
|
|
||||||
|
@body.setter
|
||||||
|
def body(self, data):
|
||||||
|
self.kwargs.data = data
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return self.kwargs.headers
|
||||||
|
|
||||||
|
|
||||||
|
def add_header(self, key, value):
|
||||||
|
self.kwargs.headers[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def remove_header(self, key):
|
||||||
|
self.kwargs.headers.pop(key, None)
|
||||||
|
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
|
@ -191,7 +213,11 @@ class HttpRequestsResponse(object):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def json(self):
|
def json(self):
|
||||||
return DotDict(self.body)
|
try:
|
||||||
|
return DotDict(self.text)
|
||||||
|
|
||||||
|
except:
|
||||||
|
return DotDict(self.body)
|
||||||
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -371,6 +397,25 @@ def fetch_actor(url):
|
||||||
return actor
|
return actor
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=512)
|
||||||
|
def fetch_instance(domain):
|
||||||
|
if not Client:
|
||||||
|
raise ValueError('Please set global client with "SetRequestsClient(client)"')
|
||||||
|
|
||||||
|
headers = {'Accept': 'application/json'}
|
||||||
|
resp = Client.request(f'https://{domain}/api/v1/instance', headers=headers)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return resp.json
|
||||||
|
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
return
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
izzylog.debug(f'HTTP {resp.status}: {resp.body}')
|
||||||
|
raise e from None
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=512)
|
@lru_cache(maxsize=512)
|
||||||
def fetch_webfinger_account(handle, domain):
|
def fetch_webfinger_account(handle, domain):
|
||||||
if not Client:
|
if not Client:
|
||||||
|
|
|
@ -220,7 +220,7 @@ class SqlSession(object):
|
||||||
return self.fetch(*args, single=False, **kwargs)
|
return self.fetch(*args, single=False, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def insert(self, table_name, **kwargs):
|
def insert(self, table_name, return_row=False, **kwargs):
|
||||||
row = self.fetch(table_name, **kwargs)
|
row = self.fetch(table_name, **kwargs)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
|
@ -232,11 +232,13 @@ class SqlSession(object):
|
||||||
if getattr(table, 'timestamp', None) and not kwargs.get('timestamp'):
|
if getattr(table, 'timestamp', None) and not kwargs.get('timestamp'):
|
||||||
kwargs['timestamp'] = datetime.now()
|
kwargs['timestamp'] = datetime.now()
|
||||||
|
|
||||||
res = self.execute(table.insert().values(**kwargs))
|
self.execute(table.insert().values(**kwargs))
|
||||||
#return self.fetch(table_name, **kwargs)
|
|
||||||
|
if return_row:
|
||||||
|
return self.fetch(table_name, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def update(self, table=None, rowid=None, row=None, **data):
|
def update(self, table=None, rowid=None, row=None, return_row=False, **data):
|
||||||
if row:
|
if row:
|
||||||
rowid = row.id
|
rowid = row.id
|
||||||
table = row._table_name
|
table = row._table_name
|
||||||
|
@ -247,6 +249,9 @@ class SqlSession(object):
|
||||||
tclass = self.table[table]
|
tclass = self.table[table]
|
||||||
self.execute(tclass.update().where(tclass.c.id == rowid).values(**data))
|
self.execute(tclass.update().where(tclass.c.id == rowid).values(**data))
|
||||||
|
|
||||||
|
if return_row:
|
||||||
|
return self.fetch(table, id=rowid)
|
||||||
|
|
||||||
|
|
||||||
def remove(self, table=None, rowid=None, row=None):
|
def remove(self, table=None, rowid=None, row=None):
|
||||||
if row:
|
if row:
|
||||||
|
|
Loading…
Reference in a new issue