1s autopkgtest [20:45:54]: starting date and time: 2025-06-05 20:45:54+0000 1s autopkgtest [20:45:54]: git checkout: 9986aa8c Merge branch 'skia/fix_network_interface' into 'ubuntu/production' 1s autopkgtest [20:45:54]: host juju-7f2275-prod-proposed-migration-environment-15; command line: /home/ubuntu/autopkgtest/runner/autopkgtest --output-dir /tmp/autopkgtest-work.9g9ii25t/out --timeout-copy=6000 --setup-commands /home/ubuntu/autopkgtest-cloud/worker-config-production/setup-canonical.sh --apt-pocket=proposed=src:python3-defaults --apt-upgrade pypuppetdb --timeout-short=300 --timeout-copy=20000 --timeout-build=20000 --env=ADT_TEST_TRIGGERS=python3-defaults/3.13.4-1 -- ssh -s /home/ubuntu/autopkgtest/ssh-setup/nova -- --flavor autopkgtest-cpu2-ram4-disk20-amd64 --security-groups autopkgtest-juju-7f2275-prod-proposed-migration-environment-15@sto01-1.secgroup --name adt-questing-amd64-pypuppetdb-20250605-204553-juju-7f2275-prod-proposed-migration-environment-15-854d2b87-e43b-4ee2-93eb-4560b4088edd --image adt/ubuntu-questing-amd64-server --keyname testbed-juju-7f2275-prod-proposed-migration-environment-15 --net-id=net_prod-autopkgtest-workers-amd64 -e TERM=linux --mirror=http://ftpmaster.internal/ubuntu/ 63s autopkgtest [20:46:56]: testbed dpkg architecture: amd64 63s autopkgtest [20:46:56]: testbed apt version: 3.1.0ubuntu3 63s autopkgtest [20:46:56]: @@@@@@@@@@@@@@@@@@@@ test bed setup 63s autopkgtest [20:46:56]: testbed release detected to be: None 64s autopkgtest [20:46:57]: updating testbed package index (apt update) 64s Get:1 http://ftpmaster.internal/ubuntu questing-proposed InRelease [249 kB] 64s Hit:2 http://ftpmaster.internal/ubuntu questing InRelease 64s Hit:3 http://ftpmaster.internal/ubuntu questing-updates InRelease 64s Hit:4 http://ftpmaster.internal/ubuntu questing-security InRelease 64s Get:5 http://ftpmaster.internal/ubuntu questing-proposed/main Sources [55.3 kB] 65s Get:6 http://ftpmaster.internal/ubuntu questing-proposed/universe Sources [438 kB] 65s Get:7 http://ftpmaster.internal/ubuntu questing-proposed/restricted Sources [3168 B] 65s Get:8 http://ftpmaster.internal/ubuntu questing-proposed/multiverse Sources [13.0 kB] 65s Get:9 http://ftpmaster.internal/ubuntu questing-proposed/main i386 Packages [46.5 kB] 65s Get:10 http://ftpmaster.internal/ubuntu questing-proposed/main amd64 Packages [82.1 kB] 65s Get:11 http://ftpmaster.internal/ubuntu questing-proposed/restricted amd64 Packages [8100 B] 65s Get:12 http://ftpmaster.internal/ubuntu questing-proposed/restricted i386 Packages [2420 B] 65s Get:13 http://ftpmaster.internal/ubuntu questing-proposed/universe i386 Packages [163 kB] 65s Get:14 http://ftpmaster.internal/ubuntu questing-proposed/universe amd64 Packages [388 kB] 65s Get:15 http://ftpmaster.internal/ubuntu questing-proposed/multiverse i386 Packages [4068 B] 65s Get:16 http://ftpmaster.internal/ubuntu questing-proposed/multiverse amd64 Packages [10.8 kB] 65s Fetched 1464 kB in 1s (2415 kB/s) 66s Reading package lists... 66s autopkgtest [20:46:59]: upgrading testbed (apt dist-upgrade and autopurge) 66s Reading package lists... 66s Building dependency tree... 66s Reading state information... 67s Calculating upgrade... 67s The following packages will be upgraded: 67s libpython3-stdlib python3 python3-minimal 67s 3 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 67s Need to get 62.3 kB of archives. 67s After this operation, 0 B of additional disk space will be used. 67s Get:1 http://ftpmaster.internal/ubuntu questing-proposed/main amd64 python3-minimal amd64 3.13.4-1 [27.9 kB] 67s Get:2 http://ftpmaster.internal/ubuntu questing-proposed/main amd64 python3 amd64 3.13.4-1 [24.0 kB] 67s Get:3 http://ftpmaster.internal/ubuntu questing-proposed/main amd64 libpython3-stdlib amd64 3.13.4-1 [10.5 kB] 67s Fetched 62.3 kB in 0s (6084 kB/s) 67s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 80922 files and directories currently installed.) 67s Preparing to unpack .../python3-minimal_3.13.4-1_amd64.deb ... 67s Unpacking python3-minimal (3.13.4-1) over (3.13.3-1) ... 67s Setting up python3-minimal (3.13.4-1) ... 67s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 80922 files and directories currently installed.) 67s Preparing to unpack .../python3_3.13.4-1_amd64.deb ... 67s Unpacking python3 (3.13.4-1) over (3.13.3-1) ... 67s Preparing to unpack .../libpython3-stdlib_3.13.4-1_amd64.deb ... 67s Unpacking libpython3-stdlib:amd64 (3.13.4-1) over (3.13.3-1) ... 67s Setting up libpython3-stdlib:amd64 (3.13.4-1) ... 67s Setting up python3 (3.13.4-1) ... 68s Processing triggers for man-db (2.13.1-1) ... 68s Reading package lists... 68s Building dependency tree... 68s Reading state information... 68s Solving dependencies... 69s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 71s autopkgtest [20:47:04]: testbed running kernel: Linux 6.14.0-15-generic #15-Ubuntu SMP PREEMPT_DYNAMIC Sun Apr 6 15:05:05 UTC 2025 71s autopkgtest [20:47:04]: @@@@@@@@@@@@@@@@@@@@ apt-source pypuppetdb 72s Get:1 http://ftpmaster.internal/ubuntu questing/universe pypuppetdb 3.2.0-1 (dsc) [2327 B] 72s Get:2 http://ftpmaster.internal/ubuntu questing/universe pypuppetdb 3.2.0-1 (tar) [47.7 kB] 72s Get:3 http://ftpmaster.internal/ubuntu questing/universe pypuppetdb 3.2.0-1 (diff) [3756 B] 72s gpgv: Signature made Sat Jan 20 14:58:34 2024 UTC 72s gpgv: using RSA key 8F6DE104377F3B11E741748731F3144544A1741A 72s gpgv: issuer "tchet@debian.org" 72s gpgv: Can't check signature: No public key 72s dpkg-source: warning: cannot verify inline signature for ./pypuppetdb_3.2.0-1.dsc: no acceptable signature found 72s autopkgtest [20:47:05]: testing package pypuppetdb version 3.2.0-1 72s autopkgtest [20:47:05]: build not needed 73s autopkgtest [20:47:06]: test unittests: preparing testbed 73s Reading package lists... 73s Building dependency tree... 73s Reading state information... 73s Solving dependencies... 73s The following NEW packages will be installed: 73s git git-man liberror-perl python3-all python3-bandit python3-git 73s python3-gitdb python3-httpretty python3-importlib-metadata python3-iniconfig 73s python3-jschema-to-python python3-jsonpickle python3-mypy 73s python3-mypy-extensions python3-pbr python3-pluggy python3-psutil 73s python3-pypuppetdb python3-pytest python3-sarif-python-om python3-smmap 73s python3-stevedore 73s 0 upgraded, 22 newly installed, 0 to remove and 0 not upgraded. 73s Need to get 16.1 MB of archives. 73s After this operation, 78.1 MB of additional disk space will be used. 73s Get:1 http://ftpmaster.internal/ubuntu questing/main amd64 liberror-perl all 0.17030-1 [23.5 kB] 73s Get:2 http://ftpmaster.internal/ubuntu questing/main amd64 git-man all 1:2.48.1-0ubuntu1 [1148 kB] 74s Get:3 http://ftpmaster.internal/ubuntu questing/main amd64 git amd64 1:2.48.1-0ubuntu1 [4759 kB] 75s Get:4 http://ftpmaster.internal/ubuntu questing-proposed/main amd64 python3-all amd64 3.13.4-1 [880 B] 75s Get:5 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-smmap all 6.0.0-1 [20.5 kB] 75s Get:6 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-gitdb all 4.0.12-1 [46.4 kB] 75s Get:7 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-git all 3.1.44-1 [148 kB] 75s Get:8 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-httpretty all 1.1.4-4 [23.1 kB] 75s Get:9 http://ftpmaster.internal/ubuntu questing/main amd64 python3-importlib-metadata all 8.7.0-2 [21.0 kB] 75s Get:10 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-iniconfig all 1.1.1-2 [6024 B] 75s Get:11 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-jsonpickle all 4.0.2+dfsg-2 [40.1 kB] 75s Get:12 http://ftpmaster.internal/ubuntu questing/main amd64 python3-pbr all 6.1.1-0ubuntu1 [58.2 kB] 75s Get:13 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-jschema-to-python all 1.2.3-3 [8120 B] 75s Get:14 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-mypy-extensions all 1.0.0-1 [6148 B] 75s Get:15 http://ftpmaster.internal/ubuntu questing/main amd64 python3-psutil amd64 5.9.8-2build3 [196 kB] 75s Get:16 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-mypy amd64 1.15.0-5 [9215 kB] 75s Get:17 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-pluggy all 1.5.0-1 [21.0 kB] 76s Get:18 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-pypuppetdb all 3.2.0-1 [28.9 kB] 76s Get:19 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-pytest all 8.3.5-2 [252 kB] 76s Get:20 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-sarif-python-om all 1.0.4-3 [12.5 kB] 76s Get:21 http://ftpmaster.internal/ubuntu questing/main amd64 python3-stevedore all 1:5.4.1-0ubuntu1 [21.2 kB] 76s Get:22 http://ftpmaster.internal/ubuntu questing/universe amd64 python3-bandit all 1.7.10-2 [75.1 kB] 76s Fetched 16.1 MB in 2s (6854 kB/s) 76s Selecting previously unselected package liberror-perl. 76s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 80922 files and directories currently installed.) 76s Preparing to unpack .../00-liberror-perl_0.17030-1_all.deb ... 76s Unpacking liberror-perl (0.17030-1) ... 76s Selecting previously unselected package git-man. 76s Preparing to unpack .../01-git-man_1%3a2.48.1-0ubuntu1_all.deb ... 76s Unpacking git-man (1:2.48.1-0ubuntu1) ... 76s Selecting previously unselected package git. 76s Preparing to unpack .../02-git_1%3a2.48.1-0ubuntu1_amd64.deb ... 76s Unpacking git (1:2.48.1-0ubuntu1) ... 76s Selecting previously unselected package python3-all. 76s Preparing to unpack .../03-python3-all_3.13.4-1_amd64.deb ... 76s Unpacking python3-all (3.13.4-1) ... 76s Selecting previously unselected package python3-smmap. 76s Preparing to unpack .../04-python3-smmap_6.0.0-1_all.deb ... 76s Unpacking python3-smmap (6.0.0-1) ... 76s Selecting previously unselected package python3-gitdb. 76s Preparing to unpack .../05-python3-gitdb_4.0.12-1_all.deb ... 76s Unpacking python3-gitdb (4.0.12-1) ... 76s Selecting previously unselected package python3-git. 76s Preparing to unpack .../06-python3-git_3.1.44-1_all.deb ... 76s Unpacking python3-git (3.1.44-1) ... 76s Selecting previously unselected package python3-httpretty. 76s Preparing to unpack .../07-python3-httpretty_1.1.4-4_all.deb ... 76s Unpacking python3-httpretty (1.1.4-4) ... 76s Selecting previously unselected package python3-importlib-metadata. 76s Preparing to unpack .../08-python3-importlib-metadata_8.7.0-2_all.deb ... 76s Unpacking python3-importlib-metadata (8.7.0-2) ... 76s Selecting previously unselected package python3-iniconfig. 76s Preparing to unpack .../09-python3-iniconfig_1.1.1-2_all.deb ... 76s Unpacking python3-iniconfig (1.1.1-2) ... 76s Selecting previously unselected package python3-jsonpickle. 76s Preparing to unpack .../10-python3-jsonpickle_4.0.2+dfsg-2_all.deb ... 76s Unpacking python3-jsonpickle (4.0.2+dfsg-2) ... 76s Selecting previously unselected package python3-pbr. 76s Preparing to unpack .../11-python3-pbr_6.1.1-0ubuntu1_all.deb ... 76s Unpacking python3-pbr (6.1.1-0ubuntu1) ... 76s Selecting previously unselected package python3-jschema-to-python. 76s Preparing to unpack .../12-python3-jschema-to-python_1.2.3-3_all.deb ... 76s Unpacking python3-jschema-to-python (1.2.3-3) ... 76s Selecting previously unselected package python3-mypy-extensions. 76s Preparing to unpack .../13-python3-mypy-extensions_1.0.0-1_all.deb ... 76s Unpacking python3-mypy-extensions (1.0.0-1) ... 76s Selecting previously unselected package python3-psutil. 76s Preparing to unpack .../14-python3-psutil_5.9.8-2build3_amd64.deb ... 76s Unpacking python3-psutil (5.9.8-2build3) ... 76s Selecting previously unselected package python3-mypy. 76s Preparing to unpack .../15-python3-mypy_1.15.0-5_amd64.deb ... 76s Unpacking python3-mypy (1.15.0-5) ... 77s Selecting previously unselected package python3-pluggy. 77s Preparing to unpack .../16-python3-pluggy_1.5.0-1_all.deb ... 77s Unpacking python3-pluggy (1.5.0-1) ... 77s Selecting previously unselected package python3-pypuppetdb. 77s Preparing to unpack .../17-python3-pypuppetdb_3.2.0-1_all.deb ... 77s Unpacking python3-pypuppetdb (3.2.0-1) ... 77s Selecting previously unselected package python3-pytest. 77s Preparing to unpack .../18-python3-pytest_8.3.5-2_all.deb ... 77s Unpacking python3-pytest (8.3.5-2) ... 77s Selecting previously unselected package python3-sarif-python-om. 77s Preparing to unpack .../19-python3-sarif-python-om_1.0.4-3_all.deb ... 77s Unpacking python3-sarif-python-om (1.0.4-3) ... 77s Selecting previously unselected package python3-stevedore. 77s Preparing to unpack .../20-python3-stevedore_1%3a5.4.1-0ubuntu1_all.deb ... 77s Unpacking python3-stevedore (1:5.4.1-0ubuntu1) ... 77s Selecting previously unselected package python3-bandit. 77s Preparing to unpack .../21-python3-bandit_1.7.10-2_all.deb ... 77s Unpacking python3-bandit (1.7.10-2) ... 77s Setting up python3-iniconfig (1.1.1-2) ... 77s Setting up python3-httpretty (1.1.4-4) ... 77s Setting up python3-jsonpickle (4.0.2+dfsg-2) ... 77s Setting up python3-importlib-metadata (8.7.0-2) ... 77s Setting up python3-pbr (6.1.1-0ubuntu1) ... 77s Setting up python3-mypy-extensions (1.0.0-1) ... 77s Setting up python3-all (3.13.4-1) ... 77s Setting up python3-psutil (5.9.8-2build3) ... 78s Setting up liberror-perl (0.17030-1) ... 78s Setting up python3-sarif-python-om (1.0.4-3) ... 78s Setting up python3-jschema-to-python (1.2.3-3) ... 78s Setting up python3-mypy (1.15.0-5) ... 79s Setting up python3-pluggy (1.5.0-1) ... 79s Setting up python3-stevedore (1:5.4.1-0ubuntu1) ... 79s Setting up git-man (1:2.48.1-0ubuntu1) ... 79s Setting up python3-pypuppetdb (3.2.0-1) ... 79s Setting up python3-smmap (6.0.0-1) ... 79s Setting up python3-pytest (8.3.5-2) ... 79s Setting up python3-gitdb (4.0.12-1) ... 79s Setting up git (1:2.48.1-0ubuntu1) ... 79s Setting up python3-git (3.1.44-1) ... 79s Setting up python3-bandit (1.7.10-2) ... 80s Processing triggers for man-db (2.13.1-1) ... 81s autopkgtest [20:47:14]: test unittests: [----------------------- 81s === python3.13 === 82s ============================= test session starts ============================== 82s platform linux -- Python 3.13.4, pytest-8.3.5, pluggy-1.5.0 82s rootdir: /tmp/autopkgtest.dgrj1r/autopkgtest_tmp 82s plugins: typeguard-4.4.2 82s collected 163 items 82s 82s tests/test_api_base_init.py ............................ [ 17%] 84s tests/test_api_base_query.py ...FFFFFFFFFFFFFFFFFF.FF [ 31%] 84s tests/test_api_base_url.py .... [ 34%] 84s tests/test_api_metrics.py FFFFFFFFF. [ 40%] 85s tests/test_api_other.py FFF.FF [ 44%] 85s tests/test_api_pql.py FF..... [ 48%] 85s tests/test_api_query.py FF.FFF [ 52%] 85s tests/test_connect.py .. [ 53%] 85s tests/test_querybuilder.py ...................................... [ 76%] 86s tests/test_types.py .............................. [ 95%] 86s tests/test_utils.py ........ [100%] 86s 86s =================================== FAILURES =================================== 86s _____________ TestBaseAPIQuery.test_setting_headers_without_token ______________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_setting_headers_without_token(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes") # need to query some endpoint 86s 86s tests/test_api_base_query.py:59: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(221 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via GET at 1749156433.9433608 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _______________ TestBaseAPIQuery.test_setting_headers_with_token _______________ 86s 86s self = 86s method = 'GET', url = '/pdb/query/v4/nodes', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'X-Authentication': 'tokenstring'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = None, conn = None, release_this_conn = True 86s http_tunnel_required = True, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s > self._prepare_proxy(conn) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:474: in increment 86s raise reraise(type(error), error, _stacktrace) 86s /usr/lib/python3/dist-packages/urllib3/util/util.py:38: in reraise 86s raise value.with_traceback(tb) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: in urlopen 86s self._prepare_proxy(conn) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3.13/http/client.py:300: ProtocolError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s token_api = 86s 86s def test_setting_headers_with_token(self, token_api): 86s httpretty.enable() 86s stub_request("https://localhost:8080/pdb/query/v4/nodes") 86s > token_api._query("nodes") # need to query some endpoint 86s 86s tests/test_api_base_query.py:73: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s > raise ConnectionError(err, request=request) 86s E requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:682: ConnectionError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(57 bytes) to ://localhost:8080localhost:8080 via CONNECT at 1749156434.0522063 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTPS. 86s _______________________ TestBaseAPIQuery.test_with_path ________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes/node1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes/node1', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes/node1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes/node1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_path(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes/node1") 86s > api._query("nodes", path="node1") 86s 86s tests/test_api_base_query.py:92: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes/node1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(227 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes/node1 via GET at 1749156434.1693258 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _____________________ TestBaseAPIQuery.test_with_url_path ______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/puppetdb/pdb/query/v4/nodes' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/puppetdb/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/puppetdb/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/puppetdb/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_url_path(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/puppetdb/pdb/query/v4/nodes") 86s api.url_path = "/puppetdb" 86s > api._query("nodes") 86s 86s tests/test_api_base_query.py:101: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/puppetdb/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(230 bytes) to ://localhost:8080http://localhost:8080/puppetdb/pdb/query/v4/nodes via GET at 1749156434.2597566 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________ TestBaseAPIQuery.test_with_password_authorization _______________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Authorization': 'Basic cHVwcGV0ZGI6cGFzc3dvcmQxMjM='} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_password_authorization(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s api.session.auth = ("puppetdb", "password123") 86s > api._query("nodes") 86s 86s tests/test_api_base_query.py:110: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(272 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via GET at 1749156434.3618703 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________ TestBaseAPIQuery.test_with_token_authorization ________________ 86s 86s self = 86s method = 'GET', url = '/pdb/query/v4/nodes', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'X-Authentication': 'tokenstring'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = None, conn = None, release_this_conn = True 86s http_tunnel_required = True, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s > self._prepare_proxy(conn) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:474: in increment 86s raise reraise(type(error), error, _stacktrace) 86s /usr/lib/python3/dist-packages/urllib3/util/util.py:38: in reraise 86s raise value.with_traceback(tb) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: in urlopen 86s self._prepare_proxy(conn) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3.13/http/client.py:300: ProtocolError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s token_api = 86s 86s def test_with_token_authorization(self, token_api): 86s httpretty.enable() 86s stub_request("https://localhost:8080/pdb/query/v4/nodes") 86s > token_api._query("nodes") 86s 86s tests/test_api_base_query.py:124: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s > raise ConnectionError(err, request=request) 86s E requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:682: ConnectionError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(57 bytes) to ://localhost:8080localhost:8080 via CONNECT at 1749156434.452472 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTPS. 86s _______________________ TestBaseAPIQuery.test_with_query _______________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_query(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", query='["certname", "=", "node1"]') 86s 86s tests/test_api_base_query.py:131: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(276 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?query=%5B%22certname%22%2C+%22%3D%22%2C+%22node1%22%5D via GET at 1749156434.5599506 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _______________________ TestBaseAPIQuery.test_with_order _______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?order_by=ted' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='order_by=ted', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?order_by=ted' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?order_by=ted (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_order(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", order_by="ted") 86s 86s tests/test_api_base_query.py:141: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?order_by=ted (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(234 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?order_by=ted via GET at 1749156434.6506314 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _______________________ TestBaseAPIQuery.test_with_limit _______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?limit=1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='limit=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?limit=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?limit=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_limit(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", limit=1) 86s 86s tests/test_api_base_query.py:149: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?limit=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(229 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?limit=1 via GET at 1749156434.742372 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________ TestBaseAPIQuery.test_with_include_total ___________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?include_total=true', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='include_total=true', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?include_total=true' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?include_total=true (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_include_total(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", include_total=True) 86s 86s tests/test_api_base_query.py:157: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?include_total=true (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(240 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?include_total=true via GET at 1749156434.8332057 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________________ TestBaseAPIQuery.test_with_offset _______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?offset=1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='offset=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?offset=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?offset=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_offset(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", offset=1) 86s 86s tests/test_api_base_query.py:165: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?offset=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(230 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?offset=1 via GET at 1749156434.922925 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________ TestBaseAPIQuery.test_with_summarize_by ____________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?summarize_by=1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='summarize_by=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?summarize_by=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?summarize_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_summarize_by(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", summarize_by=1) 86s 86s tests/test_api_base_query.py:173: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?summarize_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(236 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?summarize_by=1 via GET at 1749156435.0147996 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _____________________ TestBaseAPIQuery.test_with_count_by ______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?count_by=1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='count_by=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?count_by=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?count_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_count_by(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", count_by=1) 86s 86s tests/test_api_base_query.py:181: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?count_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(232 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?count_by=1 via GET at 1749156435.104672 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________ TestBaseAPIQuery.test_with_count_filter ____________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?counts_filter=1' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='counts_filter=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes?counts_filter=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?counts_filter=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_count_filter(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", count_filter=1) 86s 86s tests/test_api_base_query.py:189: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?counts_filter=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(237 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?counts_filter=1 via GET at 1749156435.1940408 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ____________________ TestBaseAPIQuery.test_with_payload_get ____________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?foo=bar&count_by=1', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='foo=bar&count_by=1', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?foo=bar&count_by=1' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?foo=bar&count_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_payload_get(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes") 86s > api._query("nodes", payload={"foo": "bar"}, count_by=1) 86s 86s tests/test_api_base_query.py:197: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?foo=bar&count_by=1 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(240 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?foo=bar&count_by=1 via GET at 1749156435.2862577 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________ TestBaseAPIQuery.test_with_payload_post ____________________ 86s 86s self = 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s body = '{"foo": "bar", "count_by": 1}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '29'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_with_payload_post(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes", method=httpretty.POST) 86s > api._query("nodes", payload={"foo": "bar"}, count_by=1, request_method="POST") 86s 86s tests/test_api_base_query.py:208: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:418: in _make_request 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(242 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via POST at 1749156435.3751516 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _____________________ TestBaseAPIQuery.test_response_empty _____________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_response_empty(self, api): 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/pdb/query/v4/nodes", 86s body=json.dumps(None), 86s ) 86s with pytest.raises(pypuppetdb.errors.EmptyResponseError): 86s > api._query("nodes") 86s 86s tests/test_api_base_query.py:223: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(221 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via GET at 1749156435.4659324 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________ TestBaseAPIQuery.test_response_x_records ___________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?include_total=true', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='include_total=true', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?include_total=true' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?include_total=true (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_response_x_records(self, api): 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/pdb/query/v4/nodes", 86s adding_headers={"X-Records": 256}, 86s body="[]", 86s ) 86s > api._query("nodes", include_total=True) 86s 86s tests/test_api_base_query.py:233: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?include_total=true (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(240 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?include_total=true via GET at 1749156435.5582423 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________ TestBaseAPIQuery.test_query_with_post[string] _________________ 86s 86s self = 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s body = '{"query": "[\\"certname\\", \\"=\\", \\"node1\\"]", "count_by": 1}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '60'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s query = '["certname", "=", "node1"]' 86s 86s def test_query_with_post(self, api, query): 86s httpretty.reset() 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes", method=httpretty.POST) 86s > api._query("nodes", query=query, count_by=1, request_method="POST") 86s 86s tests/test_api_base_query.py:250: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:418: in _make_request 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(242 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via POST at 1749156435.6501346 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _____________ TestBaseAPIQuery.test_query_with_post[QueryBuilder] ______________ 86s 86s self = 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s body = '{"query": "[\\"=\\", \\"certname\\", \\"node1\\"]", "count_by": 1}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '60'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST', url = 'http://localhost:8080/pdb/query/v4/nodes' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s query = Query: ["=", "certname", "node1"] 86s 86s def test_query_with_post(self, api, query): 86s httpretty.reset() 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/nodes", method=httpretty.POST) 86s > api._query("nodes", query=query, count_by=1, request_method="POST") 86s 86s tests/test_api_base_query.py:250: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:418: in _make_request 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(242 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes via POST at 1749156435.7395527 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________________ TestMetricsAPI.test_metric_v1 _________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans/test' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v1/mbeans/test', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans/test' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans/test (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v1(self, api): 86s httpretty.enable() 86s httpretty.enable() 86s stub_request("http://localhost:8080/metrics/v1/mbeans/test") 86s > api.metric("test", version="v1") 86s 86s tests/test_api_metrics.py:23: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:37: in metric 86s return self._query("mbean", path=metric) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans/test (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(225 bytes) to ://localhost:8080http://localhost:8080/metrics/v1/mbeans/test via GET at 1749156435.8473783 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________________ TestMetricsAPI.test_metric_v1_list ______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v1/mbeans', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans', response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v1_list(self, api): 86s httpretty.enable() 86s httpretty.enable() 86s stub_request("http://localhost:8080/metrics/v1/mbeans") 86s > api.metric(version="v1") 86s 86s tests/test_api_metrics.py:30: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:37: in metric 86s return self._query("mbean", path=metric) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(220 bytes) to ://localhost:8080http://localhost:8080/metrics/v1/mbeans via GET at 1749156435.9350266 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________ TestMetricsAPI.test_metric_v1_version_constructor _______________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans/test' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v1/mbeans/test', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v1/mbeans/test' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans/test (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s 86s def test_metric_v1_version_constructor(self): 86s api = pypuppetdb.api.API(metric_api_version="v1") 86s httpretty.enable() 86s httpretty.enable() 86s stub_request("http://localhost:8080/metrics/v1/mbeans/test") 86s > api.metric("test") 86s 86s tests/test_api_metrics.py:38: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:37: in metric 86s return self._query("mbean", path=metric) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v1/mbeans/test (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(225 bytes) to ://localhost:8080http://localhost:8080/metrics/v1/mbeans/test via GET at 1749156436.0232847 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________________ TestMetricsAPI.test_metric_v2 _________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/read/test%3Aname%3DNum', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v2(self, api): 86s metrics_body = { 86s "request": {"mbean": "test:name=Num", "type": "read"}, 86s "value": {"Value": 0}, 86s "timestamp": 0, 86s "status": 200, 86s } 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/metrics/v2/read/test:name=Num", 86s body=json.dumps(metrics_body), 86s ) 86s > metric = api.metric("test:name=Num") 86s 86s tests/test_api_metrics.py:55: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:31: in metric 86s res = self._query("metrics", path=self._escape_metric_name(metric)) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(236 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/read/test%3Aname%3DNum via GET at 1749156436.1144402 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________ TestMetricsAPI.test_metric_v2_version_constructor _______________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/read/test%3Aname%3DNum', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s 86s def test_metric_v2_version_constructor(self): 86s api = pypuppetdb.api.API(metric_api_version="v2") 86s metrics_body = { 86s "request": {"mbean": "test:name=Num", "type": "read"}, 86s "value": {"Value": 0}, 86s "timestamp": 0, 86s "status": 200, 86s } 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/metrics/v2/read/test:name=Num", 86s body=json.dumps(metrics_body), 86s ) 86s > metric = api.metric("test:name=Num") 86s 86s tests/test_api_metrics.py:76: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:31: in metric 86s res = self._query("metrics", path=self._escape_metric_name(metric)) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(236 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/read/test%3Aname%3DNum via GET at 1749156436.202903 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _________________ TestMetricsAPI.test_metric_v2_version_string _________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/read/test%3Aname%3DNum', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v2_version_string(self, api): 86s metrics_body = { 86s "request": {"mbean": "test:name=Num", "type": "read"}, 86s "value": {"Value": 0}, 86s "timestamp": 0, 86s "status": 200, 86s } 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/metrics/v2/read/test:name=Num", 86s body=json.dumps(metrics_body), 86s ) 86s > metric = api.metric("test:name=Num", version="v2") 86s 86s tests/test_api_metrics.py:96: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:31: in metric 86s res = self._query("metrics", path=self._escape_metric_name(metric)) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(236 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/read/test%3Aname%3DNum via GET at 1749156436.2935252 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _____________________ TestMetricsAPI.test_metric_v2_error ______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/read/test%3Aname%3DNum', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/read/test%3Aname%3DNum' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v2_error(self, api): 86s metrics_body = { 86s "request": {"mbean": "test:name=Num", "type": "read"}, 86s "error_type": "javax.management.InstanceNotFoundException", 86s "error": "javax.management.InstanceNotFoundException : test:name=Num", 86s "status": 404, 86s } 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/metrics/v2/read/test:name=Num", 86s body=json.dumps(metrics_body), 86s ) 86s with pytest.raises(pypuppetdb.errors.APIError): 86s > api.metric("test:name=Num") 86s 86s tests/test_api_metrics.py:117: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:31: in metric 86s res = self._query("metrics", path=self._escape_metric_name(metric)) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aname%3DNum (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(236 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/read/test%3Aname%3DNum via GET at 1749156436.387961 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________ TestMetricsAPI.test_metric_v2_escape_special_characters ____________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v2_escape_special_characters(self, api): 86s metrics_body = { 86s "request": {"mbean": "test:name=Num", "type": "read"}, 86s "value": {"Value": 0}, 86s "timestamp": 0, 86s "status": 200, 86s } 86s 86s httpretty.enable() 86s metric_name = 'test:special/chars!metric"name' 86s metric_escaped = 'test:special!/chars!!metric!"name' 86s metric_escaped_urlencoded = "test%3Aspecial%21/chars%21%21metric%21%22name" 86s httpretty.register_uri( 86s httpretty.GET, 86s ("http://localhost:8080/metrics/v2/read/" + metric_escaped), 86s body=json.dumps(metrics_body), 86s ) 86s > metric = api.metric(metric_name) 86s 86s tests/test_api_metrics.py:139: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:31: in metric 86s res = self._query("metrics", path=self._escape_metric_name(metric)) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(264 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/read/test%3Aspecial%21/chars%21%21metric%21%22name via GET at 1749156436.4777412 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ______________________ TestMetricsAPI.test_metric_v2_list ______________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/list', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/metrics/v2/list', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/metrics/v2/list', response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/list (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_metric_v2_list(self, api): 86s # test metric() (no arguments) 86s metrics_body = { 86s "request": {"type": "list"}, 86s "value": { 86s "java.util.logging": {"type=Logging": {}}, 86s }, 86s "timestamp": 0, 86s "status": 200, 86s } 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, 86s "http://localhost:8080/metrics/v2/list", 86s body=json.dumps(metrics_body), 86s ) 86s > metric = api.metric() 86s 86s tests/test_api_metrics.py:164: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/metrics.py:29: in metric 86s res = self._query("metrics-list") 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/metrics/v2/list (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(218 bytes) to ://localhost:8080http://localhost:8080/metrics/v2/list via GET at 1749156436.5677652 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _________________________ TestCommandAPI.test_command __________________________ 86s 86s self = 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s body = '{"certname": "testnode"}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '24'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/cmd/v1', query='command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_command(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/cmd/v1", method=httpretty.POST) 86s > api.command("deactivate node", {"certname": "testnode"}) 86s 86s tests/test_api_other.py:32: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/command.py:19: in command 86s return self._cmd(command, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/command.py:58: in _cmd 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(336 bytes) to ://localhost:8080http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a via POST at 1749156436.6577165 86s ERROR pypuppetdb.api.command:command.py:87 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _______________________ TestCommandAPI.test_cmd[string] ________________________ 86s 86s self = 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s body = '{"certname": "testnode"}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '24'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/cmd/v1', query='command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s query = '["certname", "=", "node1"]' 86s 86s def test_cmd(self, api, query): 86s httpretty.reset() 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/cmd/v1", method=httpretty.POST) 86s node_name = "testnode" 86s > api._cmd("deactivate node", {"certname": node_name}) 86s 86s tests/test_api_other.py:42: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/command.py:58: in _cmd 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(336 bytes) to ://localhost:8080http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a via POST at 1749156436.7413318 86s ERROR pypuppetdb.api.command:command.py:87 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ____________________ TestCommandAPI.test_cmd[QueryBuilder] _____________________ 86s 86s self = 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s body = '{"certname": "testnode"}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'Content-Length': '24'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/cmd/v1', query='command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'POST' 86s url = 'http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s query = Query: ["=", "certname", "node1"] 86s 86s def test_cmd(self, api, query): 86s httpretty.reset() 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/cmd/v1", method=httpretty.POST) 86s node_name = "testnode" 86s > api._cmd("deactivate node", {"certname": node_name}) 86s 86s tests/test_api_other.py:42: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/command.py:58: in _cmd 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(336 bytes) to ://localhost:8080http://localhost:8080/pdb/cmd/v1?command=deactivate+node&version=3&certname=testnode&checksum=b93d474970e54943aec050ee399dfb85d21e143a via POST at 1749156436.8261724 86s ERROR pypuppetdb.api.command:command.py:87 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _______________ TestCommandAPI.test_cmd_with_token_authorization _______________ 86s 86s self = 86s method = 'POST' 86s url = '/pdb/cmd/v1?command=deactivate+node&version=3&certname=&checksum=1d150468bd137c6511986985d707dc451d093a9c' 86s body = '{"certname": ""}' 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8', 'X-Authentication': 'tokenstring', 'Content-Length': '16'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/pdb/cmd/v1', query='command=deactivate+node&version=3&certname=&checksum=1d150468bd137c6511986985d707dc451d093a9c', fragment=None) 86s destination_scheme = None, conn = None, release_this_conn = True 86s http_tunnel_required = True, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s > self._prepare_proxy(conn) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:474: in increment 86s raise reraise(type(error), error, _stacktrace) 86s /usr/lib/python3/dist-packages/urllib3/util/util.py:38: in reraise 86s raise value.with_traceback(tb) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:773: in urlopen 86s self._prepare_proxy(conn) 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:1042: in _prepare_proxy 86s conn.connect() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:721: in connect 86s self._tunnel() 86s /usr/lib/python3.13/http/client.py:971: in _tunnel 86s (version, code, message) = response._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3.13/http/client.py:300: ProtocolError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s token_api = 86s 86s def test_cmd_with_token_authorization(self, token_api): 86s httpretty.enable() 86s stub_request("https://localhost:8080/pdb/cmd/v1", method=httpretty.POST) 86s > token_api._cmd("deactivate node", {"certname": ""}) 86s 86s tests/test_api_other.py:69: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/command.py:58: in _cmd 86s r = self.session.post( 86s /usr/lib/python3/dist-packages/requests/sessions.py:637: in post 86s return self.request("POST", url, data=data, json=json, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s > raise ConnectionError(err, request=request) 86s E requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:682: ConnectionError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(57 bytes) to ://localhost:8080localhost:8080 via CONNECT at 1749156436.9103687 86s ERROR pypuppetdb.api.command:command.py:87 Could not reach PuppetDB on localhost:8080 over HTTPS. 86s __________________________ TestStatusAPI.test_status ___________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/status/v1/services/puppetdb-status' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/status/v1/services/puppetdb-status', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/status/v1/services/puppetdb-status' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/status/v1/services/puppetdb-status (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_status(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/status/v1/services/puppetdb-status") 86s > api.status() 86s 86s tests/test_api_other.py:78: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/status.py:19: in status 86s return self._query("status") 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/status/v1/services/puppetdb-status (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(237 bytes) to ://localhost:8080http://localhost:8080/status/v1/services/puppetdb-status via GET at 1749156437.0131197 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _________________________ TestPqlAPI.test_pql_casting __________________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4?query=nodes+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4', query='query=nodes+%7B%0A++++++++++++...%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4?query=nodes+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4?query=nodes+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_pql_casting(self, api): 86s pql_query = """ 86s nodes { 86s facts { 86s name = "operatingsystem" and 86s value = "Debian" 86s } 86s } 86s """ 86s pql_body = [ 86s { 86s "cached_catalog_status": "not_used", 86s "catalog_environment": "production", 86s "catalog_timestamp": "2016-08-15T11:06:26.275Z", 86s "certname": "greenserver.vm", 86s "deactivated": None, 86s "expired": None, 86s "facts_environment": "production", 86s "facts_timestamp": "2016-08-15T11:06:26.140Z", 86s "latest_report_hash": "4a956674b016d95a7b77c99513ba26e4a744f8d1", 86s "latest_report_noop": False, 86s "latest_report_noop_pending": None, 86s "latest_report_status": "changed", 86s "report_environment": "production", 86s "report_timestamp": "2016-08-15T11:06:18.393Z", 86s }, 86s { 86s "cached_catalog_status": "not_used", 86s "catalog_environment": "production", 86s "catalog_timestamp": "2016-08-15T11:06:26.275Z", 86s "certname": "blueserver.vm", 86s "deactivated": None, 86s "expired": None, 86s "facts_environment": "production", 86s "facts_timestamp": "2016-08-15T11:06:26.140Z", 86s "latest_report_hash": "4a956674b016d95a7b77c99513ba26e4a744f8d1", 86s "latest_report_noop": False, 86s "latest_report_noop_pending": None, 86s "latest_report_status": "changed", 86s "report_environment": "production", 86s "report_timestamp": "2016-08-15T11:06:18.393Z", 86s }, 86s ] 86s pql_url = "http://localhost:8080/pdb/query/v4" 86s 86s httpretty.enable() 86s httpretty.register_uri(httpretty.GET, pql_url, body=json.dumps(pql_body)) 86s 86s > nodes = list(api.pql(pql_query)) 86s 86s tests/test_api_pql.py:57: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/pql.py:87: in pql 86s for element in self._pql(pql=pql): 86s /usr/lib/python3/dist-packages/pypuppetdb/api/pql.py:47: in _pql 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4?query=nodes+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(379 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4?query=nodes+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D via GET at 1749156437.0999198 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________________ TestPqlAPI.test_pql_no_casting ________________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4?query=nodes%5Bcertname%5D+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4', query='query=nodes%5Bcertname%5D+%7B%...%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4?query=nodes%5Bcertname%5D+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4?query=nodes%5Bcertname%5D+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_pql_no_casting(self, api): 86s pql_query = """ 86s nodes[certname] { 86s facts { 86s name = "operatingsystem" and 86s value = "Debian" 86s } 86s } 86s """ 86s pql_body = [ 86s {"certname": "foo.example.com"}, 86s {"certname": "bar.example.com"}, 86s ] 86s pql_url = "http://localhost:8080/pdb/query/v4" 86s 86s httpretty.enable() 86s httpretty.register_uri(httpretty.GET, pql_url, body=json.dumps(pql_body)) 86s 86s > elements = list(api.pql(pql_query)) 86s 86s tests/test_api_pql.py:83: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/pql.py:87: in pql 86s for element in self._pql(pql=pql): 86s /usr/lib/python3/dist-packages/pypuppetdb/api/pql.py:47: in _pql 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4?query=nodes%5Bcertname%5D+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(393 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4?query=nodes%5Bcertname%5D+%7B%0A++++++++++++facts+%7B%0A++++++++++++++name+%3D+%22operatingsystem%22+and%0A++++++++++++++value+%3D+%22Debian%22%0A++++++++++++%7D%0A++++++++++%7D via GET at 1749156437.1880767 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ___________________________ TestQueryAPI.test_facts ____________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/facts', body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/facts', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/facts' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/facts (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_facts(self, api): 86s facts_body = [ 86s { 86s "certname": "test_certname", 86s "name": "test_name", 86s "value": "test_value", 86s "environment": "test_environment", 86s } 86s ] 86s facts_url = "http://localhost:8080/pdb/query/v4/facts" 86s 86s httpretty.enable() 86s httpretty.register_uri(httpretty.GET, facts_url, body=json.dumps(facts_body)) 86s 86s > for fact in api.facts(): 86s 86s tests/test_api_query.py:43: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/query.py:140: in facts 86s facts = self._query("facts", path=path, **kwargs) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/facts (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(221 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/facts via GET at 1749156437.2777174 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _________________________ TestQueryAPI.test_fact_names _________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/fact-names' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/fact-names', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/fact-names' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/fact-names (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_fact_names(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/fact-names") 86s > api.fact_names() 86s 86s tests/test_api_query.py:54: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/query.py:331: in fact_names 86s return self._query("fact-names") 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/fact-names (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(226 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/fact-names via GET at 1749156437.366914 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________________ TestQueryAPI.test_environments ________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/environments' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/environments', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/environments' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/environments (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_environments(self, api): 86s httpretty.enable() 86s stub_request("http://localhost:8080/pdb/query/v4/environments") 86s > api.environments() 86s 86s tests/test_api_query.py:66: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/query.py:116: in environments 86s return self._query("environments", **kwargs) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/environments (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(228 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/environments via GET at 1749156437.4731276 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s _________________________ TestQueryAPI.test_inventory __________________________ 86s 86s self = 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/inventory' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/inventory', query=None, fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET', url = 'http://localhost:8080/pdb/query/v4/inventory' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/inventory (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_inventory(self, api): 86s inventory_body = [ 86s { 86s "certname": "test_certname", 86s "timestamp": "2017-06-05T20:18:23.374Z", 86s "environment": "test_environment", 86s "facts": "test_facts", 86s "trusted": "test_trusted", 86s } 86s ] 86s inventory_url = "http://localhost:8080/pdb/query/v4/inventory" 86s 86s httpretty.enable() 86s httpretty.register_uri( 86s httpretty.GET, inventory_url, body=json.dumps(inventory_body) 86s ) 86s > for inv in api.inventory(): 86s 86s tests/test_api_query.py:87: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/query.py:360: in inventory 86s inventory = self._query("inventory", **kwargs) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/inventory (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(225 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/inventory via GET at 1749156437.5617447 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s ________________________ TestQueryAPI.test_nodes_single ________________________ 86s 86s self = 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22' 86s body = None 86s headers = {'content-type': 'application/json', 'accept': 'application/json', 'accept-charset': 'utf-8'} 86s retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s redirect = False, assert_same_host = False 86s timeout = Timeout(connect=10, read=10, total=None), pool_timeout = None 86s release_conn = False, chunked = False, body_pos = None, preload_content = False 86s decode_content = False, response_kw = {} 86s parsed_url = Url(scheme='http', auth=None, host='localhost', port=8080, path='/pdb/query/v4/nodes', query='query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22', fragment=None) 86s destination_scheme = 'http', conn = None, release_this_conn = True 86s http_tunnel_required = False, err = None, clean_exit = False 86s 86s def urlopen( # type: ignore[override] 86s self, 86s method: str, 86s url: str, 86s body: _TYPE_BODY | None = None, 86s headers: typing.Mapping[str, str] | None = None, 86s retries: Retry | bool | int | None = None, 86s redirect: bool = True, 86s assert_same_host: bool = True, 86s timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, 86s pool_timeout: int | None = None, 86s release_conn: bool | None = None, 86s chunked: bool = False, 86s body_pos: _TYPE_BODY_POSITION | None = None, 86s preload_content: bool = True, 86s decode_content: bool = True, 86s **response_kw: typing.Any, 86s ) -> BaseHTTPResponse: 86s """ 86s Get a connection from the pool and perform an HTTP request. This is the 86s lowest level call for making a request, so you'll need to specify all 86s the raw details. 86s 86s .. note:: 86s 86s More commonly, it's appropriate to use a convenience method 86s such as :meth:`request`. 86s 86s .. note:: 86s 86s `release_conn` will only behave as expected if 86s `preload_content=False` because we want to make 86s `preload_content=False` the default behaviour someday soon without 86s breaking backwards compatibility. 86s 86s :param method: 86s HTTP request method (such as GET, POST, PUT, etc.) 86s 86s :param url: 86s The URL to perform the request on. 86s 86s :param body: 86s Data to send in the request body, either :class:`str`, :class:`bytes`, 86s an iterable of :class:`str`/:class:`bytes`, or a file-like object. 86s 86s :param headers: 86s Dictionary of custom headers to send, such as User-Agent, 86s If-None-Match, etc. If None, pool headers are used. If provided, 86s these headers completely replace any pool-specific headers. 86s 86s :param retries: 86s Configure the number of retries to allow before raising a 86s :class:`~urllib3.exceptions.MaxRetryError` exception. 86s 86s If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a 86s :class:`~urllib3.util.retry.Retry` object for fine-grained control 86s over different types of retries. 86s Pass an integer number to retry connection errors that many times, 86s but no other types of errors. Pass zero to never retry. 86s 86s If ``False``, then retries are disabled and any exception is raised 86s immediately. Also, instead of raising a MaxRetryError on redirects, 86s the redirect response will be returned. 86s 86s :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. 86s 86s :param redirect: 86s If True, automatically handle redirects (status codes 301, 302, 86s 303, 307, 308). Each redirect counts as a retry. Disabling retries 86s will disable redirect, too. 86s 86s :param assert_same_host: 86s If ``True``, will make sure that the host of the pool requests is 86s consistent else will raise HostChangedError. When ``False``, you can 86s use the pool on an HTTP proxy and request foreign hosts. 86s 86s :param timeout: 86s If specified, overrides the default timeout for this one 86s request. It may be a float (in seconds) or an instance of 86s :class:`urllib3.util.Timeout`. 86s 86s :param pool_timeout: 86s If set and the pool is set to block=True, then this method will 86s block for ``pool_timeout`` seconds and raise EmptyPoolError if no 86s connection is available within the time period. 86s 86s :param bool preload_content: 86s If True, the response's body will be preloaded into memory. 86s 86s :param bool decode_content: 86s If True, will attempt to decode the body based on the 86s 'content-encoding' header. 86s 86s :param release_conn: 86s If False, then the urlopen call will not release the connection 86s back into the pool once a response is received (but will release if 86s you read the entire contents of the response such as when 86s `preload_content=True`). This is useful if you're not preloading 86s the response's content immediately. You will need to call 86s ``r.release_conn()`` on the response ``r`` to return the connection 86s back into the pool. If None, it takes the value of ``preload_content`` 86s which defaults to ``True``. 86s 86s :param bool chunked: 86s If True, urllib3 will send the body using chunked transfer 86s encoding. Otherwise, urllib3 will send the body using the standard 86s content-length form. Defaults to False. 86s 86s :param int body_pos: 86s Position to seek to in file-like body in the event of a retry or 86s redirect. Typically this won't need to be set because urllib3 will 86s auto-populate the value when needed. 86s """ 86s parsed_url = parse_url(url) 86s destination_scheme = parsed_url.scheme 86s 86s if headers is None: 86s headers = self.headers 86s 86s if not isinstance(retries, Retry): 86s retries = Retry.from_int(retries, redirect=redirect, default=self.retries) 86s 86s if release_conn is None: 86s release_conn = preload_content 86s 86s # Check host 86s if assert_same_host and not self.is_same_host(url): 86s raise HostChangedError(self, url, retries) 86s 86s # Ensure that the URL we're connecting to is properly encoded 86s if url.startswith("/"): 86s url = to_str(_encode_target(url)) 86s else: 86s url = to_str(parsed_url.url) 86s 86s conn = None 86s 86s # Track whether `conn` needs to be released before 86s # returning/raising/recursing. Update this variable if necessary, and 86s # leave `release_conn` constant throughout the function. That way, if 86s # the function recurses, the original value of `release_conn` will be 86s # passed down into the recursive call, and its value will be respected. 86s # 86s # See issue #651 [1] for details. 86s # 86s # [1] 86s release_this_conn = release_conn 86s 86s http_tunnel_required = connection_requires_http_tunnel( 86s self.proxy, self.proxy_config, destination_scheme 86s ) 86s 86s # Merge the proxy headers. Only done when not using HTTP CONNECT. We 86s # have to copy the headers dict so we can safely change it without those 86s # changes being reflected in anyone else's copy. 86s if not http_tunnel_required: 86s headers = headers.copy() # type: ignore[attr-defined] 86s headers.update(self.proxy_headers) # type: ignore[union-attr] 86s 86s # Must keep the exception bound to a separate variable or else Python 3 86s # complains about UnboundLocalError. 86s err = None 86s 86s # Keep track of whether we cleanly exited the except block. This 86s # ensures we do proper cleanup in finally. 86s clean_exit = False 86s 86s # Rewind body position, if needed. Record current position 86s # for future rewinds in the event of a redirect/retry. 86s body_pos = set_file_position(body, body_pos) 86s 86s try: 86s # Request a connection from the queue. 86s timeout_obj = self._get_timeout(timeout) 86s conn = self._get_conn(timeout=pool_timeout) 86s 86s conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] 86s 86s # Is this a closed/new connection that requires CONNECT tunnelling? 86s if self.proxy is not None and http_tunnel_required and conn.is_closed: 86s try: 86s self._prepare_proxy(conn) 86s except (BaseSSLError, OSError, SocketTimeout) as e: 86s self._raise_timeout( 86s err=e, url=self.proxy.url, timeout_value=conn.timeout 86s ) 86s raise 86s 86s # If we're going to release the connection in ``finally:``, then 86s # the response doesn't need to know about the connection. Otherwise 86s # it will also try to release it and we'll have a double-release 86s # mess. 86s response_conn = conn if not release_conn else None 86s 86s # Make the request on the HTTPConnection object 86s > response = self._make_request( 86s conn, 86s method, 86s url, 86s timeout=timeout_obj, 86s body=body, 86s headers=headers, 86s chunked=chunked, 86s retries=retries, 86s response_conn=response_conn, 86s preload_content=preload_content, 86s decode_content=decode_content, 86s **response_kw, 86s ) 86s 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:787: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:534: in _make_request 86s response = conn.getresponse() 86s /usr/lib/python3/dist-packages/urllib3/connection.py:516: in getresponse 86s httplib_response = super().getresponse() 86s /usr/lib/python3.13/http/client.py:1430: in getresponse 86s response.begin() 86s /usr/lib/python3.13/http/client.py:331: in begin 86s version, status, reason = self._read_status() 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s 86s def _read_status(self): 86s line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") 86s if len(line) > _MAXLINE: 86s raise LineTooLong("status line") 86s if self.debuglevel > 0: 86s print("reply:", repr(line)) 86s if not line: 86s # Presumably, the server closed the connection before 86s # sending a valid response. 86s > raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s E http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s /usr/lib/python3.13/http/client.py:300: RemoteDisconnected 86s 86s The above exception was the direct cause of the following exception: 86s Traceback (most recent call last): 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 787, in urlopen 86s response = self._make_request( 86s conn, 86s ...<10 lines>... 86s **response_kw, 86s ) 86s File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 534, in _make_request 86s response = conn.getresponse() 86s File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 516, in getresponse 86s httplib_response = super().getresponse() 86s File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse 86s response.begin() 86s ~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 331, in begin 86s version, status, reason = self._read_status() 86s ~~~~~~~~~~~~~~~~~^^ 86s File "/usr/lib/python3.13/http/client.py", line 300, in _read_status 86s raise RemoteDisconnected("Remote end closed connection without" 86s " response") 86s http.client.RemoteDisconnected: Remote end closed connection without response 86s 86s The above exception was the direct cause of the following exception: 86s 86s urllib3.exceptions.ProxyError: ('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s 86s The above exception was the direct cause of the following exception: 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s > resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:667: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/urllib3/connectionpool.py:841: in urlopen 86s retries = retries.increment( 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = Retry(total=0, connect=None, read=False, redirect=None, status=None) 86s method = 'GET' 86s url = 'http://localhost:8080/pdb/query/v4/nodes?query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22' 86s response = None 86s error = ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response')) 86s _pool = 86s _stacktrace = 86s 86s def increment( 86s self, 86s method: str | None = None, 86s url: str | None = None, 86s response: BaseHTTPResponse | None = None, 86s error: Exception | None = None, 86s _pool: ConnectionPool | None = None, 86s _stacktrace: TracebackType | None = None, 86s ) -> Self: 86s """Return a new Retry object with incremented retry counters. 86s 86s :param response: A response object, or None, if the server did not 86s return a response. 86s :type response: :class:`~urllib3.response.BaseHTTPResponse` 86s :param Exception error: An error encountered during the request, or 86s None if the response was received successfully. 86s 86s :return: A new ``Retry`` object. 86s """ 86s if self.total is False and error: 86s # Disabled, indicate to re-raise the error. 86s raise reraise(type(error), error, _stacktrace) 86s 86s total = self.total 86s if total is not None: 86s total -= 1 86s 86s connect = self.connect 86s read = self.read 86s redirect = self.redirect 86s status_count = self.status 86s other = self.other 86s cause = "unknown" 86s status = None 86s redirect_location = None 86s 86s if error and self._is_connection_error(error): 86s # Connect retry? 86s if connect is False: 86s raise reraise(type(error), error, _stacktrace) 86s elif connect is not None: 86s connect -= 1 86s 86s elif error and self._is_read_error(error): 86s # Read retry? 86s if read is False or method is None or not self._is_method_retryable(method): 86s raise reraise(type(error), error, _stacktrace) 86s elif read is not None: 86s read -= 1 86s 86s elif error: 86s # Other retry? 86s if other is not None: 86s other -= 1 86s 86s elif response and response.get_redirect_location(): 86s # Redirect retry? 86s if redirect is not None: 86s redirect -= 1 86s cause = "too many redirects" 86s response_redirect_location = response.get_redirect_location() 86s if response_redirect_location: 86s redirect_location = response_redirect_location 86s status = response.status 86s 86s else: 86s # Incrementing because of a server error like a 500 in 86s # status_forcelist and the given method is in the allowed_methods 86s cause = ResponseError.GENERIC_ERROR 86s if response and response.status: 86s if status_count is not None: 86s status_count -= 1 86s cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) 86s status = response.status 86s 86s history = self.history + ( 86s RequestHistory(method, url, error, status, redirect_location), 86s ) 86s 86s new_retry = self.new( 86s total=total, 86s connect=connect, 86s read=read, 86s redirect=redirect, 86s status=status_count, 86s other=other, 86s history=history, 86s ) 86s 86s if new_retry.is_exhausted(): 86s reason = error or ResponseError(cause) 86s > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] 86s E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/urllib3/util/retry.py:519: MaxRetryError 86s 86s During handling of the above exception, another exception occurred: 86s 86s self = 86s api = 86s 86s def test_nodes_single(self, api): 86s body = { 86s "cached_catalog_status": "not_used", 86s "catalog_environment": "production", 86s "catalog_timestamp": "2016-08-15T11:06:26.275Z", 86s "certname": "greenserver.vm", 86s "deactivated": None, 86s "expired": None, 86s "facts_environment": "production", 86s "facts_timestamp": "2016-08-15T11:06:26.140Z", 86s "latest_report_hash": "4a956674b016d95a7b77c99513ba26e4a744f8d1", 86s "latest_report_noop": False, 86s "latest_report_noop_pending": None, 86s "latest_report_status": "changed", 86s "report_environment": "production", 86s "report_timestamp": "2016-08-15T11:06:18.393Z", 86s } 86s url = "http://localhost:8080/pdb/query/v4/nodes" 86s 86s httpretty.enable() 86s httpretty.register_uri(httpretty.GET, url, body=json.dumps(body)) 86s 86s > nodes = list(api.nodes(query='["=","certname","greenserver.vm"')) 86s 86s tests/test_api_query.py:117: 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s /usr/lib/python3/dist-packages/pypuppetdb/api/query.py:51: in nodes 86s nodes = self._query("nodes", **kwargs) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:389: in _query 86s return self._make_request(url, request_method, payload) 86s /usr/lib/python3/dist-packages/pypuppetdb/api/base.py:410: in _make_request 86s r = self.session.get( 86s /usr/lib/python3/dist-packages/requests/sessions.py:602: in get 86s return self.request("GET", url, **kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:589: in request 86s resp = self.send(prep, **send_kwargs) 86s /usr/lib/python3/dist-packages/requests/sessions.py:703: in send 86s r = adapter.send(request, **kwargs) 86s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 86s 86s self = 86s request = , stream = False 86s timeout = Timeout(connect=10, read=10, total=None), verify = True 86s cert = (None, None) 86s proxies = OrderedDict({'https': 'http://egress.ps7.internal:3128/', 'http': 'http://egress.ps7.internal:3128/'}) 86s 86s def send( 86s self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None 86s ): 86s """Sends PreparedRequest object. Returns Response object. 86s 86s :param request: The :class:`PreparedRequest ` being sent. 86s :param stream: (optional) Whether to stream the request content. 86s :param timeout: (optional) How long to wait for the server to send 86s data before giving up, as a float, or a :ref:`(connect timeout, 86s read timeout) ` tuple. 86s :type timeout: float or tuple or urllib3 Timeout object 86s :param verify: (optional) Either a boolean, in which case it controls whether 86s we verify the server's TLS certificate, or a string, in which case it 86s must be a path to a CA bundle to use 86s :param cert: (optional) Any user-provided SSL certificate to be trusted. 86s :param proxies: (optional) The proxies dictionary to apply to the request. 86s :rtype: requests.Response 86s """ 86s 86s try: 86s conn = self.get_connection_with_tls_context( 86s request, verify, proxies=proxies, cert=cert 86s ) 86s except LocationValueError as e: 86s raise InvalidURL(e, request=request) 86s 86s self.cert_verify(conn, request.url, verify, cert) 86s url = self.request_url(request, proxies) 86s self.add_headers( 86s request, 86s stream=stream, 86s timeout=timeout, 86s verify=verify, 86s cert=cert, 86s proxies=proxies, 86s ) 86s 86s chunked = not (request.body is None or "Content-Length" in request.headers) 86s 86s if isinstance(timeout, tuple): 86s try: 86s connect, read = timeout 86s timeout = TimeoutSauce(connect=connect, read=read) 86s except ValueError: 86s raise ValueError( 86s f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " 86s f"or a single float to set both timeouts to the same value." 86s ) 86s elif isinstance(timeout, TimeoutSauce): 86s pass 86s else: 86s timeout = TimeoutSauce(connect=timeout, read=timeout) 86s 86s try: 86s resp = conn.urlopen( 86s method=request.method, 86s url=url, 86s body=request.body, 86s headers=request.headers, 86s redirect=False, 86s assert_same_host=False, 86s preload_content=False, 86s decode_content=False, 86s retries=self.max_retries, 86s timeout=timeout, 86s chunked=chunked, 86s ) 86s 86s except (ProtocolError, OSError) as err: 86s raise ConnectionError(err, request=request) 86s 86s except MaxRetryError as e: 86s if isinstance(e.reason, ConnectTimeoutError): 86s # TODO: Remove this in 3.0.0: see #2811 86s if not isinstance(e.reason, NewConnectionError): 86s raise ConnectTimeout(e, request=request) 86s 86s if isinstance(e.reason, ResponseError): 86s raise RetryError(e, request=request) 86s 86s if isinstance(e.reason, _ProxyError): 86s > raise ProxyError(e, request=request) 86s E requests.exceptions.ProxyError: HTTPConnectionPool(host='egress.ps7.internal', port=3128): Max retries exceeded with url: http://localhost:8080/pdb/query/v4/nodes?query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22 (Caused by ProxyError('Unable to connect to proxy', RemoteDisconnected('Remote end closed connection without response'))) 86s 86s /usr/lib/python3/dist-packages/requests/adapters.py:694: ProxyError 86s ------------------------------ Captured log call ------------------------------- 86s WARNING httpretty.core:core.py:638 real call to socket.connect() for ('egress.ps7.internal', 3128) 86s WARNING httpretty.core:core.py:725 httpretty.core.socket("egress.ps7.internal:3128").real_sendall(280 bytes) to ://localhost:8080http://localhost:8080/pdb/query/v4/nodes?query=%5B%22%3D%22%2C%22certname%22%2C%22greenserver.vm%22 via GET at 1749156437.650675 86s ERROR pypuppetdb.api.base:base.py:454 Could not reach PuppetDB on localhost:8080 over HTTP. 86s =========================== short test summary info ============================ 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_setting_headers_without_token 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_setting_headers_with_token 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_path - reque... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_url_path - r... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_password_authorization 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_token_authorization 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_query - requ... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_order - requ... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_limit - requ... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_include_total 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_offset - req... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_summarize_by 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_count_by - r... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_count_filter 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_payload_get 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_with_payload_post 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_response_empty - ... 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_response_x_records 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_query_with_post[string] 86s FAILED tests/test_api_base_query.py::TestBaseAPIQuery::test_query_with_post[QueryBuilder] 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v1 - requests.e... 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v1_list - reque... 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v1_version_constructor 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2 - requests.e... 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2_version_constructor 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2_version_string 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2_error - requ... 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2_escape_special_characters 86s FAILED tests/test_api_metrics.py::TestMetricsAPI::test_metric_v2_list - reque... 86s FAILED tests/test_api_other.py::TestCommandAPI::test_command - requests.excep... 86s FAILED tests/test_api_other.py::TestCommandAPI::test_cmd[string] - requests.e... 86s FAILED tests/test_api_other.py::TestCommandAPI::test_cmd[QueryBuilder] - requ... 86s FAILED tests/test_api_other.py::TestCommandAPI::test_cmd_with_token_authorization 86s FAILED tests/test_api_other.py::TestStatusAPI::test_status - requests.excepti... 86s FAILED tests/test_api_pql.py::TestPqlAPI::test_pql_casting - requests.excepti... 86s FAILED tests/test_api_pql.py::TestPqlAPI::test_pql_no_casting - requests.exce... 86s FAILED tests/test_api_query.py::TestQueryAPI::test_facts - requests.exception... 86s FAILED tests/test_api_query.py::TestQueryAPI::test_fact_names - requests.exce... 86s FAILED tests/test_api_query.py::TestQueryAPI::test_environments - requests.ex... 86s FAILED tests/test_api_query.py::TestQueryAPI::test_inventory - requests.excep... 86s FAILED tests/test_api_query.py::TestQueryAPI::test_nodes_single - requests.ex... 86s ======================== 41 failed, 122 passed in 4.31s ======================== 86s autopkgtest [20:47:19]: test unittests: -----------------------] 87s autopkgtest [20:47:20]: test unittests: - - - - - - - - - - results - - - - - - - - - - 87s unittests FAIL non-zero exit status 1 87s autopkgtest [20:47:20]: @@@@@@@@@@@@@@@@@@@@ summary 87s unittests FAIL non-zero exit status 1 379s nova [W] Skipping flock for amd64 379s Creating nova instance adt-questing-amd64-pypuppetdb-20250605-204553-juju-7f2275-prod-proposed-migration-environment-15-854d2b87-e43b-4ee2-93eb-4560b4088edd from image adt/ubuntu-questing-amd64-server-20250605.img (UUID 45f30bc4-80eb-46e1-b207-2b072dfeebbd)...