blob: cadf0ba9ab5876c84cd4852aedfd7a7071625fd2 [file] [log] [blame]
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import annotations
import datetime as dt
import logging
import urllib.parse as urlparse
from typing import TYPE_CHECKING, Any, Callable, Mapping, Optional
import requests
if TYPE_CHECKING:
from crossbench.runner.timing import AnyTime
DEFAULT_REQUEST_TIMEOUT = dt.timedelta(seconds=10)
RequestException = requests.RequestException
HTTPError = requests.HTTPError
Response = requests.Response
def retry_timeout_request(
url: str, timeout: AnyTime, retry: int, verbose: bool, method: str,
send_request: Callable[[float], requests.Response]) -> requests.Response:
deadline = dt.datetime.now() + dt.timedelta(seconds=to_seconds(timeout))
request_timeout_seconds = to_seconds(timeout)
i = 0
while True:
try:
if verbose:
logging.debug("%s: url: %s", method, url)
response = send_request(request_timeout_seconds)
response.raise_for_status()
return response
except requests.RequestException as e:
request_timeout_seconds = (deadline - dt.datetime.now()).total_seconds()
if i < retry and request_timeout_seconds > 0:
i += 1
if verbose:
logging.warning("%s request failed url=%s, retrying: %s", method, url,
e)
continue
if verbose:
logging.error("%s request failed url=%s", method, url)
logging.debug("RequestException: %s", e)
raise e
def get(url: str,
timeout: AnyTime = DEFAULT_REQUEST_TIMEOUT,
retry: int = 0,
verbose: bool = True) -> requests.Response:
return retry_timeout_request(
url, timeout, retry, verbose, "GET",
lambda request_timeout_seconds: requests.get(
url, timeout=request_timeout_seconds))
def post(url: str,
body_json: Optional[Any] = None,
headers: Optional[Mapping[str, str]] = None,
timeout: AnyTime = DEFAULT_REQUEST_TIMEOUT,
retry: int = 0,
verbose: bool = True) -> requests.Response:
return retry_timeout_request(
url, timeout, retry, verbose, "POST",
lambda request_timeout_seconds: requests.post(
url, headers=headers, json=body_json, timeout=request_timeout_seconds
))
def to_seconds(delta: AnyTime) -> float:
if isinstance(delta, dt.timedelta):
return delta.total_seconds()
return delta
def update_url_query(url: str, query_params: Mapping[str, str]) -> str:
parsed_url = urlparse.urlparse(url)
query = dict(urlparse.parse_qsl(parsed_url.query))
query.update(query_params)
parsed_url = parsed_url._replace(query=urlparse.urlencode(query, doseq=True))
return parsed_url.geturl()
def quote(value: str) -> str:
return urlparse.quote(value)