mirror of
https://github.com/status-im/PyGithub.git
synced 2026-08-31 10:51:14 +00:00
Raise error on unsupported redirects, log supported redirects (#2524)
This commit is contained in:
+32
-5
@@ -386,7 +386,7 @@ class Requester:
|
||||
if not self.__installation_authorization:
|
||||
return
|
||||
if self._must_refresh_token():
|
||||
logging.debug("Refreshing access token")
|
||||
self._logger.debug("Refreshing access token")
|
||||
self._refresh_token()
|
||||
|
||||
def _refresh_token(self) -> None:
|
||||
@@ -617,7 +617,30 @@ class Requester:
|
||||
return self.__requestRaw(original_cnx, verb, url, requestHeaders, input)
|
||||
|
||||
if status == 301 and "location" in responseHeaders:
|
||||
o = urllib.parse.urlparse(responseHeaders["location"])
|
||||
location = responseHeaders["location"]
|
||||
o = urllib.parse.urlparse(location)
|
||||
if o.scheme != self.__scheme:
|
||||
raise RuntimeError(
|
||||
f"Github server redirected from {self.__scheme} protocol to {o.scheme}, "
|
||||
f"please correct your Github server URL via base_url: Github(base_url=...)"
|
||||
)
|
||||
if o.hostname != self.__hostname:
|
||||
raise RuntimeError(
|
||||
f"Github server redirected from host {self.__hostname} to {o.hostname}, "
|
||||
f"please correct your Github server URL via base_url: Github(base_url=...)"
|
||||
)
|
||||
if o.path == url:
|
||||
port = ":" + str(self.__port) if self.__port is not None else ""
|
||||
requested_location = f"{self.__scheme}://{self.__hostname}{port}{url}"
|
||||
raise RuntimeError(
|
||||
f"Requested {requested_location} but server redirected to {location}, "
|
||||
f"you may need to correct your Github server URL "
|
||||
f"via base_url: Github(base_url=...)"
|
||||
)
|
||||
if self._logger.isEnabledFor(logging.INFO):
|
||||
self._logger.info(
|
||||
f"Following Github server redirection from {url} to {o.path}"
|
||||
)
|
||||
return self.__requestRaw(original_cnx, verb, o.path, requestHeaders, input)
|
||||
|
||||
return status, responseHeaders, output
|
||||
@@ -671,10 +694,14 @@ class Requester:
|
||||
|
||||
return self.__connection
|
||||
|
||||
def __log(self, verb, url, requestHeaders, input, status, responseHeaders, output):
|
||||
@property
|
||||
def _logger(self):
|
||||
if self.__logger is None:
|
||||
self.__logger = logging.getLogger(__name__)
|
||||
if self.__logger.isEnabledFor(logging.DEBUG):
|
||||
return self.__logger
|
||||
|
||||
def __log(self, verb, url, requestHeaders, input, status, responseHeaders, output):
|
||||
if self._logger.isEnabledFor(logging.DEBUG):
|
||||
headersForRequest = requestHeaders.copy()
|
||||
if "Authorization" in requestHeaders:
|
||||
if requestHeaders["Authorization"].startswith("Basic"):
|
||||
@@ -689,7 +716,7 @@ class Requester:
|
||||
headersForRequest[
|
||||
"Authorization"
|
||||
] = "(unknown auth removed)" # pragma no cover (Cannot happen, but could if we add an authentication method => be prepared)
|
||||
self.__logger.debug(
|
||||
self._logger.debug(
|
||||
"%s %s://%s%s %s %s ==> %i %s %s",
|
||||
verb,
|
||||
self.__scheme,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections import OrderedDict
|
||||
from io import BufferedReader
|
||||
from logging import Logger
|
||||
from typing import Any, Callable, Dict, Iterator, Optional, Tuple, Union
|
||||
|
||||
from requests.models import Response
|
||||
@@ -51,6 +52,7 @@ class HTTPSRequestsConnectionClass:
|
||||
class Requester:
|
||||
__installation_authorization: Optional[InstallationAuthorization] = ...
|
||||
__app_auth: Optional[AppAuthentication] = ...
|
||||
__logger: Logger
|
||||
def DEBUG_ON_RESPONSE(
|
||||
self, statusCode: int, responseHeader: Dict[str, str], data: str
|
||||
) -> None: ...
|
||||
@@ -85,6 +87,8 @@ class Requester:
|
||||
headers: Dict[str, Any],
|
||||
output: str,
|
||||
) -> Any: ...
|
||||
@property
|
||||
def _logger(self) -> Logger: ...
|
||||
def __log(
|
||||
self,
|
||||
verb: str,
|
||||
|
||||
+14
-4
@@ -72,12 +72,22 @@ from .InputFileContent import InputFileContent
|
||||
from .InputGitAuthor import InputGitAuthor
|
||||
from .InputGitTreeElement import InputGitTreeElement
|
||||
|
||||
# set log level to INFO for github
|
||||
logger = logging.getLogger("github")
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
|
||||
|
||||
def set_log_level(level: int):
|
||||
"""
|
||||
Set the log level of the github logger, e.g. set_log_level(logging.WARNING)
|
||||
:param level: log level
|
||||
"""
|
||||
logger.setLevel(level)
|
||||
|
||||
|
||||
def enable_console_debug_logging(): # pragma no cover (Function useful only outside test environment)
|
||||
"""
|
||||
This function sets up a very simple logging configuration (log everything on standard output) that is useful for troubleshooting.
|
||||
"""
|
||||
|
||||
logger = logging.getLogger("github")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
set_log_level(logging.DEBUG)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
https
|
||||
GET
|
||||
www.github.com
|
||||
None
|
||||
/repos/PyGithub/PyGithub
|
||||
{'User-Agent': 'PyGithub/Python'}
|
||||
None
|
||||
301
|
||||
[('Content-Length', '0'), ('Location', 'https://github.com/repos/PyGithub/PyGithub')]
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
https
|
||||
GET
|
||||
api.github.com
|
||||
None
|
||||
/repos/PyGithub/PyGithub
|
||||
{'User-Agent': 'PyGithub/Python'}
|
||||
None
|
||||
301
|
||||
[('Content-Length', '0'), ('Location', 'https://api.github.com:443/repos/PyGithub/PyGithub')]
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
http
|
||||
GET
|
||||
api.github.com
|
||||
None
|
||||
/repos/PyGithub/PyGithub
|
||||
{'User-Agent': 'PyGithub/Python'}
|
||||
None
|
||||
301
|
||||
[('Content-Length', '0'), ('Location', 'https://api.github.com/repos/PyGithub/PyGithub')]
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,93 @@
|
||||
############################ Copyrights and license ############################
|
||||
# #
|
||||
# Copyright 2023 Enrico Minack <github@enrico.minack.dev> #
|
||||
# #
|
||||
# This file is part of PyGithub. #
|
||||
# http://pygithub.readthedocs.io/ #
|
||||
# #
|
||||
# PyGithub is free software: you can redistribute it and/or modify it under #
|
||||
# the terms of the GNU Lesser General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public License #
|
||||
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
from unittest import mock
|
||||
|
||||
import github
|
||||
|
||||
from . import Framework
|
||||
|
||||
|
||||
class Requester(Framework.TestCase):
|
||||
logger = None
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.logger = mock.MagicMock()
|
||||
github.Requester.Requester.injectLogger(self.logger)
|
||||
|
||||
def tearDown(self):
|
||||
github.Requester.Requester.resetLogger()
|
||||
super().tearDown()
|
||||
|
||||
def testLoggingRedirection(self):
|
||||
self.assertEqual(self.g.get_repo("EnricoMi/test").name, "test-renamed")
|
||||
self.logger.info.assert_called_once_with(
|
||||
"Following Github server redirection from /repos/EnricoMi/test to /repositories/638123443"
|
||||
)
|
||||
|
||||
def testBaseUrlSchemeRedirection(self):
|
||||
gh = github.Github(base_url="http://api.github.com")
|
||||
with self.assertRaises(RuntimeError) as exc:
|
||||
gh.get_repo("PyGithub/PyGithub")
|
||||
self.assertEqual(
|
||||
exc.exception.args,
|
||||
(
|
||||
"Github server redirected from http protocol to https, please correct your "
|
||||
"Github server URL via base_url: Github(base_url=...)",
|
||||
),
|
||||
)
|
||||
|
||||
def testBaseUrlHostRedirection(self):
|
||||
gh = github.Github(base_url="https://www.github.com")
|
||||
with self.assertRaises(RuntimeError) as exc:
|
||||
gh.get_repo("PyGithub/PyGithub")
|
||||
self.assertEqual(
|
||||
exc.exception.args,
|
||||
(
|
||||
"Github server redirected from host www.github.com to github.com, "
|
||||
"please correct your Github server URL via base_url: Github(base_url=...)",
|
||||
),
|
||||
)
|
||||
|
||||
def testBaseUrlPortRedirection(self):
|
||||
# replay data forged
|
||||
gh = github.Github(base_url="https://api.github.com")
|
||||
with self.assertRaises(RuntimeError) as exc:
|
||||
gh.get_repo("PyGithub/PyGithub")
|
||||
self.assertEqual(
|
||||
exc.exception.args,
|
||||
(
|
||||
"Requested https://api.github.com/repos/PyGithub/PyGithub but server "
|
||||
"redirected to https://api.github.com:443/repos/PyGithub/PyGithub, "
|
||||
"you may need to correct your Github server URL "
|
||||
"via base_url: Github(base_url=...)",
|
||||
),
|
||||
)
|
||||
|
||||
def testBaseUrlPrefixRedirection(self):
|
||||
# replay data forged
|
||||
gh = github.Github(base_url="https://api.github.com/api/v3")
|
||||
self.assertEqual(gh.get_repo("PyGithub/PyGithub").name, "PyGithub")
|
||||
self.logger.info.assert_called_once_with(
|
||||
"Following Github server redirection from /api/v3/repos/PyGithub/PyGithub to /repos/PyGithub/PyGithub"
|
||||
)
|
||||
Reference in New Issue
Block a user