Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/5547 #5968

Merged
merged 5 commits into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions doc/restapi/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ Some requests require one or more parameters. These parameters are passed using

.. code-block:: none

curl -X PUT -H "X-Api-Key: <YOUR API KEY>" http://localhost:8085/mychannel/rssfeeds/http%3A%2F%2Frssfeed.com%2Frss.xml
curl -X PUT -H "X-Api-Key: <YOUR API KEY>" http://localhost:52194/mychannel/rssfeeds/http%3A%2F%2Frssfeed.com%2Frss.xml

Alternatively, requests can be made using Swagger UI by starting Tribler and opening `http://localhost:8085/docs` in a browser.
Alternatively, requests can be made using Swagger UI by starting Tribler and opening `http://localhost:52194/docs` in a browser.

Note: 52194 is a default port value. It can be changed by setting up "CORE_API_PORT" environment variable.

Error handling
==============
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,31 @@
import struct
import sys

logger = logging.getLogger(__name__)
MAX_PORT = 65535

logger = logging.getLogger("NetworkUtils")

CLAIMED_PORTS = []


class FreePortNotFoundError(Exception):
pass


def get_first_free_port(start=5000, limit=100, family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
stop = min(MAX_PORT, start + limit)
logger.info(f'Looking for first free port in range [{start}..{stop}]')

for port in range(start, stop):
if _test_port(family, socket_type, port):
logger.info(f'{port} is free')
return port

logger.info(f'{port} in use')

raise FreePortNotFoundError(f'Free port not found in range [{start}..{stop}]')


def get_random_port(socket_type="all", min_port=5000, max_port=60000):
"""Gets a random port number that works.
@param socket_type: Type of the socket, can be "all", "tcp", or "udp".
Expand All @@ -22,11 +42,11 @@ def get_random_port(socket_type="all", min_port=5000, max_port=60000):
assert socket_type in ("all", "tcp", "udp"), f"Invalid socket type {type(socket_type)}"
assert isinstance(min_port, int), f"Invalid min_port type {type(min_port)}"
assert isinstance(max_port, int), f"Invalid max_port type {type(max_port)}"
assert 0 < min_port <= max_port <= 65535, f"Invalid min_port and mac_port values {min_port}, {max_port}"
assert 0 < min_port <= max_port <= MAX_PORT, f"Invalid min_port and mac_port values {min_port}, {max_port}"

working_port = None
try_port = random.randint(min_port, max_port)
while try_port <= 65535:
while try_port <= MAX_PORT:
if check_random_port(try_port, socket_type):
working_port = try_port
break
Expand All @@ -47,7 +67,7 @@ def check_random_port(port, socket_type="all"):
"""
assert socket_type in ("all", "tcp", "udp"), f"Invalid socket type {type(socket_type)}"
assert isinstance(port, int), f"Invalid port type {type(port)}"
assert 0 < port <= 65535, f"Invalid port value {port}"
assert 0 < port <= MAX_PORT, f"Invalid port value {port}"

# only support IPv4 for now
_family = socket.AF_INET
Expand Down Expand Up @@ -80,7 +100,7 @@ def _test_port(family, sock_type, port):
"""
assert family in (socket.AF_INET,), f"Invalid family value {family}"
assert sock_type in (socket.SOCK_DGRAM, socket.SOCK_STREAM), f"Invalid sock_type value {sock_type}"
assert 0 < port <= 65535, f"Invalid port value {port}"
assert 0 < port <= MAX_PORT, f"Invalid port value {port}"

try:
with socket.socket(family, sock_type) as s:
Expand All @@ -89,8 +109,7 @@ def _test_port(family, sock_type, port):
s.bind(('', port))
is_port_working = True
except OSError as e:
logger.debug("Port test failed (port=%s, family=%s, type=%s): %s",
port, family, sock_type, e)
logger.debug("Port test failed (port=%s, family=%s, type=%s): %s", port, family, sock_type, e)
is_port_working = False
return is_port_working

Expand Down
2 changes: 1 addition & 1 deletion src/tribler-core/run_bandwidth_crawler.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async def start_crawler(tribler_config):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=('Start a crawler in the bandwidth accounting community'))
parser.add_argument('--statedir', '-s', default='bw_crawler', type=str, help='Use an alternate statedir')
parser.add_argument('--restapi', '-p', default=8085, type=str, help='Use an alternate port for the REST API',
parser.add_argument('--restapi', '-p', default=52194, type=str, help='Use an alternate port for the REST API',
action=PortAction, metavar='{0..65535}')
args = parser.parse_args(sys.argv[1:])

Expand Down
8 changes: 5 additions & 3 deletions src/tribler-core/run_tunnel_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def on_circuit_reject(self, reject_time, balance):

def tribler_started(self):
async def signal_handler(sig):
print(f"Received shut down signal {sig}")
print(f"Received shut down signal {sig}") # noqa: T001
if not self._stopping:
self._stopping = True
await self.session.shutdown()
Expand Down Expand Up @@ -176,8 +176,10 @@ def main(argv):
parser.add_argument('--help', '-h', action='help', default=argparse.SUPPRESS, help='Show this help message and exit')
parser.add_argument('--ipv8_port', '-d', default=-1, type=int, help='IPv8 port', action=PortAction, metavar='{0..65535}')
parser.add_argument('--ipv8_address', '-i', default='0.0.0.0', type=str, help='IPv8 listening address', action=IPAction)
parser.add_argument('--ipv8_bootstrap_override', '-b', default=None, type=str, help='Force the usage of specific IPv8 bootstrap server (ip:port)', action=IPPortAction)
parser.add_argument('--restapi', '-p', default=8085, type=str, help='Use an alternate port for the REST API', action=PortAction, metavar='{0..65535}')
parser.add_argument('--ipv8_bootstrap_override', '-b', default=None, type=str,
help='Force the usage of specific IPv8 bootstrap server (ip:port)', action=IPPortAction)
parser.add_argument('--restapi', '-p', default=52194, type=str,
help='Use an alternate port for the REST API', action=PortAction, metavar='{0..65535}')
parser.add_argument('--cert-file', '-e', help='Path to combined certificate/key file. If not given HTTP is used.')
parser.add_argument('--api-key', '-k', help='API key to use. If not given API key protection is disabled.')
parser.add_argument('--random_slots', '-r', default=10, type=int, help='Specifies the number of random slots')
Expand Down
2 changes: 1 addition & 1 deletion src/tribler-core/tribler_core/config/tribler_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

from validate import Validator

from tribler_common.network_utils import get_random_port
from tribler_common.simpledefs import MAX_LIBTORRENT_RATE_LIMIT

from tribler_core.exceptions import InvalidConfigException
from tribler_core.modules.libtorrent.download_config import get_default_dest_dir
from tribler_core.utilities import path_util
from tribler_core.utilities.install_dir import get_lib_path
from tribler_core.utilities.network_utils import get_random_port
from tribler_core.utilities.path_util import Path

CONFIG_FILENAME = 'triblerd.conf'
Expand Down
2 changes: 1 addition & 1 deletion src/tribler-core/tribler_core/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import pytest

from tribler_common.network_utils import get_random_port
from tribler_common.simpledefs import DLSTATUS_SEEDING

from tribler_core.config.tribler_config import TriblerConfig
Expand All @@ -21,7 +22,6 @@
from tribler_core.tests.tools.common import TESTS_DATA_DIR, TESTS_DIR
from tribler_core.tests.tools.tracker.udp_tracker import UDPTracker
from tribler_core.upgrade.db72_to_pony import DispersyToPonyMigration
from tribler_core.utilities.network_utils import get_random_port
from tribler_core.utilities.unicode import hexlify


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
from ipv8.test.mocking.ipv8 import MockIPv8
from ipv8.util import succeed

from tribler_common.network_utils import get_random_port

from tribler_core.modules.bandwidth_accounting.community import BandwidthAccountingCommunity
from tribler_core.modules.tunnel.community.payload import BandwidthTransactionPayload
from tribler_core.modules.tunnel.community.triblertunnel_community import PEER_FLAG_EXIT_HTTP, TriblerTunnelCommunity
from tribler_core.tests.tools.base_test import MockObject
from tribler_core.tests.tools.tracker.http_tracker import HTTPTracker
from tribler_core.utilities.network_utils import get_random_port
from tribler_core.utilities.path_util import mkdtemp


Expand Down
2 changes: 1 addition & 1 deletion src/tribler-core/tribler_core/restapi/events_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ async def get_events(self, request):

.. sourcecode:: none

curl -X GET http://localhost:8085/events
curl -X GET http://localhost:52194/events
"""

# Setting content-type to text/event-stream to ensure browsers will handle the content properly
Expand Down
21 changes: 20 additions & 1 deletion src/tribler-core/tribler_core/tests/test_network_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

import pytest

from tribler_core.utilities.network_utils import autodetect_socket_style, get_random_port
from tribler_common.network_utils import (
FreePortNotFoundError,
autodetect_socket_style,
get_first_free_port,
get_random_port,
)


def test_get_random_port():
Expand Down Expand Up @@ -43,3 +48,17 @@ def test_get_random_port_invalid_type():
def test_autodetect_socket_style():
style = autodetect_socket_style()
assert style == 0 or autodetect_socket_style() == 1


def test_get_first_free_port():
# target port is free
assert get_first_free_port(start=50000) == 50000

# target port is locked
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('', 50000))
assert get_first_free_port(start=50000) == 50001

# no free ports found
with pytest.raises(FreePortNotFoundError):
get_first_free_port(start=50000, limit=0)
6 changes: 3 additions & 3 deletions src/tribler-gui/tribler_gui/core_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def on_core_read_ready(self):
if b'Traceback' in raw_output:
self.core_traceback = decoded_output
self.core_traceback_timestamp = int(round(time.time() * 1000))
print(decoded_output.strip())
print(decoded_output.strip()) # noqa: T001

def on_core_finished(self, exit_code, exit_status):
if self.shutting_down and self.should_stop_on_shutdown:
Expand All @@ -84,8 +84,8 @@ def on_core_finished(self, exit_code, exit_status):

def start(self, core_args=None, core_env=None):
"""
First test whether we already have a Tribler process listening on port 8085. If so, use that one and don't
start a new, fresh session.
First test whether we already have a Tribler process listening on port <CORE_API_PORT>.
If so, use that one and don't start a new, fresh session.
"""

def on_request_error(_):
Expand Down
2 changes: 1 addition & 1 deletion src/tribler-gui/tribler_gui/defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

DEFAULT_API_PROTOCOL = "http"
DEFAULT_API_HOST = "localhost"
DEFAULT_API_PORT = 8085
DEFAULT_API_PORT = 52194

# Define stacked widget page indices
PAGE_SEARCH_RESULTS = 0
Expand Down
4 changes: 2 additions & 2 deletions src/tribler-gui/tribler_gui/i18n/pt_BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1238,8 +1238,8 @@ per download</source>
</message>
<message>
<location filename="../qt_resources/mainwindow.ui" line="2356"/>
<source>Port (defaults to 8085)</source>
<translation>Porta (padrão é 8085)</translation>
<source>Port (defaults to 52194)</source>
<translation>Porta (padrão é 52194)</translation>
</message>
<message>
<location filename="../qt_resources/mainwindow.ui" line="2394"/>
Expand Down
2 changes: 1 addition & 1 deletion src/tribler-gui/tribler_gui/qt_resources/mainwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -2218,7 +2218,7 @@ color: white;</string>
<item row="13" column="0">
<widget class="QLabel" name="label_62">
<property name="text">
<string>Port (defaults to 8085)</string>
<string>Port (defaults to 52194)</string>
</property>
</widget>
</item>
Expand Down
5 changes: 3 additions & 2 deletions src/tribler-gui/tribler_gui/tests/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

import pytest

from tribler_common.network_utils import get_random_port

from tribler_core.tests.tools.common import TORRENT_UBUNTU_FILE
from tribler_core.utilities.network_utils import get_random_port

import tribler_gui
import tribler_gui.core_manager as core_manager
Expand Down Expand Up @@ -63,7 +64,7 @@ def tribler_api(api_port, tmpdir_factory):
def on_core_read_ready():
raw_output = bytes(core_process.readAll())
decoded_output = raw_output.decode(errors="replace")
print(decoded_output.strip())
print(decoded_output.strip()) # noqa: T001

core_process.setProcessEnvironment(core_env)
core_process.setReadChannel(QProcess.StandardOutput)
Expand Down
6 changes: 5 additions & 1 deletion src/tribler-gui/tribler_gui/tribler_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
QTreeWidget,
)

from tribler_common.network_utils import get_first_free_port

from tribler_core.modules.process_checker import ProcessChecker
from tribler_core.utilities.unicode import hexlify
from tribler_core.version import version_id
Expand Down Expand Up @@ -108,10 +110,12 @@ def __init__(self, core_args=None, core_env=None, api_port=None, api_key=None):

self.setWindowIcon(QIcon(QPixmap(get_image_path('tribler.png'))))

self.gui_settings = QSettings()
self.gui_settings = QSettings('nl.tudelft.tribler')
api_port = api_port or int(get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT))
api_key = api_key or get_gui_setting(self.gui_settings, "api_key", hexlify(os.urandom(16)).encode('utf-8'))
self.gui_settings.setValue("api_key", api_key)

api_port = get_first_free_port(start=api_port, limit=100)
request_manager.port, request_manager.key = api_port, api_key

self.tribler_started = False
Expand Down