Skip to content

Commit

Permalink
Add user-agent header to version check request
Browse files Browse the repository at this point in the history
  • Loading branch information
kozlovsky committed May 31, 2021
1 parent 6e33cb7 commit 3349d8b
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 16 deletions.
61 changes: 56 additions & 5 deletions src/tribler-core/tribler_core/modules/tests/test_versioncheck.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,65 @@
import json
from asyncio import sleep
from unittest.mock import Mock

from aiohttp import web

import pytest

from tribler_core.modules import versioncheck_manager
from tribler_core.modules.versioncheck_manager import VersionCheckManager
from tribler_core.modules.versioncheck_manager import VersionCheckManager, get_user_agent_string
from tribler_core.restapi.rest_endpoint import RESTResponse
from tribler_core.version import version_id

# pylint: disable=unused-argument

# Assuming this is always a newer version id
NEW_VERSION_ID = 'v1337.0'


def make_platform_mock():
platform_mock = Mock()
platform_mock.machine = lambda: 'Something64'
platform_mock.system = lambda: 'OsName'
platform_mock.release = lambda: '123'
platform_mock.version = lambda: '123.56.67' # currently not used
platform_mock.python_version = lambda: '3.10.1'
platform_mock.architecture = lambda: ('64bit', 'FooBar') # currently only first item is used
return platform_mock

TEST_USER_AGENT = f'Tribler/{version_id} (machine=Something64; os=OsName 123; python=3.10.1; executable=64bit)'


@pytest.fixture(name='version_check_manager')
async def fixture_version_check_manager(free_port, session):
prev_platform = versioncheck_manager.platform
prev_urls = versioncheck_manager.VERSION_CHECK_URLS
versioncheck_manager.platform = make_platform_mock()
versioncheck_manager.VERSION_CHECK_URLS = [f"http://localhost:{free_port}"]
version_check_manager = VersionCheckManager(session)
yield version_check_manager
await version_check_manager.stop()
try:
yield version_check_manager
finally:
try:
await version_check_manager.stop()
finally:
versioncheck_manager.VERSION_CHECK_URLS = prev_urls
versioncheck_manager.platform = prev_platform


response = None
response_code = 200
response_lag = 0 # in seconds

last_request_user_agent = None


async def handle_version_request(_):
global response, response_code, response_lag # pylint: disable=global-statement
async def handle_version_request(request):
global response, response_code, response_lag, last_request_user_agent # pylint: disable=global-statement
if response_lag > 0:
await sleep(response_lag)
user_agent = request.headers.get('User-Agent')
last_request_user_agent = user_agent
return RESTResponse(response, status=response_code)


Expand Down Expand Up @@ -68,6 +98,15 @@ async def test_start(version_check_manager, version_server):
vcm.version_id = old_id


@pytest.mark.asyncio
async def test_user_agent(version_check_manager, version_server):
global response, last_request_user_agent # pylint: disable=global-statement
response = json.dumps({'name': 'v1.0'})
last_request_user_agent = None
await version_check_manager.check_new_version()
assert last_request_user_agent == TEST_USER_AGENT


@pytest.mark.asyncio
async def test_old_version(version_check_manager, version_server):
global response # pylint: disable=global-statement
Expand Down Expand Up @@ -165,3 +204,15 @@ async def test_fallback_on_multiple_urls(free_port, version_check_manager, versi
assert has_new_version

vcm.VERSION_CHECK_URLS = vcm_old_urls


def test_useragent_string():
platform = Mock()
platform.machine = lambda: 'AMD64'
platform.system = lambda: 'Windows'
platform.release = lambda: '10'
platform.version = lambda: '10.0.19041'
platform.python_version = lambda: '3.9.1'
platform.architecture = lambda: ('64bit', 'WindowsPE')
s = get_user_agent_string('1.2.3', platform)
assert s == 'Tribler/1.2.3 (machine=AMD64; os=Windows 10; python=3.9.1; executable=64bit)'
38 changes: 27 additions & 11 deletions src/tribler-core/tribler_core/modules/versioncheck_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import logging
import platform
from distutils.version import LooseVersion

from aiohttp import (
Expand All @@ -23,6 +24,19 @@
VERSION_CHECK_TIMEOUT = 5 # Five seconds timeout


def get_user_agent_string(tribler_version, platform_module):
machine = platform_module.machine() # like 'AMD64'
os_name = platform_module.system() # like 'Windows'
os_release = platform_module.release() # like '10'
python_version = platform_module.python_version() # like '3.9.1'
program_achitecture = platform_module.architecture()[0] # like '64bit'

user_agent = f'Tribler/{tribler_version} ' \
f'(machine={machine}; os={os_name} {os_release}; ' \
f'python={python_version}; executable={program_achitecture})'
return user_agent


class VersionCheckManager(TaskManager):

def __init__(self, session):
Expand All @@ -41,33 +55,35 @@ async def stop(self):
async def check_new_version(self):
for version_check_url in VERSION_CHECK_URLS:
try:
if await asyncio.wait_for(self.check_new_version_api(version_check_url), VERSION_CHECK_TIMEOUT):
return True
timeout = VERSION_CHECK_TIMEOUT + 1 # make outer timeout a bit bigger than inner timeout
return await asyncio.wait_for(self.check_new_version_api(version_check_url), timeout)
except asyncio.TimeoutError:
self._logger.warning("Checking for new version failed for %s", version_check_url)
return False

async def check_new_version_api(self, version_check_url):
headers = {
'User-Agent': get_user_agent_string(version_id, platform)
}
try:
async with ClientSession(raise_for_status=True) as session:
response = await session.get(version_check_url, timeout=ClientTimeout(total=VERSION_CHECK_TIMEOUT))
response = await session.get(version_check_url, headers=headers,
timeout=ClientTimeout(total=VERSION_CHECK_TIMEOUT))
response_dict = await response.json(content_type=None)
version = response_dict['name'][1:]
if LooseVersion(version) > LooseVersion(version_id):
self.session.notifier.notify(NTFY.TRIBLER_NEW_VERSION, version)
return True
return False

except (ServerConnectionError, ClientConnectionError) as e:
self._logger.error("Error when performing version check request: %s", e)
return False
except ClientResponseError as e:
self._logger.warning("Got response code %s when performing version check request", e.status)
return False
except ContentTypeError:
self._logger.warning("Response was not in JSON format")
return False
except ClientResponseError as e:
self._logger.warning("Got response code %s when performing version check request", e.status)
except asyncio.TimeoutError:
self._logger.warning("Checking for new version failed for %s", version_check_url)
return False
except ValueError as ve:
raise ValueError(f"Failed to parse Tribler version response.\nError:{ve}")
raise ValueError(f"Failed to parse Tribler version response.\nError:{ve}") # pylint: disable=raise-missing-from

return False

0 comments on commit 3349d8b

Please sign in to comment.