Skip to content

Commit 4e3b52a

Browse files
committed
Use urljoin to join base and relative path inside API
This follows the specifications correctly, but beware that specifying a base URL that doesn't end with a `/` leads to strange results, see #2378 Replace `exit(1)` by an exception so this can be caught also if this code runs asynchronously in a thread or separate process.
1 parent 16e9b1f commit 4e3b52a

File tree

1 file changed

+15
-6
lines changed

1 file changed

+15
-6
lines changed

misc-tools/dj_utils.py.in

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import requests
1111
import requests.utils
1212
import subprocess
1313
import sys
14-
from urllib.parse import urlparse
14+
from urllib.parse import urlparse, urljoin
1515

1616
_myself = os.path.basename(sys.argv[0])
1717
_default_user_agent = requests.utils.default_user_agent()
@@ -48,6 +48,13 @@ def parse_api_response(endpoint: str, response: requests.Response) -> bytes:
4848
return response.content
4949

5050

51+
def is_relative(url: str) -> bool:
52+
parsed = urlparse(url)
53+
54+
return (parsed.scheme=='' and parsed.netloc=='' and
55+
(len(parsed.path)==0 or parsed.path[0]!='/'))
56+
57+
5158
def do_api_request(endpoint: str, method: str = 'GET', jsonData: dict = {},
5259
data: dict = {}, files: dict = {}, decode: bool = True):
5360
'''Perform an API call to the given endpoint and return its data.
@@ -56,7 +63,8 @@ def do_api_request(endpoint: str, method: str = 'GET', jsonData: dict = {},
5663
API via HTTP or CLI.
5764

5865
Parameters:
59-
endpoint (str): the endpoint to call
66+
endpoint (str): the endpoint to call, relative to `domjudge_api_url`;
67+
it may also contain a complete URL, which will then be used as-is
6068
method (str): the method to use, GET or PUT are supported
6169
jsonData (dict): the JSON data to PUT. Only used when method is PUT
6270
data (dict): data to pass in a POST request
@@ -73,12 +81,14 @@ def do_api_request(endpoint: str, method: str = 'GET', jsonData: dict = {},
7381
# For the recursive call below to ignore SSL validation:
7482
orig_kwargs = locals()
7583

76-
if domjudge_api_url is None:
84+
if domjudge_api_url is None and is_relative(endpoint):
7785
result = api_via_cli(endpoint, method, {}, {}, jsonData)
7886
else:
87+
if not is_relative(endpoint):
88+
raise RuntimeError(f'Cannot access non-relative URL {endpoint} without API base URL')
7989
global ca_check
80-
url = f'{domjudge_api_url}/{endpoint}'
8190
parsed = urlparse(domjudge_api_url)
91+
url = urljoin(domjudge_api_url, endpoint)
8292
auth = None
8393
if parsed.username and parsed.password:
8494
auth = (parsed.username, parsed.password)
@@ -104,8 +114,7 @@ def do_api_request(endpoint: str, method: str = 'GET', jsonData: dict = {},
104114
ca_check = not confirm(
105115
"Can not verify certificate, ignore certificate check?", False)
106116
if ca_check:
107-
print('Can not verify certificate chain for DOMserver.')
108-
exit(1)
117+
raise RuntimeError(f'Cannot verify certificate chain for {url}')
109118
else:
110119
# Retry with SSL verification disabled
111120
return do_api_request(**orig_kwargs)

0 commit comments

Comments
 (0)