aiohttp based Async Authenticated API Client

This commit is contained in:
hackrush 2018-08-11 15:55:36 +05:30 committed by Jack Robison
parent ab3b0134b5
commit 083bf4f61d
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
2 changed files with 55 additions and 78 deletions

View file

@ -16,7 +16,7 @@ from lbrynet.core.system_info import get_platform
async def execute_command(method, params, conf_path=None):
# this check if the daemon is running or not
try:
api = LBRYAPIClient.get_client(conf_path)
api = await LBRYAPIClient.get_client(conf_path)
await api.status()
except (ClientConnectorError, ConnectionError):
print("Could not connect to daemon. Are you sure it's running?")
@ -25,6 +25,8 @@ async def execute_command(method, params, conf_path=None):
# this actually executes the method
try:
resp = await api.call(method, params)
if not api.session.closed:
await api.session.close()
print(json.dumps(resp["result"], indent=2))
except KeyError:
if resp["error"]["code"] == -32500:

View file

@ -1,11 +1,9 @@
# pylint: skip-file
import os
import json
import aiohttp
from urllib.parse import urlparse
import requests
from requests.cookies import RequestsCookieJar
import logging
from urllib.parse import urlparse
from lbrynet import conf
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
@ -17,12 +15,6 @@ HTTP_TIMEOUT = 30
SCHEME = "http"
def copy_cookies(cookies):
result = RequestsCookieJar()
result.update(cookies)
return result
class JSONRPCException(Exception):
def __init__(self, rpc_error):
super().__init__()
@ -42,7 +34,7 @@ class UnAuthAPIClient:
return f
@classmethod
def from_url(cls, url):
async def from_url(cls, url):
url_fragment = urlparse(url)
host = url_fragment.hostname
port = url_fragment.port
@ -55,14 +47,14 @@ class UnAuthAPIClient:
return await resp.json()
class AuthAPIClient:
def __init__(self, key, timeout, connection, count, cookies, url, login_url):
class AsyncAuthAPIClient:
def __init__(self, key, session, cookies, url, login_url):
self.session = session
self.__api_key = key
self.__service_url = login_url
self.__id_count = count
self.__login_url = login_url
self.__id_count = 0
self.__url = url
self.__conn = connection
self.__cookies = copy_cookies(cookies)
self.__cookies = cookies
def __getattr__(self, name):
if name.startswith('__') and name.endswith('__'):
@ -76,6 +68,7 @@ class AuthAPIClient:
async def call(self, method, params=None):
params = params or {}
self.__id_count += 1
pre_auth_post_data = {
'version': '2',
'method': method,
@ -83,71 +76,53 @@ class AuthAPIClient:
'id': self.__id_count
}
to_auth = get_auth_message(pre_auth_post_data)
pre_auth_post_data.update({'hmac': self.__api_key.get_hmac(to_auth).decode()})
auth_msg = self.__api_key.get_hmac(to_auth).decode()
pre_auth_post_data.update({'hmac': auth_msg})
post_data = json.dumps(pre_auth_post_data)
cookies = copy_cookies(self.__cookies)
req = requests.Request(
method='POST', url=self.__service_url, data=post_data, cookies=cookies,
headers={
'Host': self.__url.hostname,
'User-Agent': USER_AGENT,
'Content-type': 'application/json'
}
)
http_response = self.__conn.send(req.prepare())
if http_response is None:
raise JSONRPCException({'code': -342, 'message': 'missing HTTP response from server'})
http_response.raise_for_status()
next_secret = http_response.headers.get(LBRY_SECRET, False)
if next_secret:
self.__api_key.secret = next_secret
self.__cookies = copy_cookies(http_response.cookies)
return http_response.json()
headers = {
'Host': self.__url.hostname,
'User-Agent': USER_AGENT,
'Content-type': 'application/json'
}
async with self.session.post(self.__login_url, data=post_data, headers=headers) as resp:
if resp is None:
raise JSONRPCException({'code': -342, 'message': 'missing HTTP response from server'})
resp.raise_for_status()
next_secret = resp.headers.get(LBRY_SECRET, False)
if next_secret:
self.__api_key.secret = next_secret
return await resp.json()
@classmethod
def config(cls, key_name=None, key=None, pw_path=None, timeout=HTTP_TIMEOUT, connection=None, count=0,
cookies=None, auth=None, url=None, login_url=None):
async def get_client(cls, key_name=None):
api_key_name = key_name or API_KEY_NAME
pw_path = os.path.join(conf.settings['data_dir'], ".api_keys") if not pw_path else pw_path
if not key:
keys = load_api_keys(pw_path)
api_key = keys.get(api_key_name, False)
else:
api_key = APIKey(name=api_key_name, secret=key)
if login_url is None:
service_url = "http://{}:{}@{}:{}".format(
api_key_name, api_key.secret, conf.settings['api_host'], conf.settings['api_port']
)
else:
service_url = login_url
id_count = count
if auth is None and connection is None and cookies is None and url is None:
# This is a new client instance, start an authenticated session
url = urlparse(service_url)
conn = requests.Session()
req = requests.Request(
method='POST', url=service_url, headers={
'Host': url.hostname,
'User-Agent': USER_AGENT,
'Content-type': 'application/json'
})
r = req.prepare()
http_response = conn.send(r)
cookies = RequestsCookieJar()
cookies.update(http_response.cookies)
uid = cookies.get(TWISTED_SESSION)
api_key = APIKey.new(seed=uid.encode())
else:
# This is a client that already has a session, use it
conn = connection
if not cookies.get(LBRY_SECRET):
raise Exception("Missing cookie")
secret = cookies.get(LBRY_SECRET)
api_key = APIKey(secret, api_key_name)
return cls(api_key, timeout, conn, id_count, cookies, url, service_url)
pw_path = os.path.join(conf.settings['data_dir'], ".api_keys")
keys = load_api_keys(pw_path)
api_key = keys.get(api_key_name, False)
login_url = "http://{}:{}@{}:{}".format(api_key_name, api_key.secret, conf.settings['api_host'],
conf.settings['api_port'])
url = urlparse(login_url)
headers = {
'Host': url.hostname,
'User-Agent': USER_AGENT,
'Content-type': 'application/json'
}
session = aiohttp.ClientSession()
async with session.post(login_url, headers=headers) as r:
cookies = r.cookies
uid = cookies.get(TWISTED_SESSION).value
api_key = APIKey.new(seed=uid.encode())
return cls(api_key, session, cookies, url, login_url)
class LBRYAPIClient:
@ -156,5 +131,5 @@ class LBRYAPIClient:
conf.conf_file = conf_path
if not conf.settings:
conf.initialize_settings()
return AuthAPIClient.config() if conf.settings['use_auth_http'] else \
return AsyncAuthAPIClient.get_client() if conf.settings['use_auth_http'] else \
UnAuthAPIClient.from_url(conf.settings.get_api_connection_string())