diff --git a/src/tribler-core/tribler_core/components/libtorrent/restapi/tests/test_torrentinfo_endpoint.py b/src/tribler-core/tribler_core/components/libtorrent/restapi/tests/test_torrentinfo_endpoint.py index 99dce69fc51..6ebae3946f8 100644 --- a/src/tribler-core/tribler_core/components/libtorrent/restapi/tests/test_torrentinfo_endpoint.py +++ b/src/tribler-core/tribler_core/components/libtorrent/restapi/tests/test_torrentinfo_endpoint.py @@ -1,6 +1,5 @@ import json import shutil -import urllib from binascii import unhexlify from unittest.mock import Mock, patch from urllib.parse import quote_plus, unquote_plus @@ -40,37 +39,33 @@ async def test_get_torrentinfo(mock_dlmgr, tmp_path, rest_api, endpoint): """ Testing whether the API returns a correct dictionary with torrent info. """ + + def _path(file): + return f'file:{TESTS_DATA_DIR / file}' + endpoint.download_manager = mock_dlmgr shutil.copyfile(TORRENT_UBUNTU_FILE, tmp_path / 'ubuntu.torrent') def verify_valid_dict(json_data): metainfo_dict = json.loads(unhexlify(json_data['metainfo'])) - # FIXME: This check is commented out because json.dump garbles pieces binary data during transfer. - # To fix it, we must switch to some encoding scheme that is able to encode and decode raw binary - # fields in the dicts. - # However, for this works fine at the moment because we never use pieces data in the GUI. - # assert TorrentDef.load_from_dict(metainfo_dict) assert 'info' in metainfo_dict - def path_to_url(path): - return urllib.request.pathname2url(str(path)) - mock_dlmgr.downloads = {} mock_dlmgr.metainfo_requests = {} mock_dlmgr.get_channel_downloads = lambda: [] mock_dlmgr.shutdown = lambda: succeed(None) mock_dlmgr.notifier = Mock() - await do_request(rest_api, 'torrentinfo', expected_code=400) - await do_request(rest_api, 'torrentinfo?uri=def', expected_code=400) + url = 'torrentinfo' + await do_request(rest_api, url, expected_code=400) + await do_request(rest_api, url, params={'uri': 'def'}, expected_code=400) - path = "file:" + path_to_url(TESTS_DATA_DIR / "bak_single.torrent") - verify_valid_dict(await do_request(rest_api, f'torrentinfo?uri={path}', expected_code=200)) + response = await do_request(rest_api, url, params={'uri': _path('bak_single.torrent')}, expected_code=200) + verify_valid_dict(response) # Corrupt file - path = "file:" + path_to_url(TESTS_DATA_DIR / "test_rss.xml") - await do_request(rest_api, f'torrentinfo?uri={path}', expected_code=500) + await do_request(rest_api, url, params={'uri': _path('test_rss.xml')}, expected_code=500) path = "http://localhost:1234/ubuntu.torrent" @@ -79,7 +74,7 @@ async def mock_http_query(*_): return f.read() with patch("tribler_core.components.libtorrent.restapi.torrentinfo_endpoint.query_http_uri", new=mock_http_query): - verify_valid_dict(await do_request(rest_api, f'torrentinfo?uri={quote_plus(path)}', expected_code=200)) + verify_valid_dict(await do_request(rest_api, url, params={'uri': quote_plus(path)}, expected_code=200)) path = quote_plus(f'magnet:?xt=urn:btih:{hexlify(UBUNTU_1504_INFOHASH)}' f'&dn=test torrent&tr=http://ubuntu.org/ann') @@ -146,6 +141,7 @@ async def test_on_got_invalid_metainfo(mock_dlmgr, rest_api): """ Test whether the right operations happen when we receive an invalid metainfo object """ + def get_metainfo(*_, **__): return succeed("abcd") diff --git a/src/tribler-core/tribler_core/components/libtorrent/restapi/torrentinfo_endpoint.py b/src/tribler-core/tribler_core/components/libtorrent/restapi/torrentinfo_endpoint.py index fa298b3ca26..6bae10eeb4a 100644 --- a/src/tribler-core/tribler_core/components/libtorrent/restapi/torrentinfo_endpoint.py +++ b/src/tribler-core/tribler_core/components/libtorrent/restapi/torrentinfo_endpoint.py @@ -11,7 +11,6 @@ from marshmallow.fields import String from tribler_common.simpledefs import NTFY -from tribler_common.utilities import uri_to_path from tribler_core.components.libtorrent.download_manager.download_manager import DownloadManager from tribler_core.components.libtorrent.torrentdef import TorrentDef @@ -26,6 +25,10 @@ from tribler_core.utilities.unicode import hexlify, recursive_unicode from tribler_core.utilities.utilities import bdecode_compat, froze_it, parse_magnetlink +MAGNET = 'magnet' +HTTP = 'http' +FILE = 'file:' + async def query_http_uri(uri: str) -> bytes: # This is moved to a separate method to be able to patch it separately, @@ -68,29 +71,29 @@ def setup_routes(self): } ) async def get_torrent_info(self, request): - args = request.query + params = request.query + hops = params.get('hops', None) + uri = params.get('uri') - hops = None - if 'hops' in args: + if hops: try: - hops = int(args['hops']) + hops = int(hops) except ValueError: - return RESTResponse({"error": f"wrong value of 'hops' parameter: {repr(args['hops'])}"}, - status=HTTP_BAD_REQUEST) + return RESTResponse({"error": f"wrong value of 'hops' parameter: {hops}"}, status=HTTP_BAD_REQUEST) - if 'uri' not in args or not args['uri']: + if not uri: return RESTResponse({"error": "uri parameter missing"}, status=HTTP_BAD_REQUEST) - uri = args['uri'] metainfo = None - if uri.startswith('file:'): + if uri.startswith(FILE): + file = uri[len(FILE):] try: - filename = uri_to_path(uri) - tdef = TorrentDef.load(filename) - metainfo = tdef.get_metainfo() + tdef = TorrentDef.load(file) + metainfo = tdef.metainfo except (TypeError, RuntimeError): - return RESTResponse({"error": "error while decoding torrent file"}, status=HTTP_INTERNAL_SERVER_ERROR) - elif uri.startswith('http'): + return RESTResponse({"error": f"error while decoding torrent file: {file}"}, + status=HTTP_INTERNAL_SERVER_ERROR) + elif uri.startswith(HTTP): try: response = await query_http_uri(uri) except (ServerConnectionError, ClientResponseError) as e: @@ -102,7 +105,7 @@ async def get_torrent_info(self, request): metainfo = await self.download_manager.get_metainfo(infohash, timeout=60, hops=hops, url=response) else: metainfo = bdecode_compat(response) - elif uri.startswith('magnet'): + elif uri.startswith(MAGNET): infohash = parse_magnetlink(uri)[1] if infohash is None: return RESTResponse({"error": "missing infohash"}, status=HTTP_BAD_REQUEST) diff --git a/src/tribler-core/tribler_core/components/restapi/rest/base_api_test.py b/src/tribler-core/tribler_core/components/restapi/rest/base_api_test.py index a5d2edba174..0174076aef2 100644 --- a/src/tribler-core/tribler_core/components/restapi/rest/base_api_test.py +++ b/src/tribler-core/tribler_core/components/restapi/rest/base_api_test.py @@ -1,5 +1,6 @@ import json from json import JSONDecodeError +from typing import Dict, Optional from aiohttp import ClientSession @@ -37,12 +38,13 @@ async def do_real_request(port, endpoint, expected_code=200, expected_json=None, async def do_request(test_client, url, expected_code=200, expected_json=None, - request_type='GET', post_data=None, headers=None, json_response=True): + request_type='GET', post_data=None, headers=None, json_response=True, + params: Optional[Dict] = None): post_data = post_data or {} data = json.dumps(path_to_str(post_data)) if isinstance(post_data, (dict, list)) else post_data headers = headers or {'User-Agent': 'Tribler ' + version_id} - async with test_client.request(request_type, url, data=data, headers=headers, ssl=False) as response: + async with test_client.request(request_type, url, data=data, headers=headers, ssl=False, params=params) as response: status = response.status try: response = (await response.json(content_type=None) diff --git a/src/tribler-core/tribler_core/tests/tools/common.py b/src/tribler-core/tribler_core/tests/tools/common.py index 26714c22711..13e9f074d8f 100644 --- a/src/tribler-core/tribler_core/tests/tools/common.py +++ b/src/tribler-core/tribler_core/tests/tools/common.py @@ -1,8 +1,9 @@ import binascii -from pathlib import Path import tribler_common +from tribler_core.utilities.path_util import Path + UBUNTU_1504_INFOHASH = binascii.unhexlify('FC8A15A2FAF2734DBB1DC5F7AFDC5C9BEAEB1F59') TESTS_DIR = Path(__file__).parent diff --git a/src/tribler-gui/tribler_gui/dialogs/dialogcontainer.py b/src/tribler-gui/tribler_gui/dialogs/dialogcontainer.py index af7fd4551b4..80454e7eaad 100644 --- a/src/tribler-gui/tribler_gui/dialogs/dialogcontainer.py +++ b/src/tribler-gui/tribler_gui/dialogs/dialogcontainer.py @@ -1,3 +1,5 @@ +import logging + from PyQt5.QtCore import QPoint from PyQt5.QtGui import QPainter from PyQt5.QtWidgets import QStyle, QStyleOption, QWidget @@ -15,6 +17,7 @@ def __init__(self, parent, left_right_margin=100): self.dialog_widget = QWidget(self) self.left_right_margin = left_right_margin # The margin at the left and right of the dialog window self.closed = False + self.logger = logging.getLogger(self.__class__.__name__) connect(self.window().resize_event, self.on_main_window_resize) def paintEvent(self, _): diff --git a/src/tribler-gui/tribler_gui/dialogs/startdownloaddialog.py b/src/tribler-gui/tribler_gui/dialogs/startdownloaddialog.py index 848fb8606ab..4fdf319e00d 100644 --- a/src/tribler-gui/tribler_gui/dialogs/startdownloaddialog.py +++ b/src/tribler-gui/tribler_gui/dialogs/startdownloaddialog.py @@ -21,11 +21,14 @@ get_image_path, get_ui_file_path, is_dir_writable, - quote_plus_unicode, tr, ) from tribler_gui.widgets.torrentfiletreewidget import TORRENT_FILES_TREE_STYLESHEET +MAGNET = 'magnet:' + +FILE = 'file:' + class StartDownloadDialog(DialogContainer): @@ -36,9 +39,9 @@ def __init__(self, parent, download_uri): DialogContainer.__init__(self, parent) torrent_name = download_uri - if torrent_name.startswith('file:'): - torrent_name = uri_to_path(torrent_name).stem - elif torrent_name.startswith('magnet:'): + if torrent_name.startswith(FILE): + torrent_name = torrent_name[len(FILE):] + elif torrent_name.startswith(MAGNET): torrent_name = unquote_plus(torrent_name) self.download_uri = download_uri @@ -142,10 +145,11 @@ def perform_files_request(self): return direct = not self.dialog_widget.anon_download_checkbox.isChecked() - request = f"torrentinfo?uri={quote_plus_unicode(self.download_uri)}" - if direct is True: - request = request + "&hops=0" - self.rest_request = TriblerNetworkRequest(request, self.on_received_metainfo, capture_core_errors=False) + params = {'uri': self.download_uri} + if direct: + params['hops'] = 0 + self.rest_request = TriblerNetworkRequest('torrentinfo', self.on_received_metainfo, capture_core_errors=False, + url_params=params) if self.metainfo_retries <= METAINFO_MAX_RETRIES: fetch_mode = tr("directly") if direct else tr("anonymously") @@ -169,7 +173,6 @@ def perform_files_request(self): def on_received_metainfo(self, response): if not response or not self or self.closed or self.has_metainfo: return - if 'error' in response: if response['error'] == 'metainfo error': # If it failed to load metainfo for max number of times, show an error message in red. diff --git a/src/tribler-gui/tribler_gui/start_gui.py b/src/tribler-gui/tribler_gui/start_gui.py index 056caf7a564..4588c11f8b8 100644 --- a/src/tribler-gui/tribler_gui/start_gui.py +++ b/src/tribler-gui/tribler_gui/start_gui.py @@ -6,6 +6,7 @@ from tribler_common.logger import load_logger_config from tribler_common.sentry_reporter.sentry_reporter import SentryStrategy + from tribler_core.check_os import ( check_and_enable_code_tracing, check_environment, @@ -14,6 +15,7 @@ error_and_exit, ) from tribler_core.exceptions import TriblerException + from tribler_gui import gui_sentry_reporter from tribler_gui.tribler_app import TriblerApplication from tribler_gui.tribler_window import TriblerWindow @@ -53,7 +55,7 @@ def run_gui(api_port, api_key, root_state_dir, parsed_args): if app.is_running(): # if an application is already running, then send the command line # argument to it and close the current instance - logger.info(f'GUI Application already running. Passing a torrent file path to it.') + logger.info('GUI Application is already running. Passing a torrent file path to it.') for arg in sys.argv[1:]: if os.path.exists(arg) and arg.endswith(".torrent"): app.send_message(f"file:{arg}")