Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
970e8cec98 | ||
|
|
d6ebc4a2f1 | ||
|
|
9a40d12778 | ||
|
|
0c030f78d2 | ||
|
|
555b870eb1 | ||
|
|
d6dded3f00 | ||
|
|
bf24b7d8d1 | ||
|
|
2d5f54779a | ||
|
|
f1bb07d39b | ||
|
|
60047ade64 | ||
|
|
31ebb8102c | ||
|
|
cc23d1c644 | ||
|
|
0790ea4250 | ||
|
|
e45b428960 | ||
|
|
2a438c27b5 | ||
|
|
2daa7b52a7 | ||
|
|
8dd3b26bf5 | ||
|
|
13d892bdbe | ||
|
|
1604e20fc8 | ||
|
|
eeafb6ab3a | ||
|
|
a94e9b5308 | ||
|
|
a58d7f2ffb | ||
|
|
c0813a2d91 | ||
|
|
eea3bbf9ac | ||
|
|
f3f2611d83 | ||
|
|
a0e79bad05 | ||
|
|
0a7f662aa7 | ||
|
|
5f1c3c22ff | ||
|
|
541aa80ca3 | ||
|
|
d3b3399ece | ||
|
|
0ec2780c29 | ||
|
|
b8be93a721 | ||
|
|
3587a5f869 | ||
|
|
60389df6d6 | ||
|
|
382fc2c0c6 | ||
|
|
7a13c041db | ||
|
|
58cea7a728 | ||
|
|
9439fad038 | ||
|
|
8fa4302322 | ||
|
|
28855fd43a | ||
|
|
5fc10bf1f5 | ||
|
|
46c1a3d8c8 | ||
|
|
6106a63eb6 | ||
|
|
4f3f189d6b | ||
|
|
a5a0e4b587 | ||
|
|
3ff3ff21dd | ||
|
|
96b22fa18c | ||
|
|
b0e6c9bf85 | ||
|
|
72eccc8dd8 | ||
|
|
25939d8784 | ||
|
|
bfba9dc68c | ||
|
|
f23346a9b8 | ||
|
|
f3f9784419 | ||
|
|
b5bd0f14cc | ||
|
|
242d3113c0 | ||
|
|
1396eb6f7a | ||
|
|
e4c821a247 | ||
|
|
421b1f1757 | ||
|
|
1447bccc05 | ||
|
|
3b5978fc5d | ||
|
|
1ddf014f47 | ||
|
|
92f9e431c9 | ||
|
|
951dd15fa6 | ||
|
|
51d0d83eaf | ||
|
|
c7c2ebf1a7 | ||
|
|
a64f32ba45 | ||
|
|
a25fde6989 | ||
|
|
ba67dc8dcc | ||
|
|
769bc3ae5c | ||
|
|
d6ffd868ee | ||
|
|
3fd309a5c1 | ||
|
|
ec84f2c539 | ||
|
|
889910c77a | ||
|
|
b37878d340 | ||
|
|
f6707042d8 | ||
|
|
0b4d494192 | ||
|
|
15849947ec | ||
|
|
c6de5a14e5 | ||
|
|
e66a07b286 | ||
|
|
9e98a8749a | ||
|
|
c32b046243 | ||
|
|
89cde235be | ||
|
|
d7490c9d2d | ||
|
|
e9b1217cff | ||
|
|
ad761abcdd | ||
|
|
839a8edec3 | ||
|
|
a8e9c1b436 | ||
|
|
b55bb15c35 | ||
|
|
8187768622 | ||
|
|
f75b9504fc | ||
|
|
8199a2b389 | ||
|
|
42a3b5cce8 | ||
|
|
f5a7aefc2d | ||
|
|
ee93fac6b2 | ||
|
|
a775435c4b | ||
|
|
6c8d9b1d0f | ||
|
|
881281250f | ||
|
|
29fc8d1e93 | ||
|
|
8b560ecb24 | ||
|
|
4585a7fbda | ||
|
|
16dc70df34 | ||
|
|
bea231b033 | ||
|
|
2ee5b0b01c | ||
|
|
89f0eb91b3 | ||
|
|
005571d118 | ||
|
|
d63e94f552 | ||
|
|
e9fa2e2da3 | ||
|
|
678fca8423 | ||
|
|
d8152769ce | ||
|
|
9ff1a25ef5 | ||
|
|
4bd06cd325 | ||
|
|
09a241f59e | ||
|
|
6ad493650c | ||
|
|
9acca90bf6 | ||
|
|
c7933453cf | ||
|
|
ea49261a27 | ||
|
|
8112fcc7be | ||
|
|
2c193bda0c | ||
|
|
34a64bc65e | ||
|
|
9b6c62e235 | ||
|
|
cb7fcd7ebd | ||
|
|
aa4cc78627 | ||
|
|
dfe46c1b7f | ||
|
|
2ecdb68561 | ||
|
|
fecf3dc472 | ||
|
|
cdbc2e2715 | ||
|
|
22db55a889 | ||
|
|
6e5b15d542 | ||
|
|
53a0562331 |
3
.github/ISSUE_TEMPLATE/Custom.md
vendored
3
.github/ISSUE_TEMPLATE/Custom.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Request for Help
|
||||
about: Guidance on using Requests.
|
||||
labels:
|
||||
- "Question/Not a bug"
|
||||
- "actions/autoclose-qa"
|
||||
|
||||
---
|
||||
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels:
|
||||
- "Feature Request"
|
||||
- "actions/autoclose-feat"
|
||||
|
||||
---
|
||||
|
||||
|
||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
ignore:
|
||||
# Ignore all patch releases as we can manually
|
||||
# upgrade if we run into a bug and need a fix.
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-patch"]
|
||||
35
.github/workflows/close-issues.yml
vendored
Normal file
35
.github/workflows/close-issues.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: 'Autoclose Issues'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
close_qa:
|
||||
if: github.event.label.name == 'actions/autoclose-qa'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh issue close $ISSUE_URL \
|
||||
--comment "As described in the template, we won't be able to answer questions on this issue tracker. Please use [Stack Overflow](https://stackoverflow.com/)" \
|
||||
--reason completed
|
||||
gh issue lock $ISSUE_URL --reason off_topic
|
||||
close_feature_request:
|
||||
if: github.event.label.name == 'actions/autoclose-feat'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh issue close $ISSUE_URL \
|
||||
--comment "As described in the template, Requests is not accepting feature requests" \
|
||||
--reason "not planned"
|
||||
gh issue lock $ISSUE_URL --reason off_topic
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
|
||||
with:
|
||||
languages: "python"
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -70,4 +70,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Run pre-commit
|
||||
uses: pre-commit/action@v3.0.0
|
||||
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # v3.0.0
|
||||
|
||||
2
.github/workflows/lock-issues.yml
vendored
2
.github/workflows/lock-issues.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
if: github.repository_owner == 'psf'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v3
|
||||
- uses: dessant/lock-threads@d42e5f49803f3c4e14ffee0378e31481265dda22 # v5.0.0
|
||||
with:
|
||||
issue-lock-inactive-days: 90
|
||||
pr-lock-inactive-days: 90
|
||||
|
||||
39
.github/workflows/run-tests.yml
vendored
39
.github/workflows/run-tests.yml
vendored
@@ -12,23 +12,48 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev", "pypy-3.8", "pypy-3.9"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.10"]
|
||||
os: [ubuntu-22.04, macOS-latest, windows-latest]
|
||||
# Python 3.8 and 3.9 do not run on macOS-latest which
|
||||
# is now using arm64 hardware.
|
||||
# https://github.com/actions/setup-python/issues/696#issuecomment-1637587760
|
||||
exclude:
|
||||
- { python-version: "3.8", os: "macos-latest" }
|
||||
- { python-version: "3.9", os: "macos-latest" }
|
||||
include:
|
||||
# pypy-3.7 on Windows and Mac OS currently fails trying to compile
|
||||
# cryptography. Moving pypy-3.7 to only test linux.
|
||||
- python-version: pypy-3.7
|
||||
os: ubuntu-latest
|
||||
- { python-version: "3.8", os: "macos-13" }
|
||||
- { python-version: "3.9", os: "macos-13" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
make
|
||||
- name: Run tests
|
||||
run: |
|
||||
make ci
|
||||
|
||||
no_chardet:
|
||||
name: "No Character Detection"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
- name: 'Set up Python 3.8'
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
make
|
||||
python -m pip uninstall -y "charset_normalizer" "chardet"
|
||||
- name: Run tests
|
||||
run: |
|
||||
make ci
|
||||
|
||||
@@ -2,7 +2,7 @@ exclude: 'docs/|ext/'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
@@ -13,16 +13,16 @@ repos:
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
rev: 23.7.0
|
||||
hooks:
|
||||
- id: black
|
||||
exclude: tests/test_lowlevel.py
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.1
|
||||
rev: v3.10.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
||||
29
.readthedocs.yaml
Normal file
29
.readthedocs.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
builder: "dirhtml"
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
# Optional but recommended, declare the Python requirements required
|
||||
# to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- path: .
|
||||
- requirements: docs/requirements.txt
|
||||
@@ -8,7 +8,7 @@ Keepers of the Crystals
|
||||
|
||||
Previous Keepers of Crystals
|
||||
````````````````````````````
|
||||
- Kenneth Reitz <me@kennethreitz.org> `@ken-reitz <https://github.com/ken-reitz>`_, reluctant Keeper of the Master Crystal.
|
||||
- Kenneth Reitz <me@kennethreitz.org> `@kennethreitz <https://github.com/kennethreitz>`_, reluctant Keeper of the Master Crystal.
|
||||
- Cory Benfield <cory@lukasa.co.uk> `@lukasa <https://github.com/lukasa>`_
|
||||
- Ian Cordasco <graffatcolmingov@gmail.com> `@sigmavirus24 <https://github.com/sigmavirus24>`_.
|
||||
|
||||
@@ -192,3 +192,4 @@ Patches and Suggestions
|
||||
- Alessio Izzo (`@aless10 <https://github.com/aless10>`_)
|
||||
- Sylvain Marié (`@smarie <https://github.com/smarie>`_)
|
||||
- Hod Bin Noon (`@hodbn <https://github.com/hodbn>`_)
|
||||
- Mike Fiedler (`@miketheman <https://github.com/miketheman>`_)
|
||||
|
||||
55
HISTORY.md
55
HISTORY.md
@@ -6,6 +6,59 @@ dev
|
||||
|
||||
- \[Short description of non-trivial change.\]
|
||||
|
||||
2.32.1 (2024-05-20)
|
||||
-------------------
|
||||
|
||||
**Bugfixes**
|
||||
- Add missing test certs to the sdist distributed on PyPI.
|
||||
|
||||
|
||||
2.32.0 (2024-05-20)
|
||||
-------------------
|
||||
|
||||
**Security**
|
||||
- Fixed an issue where setting `verify=False` on the first request from a
|
||||
Session will cause subsequent requests to the _same origin_ to also ignore
|
||||
cert verification, regardless of the value of `verify`.
|
||||
(https://github.com/psf/requests/security/advisories/GHSA-9wx4-h78v-vm56)
|
||||
|
||||
**Improvements**
|
||||
- `verify=True` now reuses a global SSLContext which should improve
|
||||
request time variance between first and subsequent requests. It should
|
||||
also minimize certificate load time on Windows systems when using a Python
|
||||
version built with OpenSSL 3.x. (#6667)
|
||||
- Requests now supports optional use of character detection
|
||||
(`chardet` or `charset_normalizer`) when repackaged or vendored.
|
||||
This enables `pip` and other projects to minimize their vendoring
|
||||
surface area. The `Response.text()` and `apparent_encoding` APIs
|
||||
will default to `utf-8` if neither library is present. (#6702)
|
||||
|
||||
**Bugfixes**
|
||||
- Fixed bug in length detection where emoji length was incorrectly
|
||||
calculated in the request content-length. (#6589)
|
||||
- Fixed deserialization bug in JSONDecodeError. (#6629)
|
||||
- Fixed bug where an extra leading `/` (path separator) could lead
|
||||
urllib3 to unnecessarily reparse the request URI. (#6644)
|
||||
|
||||
**Deprecations**
|
||||
|
||||
- Requests has officially added support for CPython 3.12 (#6503)
|
||||
- Requests has officially added support for PyPy 3.9 and 3.10 (#6641)
|
||||
- Requests has officially dropped support for CPython 3.7 (#6642)
|
||||
- Requests has officially dropped support for PyPy 3.7 and 3.8 (#6641)
|
||||
|
||||
**Documentation**
|
||||
- Various typo fixes and doc improvements.
|
||||
|
||||
**Packaging**
|
||||
- Requests has started adopting some modern packaging practices.
|
||||
The source files for the projects (formerly `requests`) is now located
|
||||
in `src/requests` in the Requests sdist. (#6506)
|
||||
- Starting in Requests 2.33.0, Requests will migrate to a PEP 517 build system
|
||||
using `hatchling`. This should not impact the average user, but extremely old
|
||||
versions of packaging utilities may have issues with the new packaging format.
|
||||
|
||||
|
||||
2.31.0 (2023-05-22)
|
||||
-------------------
|
||||
|
||||
@@ -14,7 +67,7 @@ dev
|
||||
forwarding of `Proxy-Authorization` headers to destination servers when
|
||||
following HTTPS redirects.
|
||||
|
||||
When proxies are defined with user info (https://user:pass@proxy:8080), Requests
|
||||
When proxies are defined with user info (`https://user:pass@proxy:8080`), Requests
|
||||
will construct a `Proxy-Authorization` header that is attached to the request to
|
||||
authenticate with the proxy.
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
include README.md LICENSE NOTICE HISTORY.md pytest.ini requirements-dev.txt
|
||||
include README.md LICENSE NOTICE HISTORY.md requirements-dev.txt
|
||||
recursive-include tests *.py
|
||||
recursive-include tests/certs *
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,23 +1,23 @@
|
||||
.PHONY: docs
|
||||
init:
|
||||
pip install -r requirements-dev.txt
|
||||
python -m pip install -r requirements-dev.txt
|
||||
test:
|
||||
# This runs all of the tests on all supported Python versions.
|
||||
tox -p
|
||||
ci:
|
||||
pytest tests --junitxml=report.xml
|
||||
python -m pytest tests --junitxml=report.xml
|
||||
|
||||
test-readme:
|
||||
python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo "README.rst and HISTORY.rst ok") || echo "Invalid markup in README.rst or HISTORY.rst!"
|
||||
|
||||
flake8:
|
||||
flake8 --ignore=E501,F401,E128,E402,E731,F821 requests
|
||||
python -m flake8 src/requests
|
||||
|
||||
coverage:
|
||||
pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=requests tests
|
||||
python -m pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=src/requests tests
|
||||
|
||||
publish:
|
||||
pip install 'twine>=1.5.0'
|
||||
python -m pip install 'twine>=1.5.0'
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
rm -fr build dist .egg requests.egg-info
|
||||
|
||||
@@ -21,7 +21,7 @@ Requests allows you to send HTTP/1.1 requests extremely easily. There’s no nee
|
||||
|
||||
Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code.
|
||||
|
||||
[](https://pepy.tech/project/requests)
|
||||
[](https://pepy.tech/project/requests)
|
||||
[](https://pypi.org/project/requests)
|
||||
[](https://github.com/psf/requests/graphs/contributors)
|
||||
|
||||
@@ -33,7 +33,7 @@ Requests is available on PyPI:
|
||||
$ python -m pip install requests
|
||||
```
|
||||
|
||||
Requests officially supports Python 3.7+.
|
||||
Requests officially supports Python 3.8+.
|
||||
|
||||
## Supported Features & Best–Practices
|
||||
|
||||
|
||||
12
docs/_templates/sidebarintro.html
vendored
12
docs/_templates/sidebarintro.html
vendored
@@ -16,15 +16,15 @@
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/user/quickstart/">Quickstart</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/user/advanced/">Advanced Usage</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/api/">API Reference</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/community/updates/#release-history">Release History</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/dev/contributing/">Contributors Guide</a></li>
|
||||
<li><a href="{{ pathto('user/quickstart') }}">Quickstart</a></li>
|
||||
<li><a href="{{ pathto('user/advanced') }}">Advanced Usage</a></li>
|
||||
<li><a href="{{ pathto('api') }}">API Reference</a></li>
|
||||
<li><a href="{{ pathto('community/updates') + '#release-history' }}">Release History</a></li>
|
||||
<li><a href="{{ pathto('dev/contributing') }}">Contributors Guide</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/community/recommended/">Recommended Packages and Extensions</a></li>
|
||||
<li><a href="{{ pathto('community/recommended') }}">Recommended Packages and Extensions</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
|
||||
12
docs/_templates/sidebarlogo.html
vendored
12
docs/_templates/sidebarlogo.html
vendored
@@ -11,15 +11,15 @@
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/user/quickstart/">Quickstart</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/user/advanced/">Advanced Usage</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/api/">API Reference</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/community/updates/#release-history">Release History</a></li>
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/dev/contributing/">Contributors Guide</a></li>
|
||||
<li><a href="{{ pathto('user/quickstart') }}">Quickstart</a></li>
|
||||
<li><a href="{{ pathto('user/advanced') }}">Advanced Usage</a></li>
|
||||
<li><a href="{{ pathto('api') }}">API Reference</a></li>
|
||||
<li><a href="{{ pathto('community/updates') + '#release-history' }}">Release History</a></li>
|
||||
<li><a href="{{ pathto('dev/contributing') }}">Contributors Guide</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
<li><a href="https://requests.readthedocs.io/en/latest/community/recommended/">Recommended Packages and Extensions</a></li>
|
||||
<li><a href="{{ pathto('community/recommended') }}">Recommended Packages and Extensions</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Custom User-Agents?
|
||||
-------------------
|
||||
|
||||
Requests allows you to easily override User-Agent strings, along with
|
||||
any other HTTP Header. See `documentation about headers <https://requests.readthedocs.io/en/latest/user/quickstart/#custom-headers>`_.
|
||||
any other HTTP Header. See :ref:`documentation about headers <custom-headers>`.
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ Chris Adams gave an excellent summary on
|
||||
Python 3 Support?
|
||||
-----------------
|
||||
|
||||
Yes! Requests officially supports Python 3.7+ and PyPy.
|
||||
Yes! Requests officially supports Python 3.8+ and PyPy.
|
||||
|
||||
Python 2 Support?
|
||||
-----------------
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
Integrations
|
||||
============
|
||||
|
||||
Python for iOS
|
||||
--------------
|
||||
|
||||
Requests is built into the wonderful `Python for iOS <https://itunes.apple.com/us/app/python-2.7-for-ios/id485729872?mt=Python8>`_ runtime!
|
||||
|
||||
To give it a try, simply::
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
Articles & Talks
|
||||
================
|
||||
- `Python for the Web <https://www.gun.io/blog/python-for-the-web>`_ teaches how to use Python to interact with the web, using Requests.
|
||||
- `Daniel Greenfeld's Review of Requests <https://pydanny.blogspot.com/2011/05/python-http-requests-for-humans.html>`_
|
||||
- `My 'Python for Humans' talk <http://python-for-humans.heroku.com>`_ ( `audio <https://codeconf.s3.amazonaws.com/2011/pycodeconf/talks/PyCodeConf2011%20-%20Kenneth%20Reitz.m4a>`_ )
|
||||
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.com/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
|
||||
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.io/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
|
||||
- `Blog post about Requests via Yum <https://arunsag.wordpress.com/2011/08/17/new-package-python-requests-http-for-humans/>`_
|
||||
- `Russian blog post introducing Requests <https://habr.com/post/126262/>`_
|
||||
- `Sending JSON in Requests <http://www.coglib.com/~icordasc/blog/2014/11/sending-json-in-requests.html>`_
|
||||
|
||||
@@ -9,7 +9,7 @@ Requests: HTTP for Humans™
|
||||
Release v\ |version|. (:ref:`Installation <install>`)
|
||||
|
||||
|
||||
.. image:: https://pepy.tech/badge/requests/month
|
||||
.. image:: https://static.pepy.tech/badge/requests/month
|
||||
:target: https://pepy.tech/project/requests
|
||||
:alt: Requests Downloads Per Month Badge
|
||||
|
||||
@@ -72,7 +72,7 @@ Requests is ready for today's web.
|
||||
- Chunked Requests
|
||||
- ``.netrc`` Support
|
||||
|
||||
Requests officially supports Python 3.7+, and runs great on PyPy.
|
||||
Requests officially supports Python 3.8+, and runs great on PyPy.
|
||||
|
||||
|
||||
The User Guide
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Pinning to avoid unexpected breakages.
|
||||
# Used by RTD to generate docs.
|
||||
Sphinx==4.2.0
|
||||
Sphinx==7.2.6
|
||||
|
||||
@@ -291,7 +291,7 @@ versions of Requests.
|
||||
For the sake of security we recommend upgrading certifi frequently!
|
||||
|
||||
.. _HTTP persistent connection: https://en.wikipedia.org/wiki/HTTP_persistent_connection
|
||||
.. _connection pooling: https://urllib3.readthedocs.io/en/latest/reference/index.html#module-urllib3.connectionpool
|
||||
.. _connection pooling: https://urllib3.readthedocs.io/en/latest/reference/urllib3.connectionpool.html
|
||||
.. _certifi: https://certifiio.readthedocs.io/
|
||||
.. _Mozilla trust store: https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt
|
||||
|
||||
@@ -666,6 +666,8 @@ You override this default certificate bundle by setting the ``REQUESTS_CA_BUNDLE
|
||||
>>> import requests
|
||||
>>> requests.get('https://example.org')
|
||||
|
||||
.. _socks:
|
||||
|
||||
SOCKS
|
||||
^^^^^
|
||||
|
||||
@@ -946,7 +948,7 @@ Link Headers
|
||||
Many HTTP APIs feature Link headers. They make APIs more self describing and
|
||||
discoverable.
|
||||
|
||||
GitHub uses these for `pagination <https://developer.github.com/v3/#pagination>`_
|
||||
GitHub uses these for `pagination <https://docs.github.com/en/rest/guides/using-pagination-in-the-rest-api>`_
|
||||
in their API, for example::
|
||||
|
||||
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
|
||||
@@ -994,6 +996,10 @@ The mount call registers a specific instance of a Transport Adapter to a
|
||||
prefix. Once mounted, any HTTP request made using that session whose URL starts
|
||||
with the given prefix will use the given Transport Adapter.
|
||||
|
||||
.. note:: The adapter will be chosen based on a longest prefix match. Be mindful
|
||||
prefixes such as ``http://localhost`` will also match ``http://localhost.other.com``
|
||||
or ``http://localhost@other.com``. It's recommended to terminate full hostnames with a ``/``.
|
||||
|
||||
Many of the details of implementing a Transport Adapter are beyond the scope of
|
||||
this documentation, but take a look at the next example for a simple SSL use-
|
||||
case. For more than that, you might look at subclassing the
|
||||
@@ -1026,8 +1032,30 @@ library to use SSLv3::
|
||||
num_pools=connections, maxsize=maxsize,
|
||||
block=block, ssl_version=ssl.PROTOCOL_SSLv3)
|
||||
|
||||
Example: Automatic Retries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, Requests does not retry failed connections. However, it is possible
|
||||
to implement automatic retries with a powerful array of features, including
|
||||
backoff, within a Requests :class:`Session <requests.Session>` using the
|
||||
`urllib3.util.Retry`_ class::
|
||||
|
||||
from urllib3.util import Retry
|
||||
from requests import Session
|
||||
from requests.adapters import HTTPAdapter
|
||||
|
||||
s = Session()
|
||||
retries = Retry(
|
||||
total=3,
|
||||
backoff_factor=0.1,
|
||||
status_forcelist=[502, 503, 504],
|
||||
allowed_methods={'POST'},
|
||||
)
|
||||
s.mount('https://', HTTPAdapter(max_retries=retries))
|
||||
|
||||
.. _`described here`: https://kenreitz.org/essays/2012/06/14/the-future-of-python-http
|
||||
.. _`urllib3`: https://github.com/urllib3/urllib3
|
||||
.. _`urllib3.util.Retry`: https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry
|
||||
|
||||
.. _blocking-or-nonblocking:
|
||||
|
||||
@@ -1055,7 +1083,7 @@ Header Ordering
|
||||
|
||||
In unusual circumstances you may want to provide headers in an ordered manner. If you pass an ``OrderedDict`` to the ``headers`` keyword argument, that will provide the headers with an ordering. *However*, the ordering of the default headers used by Requests will be preferred, which means that if you override default headers in the ``headers`` keyword argument, they may appear out of order compared to other headers in that keyword argument.
|
||||
|
||||
If this is problematic, users should consider setting the default headers on a :class:`Session <requests.Session>` object, by setting :attr:`Session <requests.Session.headers>` to a custom ``OrderedDict``. That ordering will always be preferred.
|
||||
If this is problematic, users should consider setting the default headers on a :class:`Session <requests.Session>` object, by setting :attr:`Session.headers <requests.Session.headers>` to a custom ``OrderedDict``. That ordering will always be preferred.
|
||||
|
||||
.. _timeouts:
|
||||
|
||||
@@ -1071,7 +1099,7 @@ The **connect** timeout is the number of seconds Requests will wait for your
|
||||
client to establish a connection to a remote machine (corresponding to the
|
||||
`connect()`_) call on the socket. It's a good practice to set connect timeouts
|
||||
to slightly larger than a multiple of 3, which is the default `TCP packet
|
||||
retransmission window <https://www.hjp.at/doc/rfc/rfc2988.txt>`_.
|
||||
retransmission window <https://datatracker.ietf.org/doc/html/rfc2988>`_.
|
||||
|
||||
Once your client has connected to the server and sent the HTTP request, the
|
||||
**read** timeout is the number of seconds the client will wait for the server
|
||||
@@ -1096,4 +1124,18 @@ coffee.
|
||||
|
||||
r = requests.get('https://github.com', timeout=None)
|
||||
|
||||
.. note:: The connect timeout applies to each connection attempt to an IP address.
|
||||
If multiple addresses exist for a domain name, the underlying ``urllib3`` will
|
||||
try each address sequentially until one successfully connects.
|
||||
This may lead to an effective total connection timeout *multiple* times longer
|
||||
than the specified time, e.g. an unresponsive server having both IPv4 and IPv6
|
||||
addresses will have its perceived timeout *doubled*, so take that into account
|
||||
when setting the connection timeout.
|
||||
.. note:: Neither the connect nor read timeouts are `wall clock`_. This means
|
||||
that if you start a request, and look at the time, and then look at
|
||||
the time when the request finishes or times out, the real-world time
|
||||
may be greater than what you specified.
|
||||
|
||||
|
||||
.. _`wall clock`: https://wiki.php.net/rfc/max_execution_wall_time
|
||||
.. _`connect()`: https://linux.die.net/man/2/connect
|
||||
|
||||
@@ -201,6 +201,8 @@ may better fit your use cases.
|
||||
were returned, use ``Response.raw``.
|
||||
|
||||
|
||||
.. _custom-headers:
|
||||
|
||||
Custom Headers
|
||||
--------------
|
||||
|
||||
@@ -566,6 +568,3 @@ All exceptions that Requests explicitly raises inherit from
|
||||
-----------------------
|
||||
|
||||
Ready for more? Check out the :ref:`advanced <advanced>` section.
|
||||
|
||||
|
||||
If you're on the job market, consider taking `this programming quiz <https://triplebyte.com/a/b1i2FB8/requests-docs-1>`_. A substantial donation will be made to this project, if you find a job through this platform.
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
src_paths = ["requests", "test"]
|
||||
src_paths = ["src/requests", "test"]
|
||||
honor_noqa = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--doctest-modules"
|
||||
doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS"
|
||||
minversion = "6.2"
|
||||
testpaths = [
|
||||
"requests",
|
||||
"tests",
|
||||
]
|
||||
testpaths = ["tests"]
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
-e .[socks]
|
||||
pytest>=2.8.0,<=6.2.5
|
||||
pytest>=2.8.0,<9
|
||||
pytest-cov
|
||||
pytest-httpbin==2.0.0
|
||||
pytest-mock==2.0.0
|
||||
httpbin==0.7.0
|
||||
httpbin~=0.10.0
|
||||
trustme
|
||||
wheel
|
||||
cryptography<40.0.0; python_version <= '3.7' and platform_python_implementation == 'PyPy'
|
||||
|
||||
# Flask Stack
|
||||
Flask>1.0,<2.0
|
||||
markupsafe<2.1
|
||||
|
||||
@@ -7,11 +7,11 @@ requires-dist =
|
||||
certifi>=2017.4.17
|
||||
charset_normalizer>=2,<4
|
||||
idna>=2.5,<4
|
||||
urllib3>=1.21.1,<1.27
|
||||
urllib3>=1.21.1,<3
|
||||
|
||||
[flake8]
|
||||
ignore = E203, E501, W503
|
||||
per-file-ignores =
|
||||
requests/__init__.py:E402, F401
|
||||
requests/compat.py:E402, F401
|
||||
src/requests/__init__.py:E402, F401
|
||||
src/requests/compat.py:E402, F401
|
||||
tests/compat.py:F401
|
||||
|
||||
12
setup.py
12
setup.py
@@ -7,7 +7,7 @@ from setuptools import setup
|
||||
from setuptools.command.test import test as TestCommand
|
||||
|
||||
CURRENT_PYTHON = sys.version_info[:2]
|
||||
REQUIRED_PYTHON = (3, 7)
|
||||
REQUIRED_PYTHON = (3, 8)
|
||||
|
||||
if CURRENT_PYTHON < REQUIRED_PYTHON:
|
||||
sys.stderr.write(
|
||||
@@ -20,7 +20,7 @@ you're trying to install it on Python {}.{}. To resolve this,
|
||||
consider upgrading to a supported Python version.
|
||||
|
||||
If you can't upgrade your Python version, you'll need to
|
||||
pin to an older version of Requests (<2.28).
|
||||
pin to an older version of Requests (<2.32.0).
|
||||
""".format(
|
||||
*(REQUIRED_PYTHON + CURRENT_PYTHON)
|
||||
)
|
||||
@@ -75,7 +75,7 @@ test_requirements = [
|
||||
|
||||
about = {}
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
with open(os.path.join(here, "requests", "__version__.py"), "r", "utf-8") as f:
|
||||
with open(os.path.join(here, "src", "requests", "__version__.py"), "r", "utf-8") as f:
|
||||
exec(f.read(), about)
|
||||
|
||||
with open("README.md", "r", "utf-8") as f:
|
||||
@@ -92,9 +92,9 @@ setup(
|
||||
url=about["__url__"],
|
||||
packages=["requests"],
|
||||
package_data={"": ["LICENSE", "NOTICE"]},
|
||||
package_dir={"requests": "requests"},
|
||||
package_dir={"": "src"},
|
||||
include_package_data=True,
|
||||
python_requires=">=3.7",
|
||||
python_requires=">=3.8",
|
||||
install_requires=requires,
|
||||
license=about["__license__"],
|
||||
zip_safe=False,
|
||||
@@ -107,11 +107,11 @@ setup(
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
|
||||
@@ -83,7 +83,11 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver
|
||||
# charset_normalizer >= 2.0.0 < 4.0.0
|
||||
assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
|
||||
else:
|
||||
raise Exception("You need either charset_normalizer or chardet installed")
|
||||
warnings.warn(
|
||||
"Unable to find acceptable character detection dependency "
|
||||
"(chardet or charset_normalizer).",
|
||||
RequestsDependencyWarning,
|
||||
)
|
||||
|
||||
|
||||
def _check_cryptography(cryptography_version):
|
||||
@@ -5,10 +5,10 @@
|
||||
__title__ = "requests"
|
||||
__description__ = "Python HTTP for Humans."
|
||||
__url__ = "https://requests.readthedocs.io"
|
||||
__version__ = "2.31.0"
|
||||
__build__ = 0x023100
|
||||
__version__ = "2.32.1"
|
||||
__build__ = 0x023201
|
||||
__author__ = "Kenneth Reitz"
|
||||
__author_email__ = "me@kennethreitz.org"
|
||||
__license__ = "Apache 2.0"
|
||||
__license__ = "Apache-2.0"
|
||||
__copyright__ = "Copyright Kenneth Reitz"
|
||||
__cake__ = "\u2728 \U0001f370 \u2728"
|
||||
@@ -8,6 +8,7 @@ and maintain connections.
|
||||
|
||||
import os.path
|
||||
import socket # noqa: F401
|
||||
import typing
|
||||
|
||||
from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
|
||||
from urllib3.exceptions import HTTPError as _HTTPError
|
||||
@@ -25,6 +26,7 @@ from urllib3.poolmanager import PoolManager, proxy_from_url
|
||||
from urllib3.util import Timeout as TimeoutSauce
|
||||
from urllib3.util import parse_url
|
||||
from urllib3.util.retry import Retry
|
||||
from urllib3.util.ssl_ import create_urllib3_context
|
||||
|
||||
from .auth import _basic_auth_str
|
||||
from .compat import basestring, urlparse
|
||||
@@ -61,11 +63,57 @@ except ImportError:
|
||||
raise InvalidSchema("Missing dependencies for SOCKS support.")
|
||||
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .models import PreparedRequest
|
||||
|
||||
|
||||
DEFAULT_POOLBLOCK = False
|
||||
DEFAULT_POOLSIZE = 10
|
||||
DEFAULT_RETRIES = 0
|
||||
DEFAULT_POOL_TIMEOUT = None
|
||||
|
||||
_preloaded_ssl_context = create_urllib3_context()
|
||||
_preloaded_ssl_context.load_verify_locations(
|
||||
extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
|
||||
)
|
||||
|
||||
|
||||
def _urllib3_request_context(
|
||||
request: "PreparedRequest",
|
||||
verify: "bool | str | None",
|
||||
client_cert: "typing.Tuple[str, str] | str | None",
|
||||
) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
|
||||
host_params = {}
|
||||
pool_kwargs = {}
|
||||
parsed_request_url = urlparse(request.url)
|
||||
scheme = parsed_request_url.scheme.lower()
|
||||
port = parsed_request_url.port
|
||||
cert_reqs = "CERT_REQUIRED"
|
||||
if verify is False:
|
||||
cert_reqs = "CERT_NONE"
|
||||
elif verify is True:
|
||||
pool_kwargs["ssl_context"] = _preloaded_ssl_context
|
||||
elif isinstance(verify, str):
|
||||
if not os.path.isdir(verify):
|
||||
pool_kwargs["ca_certs"] = verify
|
||||
else:
|
||||
pool_kwargs["ca_cert_dir"] = verify
|
||||
pool_kwargs["cert_reqs"] = cert_reqs
|
||||
if client_cert is not None:
|
||||
if isinstance(client_cert, tuple) and len(client_cert) == 2:
|
||||
pool_kwargs["cert_file"] = client_cert[0]
|
||||
pool_kwargs["key_file"] = client_cert[1]
|
||||
else:
|
||||
# According to our docs, we allow users to specify just the client
|
||||
# cert path
|
||||
pool_kwargs["cert_file"] = client_cert
|
||||
host_params = {
|
||||
"scheme": scheme,
|
||||
"host": parsed_request_url.hostname,
|
||||
"port": port,
|
||||
}
|
||||
return host_params, pool_kwargs
|
||||
|
||||
|
||||
class BaseAdapter:
|
||||
"""The Base Transport Adapter"""
|
||||
@@ -247,28 +295,26 @@ class HTTPAdapter(BaseAdapter):
|
||||
:param cert: The SSL certificate to verify.
|
||||
"""
|
||||
if url.lower().startswith("https") and verify:
|
||||
|
||||
cert_loc = None
|
||||
|
||||
# Allow self-specified cert location.
|
||||
if verify is not True:
|
||||
cert_loc = verify
|
||||
|
||||
if not cert_loc:
|
||||
cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
|
||||
|
||||
if not cert_loc or not os.path.exists(cert_loc):
|
||||
raise OSError(
|
||||
f"Could not find a suitable TLS CA certificate bundle, "
|
||||
f"invalid path: {cert_loc}"
|
||||
)
|
||||
|
||||
conn.cert_reqs = "CERT_REQUIRED"
|
||||
|
||||
if not os.path.isdir(cert_loc):
|
||||
conn.ca_certs = cert_loc
|
||||
else:
|
||||
conn.ca_cert_dir = cert_loc
|
||||
# Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
|
||||
# Otherwise, if verify is a boolean, we don't load anything since
|
||||
# the connection will be using a context with the default certificates already loaded,
|
||||
# and this avoids a call to the slow load_verify_locations()
|
||||
if verify is not True:
|
||||
# `verify` must be a str with a path then
|
||||
cert_loc = verify
|
||||
|
||||
if not os.path.exists(cert_loc):
|
||||
raise OSError(
|
||||
f"Could not find a suitable TLS CA certificate bundle, "
|
||||
f"invalid path: {cert_loc}"
|
||||
)
|
||||
|
||||
if not os.path.isdir(cert_loc):
|
||||
conn.ca_certs = cert_loc
|
||||
else:
|
||||
conn.ca_cert_dir = cert_loc
|
||||
else:
|
||||
conn.cert_reqs = "CERT_NONE"
|
||||
conn.ca_certs = None
|
||||
@@ -328,6 +374,35 @@ class HTTPAdapter(BaseAdapter):
|
||||
|
||||
return response
|
||||
|
||||
def _get_connection(self, request, verify, proxies=None, cert=None):
|
||||
# Replace the existing get_connection without breaking things and
|
||||
# ensure that TLS settings are considered when we interact with
|
||||
# urllib3 HTTP Pools
|
||||
proxy = select_proxy(request.url, proxies)
|
||||
try:
|
||||
host_params, pool_kwargs = _urllib3_request_context(request, verify, cert)
|
||||
except ValueError as e:
|
||||
raise InvalidURL(e, request=request)
|
||||
if proxy:
|
||||
proxy = prepend_scheme_if_needed(proxy, "http")
|
||||
proxy_url = parse_url(proxy)
|
||||
if not proxy_url.host:
|
||||
raise InvalidProxyURL(
|
||||
"Please check proxy URL. It is malformed "
|
||||
"and could be missing the host."
|
||||
)
|
||||
proxy_manager = self.proxy_manager_for(proxy)
|
||||
conn = proxy_manager.connection_from_host(
|
||||
**host_params, pool_kwargs=pool_kwargs
|
||||
)
|
||||
else:
|
||||
# Only scheme should be lower case
|
||||
conn = self.poolmanager.connection_from_host(
|
||||
**host_params, pool_kwargs=pool_kwargs
|
||||
)
|
||||
|
||||
return conn
|
||||
|
||||
def get_connection(self, url, proxies=None):
|
||||
"""Returns a urllib3 connection for the given URL. This should not be
|
||||
called from user code, and is only exposed for use when subclassing the
|
||||
@@ -391,6 +466,9 @@ class HTTPAdapter(BaseAdapter):
|
||||
using_socks_proxy = proxy_scheme.startswith("socks")
|
||||
|
||||
url = request.path_url
|
||||
if url.startswith("//"): # Don't confuse urllib3
|
||||
url = f"/{url.lstrip('/')}"
|
||||
|
||||
if is_proxied_http_request and not using_socks_proxy:
|
||||
url = urldefragauth(request.url)
|
||||
|
||||
@@ -451,7 +529,7 @@ class HTTPAdapter(BaseAdapter):
|
||||
"""
|
||||
|
||||
try:
|
||||
conn = self.get_connection(request.url, proxies)
|
||||
conn = self._get_connection(request, verify, proxies=proxies, cert=cert)
|
||||
except LocationValueError as e:
|
||||
raise InvalidURL(e, request=request)
|
||||
|
||||
@@ -25,7 +25,7 @@ def request(method, url, **kwargs):
|
||||
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
|
||||
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
|
||||
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
|
||||
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
|
||||
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
|
||||
to add for the file.
|
||||
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
|
||||
@@ -258,7 +258,6 @@ class HTTPDigestAuth(AuthBase):
|
||||
s_auth = r.headers.get("www-authenticate", "")
|
||||
|
||||
if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
|
||||
|
||||
self._thread_local.num_401_calls += 1
|
||||
pat = re.compile(r"digest ", flags=re.IGNORECASE)
|
||||
self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))
|
||||
@@ -7,13 +7,28 @@ between Python 2 and Python 3. It remains for backwards
|
||||
compatibility until the next major version.
|
||||
"""
|
||||
|
||||
try:
|
||||
import chardet
|
||||
except ImportError:
|
||||
import charset_normalizer as chardet
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
# -------------------
|
||||
# Character Detection
|
||||
# -------------------
|
||||
|
||||
|
||||
def _resolve_char_detection():
|
||||
"""Find supported character detection libraries."""
|
||||
chardet = None
|
||||
for lib in ("chardet", "charset_normalizer"):
|
||||
if chardet is None:
|
||||
try:
|
||||
chardet = importlib.import_module(lib)
|
||||
except ImportError:
|
||||
pass
|
||||
return chardet
|
||||
|
||||
|
||||
chardet = _resolve_char_detection()
|
||||
|
||||
# -------
|
||||
# Pythons
|
||||
# -------
|
||||
@@ -2,7 +2,7 @@
|
||||
requests.cookies
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Compatibility code to be able to use `cookielib.CookieJar` with requests.
|
||||
Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
|
||||
|
||||
requests.utils imports from here, so be careful with imports.
|
||||
"""
|
||||
@@ -23,7 +23,7 @@ except ImportError:
|
||||
class MockRequest:
|
||||
"""Wraps a `requests.Request` to mimic a `urllib2.Request`.
|
||||
|
||||
The code in `cookielib.CookieJar` expects this interface in order to correctly
|
||||
The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
|
||||
manage cookie policies, i.e., determine whether a cookie can be set, given the
|
||||
domains of the request and the cookie.
|
||||
|
||||
@@ -76,7 +76,7 @@ class MockRequest:
|
||||
return self._r.headers.get(name, self._new_headers.get(name, default))
|
||||
|
||||
def add_header(self, key, val):
|
||||
"""cookielib has no legitimate use for this method; add it back if you find one."""
|
||||
"""cookiejar has no legitimate use for this method; add it back if you find one."""
|
||||
raise NotImplementedError(
|
||||
"Cookie headers should be added with add_unredirected_header()"
|
||||
)
|
||||
@@ -104,11 +104,11 @@ class MockResponse:
|
||||
"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
|
||||
|
||||
...what? Basically, expose the parsed HTTP headers from the server response
|
||||
the way `cookielib` expects to see them.
|
||||
the way `http.cookiejar` expects to see them.
|
||||
"""
|
||||
|
||||
def __init__(self, headers):
|
||||
"""Make a MockResponse for `cookielib` to read.
|
||||
"""Make a MockResponse for `cookiejar` to read.
|
||||
|
||||
:param headers: a httplib.HTTPMessage or analogous carrying the headers
|
||||
"""
|
||||
@@ -124,7 +124,7 @@ class MockResponse:
|
||||
def extract_cookies_to_jar(jar, request, response):
|
||||
"""Extract the cookies from the response into a CookieJar.
|
||||
|
||||
:param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar)
|
||||
:param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
|
||||
:param request: our own requests.Request object
|
||||
:param response: urllib3.HTTPResponse object
|
||||
"""
|
||||
@@ -174,7 +174,7 @@ class CookieConflictError(RuntimeError):
|
||||
|
||||
|
||||
class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
|
||||
"""Compatibility class; is a cookielib.CookieJar, but exposes a dict
|
||||
"""Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
|
||||
interface.
|
||||
|
||||
This is the CookieJar we create by default for requests and sessions that
|
||||
@@ -341,7 +341,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
|
||||
self.set(name, value)
|
||||
|
||||
def __delitem__(self, name):
|
||||
"""Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s
|
||||
"""Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
|
||||
``remove_cookie_by_name()``.
|
||||
"""
|
||||
remove_cookie_by_name(self, name)
|
||||
@@ -41,6 +41,16 @@ class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
|
||||
CompatJSONDecodeError.__init__(self, *args)
|
||||
InvalidJSONError.__init__(self, *self.args, **kwargs)
|
||||
|
||||
def __reduce__(self):
|
||||
"""
|
||||
The __reduce__ method called when pickling the object must
|
||||
be the one from the JSONDecodeError (be it json/simplejson)
|
||||
as it expects all the arguments for instantiation, not just
|
||||
one like the IOError, and the MRO would by default call the
|
||||
__reduce__ method from the IOError due to the inheritance order.
|
||||
"""
|
||||
return CompatJSONDecodeError.__reduce__(self)
|
||||
|
||||
|
||||
class HTTPError(RequestException):
|
||||
"""An HTTP error occurred."""
|
||||
@@ -170,7 +170,7 @@ class RequestEncodingMixin:
|
||||
)
|
||||
)
|
||||
|
||||
for (k, v) in files:
|
||||
for k, v in files:
|
||||
# support for explicit filename
|
||||
ft = None
|
||||
fh = None
|
||||
@@ -268,7 +268,6 @@ class Request(RequestHooksMixin):
|
||||
hooks=None,
|
||||
json=None,
|
||||
):
|
||||
|
||||
# Default empty dicts for dict params.
|
||||
data = [] if data is None else data
|
||||
files = [] if files is None else files
|
||||
@@ -277,7 +276,7 @@ class Request(RequestHooksMixin):
|
||||
hooks = {} if hooks is None else hooks
|
||||
|
||||
self.hooks = default_hooks()
|
||||
for (k, v) in list(hooks.items()):
|
||||
for k, v in list(hooks.items()):
|
||||
self.register_hook(event=k, hook=v)
|
||||
|
||||
self.method = method
|
||||
@@ -790,7 +789,12 @@ class Response:
|
||||
@property
|
||||
def apparent_encoding(self):
|
||||
"""The apparent encoding, provided by the charset_normalizer or chardet libraries."""
|
||||
return chardet.detect(self.content)["encoding"]
|
||||
if chardet is not None:
|
||||
return chardet.detect(self.content)["encoding"]
|
||||
else:
|
||||
# If no character detection library is available, we'll fall back
|
||||
# to a standard Python utf-8 str.
|
||||
return "utf-8"
|
||||
|
||||
def iter_content(self, chunk_size=1, decode_unicode=False):
|
||||
"""Iterates over the response data. When stream=True is set on the
|
||||
@@ -865,7 +869,6 @@ class Response:
|
||||
for chunk in self.iter_content(
|
||||
chunk_size=chunk_size, decode_unicode=decode_unicode
|
||||
):
|
||||
|
||||
if pending is not None:
|
||||
chunk = pending + chunk
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
import chardet
|
||||
except ImportError:
|
||||
import warnings
|
||||
|
||||
import charset_normalizer as chardet
|
||||
|
||||
warnings.filterwarnings("ignore", "Trying to detect", module="charset_normalizer")
|
||||
from .compat import chardet
|
||||
|
||||
# This code exists for backwards compatibility reasons.
|
||||
# I don't like it either. Just look the other way. :)
|
||||
@@ -20,9 +13,11 @@ for package in ("urllib3", "idna"):
|
||||
if mod == package or mod.startswith(f"{package}."):
|
||||
sys.modules[f"requests.packages.{mod}"] = sys.modules[mod]
|
||||
|
||||
target = chardet.__name__
|
||||
for mod in list(sys.modules):
|
||||
if mod == target or mod.startswith(f"{target}."):
|
||||
target = target.replace(target, "chardet")
|
||||
sys.modules[f"requests.packages.{target}"] = sys.modules[mod]
|
||||
# Kinda cool, though, right?
|
||||
if chardet is not None:
|
||||
target = chardet.__name__
|
||||
for mod in list(sys.modules):
|
||||
if mod == target or mod.startswith(f"{target}."):
|
||||
imported_mod = sys.modules[mod]
|
||||
sys.modules[f"requests.packages.{mod}"] = imported_mod
|
||||
mod = mod.replace(target, "chardet")
|
||||
sys.modules[f"requests.packages.{mod}"] = imported_mod
|
||||
@@ -262,7 +262,6 @@ class SessionRedirectMixin:
|
||||
if yield_requests:
|
||||
yield req
|
||||
else:
|
||||
|
||||
resp = self.send(
|
||||
req,
|
||||
stream=stream,
|
||||
@@ -326,7 +325,7 @@ class SessionRedirectMixin:
|
||||
|
||||
# urllib3 handles proxy authorization for us in the standard adapter.
|
||||
# Avoid appending this to TLS tunneled requests where it may be leaked.
|
||||
if not scheme.startswith('https') and username and password:
|
||||
if not scheme.startswith("https") and username and password:
|
||||
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
|
||||
|
||||
return new_proxies
|
||||
@@ -389,7 +388,6 @@ class Session(SessionRedirectMixin):
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
#: A case-insensitive dictionary of headers to be sent on each
|
||||
#: :class:`Request <Request>` sent from this
|
||||
#: :class:`Session <Session>`.
|
||||
@@ -545,6 +543,8 @@ class Session(SessionRedirectMixin):
|
||||
:type allow_redirects: bool
|
||||
:param proxies: (optional) Dictionary mapping protocol or protocol and
|
||||
hostname to the URL of the proxy.
|
||||
:param hooks: (optional) Dictionary mapping hook name to one event or
|
||||
list of events, event must be callable.
|
||||
:param stream: (optional) whether to immediately download the response
|
||||
content. Defaults to ``False``.
|
||||
:param verify: (optional) Either a boolean, in which case it controls whether we verify
|
||||
@@ -711,7 +711,6 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
# Persist cookies
|
||||
if r.history:
|
||||
|
||||
# If the hooks create history then we want those cookies too
|
||||
for resp in r.history:
|
||||
extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
|
||||
@@ -759,7 +758,7 @@ class Session(SessionRedirectMixin):
|
||||
# Set environment's proxies.
|
||||
no_proxy = proxies.get("no_proxy") if proxies is not None else None
|
||||
env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
|
||||
for (k, v) in env_proxies.items():
|
||||
for k, v in env_proxies.items():
|
||||
proxies.setdefault(k, v)
|
||||
|
||||
# Look for requests environment configuration
|
||||
@@ -785,8 +784,7 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
:rtype: requests.adapters.BaseAdapter
|
||||
"""
|
||||
for (prefix, adapter) in self.adapters.items():
|
||||
|
||||
for prefix, adapter in self.adapters.items():
|
||||
if url.lower().startswith(prefix.lower()):
|
||||
return adapter
|
||||
|
||||
@@ -24,7 +24,7 @@ _codes = {
|
||||
# Informational.
|
||||
100: ("continue",),
|
||||
101: ("switching_protocols",),
|
||||
102: ("processing",),
|
||||
102: ("processing", "early-hints"),
|
||||
103: ("checkpoint",),
|
||||
122: ("uri_too_long", "request_uri_too_long"),
|
||||
200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"),
|
||||
@@ -65,8 +65,8 @@ _codes = {
|
||||
410: ("gone",),
|
||||
411: ("length_required",),
|
||||
412: ("precondition_failed", "precondition"),
|
||||
413: ("request_entity_too_large",),
|
||||
414: ("request_uri_too_large",),
|
||||
413: ("request_entity_too_large", "content_too_large"),
|
||||
414: ("request_uri_too_large", "uri_too_long"),
|
||||
415: ("unsupported_media_type", "unsupported_media", "media_type"),
|
||||
416: (
|
||||
"requested_range_not_satisfiable",
|
||||
@@ -76,10 +76,10 @@ _codes = {
|
||||
417: ("expectation_failed",),
|
||||
418: ("im_a_teapot", "teapot", "i_am_a_teapot"),
|
||||
421: ("misdirected_request",),
|
||||
422: ("unprocessable_entity", "unprocessable"),
|
||||
422: ("unprocessable_entity", "unprocessable", "unprocessable_content"),
|
||||
423: ("locked",),
|
||||
424: ("failed_dependency", "dependency"),
|
||||
425: ("unordered_collection", "unordered"),
|
||||
425: ("unordered_collection", "unordered", "too_early"),
|
||||
426: ("upgrade_required", "upgrade"),
|
||||
428: ("precondition_required", "precondition"),
|
||||
429: ("too_many_requests", "too_many"),
|
||||
@@ -97,6 +97,8 @@ if sys.platform == "win32":
|
||||
# '<local>' string by the localhost entry and the corresponding
|
||||
# canonical entry.
|
||||
proxyOverride = proxyOverride.split(";")
|
||||
# filter out empty strings to avoid re.match return true in the following code.
|
||||
proxyOverride = filter(None, proxyOverride)
|
||||
# now check if we match one of the registry values.
|
||||
for test in proxyOverride:
|
||||
if test == "<local>":
|
||||
@@ -134,6 +136,9 @@ def super_len(o):
|
||||
total_length = None
|
||||
current_position = 0
|
||||
|
||||
if isinstance(o, str):
|
||||
o = o.encode("utf-8")
|
||||
|
||||
if hasattr(o, "__len__"):
|
||||
total_length = len(o)
|
||||
|
||||
@@ -466,11 +471,7 @@ def dict_from_cookiejar(cj):
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
cookie_dict = {}
|
||||
|
||||
for cookie in cj:
|
||||
cookie_dict[cookie.name] = cookie.value
|
||||
|
||||
cookie_dict = {cookie.name: cookie.value for cookie in cj}
|
||||
return cookie_dict
|
||||
|
||||
|
||||
@@ -767,6 +768,7 @@ def should_bypass_proxies(url, no_proxy):
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
# Prioritize lowercase environment variables over uppercase
|
||||
# to keep a consistent behaviour with other http projects (curl, wget).
|
||||
def get_proxy(key):
|
||||
@@ -862,7 +864,7 @@ def select_proxy(url, proxies):
|
||||
def resolve_proxies(request, proxies, trust_env=True):
|
||||
"""This method takes proxy information from a request and configuration
|
||||
input to resolve a mapping of target proxies. This will consider settings
|
||||
such a NO_PROXY to strip proxy configurations.
|
||||
such as NO_PROXY to strip proxy configurations.
|
||||
|
||||
:param request: Request or PreparedRequest
|
||||
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
|
||||
@@ -1054,7 +1056,7 @@ def _validate_header_part(header, header_part, header_validator_index):
|
||||
if not validator.match(header_part):
|
||||
header_kind = "name" if header_validator_index == 0 else "value"
|
||||
raise InvalidHeader(
|
||||
f"Invalid leading whitespace, reserved character(s), or return"
|
||||
f"Invalid leading whitespace, reserved character(s), or return "
|
||||
f"character(s) in header {header_kind}: {header_part!r}"
|
||||
)
|
||||
|
||||
10
tests/certs/README.md
Normal file
10
tests/certs/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Testing Certificates
|
||||
|
||||
This is a collection of certificates useful for testing aspects of Requests'
|
||||
behaviour.
|
||||
|
||||
The certificates include:
|
||||
|
||||
* [expired](./expired) server certificate with a valid certificate authority
|
||||
* [mtls](./mtls) provides a valid client certificate with a 2 year validity
|
||||
* [valid](./valid) has a valid server certificate
|
||||
13
tests/certs/expired/Makefile
Normal file
13
tests/certs/expired/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
.PHONY: all clean ca server
|
||||
|
||||
ca:
|
||||
make -C $@ all
|
||||
|
||||
server:
|
||||
make -C $@ all
|
||||
|
||||
all: ca server
|
||||
|
||||
clean:
|
||||
make -C ca clean
|
||||
make -C server clean
|
||||
11
tests/certs/expired/README.md
Normal file
11
tests/certs/expired/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Expired Certificates and Configuration for Testing
|
||||
|
||||
This has a valid certificate authority in [ca](./ca) and an invalid server
|
||||
certificate in [server](./server).
|
||||
|
||||
This can all be regenerated with:
|
||||
|
||||
```
|
||||
make clean
|
||||
make all
|
||||
```
|
||||
13
tests/certs/expired/ca/Makefile
Normal file
13
tests/certs/expired/ca/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
.PHONY: all clean
|
||||
|
||||
root_files = ca-private.key ca.crt
|
||||
|
||||
ca-private.key:
|
||||
openssl genrsa -out ca-private.key 2048
|
||||
|
||||
all: ca-private.key
|
||||
openssl req -x509 -sha256 -days 7300 -key ca-private.key -out ca.crt -config ca.cnf
|
||||
ln -s ca.crt cacert.pem
|
||||
|
||||
clean:
|
||||
rm -f cacert.pem ca.crt ca-private.key *.csr
|
||||
28
tests/certs/expired/ca/ca-private.key
Normal file
28
tests/certs/expired/ca/ca-private.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHlIhe7GLCeSk8
|
||||
RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He
|
||||
mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK
|
||||
na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6
|
||||
fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm
|
||||
zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy
|
||||
e6lzFyWnAgMBAAECggEAFwzHhzcD3PQDWCus85PwZoxTeQ817BmUBGpBBOKM0gLG
|
||||
GCsT7XsmGP2NjICBy9OK+QTKawmb/wR5XK0OMUWDHXqtWn+NFIyojyo8+HEeCf8n
|
||||
4ZleTFHLnJ+d2N1etbc2qc9mY3tjpaurq8/0Tol9YH06ock1TY2+lO+a5HvMURnY
|
||||
hcWs70CamL+5B/6n67DhjzMtIW3dIXuEEceM1BW/jW8SKq0JHpQ3t+OJwID7zFaJ
|
||||
bLyOwAVheMzVGvN3yphf8tll3tMA65bNjdOzgOfZSjAy7EGjW3DyAolDw9jKLRyu
|
||||
E0gw/exNGe618oMIeUDv0KParlL4RjdiUP8l0xYOwQKBgQD3eYj9rWeqZquI9vKP
|
||||
gaSv6urb2UJLngShZUpEZRNJgBO+Ewiof0w8tpQdsnuMvWudxMLbzgiUNA+NyC/K
|
||||
CpzIXFkWnWx+A/pxs8ZO8moOfajVRayJgeOLsQZb7c4fXGsVGApbN4+cPNhTNG6d
|
||||
ucErv6tae/SzAzcLc5Vkw/ELxwKBgQDOdJ5Wl5JeKAvU/3kF6+MYWCrXxZqMjoHS
|
||||
y1BtyMX5RbdaWTCfDUu1aV3qJOJjjWQ9DJdJQcEsrTjOpD4bVdZx4w/XEG0JXAa3
|
||||
jRypVHGdeG/TjhUGJA8U+KX3a1DkcdqM9pqFYRw5Ie95Wz9YRroI+YkixqpK8d7W
|
||||
C+5BodxXIQKBgCk8Lv9V7XgPM3XW8APJbk+BrTCEuu8unUbnQcCztssAdEmvkjnB
|
||||
PErBgVyRaNTCmzPmnTFS20sWgaD2QkBAFG+uM4n5ISK+NvTLJ7fv3IwdlAw1V9Jx
|
||||
uiCElrKqpTXEiHMzVkZss5ks6j6y9duCIBXSEhM5pERPvNRDphjsLTXxAoGARSNC
|
||||
nyb1Kjjo9XR0V+pNy6pC9q1C+00B5tCVZ55zxe114Hi70pfGQcM+YxnlAoeoCNW9
|
||||
mBfAFDESNAlGjyrovIzYkiH7EcZSrYdBEOepgJ2DfWo4Wi0bK9+03K2AknAaS1iO
|
||||
GJqTtAJMSuymwu40gKroJNA42Q40nKO0LyCARGECgYEAiFRHkblBtStv22SpZxNC
|
||||
jim9yuM0ikh7Ij1lEHysc/GWb2RQNxQVk54BU2kQ0d9xwMZQTKvpF3VE9t7uGdwt
|
||||
AasWPr/tWYt35Ud0D4bNlagJJ4Xdslf8n1nkq3qqqDQbd7kkQRgwGzVr0uVg7ZfS
|
||||
26qSPQ0/aF9nagb5eHX3AuU=
|
||||
-----END PRIVATE KEY-----
|
||||
12
tests/certs/expired/ca/ca.cnf
Normal file
12
tests/certs/expired/ca/ca.cnf
Normal file
@@ -0,0 +1,12 @@
|
||||
[req]
|
||||
default_bits = 2048
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
encrypt_key = no
|
||||
distinguished_name = dn
|
||||
|
||||
[dn]
|
||||
C = US # country code
|
||||
O = Python Software Foundation # organization
|
||||
OU = python-requests # organization unit/department
|
||||
CN = Self-Signed Root CA # common name / your cert name
|
||||
20
tests/certs/expired/ca/ca.crt
Normal file
20
tests/certs/expired/ca/ca.crt
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG
|
||||
A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw
|
||||
FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv
|
||||
b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8
|
||||
RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He
|
||||
mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK
|
||||
na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6
|
||||
fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm
|
||||
zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy
|
||||
e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV
|
||||
GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws
|
||||
Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn
|
||||
9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ
|
||||
rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ
|
||||
6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg=
|
||||
-----END CERTIFICATE-----
|
||||
1
tests/certs/expired/ca/ca.srl
Normal file
1
tests/certs/expired/ca/ca.srl
Normal file
@@ -0,0 +1 @@
|
||||
4F36C3A7E075BA6452D10EEB81E7F189FF489B74
|
||||
16
tests/certs/expired/server/Makefile
Normal file
16
tests/certs/expired/server/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
.PHONY: all clean
|
||||
|
||||
server.key:
|
||||
openssl genrsa -out $@ 2048
|
||||
|
||||
server.csr: server.key
|
||||
openssl req -key $< -new -out $@ -config cert.cnf
|
||||
|
||||
server.pem: server.csr
|
||||
openssl x509 -req -CA ../ca/ca.crt -CAkey ../ca/ca-private.key -in server.csr -outform PEM -out server.pem -days 0 -CAcreateserial
|
||||
openssl x509 -in ../ca/ca.crt -outform PEM >> $@
|
||||
|
||||
all: server.pem
|
||||
|
||||
clean:
|
||||
rm -f server.*
|
||||
24
tests/certs/expired/server/cert.cnf
Normal file
24
tests/certs/expired/server/cert.cnf
Normal file
@@ -0,0 +1,24 @@
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt=no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = DE
|
||||
O = Python Software Foundation
|
||||
OU = python-requests
|
||||
CN = localhost
|
||||
|
||||
[v3_req]
|
||||
# Extensions to add to a certificate request
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = *.localhost
|
||||
DNS.1 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
19
tests/certs/expired/server/server.csr
Normal file
19
tests/certs/expired/server/server.csr
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIDHjCCAgYCAQAwbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK
|
||||
DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl
|
||||
cXVlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQCKulIMpo633iCgbkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9
|
||||
kvPt//imqtl8FhuhKqJ8FCGrVl2YIGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoA
|
||||
IPyXytIR7VH9+ch9DFInJaoA/BekMuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh
|
||||
9VjvtuRDji2iOZpznlVE2PEN80bojArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4e
|
||||
ja7ad3R9G0kB1FzNj36jrNO5WtxHO/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspP
|
||||
IltfwOQh8qq2Q2AaMHNcVjMH3gHCZADfhk/zAgMBAAGgbDBqBgkqhkiG9w0BCQ4x
|
||||
XTBbMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
|
||||
MCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAN
|
||||
BgkqhkiG9w0BAQsFAAOCAQEAfAhEhrulsZae71YFqgvzwJHm/hzXh47hErtgDXVJ
|
||||
mFqAxgF6XrnzYujlt3XQXUx/8vdrU7jH+Pe8WO1rDvFwRPMDGoBF3RX29SzyX/2F
|
||||
e102egnoRR+Hlf0Ixqu0CuTjEVnD+g4mRgXhV7LPKP4W6qGwzcVbaJ3c/zRcfqNR
|
||||
g9gN6Q6Qt4fXDc7wlx2T3nOszBLQ2XCsIyzVtOJ2sSuadqKH9Aj+mrkrLBdzVFHD
|
||||
FHnTMJ0t0+anZwd+AWDNsCr5lIwBGL634zw7/yJepMHuPFd2X24S3u8EaWPkfVQn
|
||||
lV6rLQMGjXYTe2xuYzlUCUYnKvkyPTMjSXDkxWa+WSNwyQ==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
28
tests/certs/expired/server/server.key
Normal file
28
tests/certs/expired/server/server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKulIMpo633iCg
|
||||
bkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9kvPt//imqtl8FhuhKqJ8FCGrVl2Y
|
||||
IGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoAIPyXytIR7VH9+ch9DFInJaoA/Bek
|
||||
MuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh9VjvtuRDji2iOZpznlVE2PEN80bo
|
||||
jArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4eja7ad3R9G0kB1FzNj36jrNO5WtxH
|
||||
O/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspPIltfwOQh8qq2Q2AaMHNcVjMH3gHC
|
||||
ZADfhk/zAgMBAAECggEAFSF9RvUFzyb0BEvXN44+/QaKv+4tkMmSW4Xs3rFnZ4G3
|
||||
E8nkpLUCF9ICD2z9tKNvcPScDFdKq5z7o6ToJ9faf5MRIdrBz8UlGLIO6g6l1Bjw
|
||||
vjNwJE3h+8MGjXl/IDbwXW/HgbQAeabsePPRSJRdvz2+ACn1M8VLdrLvFJA93ayW
|
||||
+n3Bk0bXdsrzqBGdoDiNzmIHI3WqdONiR9TymuJe41NJtMKxQDF+c6Y1n/X1OtBk
|
||||
s9L+u9Xr+R3H72xSYrf1KH1mFZJfTnIPoOmdEU2tVZnZj03rZhT7p8R1fVNX6OHu
|
||||
NX1Dy9VA6J7dbcqdPvTI743ByQeb+hNnqI/3hmV5eQKBgQC++1Wn3v/dxtczjA+I
|
||||
tN4a7zyjhazpB25lde55HVfCQPxmYxIYct+j6S0JkMaoLrjiEDb4pnu4Gt4MDqZa
|
||||
r0Xm8t3wD1YKUUbhpBEGvsMhAEZEIsBOcwkTiEwsoF0mKFa2mTyqAImgIQa8uFt8
|
||||
Y/oTj55XFe1x6pZKEJRg+K+QSwKBgQC59ONVkMSBirLGS+G+b2kqiBdwZB/3s3wr
|
||||
feS1xTa+deL3AChnKT9+MsVqOkxdE2TRj/mAeF+5Woa5bPMvgr9Kl7u8bulTH80l
|
||||
YA/N6FneO11/ncnkgK9wN54kd5TiOtGsGB5S5t/nEAIMUIwWrM/cRau72xNEWOhT
|
||||
Tvw7TOSF+QKBgQCa/texeiYmE24sA4vH4yIuseKAw8hlBwbtiRyVZt8GZD9zyQuy
|
||||
k+g02tUWYk0XyXN65LX4bwURkZyMJIeWKZGNsaW1YnzturDQB5tZ4g/zBIoCWkHA
|
||||
aVQAaimIPk3a3foiD5NQVUdckfEp0GVPOsSGg5R6EO23+i8mxPXnDW1OqQKBgGvf
|
||||
lelTO8tyLFdAOcqBUt6rZ/1499p3snaAZ6bSqvk95dYnr0h48y5AQaln/FiaIYg4
|
||||
HyLZsZ4S18jFXSWYkWOyNeQP6yafciBWY5StT0TN52VaoX3+8McGXKUHAcVjHbLZ
|
||||
ou2wpP6jmKyQJVQaF9LOT9uAMOMbOFrrnQLBjmfxAoGAQAnUhMFG5mwi9Otxt6Mz
|
||||
g+Gr+3JTlzwC3L7UwGdlFc3G2vSdGx/yOrfzpxPImfIBS95mibDfdvEBMer26pvw
|
||||
a/ycqybyX9d/5nPDIaJ1lc4M4cbHC/cB52JI6avr/1g8OMK7lR7b/FsPVHS1w8kl
|
||||
n6uwEjVt2+gP2o9DFTGs158=
|
||||
-----END PRIVATE KEY-----
|
||||
41
tests/certs/expired/server/server.pem
Normal file
41
tests/certs/expired/server/server.pem
Normal file
@@ -0,0 +1,41 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXjCCAkYCFE82w6fgdbpkUtEO64Hn8Yn/SJt0MA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMzIxMTQ0NVoXDTI0MDMxMzIxMTQ0NVowbTELMAkG
|
||||
A1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg
|
||||
Rm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRIwEAYDVQQDDAls
|
||||
b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKulIMpo63
|
||||
3iCgbkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9kvPt//imqtl8FhuhKqJ8FCGr
|
||||
Vl2YIGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoAIPyXytIR7VH9+ch9DFInJaoA
|
||||
/BekMuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh9VjvtuRDji2iOZpznlVE2PEN
|
||||
80bojArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4eja7ad3R9G0kB1FzNj36jrNO5
|
||||
WtxHO/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspPIltfwOQh8qq2Q2AaMHNcVjMH
|
||||
3gHCZADfhk/zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGeQdB4+iDbJ78eKhCMV
|
||||
49Cm8nyYi9215rRRJ24Bw6BtVw1ECwymxLVOEB0gHCu8kKdsFnniFBtChts/ilFg
|
||||
blIyPKTsb3+kQW9YV9QwVdFdC4mTIljujCSQ4HNUC/Vjfnz85SDKf9/3PMKRr36+
|
||||
GtSLIozudPvkNmCv68jy3RRXyCwWHc43BLMSZKPD/W+DEuXShI9OIpIlSLBx16Hz
|
||||
4ce3/1pGuITWcsw6UcRqW31oPR31QmNs5fsq5ZCojDNFzEFCA1t9LiR6UOftFUKy
|
||||
yOZWfZeAGGdK75U+XDqS9Xkr5/ic5jE0I5rT7e7r3lpvQdgIj8lSx493fczLOGHr
|
||||
YA0=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG
|
||||
A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw
|
||||
FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv
|
||||
b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8
|
||||
RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He
|
||||
mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK
|
||||
na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6
|
||||
fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm
|
||||
zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy
|
||||
e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV
|
||||
GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws
|
||||
Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn
|
||||
9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ
|
||||
rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ
|
||||
6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg=
|
||||
-----END CERTIFICATE-----
|
||||
7
tests/certs/mtls/Makefile
Normal file
7
tests/certs/mtls/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
.PHONY: all clean
|
||||
|
||||
all:
|
||||
make -C client all
|
||||
|
||||
clean:
|
||||
make -C client clean
|
||||
4
tests/certs/mtls/README.md
Normal file
4
tests/certs/mtls/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Certificate Examples for mTLS
|
||||
|
||||
This has some generated certificates for mTLS utilization. The idea is to be
|
||||
able to have testing around how Requests handles client certificates.
|
||||
16
tests/certs/mtls/client/Makefile
Normal file
16
tests/certs/mtls/client/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
.PHONY: all clean
|
||||
|
||||
client.key:
|
||||
openssl genrsa -out $@ 2048
|
||||
|
||||
client.csr: client.key
|
||||
openssl req -key $< -new -out $@ -config cert.cnf
|
||||
|
||||
client.pem: client.csr
|
||||
openssl x509 -req -CA ./ca/ca.crt -CAkey ./ca/ca-private.key -in client.csr -outform PEM -out client.pem -days 730 -CAcreateserial
|
||||
openssl x509 -in ./ca/ca.crt -outform PEM >> $@
|
||||
|
||||
all: client.pem
|
||||
|
||||
clean:
|
||||
rm -f client.*
|
||||
1
tests/certs/mtls/client/ca
Symbolic link
1
tests/certs/mtls/client/ca
Symbolic link
@@ -0,0 +1 @@
|
||||
../../expired/ca/
|
||||
26
tests/certs/mtls/client/cert.cnf
Normal file
26
tests/certs/mtls/client/cert.cnf
Normal file
@@ -0,0 +1,26 @@
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt=no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = DE
|
||||
O = Python Software Foundation
|
||||
OU = python-requests
|
||||
CN = requests
|
||||
|
||||
[v3_req]
|
||||
# Extensions to add to a certificate request
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = *.localhost
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
URI.1 = spiffe://trust.python.org/v0/maintainer/sigmavirus24/project/requests/org/psf
|
||||
URI.2 = spiffe://trust.python.org/v1/maintainer:sigmavirus24/project:requests/org:psf
|
||||
URI.3 = spiffe://trust.python.org/v1/maintainer=sigmavirus24/project=requests/org=psf
|
||||
24
tests/certs/mtls/client/client.csr
Normal file
24
tests/certs/mtls/client/client.csr
Normal file
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEGjCCAwICAQAwbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK
|
||||
DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl
|
||||
cXVlc3RzMREwDwYDVQQDDAhyZXF1ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAMn3iQycTjUzpKJChRNkcm33UB282cUwpxeqKN4ahHxBpS09HRhk
|
||||
cQYO7yErEUQwzQnBQEcIpzzeIMZIqHuCkgnySjeEJd95AIzNzGyoLLkS51TcJwgR
|
||||
v83AvT8ljA88s9h38qGTy4/TCxJgf76pfHIuC1qoKVQh3AuHj9nOxIZLUsrdDbWF
|
||||
WoLqKSVyTby+RXvSAppAR+cuBCaWStQ6xFORn48RHfc6t30ggD4rDAjyU6Vz6oR8
|
||||
ot3XmGdK0h42UdqidUWkRJajEbpkCnQSXS21IvfXKxF5sFqAXJrj9iVbUfpNPpaa
|
||||
W8IrHByngyV8amazGZrASstUVRFtWrnrcWECAwEAAaCCAWcwggFjBgkqhkiG9w0B
|
||||
CQ4xggFUMIIBUDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDAjCCAR8GA1UdEQSCARYwggESggsqLmxvY2FsaG9zdIcEfwAAAYcQAAAA
|
||||
AAAAAAAAAAAAAAAAAYZNc3BpZmZlOi8vdHJ1c3QucHl0aG9uLm9yZy92MC9tYWlu
|
||||
dGFpbmVyL3NpZ21hdmlydXMyNC9wcm9qZWN0L3JlcXVlc3RzL29yZy9wc2aGTXNw
|
||||
aWZmZTovL3RydXN0LnB5dGhvbi5vcmcvdjEvbWFpbnRhaW5lcjpzaWdtYXZpcnVz
|
||||
MjQvcHJvamVjdDpyZXF1ZXN0cy9vcmc6cHNmhk1zcGlmZmU6Ly90cnVzdC5weXRo
|
||||
b24ub3JnL3YxL21haW50YWluZXI9c2lnbWF2aXJ1czI0L3Byb2plY3Q9cmVxdWVz
|
||||
dHMvb3JnPXBzZjANBgkqhkiG9w0BAQsFAAOCAQEAwP1KJ+Evddn2RV1FM6BFkoDK
|
||||
MPDO9qwb8ea3j57SIJXZlpw168DljmuGzxJw9oys2O6FYcspbHIocAkfFwiYgVAr
|
||||
NEog6xlCdPxNBJgC3YFIKwnmBjMPG6ZCWiJn940qTbaJ/j6ZviN17uW4K7Sl+THp
|
||||
IkMv29uQTWkfg+GbZ9q1hm2m2GHhYLGLAUdJdtv7JI+yq5uxdsWaCANpH6kc8SnK
|
||||
2rik6D3iItDhHCmToHBpdEnP8J+KDzf5pJrv/g3WH8XVrl4ZzBsOhmciWF4C3Hbf
|
||||
9eu8eAsp1AsIrZOEGTfClBd7vFCES5DmI0/iRs4czQooqZPnHjOw3Azp/LujrA==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
28
tests/certs/mtls/client/client.key
Normal file
28
tests/certs/mtls/client/client.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJ94kMnE41M6Si
|
||||
QoUTZHJt91AdvNnFMKcXqijeGoR8QaUtPR0YZHEGDu8hKxFEMM0JwUBHCKc83iDG
|
||||
SKh7gpIJ8ko3hCXfeQCMzcxsqCy5EudU3CcIEb/NwL0/JYwPPLPYd/Khk8uP0wsS
|
||||
YH++qXxyLgtaqClUIdwLh4/ZzsSGS1LK3Q21hVqC6iklck28vkV70gKaQEfnLgQm
|
||||
lkrUOsRTkZ+PER33Ord9IIA+KwwI8lOlc+qEfKLd15hnStIeNlHaonVFpESWoxG6
|
||||
ZAp0El0ttSL31ysRebBagFya4/YlW1H6TT6WmlvCKxwcp4MlfGpmsxmawErLVFUR
|
||||
bVq563FhAgMBAAECggEABhWX97JJxN6JFNOjhgGzqiPA3R8lrFlv3zhNbODS9u9U
|
||||
q404xYBZIKaYhkucLzgNJUBrevhZbsL+V8WJQIH0JlU57nw5ATIjAHA+uqiXraen
|
||||
zRhTcLHK28b1AeRUA4LU+YN7jWnnawN075kf9WgjtfOJ0gcDimOkE7uCFjyyvPJA
|
||||
LG9bG+8enGjvUleKXNgmwP4Sq/GlEdGz9Qy+8ga3mtfAULUWe8haFNZXK8CN3xPp
|
||||
wmVqy7QzgH2TGN1p6Dyxib9ksSN/lOg0dShL8zgu+QXDNx2VwmVrI8Vr02vmB//0
|
||||
bYxCo5pfICPIFLjLl5yo30dvrUfYqF29PperStHGlQKBgQD/TdemlLjJNP0fvSs7
|
||||
KEVJj/22YuHK+wurNr2ZFbSdcF3v9sfiwysllmEyGr5cNYA56uUbfG+8VSw7kDll
|
||||
G+6BKK2UdlPH++6RahqWLqo4k6rsNrkq7elj8xG4gIjR5qzu2uLpjNwp2BGmIoUI
|
||||
eb1NcLfTlMcNCooV8RHjm1Z5WwKBgQDKhHkUPDcJm2/9Ltq2NZQMrCS7o4LV2uAI
|
||||
GhGpISfY+SfHkQQNZ9Fvbe6hrFeZs31nAvlTDpPEg/LGSVKA5I2EZT9gwzAQU1TD
|
||||
Cyol4xqqWFWlwze7w+RLYqX5LtXf7NJg2m5p+ZOoOzzqvTVpodDxqTlCNp2/6ICP
|
||||
vAIvWhbA8wKBgAYlr62ZIyHlHrsm6OWRwKlWyDseAmXKyasjtEj9Vs37qKdgf8ub
|
||||
+2v6RPjZ3/+EYkQCveV9h4s3WctNW7Rtib6eZh+PAdFs5X+m2GEJWpvmIlVxs9+u
|
||||
vtHjRmf04FZ9gWh26MPK2no/c51Wc3GSzNYSgrqbeHd963k/xrh+QwTFAoGAZZjb
|
||||
3UjwG4O9RPjyhCKQ6WKa8v9urbamWaoqXfziLrmgOUAJFmiU6x/tbXI2aEdhjAIz
|
||||
7nULsLS5YLx8BWmjjV3106dYP3hut4KsXGF4iSjTnts25J27tA4DUeUrKrF2QVyT
|
||||
s9qfNvCw+Np/J0Uku3e33/3iWdpcVL9vIS5C5/0CgYBEuxb3dffNRqEiNkpOUrCD
|
||||
mQTqbO3X+hin9zT3GrxQE+7KpfCfdDIqdK6c5UWHirR3HUjUPZmIFLSx8msfLl3k
|
||||
hgQw37NMV+asg0Wy3P908qbtnEA2P6aDOMQeHJoC7qEHIDOcOQ1KP3FMvOrdscwS
|
||||
f0IIDygTH6fYr329s0iXjg==
|
||||
-----END PRIVATE KEY-----
|
||||
41
tests/certs/mtls/client/client.pem
Normal file
41
tests/certs/mtls/client/client.pem
Normal file
@@ -0,0 +1,41 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXTCCAkUCFE82w6fgdbpkUtEO64Hn8Yn/SJtzMA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMzE4MzUwNFoXDTI2MDMxMzE4MzUwNFowbDELMAkG
|
||||
A1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg
|
||||
Rm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMREwDwYDVQQDDAhy
|
||||
ZXF1ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMn3iQycTjUz
|
||||
pKJChRNkcm33UB282cUwpxeqKN4ahHxBpS09HRhkcQYO7yErEUQwzQnBQEcIpzze
|
||||
IMZIqHuCkgnySjeEJd95AIzNzGyoLLkS51TcJwgRv83AvT8ljA88s9h38qGTy4/T
|
||||
CxJgf76pfHIuC1qoKVQh3AuHj9nOxIZLUsrdDbWFWoLqKSVyTby+RXvSAppAR+cu
|
||||
BCaWStQ6xFORn48RHfc6t30ggD4rDAjyU6Vz6oR8ot3XmGdK0h42UdqidUWkRJaj
|
||||
EbpkCnQSXS21IvfXKxF5sFqAXJrj9iVbUfpNPpaaW8IrHByngyV8amazGZrASstU
|
||||
VRFtWrnrcWECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHHgMckLDRV72p1FEVmCh
|
||||
AAPZjCswiPZFrwGPN57JqSWjoRB9ilKvo87aPosEO7vfa05OD/qkM/T9Qykuhati
|
||||
I1T1T7qX4Ymb5kTJIBouuflAO3uKVaq+ga2Q/HLlU5w/VoMU4RuK7+RaiRUEE3xL
|
||||
iPSMBvZpoMj695LnzcGrT5oLkFI0bTIlpQt1SFjDpHFtOj/ZdwgSbZYLoTCBXQK3
|
||||
7Y29qAj/XwEiCH63n8tJKvZcD8/ssMIMIdWhNmu+0jOWica/3WSih9Geoy6Ydtxi
|
||||
I5t9vRjC4LIipMUAF86AJIfvHJyI6aCNT420LaR6NRW0FQn5CPTHPAsKg3JkAywn
|
||||
Ew==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG
|
||||
A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw
|
||||
FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv
|
||||
b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8
|
||||
RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He
|
||||
mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK
|
||||
na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6
|
||||
fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm
|
||||
zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy
|
||||
e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV
|
||||
GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws
|
||||
Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn
|
||||
9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ
|
||||
rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ
|
||||
6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg=
|
||||
-----END CERTIFICATE-----
|
||||
1
tests/certs/valid/ca
Symbolic link
1
tests/certs/valid/ca
Symbolic link
@@ -0,0 +1 @@
|
||||
../expired/ca
|
||||
16
tests/certs/valid/server/Makefile
Normal file
16
tests/certs/valid/server/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
.PHONY: all clean
|
||||
|
||||
server.key:
|
||||
openssl genrsa -out $@ 2048
|
||||
|
||||
server.csr: server.key
|
||||
openssl req -key $< -config cert.cnf -new -out $@
|
||||
|
||||
server.pem: server.csr
|
||||
openssl x509 -req -CA ../ca/ca.crt -CAkey ../ca/ca-private.key -in server.csr -outform PEM -out server.pem -extfile cert.cnf -extensions v3_ca -days 7200 -CAcreateserial
|
||||
openssl x509 -in ../ca/ca.crt -outform PEM >> $@
|
||||
|
||||
all: server.pem
|
||||
|
||||
clean:
|
||||
rm -f server.*
|
||||
31
tests/certs/valid/server/cert.cnf
Normal file
31
tests/certs/valid/server/cert.cnf
Normal file
@@ -0,0 +1,31 @@
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt=no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = DE
|
||||
O = Python Software Foundation
|
||||
OU = python-requests
|
||||
CN = localhost
|
||||
|
||||
[v3_req]
|
||||
# Extensions to add to a certificate request
|
||||
basicConstraints = critical, CA:FALSE
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = critical, serverAuth
|
||||
subjectAltName = critical, @alt_names
|
||||
|
||||
[v3_ca]
|
||||
# Extensions to add to a certificate request
|
||||
basicConstraints = critical, CA:FALSE
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = critical, serverAuth
|
||||
subjectAltName = critical, @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = *.localhost
|
||||
DNS.1 = localhost
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
19
tests/certs/valid/server/server.csr
Normal file
19
tests/certs/valid/server/server.csr
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIDKjCCAhICAQAwbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK
|
||||
DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl
|
||||
cXVlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQChEKOx377ymuDg23By5Re1DHi2RiBKSHr85/ZTZuwP/69lHN7q
|
||||
TQEO//EMEFZ9+ZwezeJJsejjP2HO5lQZbcsWok3hbM0wVT+vApkogPvJ8WNFFWFe
|
||||
ZBnGLi/1WM9cSZpUsDJ0XCsG0RTtO27wfgZQlKQMZxTkfi971oPYxNVSjTm2JcLT
|
||||
kvwYIwxjJXPDTOgRo9TEAY3cWkCrBJN4w74GWBTM5KDDA230T7WwLuv81XD2LvYj
|
||||
YYdMBGcxPr5tYTIlp3LncbcrDRNk3pbYQk0bRJgkw2vUkteiRGjkt+dgVnLc6+MI
|
||||
W+VLXEpj+zsOZ5/R4d1pofqj9sDyDPhtNr1JAgMBAAGgeDB2BgkqhkiG9w0BCQ4x
|
||||
aTBnMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoG
|
||||
CCsGAQUFBwMBMC8GA1UdEQEB/wQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAA
|
||||
AAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAFTlFTn5Mn8JXtqB5bGjuiChe
|
||||
ClA6Y32Co4l7N0CtAlf+bExwLdpLOleTX3WnryIPALl9uBUI/67dy/STn/J1Yn86
|
||||
jWPEFwpmYNSKgQljYWcwtBdYLWfIsJO11kKdaAkOUHBEN5DKrXJ46Vs4918bD1/Q
|
||||
6ztqdrThiKc646u9xB58Hg7F0IyMWbHfs0x16ZpcN9otrIkbqOE2wzTmc65O1t1i
|
||||
HDljcSk7OnNy3a9wtLEnyPiyMqHf2k/bTlmiDRVe3cSy9xieoqmzHTnOCSASe1y9
|
||||
7lcEBQild18Jo4nACV4vCYOUwrMi/58LWW+lD6OmMnPiWUqOvMbgMffMNDpWPA==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
28
tests/certs/valid/server/server.key
Normal file
28
tests/certs/valid/server/server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQChEKOx377ymuDg
|
||||
23By5Re1DHi2RiBKSHr85/ZTZuwP/69lHN7qTQEO//EMEFZ9+ZwezeJJsejjP2HO
|
||||
5lQZbcsWok3hbM0wVT+vApkogPvJ8WNFFWFeZBnGLi/1WM9cSZpUsDJ0XCsG0RTt
|
||||
O27wfgZQlKQMZxTkfi971oPYxNVSjTm2JcLTkvwYIwxjJXPDTOgRo9TEAY3cWkCr
|
||||
BJN4w74GWBTM5KDDA230T7WwLuv81XD2LvYjYYdMBGcxPr5tYTIlp3LncbcrDRNk
|
||||
3pbYQk0bRJgkw2vUkteiRGjkt+dgVnLc6+MIW+VLXEpj+zsOZ5/R4d1pofqj9sDy
|
||||
DPhtNr1JAgMBAAECggEAIuLzBfXgCvXzlBjL2kMXd7p4EgkN+PEKnKmUr/t40b1Q
|
||||
zR6sBQWBX3GeET4fseElSQHQzCQaPNCve4xltm1S4jftFREHP7sTVHHEYWLQxuy/
|
||||
Uwkewj5927CI6ERgg82YfVP91bjaA/u5I+pt7O7rKLyNbPdN7fEMEW+FNuhpiVvg
|
||||
JMrcK1BCFL6pmIT21LyTwkacMKZSPko58pWE24MA9aSCHk6cXdwQWQK0AfQT3XGT
|
||||
C4I0hRed7LgqMH+gMuhpakiO13t8yTwxt2iQC9+aa4oSHD3BOi/CwIWfe1mHwmlr
|
||||
cj4Kof1JSnK4SVTD16T++PlnWZkF6oaLUNg+/c2C9QKBgQDOFSYIY7+HzinT2hbI
|
||||
yTIJCHpp+Iee+WVvvxjdZIPMDINrlIiHcMfXb0itUdcUO6tz0KYDMDLRC9CSP0ar
|
||||
6mBWUTHfAKF2S4JpI9JYI4PNtIpOP1NiYuyJlnh5+ytU1yIeIvl39hmLcRwI9mgz
|
||||
njy/D7yEoDCrG1dhcltubKpNXQKBgQDIFAVg0A7MNcxBZDLlk1NAME2JKOSszX8E
|
||||
VNucvZD+9l+L9V9BmwwPQdzYifv/dNp3nYn+lxRPPgze3ZWu4+PeDuGudxu0I6ll
|
||||
beFdbIcp1wbeQguzHYLjBYJqsMb4Pao5HPInjPu/HWfZlg9oZpJbKVucQwbonJLX
|
||||
lgca9KaE3QKBgA+OUx+g/+0tZ8ThGoUvgsJhzHPBWeNrKfgEcckMdFJrw2PUg3XN
|
||||
0pf1g4PpwJV7Z5bHcjCda8iR3r2bXydM+tapLF2L+6QlUQPEu3UBwUo+zY3Yg9/S
|
||||
Xc6I+DEk/4FY9+9UboZaolT/RcF7cCQtVqKJeo58VRAlcTQe4L32H+jVAoGALXX3
|
||||
Ht9HbXkP1w/YTLej4+LVy0OCag0rPiW13LBqALSkUx3GrhZ3sAPMFVuM6ad4eFNQ
|
||||
ZouXbsXvkLgSabGYNf11o/mmTtEHjWdhHKQrNgOIqPmixOkAs2quDmXqX79LLTz5
|
||||
fKkZDny0+wiQqa0cth/4k9HbAQGKj/ej16kdKPUCgYAz08Y39NnJYxRNz3tu/7C6
|
||||
jKyXKxhuZCZCt3cSWto5Tg0mVVB+2Jk2GhG1hCfZoRCP25R3FFBR1HOJgOc59T7C
|
||||
LL67FdO0+7mj/WNzHj3+9gyOYQyQgPVDaTmsJLbuzT2S+GpR94ZNliwL2NEa5baG
|
||||
B/Nb2ruRNj0GgZVw48N4XQ==
|
||||
-----END PRIVATE KEY-----
|
||||
47
tests/certs/valid/server/server.pem
Normal file
47
tests/certs/valid/server/server.pem
Normal file
@@ -0,0 +1,47 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEhTCCA22gAwIBAgIUTzbDp+B1umRS0Q7rgefxif9Im3wwDQYJKoZIhvcNAQEL
|
||||
BQAwajELMAkGA1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu
|
||||
ZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYt
|
||||
U2lnbmVkIFJvb3QgQ0EwHhcNMjQwMzE0MDAxMDAzWhcNNDMxMTMwMDAxMDAzWjBt
|
||||
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0
|
||||
d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxEjAQBgNV
|
||||
BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKEQ
|
||||
o7HfvvKa4ODbcHLlF7UMeLZGIEpIevzn9lNm7A//r2Uc3upNAQ7/8QwQVn35nB7N
|
||||
4kmx6OM/Yc7mVBltyxaiTeFszTBVP68CmSiA+8nxY0UVYV5kGcYuL/VYz1xJmlSw
|
||||
MnRcKwbRFO07bvB+BlCUpAxnFOR+L3vWg9jE1VKNObYlwtOS/BgjDGMlc8NM6BGj
|
||||
1MQBjdxaQKsEk3jDvgZYFMzkoMMDbfRPtbAu6/zVcPYu9iNhh0wEZzE+vm1hMiWn
|
||||
cudxtysNE2TelthCTRtEmCTDa9SS16JEaOS352BWctzr4whb5UtcSmP7Ow5nn9Hh
|
||||
3Wmh+qP2wPIM+G02vUkCAwEAAaOCAR4wggEaMAwGA1UdEwEB/wQCMAAwDgYDVR0P
|
||||
AQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMC8GA1UdEQEB/wQlMCOC
|
||||
CWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAdBgNVHQ4EFgQUJ90a
|
||||
UnXKPP13yDprLhG39fUrnu8wgZEGA1UdIwSBiTCBhqFupGwwajELMAkGA1UEBhMC
|
||||
VVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQL
|
||||
DA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJvb3QgQ0GC
|
||||
FA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAA4IBAQCVh4hiraRv
|
||||
JzYbS/TombP//xfVEWHXDBEYsT5GgWf7GPJ/QtSvv6uJFsK7heqLzf9f+r4Z5xMh
|
||||
YAkb0oe/Ge0T30Mo1YaBEqkKuQL9lOMcP69S9uFz2VT6I/76I8qqAu2AFhu74p8f
|
||||
qudwmQyRYo1Ryg4R/SgRhSJKF/ST/2wOusNWSsBe1s8S2PmtOb4dr3cMBGihrUzS
|
||||
DmCQpWjuiuE23HXnnYDc/EUAnEEPkLDgCsE9iLq37FPUHcHjqdYIAhmImPBpv2EL
|
||||
ftXeRWfxN2hRHpS5Fn3QuAOwfJw5tUcVXojJCJfSpL+Ac97iSjxNaDIPlyomauKw
|
||||
1rgbUkSw+9JQ
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox
|
||||
CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv
|
||||
bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l
|
||||
ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG
|
||||
A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw
|
||||
FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv
|
||||
b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8
|
||||
RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He
|
||||
mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK
|
||||
na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6
|
||||
fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm
|
||||
zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy
|
||||
e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV
|
||||
GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws
|
||||
Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn
|
||||
9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ
|
||||
rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ
|
||||
6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg=
|
||||
-----END CERTIFICATE-----
|
||||
8
tests/test_adapters.py
Normal file
8
tests/test_adapters.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import requests.adapters
|
||||
|
||||
|
||||
def test_request_url_trims_leading_path_separators():
|
||||
"""See also https://github.com/psf/requests/issues/6643."""
|
||||
a = requests.adapters.HTTPAdapter()
|
||||
p = requests.Request(method="GET", url="http://127.0.0.1:10000//v:h").prepare()
|
||||
assert "/v:h" == a.request_url(p, {})
|
||||
@@ -1,3 +1,5 @@
|
||||
from unittest import mock
|
||||
|
||||
from requests.help import info
|
||||
|
||||
|
||||
@@ -11,15 +13,15 @@ class VersionedPackage:
|
||||
self.__version__ = version
|
||||
|
||||
|
||||
def test_idna_without_version_attribute(mocker):
|
||||
def test_idna_without_version_attribute():
|
||||
"""Older versions of IDNA don't provide a __version__ attribute, verify
|
||||
that if we have such a package, we don't blow up.
|
||||
"""
|
||||
mocker.patch("requests.help.idna", new=None)
|
||||
assert info()["idna"] == {"version": ""}
|
||||
with mock.patch("requests.help.idna", new=None):
|
||||
assert info()["idna"] == {"version": ""}
|
||||
|
||||
|
||||
def test_idna_with_version_attribute(mocker):
|
||||
def test_idna_with_version_attribute():
|
||||
"""Verify we're actually setting idna version when it should be available."""
|
||||
mocker.patch("requests.help.idna", new=VersionedPackage("2.6"))
|
||||
assert info()["idna"] == {"version": "2.6"}
|
||||
with mock.patch("requests.help.idna", new=VersionedPackage("2.6")):
|
||||
assert info()["idna"] == {"version": "2.6"}
|
||||
|
||||
@@ -7,7 +7,9 @@ import json
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
import threading
|
||||
import warnings
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import urllib3
|
||||
@@ -50,6 +52,7 @@ from requests.structures import CaseInsensitiveDict
|
||||
|
||||
from . import SNIMissingWarning
|
||||
from .compat import StringIO
|
||||
from .testserver.server import TLSServer, consume_socket_content
|
||||
from .utils import override_environ
|
||||
|
||||
# Requests to this URL should always fail with a connection timeout (nothing
|
||||
@@ -75,11 +78,9 @@ except AttributeError:
|
||||
|
||||
|
||||
class TestRequests:
|
||||
|
||||
digest_auth_algo = ("MD5", "SHA-256", "SHA-512")
|
||||
|
||||
def test_entry_points(self):
|
||||
|
||||
requests.session
|
||||
requests.session().get
|
||||
requests.session().head
|
||||
@@ -510,7 +511,6 @@ class TestRequests:
|
||||
|
||||
@pytest.mark.parametrize("key", ("User-agent", "user-agent"))
|
||||
def test_user_agent_transfers(self, httpbin, key):
|
||||
|
||||
heads = {key: "Mozilla/5.0 (github.com/psf/requests)"}
|
||||
|
||||
r = requests.get(httpbin("user-agent"), headers=heads)
|
||||
@@ -647,25 +647,26 @@ class TestRequests:
|
||||
|
||||
assert sent_headers.get("Proxy-Authorization") == proxy_auth_value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url,has_proxy_auth",
|
||||
(
|
||||
('http://example.com', True),
|
||||
('https://example.com', False),
|
||||
("http://example.com", True),
|
||||
("https://example.com", False),
|
||||
),
|
||||
)
|
||||
def test_proxy_authorization_not_appended_to_https_request(self, url, has_proxy_auth):
|
||||
def test_proxy_authorization_not_appended_to_https_request(
|
||||
self, url, has_proxy_auth
|
||||
):
|
||||
session = requests.Session()
|
||||
proxies = {
|
||||
'http': 'http://test:pass@localhost:8080',
|
||||
'https': 'http://test:pass@localhost:8090',
|
||||
"http": "http://test:pass@localhost:8080",
|
||||
"https": "http://test:pass@localhost:8090",
|
||||
}
|
||||
req = requests.Request('GET', url)
|
||||
req = requests.Request("GET", url)
|
||||
prep = req.prepare()
|
||||
session.rebuild_proxies(prep, proxies)
|
||||
|
||||
assert ('Proxy-Authorization' in prep.headers) is has_proxy_auth
|
||||
assert ("Proxy-Authorization" in prep.headers) is has_proxy_auth
|
||||
|
||||
def test_basicauth_with_netrc(self, httpbin):
|
||||
auth = ("user", "pass")
|
||||
@@ -703,7 +704,6 @@ class TestRequests:
|
||||
requests.sessions.get_netrc_auth = old_auth
|
||||
|
||||
def test_DIGEST_HTTP_200_OK_GET(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
auth = HTTPDigestAuth("user", "pass")
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype, "never")
|
||||
@@ -721,7 +721,6 @@ class TestRequests:
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_DIGEST_AUTH_RETURNS_COOKIE(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype)
|
||||
auth = HTTPDigestAuth("user", "pass")
|
||||
@@ -732,7 +731,6 @@ class TestRequests:
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_DIGEST_AUTH_SETS_SESSION_COOKIES(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype)
|
||||
auth = HTTPDigestAuth("user", "pass")
|
||||
@@ -741,7 +739,6 @@ class TestRequests:
|
||||
assert s.cookies["fake"] == "fake_value"
|
||||
|
||||
def test_DIGEST_STREAM(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
auth = HTTPDigestAuth("user", "pass")
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype)
|
||||
@@ -753,7 +750,6 @@ class TestRequests:
|
||||
assert r.raw.read() == b""
|
||||
|
||||
def test_DIGESTAUTH_WRONG_HTTP_401_GET(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
auth = HTTPDigestAuth("user", "wrongpass")
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype)
|
||||
@@ -770,7 +766,6 @@ class TestRequests:
|
||||
assert r.status_code == 401
|
||||
|
||||
def test_DIGESTAUTH_QUOTES_QOP_VALUE(self, httpbin):
|
||||
|
||||
for authtype in self.digest_auth_algo:
|
||||
auth = HTTPDigestAuth("user", "pass")
|
||||
url = httpbin("digest-auth", "auth", "user", "pass", authtype)
|
||||
@@ -779,7 +774,6 @@ class TestRequests:
|
||||
assert '"auth"' in r.request.headers["Authorization"]
|
||||
|
||||
def test_POSTBIN_GET_POST_FILES(self, httpbin):
|
||||
|
||||
url = httpbin("post")
|
||||
requests.post(url).raise_for_status()
|
||||
|
||||
@@ -797,7 +791,6 @@ class TestRequests:
|
||||
requests.post(url, files=["bad file data"])
|
||||
|
||||
def test_invalid_files_input(self, httpbin):
|
||||
|
||||
url = httpbin("post")
|
||||
post = requests.post(url, files={"random-file-1": None, "random-file-2": 1})
|
||||
assert b'name="random-file-1"' not in post.request.body
|
||||
@@ -845,7 +838,6 @@ class TestRequests:
|
||||
assert post2.json()["data"] == "st"
|
||||
|
||||
def test_POSTBIN_GET_POST_FILES_WITH_DATA(self, httpbin):
|
||||
|
||||
url = httpbin("post")
|
||||
requests.post(url).raise_for_status()
|
||||
|
||||
@@ -983,12 +975,12 @@ class TestRequests:
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_env_cert_bundles(self, httpbin, mocker, env, expected):
|
||||
def test_env_cert_bundles(self, httpbin, env, expected):
|
||||
s = requests.Session()
|
||||
mocker.patch("os.environ", env)
|
||||
settings = s.merge_environment_settings(
|
||||
url=httpbin("get"), proxies={}, stream=False, verify=True, cert=None
|
||||
)
|
||||
with mock.patch("os.environ", env):
|
||||
settings = s.merge_environment_settings(
|
||||
url=httpbin("get"), proxies={}, stream=False, verify=True, cert=None
|
||||
)
|
||||
assert settings["verify"] == expected
|
||||
|
||||
def test_http_with_certificate(self, httpbin):
|
||||
@@ -1011,7 +1003,7 @@ class TestRequests:
|
||||
"SubjectAltNameWarning",
|
||||
)
|
||||
|
||||
with pytest.warns(None) as warning_records:
|
||||
with pytest.warns() as warning_records:
|
||||
warnings.simplefilter("always")
|
||||
requests.get(f"https://localhost:{port}/", verify=ca_bundle)
|
||||
|
||||
@@ -1034,7 +1026,6 @@ class TestRequests:
|
||||
requests.get(httpbin_secure("status", "200"))
|
||||
|
||||
def test_urlencoded_get_query_multivalued_param(self, httpbin):
|
||||
|
||||
r = requests.get(httpbin("get"), params={"test": ["foo", "baz"]})
|
||||
assert r.status_code == 200
|
||||
assert r.url == httpbin("get?test=foo&test=baz")
|
||||
@@ -1476,11 +1467,9 @@ class TestRequests:
|
||||
(urllib3.exceptions.SSLError, tuple(), RequestsSSLError),
|
||||
),
|
||||
)
|
||||
def test_iter_content_wraps_exceptions(
|
||||
self, httpbin, mocker, exception, args, expected
|
||||
):
|
||||
def test_iter_content_wraps_exceptions(self, httpbin, exception, args, expected):
|
||||
r = requests.Response()
|
||||
r.raw = mocker.Mock()
|
||||
r.raw = mock.Mock()
|
||||
# ReadTimeoutError can't be initialized by mock
|
||||
# so we'll manually create the instance with args
|
||||
r.raw.stream.side_effect = exception(*args)
|
||||
@@ -1715,7 +1704,7 @@ class TestRequests:
|
||||
}
|
||||
r = requests.get(httpbin("get"), headers=valid_headers)
|
||||
for key in valid_headers.keys():
|
||||
valid_headers[key] == r.request.headers[key]
|
||||
assert valid_headers[key] == r.request.headers[key]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"invalid_header, key",
|
||||
@@ -1821,6 +1810,23 @@ class TestRequests:
|
||||
|
||||
assert p.headers["Content-Length"] == length
|
||||
|
||||
def test_content_length_for_bytes_data(self, httpbin):
|
||||
data = "This is a string containing multi-byte UTF-8 ☃️"
|
||||
encoded_data = data.encode("utf-8")
|
||||
length = str(len(encoded_data))
|
||||
req = requests.Request("POST", httpbin("post"), data=encoded_data)
|
||||
p = req.prepare()
|
||||
|
||||
assert p.headers["Content-Length"] == length
|
||||
|
||||
def test_content_length_for_string_data_counts_bytes(self, httpbin):
|
||||
data = "This is a string containing multi-byte UTF-8 ☃️"
|
||||
length = str(len(data.encode("utf-8")))
|
||||
req = requests.Request("POST", httpbin("post"), data=data)
|
||||
p = req.prepare()
|
||||
|
||||
assert p.headers["Content-Length"] == length
|
||||
|
||||
def test_nonhttp_schemes_dont_check_URLs(self):
|
||||
test_urls = (
|
||||
"data:image/gif;base64,R0lGODlhAQABAHAAACH5BAUAAAAALAAAAAABAAEAAAICRAEAOw==",
|
||||
@@ -2105,16 +2111,16 @@ class TestRequests:
|
||||
next(r.iter_lines())
|
||||
assert len(list(r.iter_lines())) == 3
|
||||
|
||||
def test_session_close_proxy_clear(self, mocker):
|
||||
def test_session_close_proxy_clear(self):
|
||||
proxies = {
|
||||
"one": mocker.Mock(),
|
||||
"two": mocker.Mock(),
|
||||
"one": mock.Mock(),
|
||||
"two": mock.Mock(),
|
||||
}
|
||||
session = requests.Session()
|
||||
mocker.patch.dict(session.adapters["http://"].proxy_manager, proxies)
|
||||
session.close()
|
||||
proxies["one"].clear.assert_called_once_with()
|
||||
proxies["two"].clear.assert_called_once_with()
|
||||
with mock.patch.dict(session.adapters["http://"].proxy_manager, proxies):
|
||||
session.close()
|
||||
proxies["one"].clear.assert_called_once_with()
|
||||
proxies["two"].clear.assert_called_once_with()
|
||||
|
||||
def test_proxy_auth(self):
|
||||
adapter = HTTPAdapter()
|
||||
@@ -2715,7 +2721,7 @@ class TestPreparingURLs:
|
||||
with pytest.raises(requests.exceptions.InvalidURL):
|
||||
r.prepare()
|
||||
|
||||
@pytest.mark.parametrize("url, exception", (("http://localhost:-1", InvalidURL),))
|
||||
@pytest.mark.parametrize("url, exception", (("http://:1", InvalidURL),))
|
||||
def test_redirecting_to_bad_url(self, httpbin, url, exception):
|
||||
with pytest.raises(exception):
|
||||
requests.get(httpbin("redirect-to"), params={"url": url})
|
||||
@@ -2808,3 +2814,163 @@ class TestPreparingURLs:
|
||||
with pytest.raises(requests.exceptions.JSONDecodeError) as excinfo:
|
||||
r.json()
|
||||
assert excinfo.value.doc == r.text
|
||||
|
||||
def test_status_code_425(self):
|
||||
r1 = requests.codes.get("TOO_EARLY")
|
||||
r2 = requests.codes.get("too_early")
|
||||
r3 = requests.codes.get("UNORDERED")
|
||||
r4 = requests.codes.get("unordered")
|
||||
r5 = requests.codes.get("UNORDERED_COLLECTION")
|
||||
r6 = requests.codes.get("unordered_collection")
|
||||
|
||||
assert r1 == 425
|
||||
assert r2 == 425
|
||||
assert r3 == 425
|
||||
assert r4 == 425
|
||||
assert r5 == 425
|
||||
assert r6 == 425
|
||||
|
||||
def test_different_connection_pool_for_tls_settings_verify_True(self):
|
||||
def response_handler(sock):
|
||||
consume_socket_content(sock, timeout=0.5)
|
||||
sock.send(
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
b"Content-Length: 18\r\n\r\n"
|
||||
b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n'
|
||||
)
|
||||
|
||||
s = requests.Session()
|
||||
close_server = threading.Event()
|
||||
server = TLSServer(
|
||||
handler=response_handler,
|
||||
wait_to_close_event=close_server,
|
||||
requests_to_handle=3,
|
||||
cert_chain="tests/certs/expired/server/server.pem",
|
||||
keyfile="tests/certs/expired/server/server.key",
|
||||
)
|
||||
|
||||
with server as (host, port):
|
||||
url = f"https://{host}:{port}"
|
||||
r1 = s.get(url, verify=False)
|
||||
assert r1.status_code == 200
|
||||
|
||||
# Cannot verify self-signed certificate
|
||||
with pytest.raises(requests.exceptions.SSLError):
|
||||
s.get(url)
|
||||
|
||||
close_server.set()
|
||||
assert 2 == len(s.adapters["https://"].poolmanager.pools)
|
||||
|
||||
def test_different_connection_pool_for_tls_settings_verify_bundle_expired_cert(
|
||||
self,
|
||||
):
|
||||
def response_handler(sock):
|
||||
consume_socket_content(sock, timeout=0.5)
|
||||
sock.send(
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
b"Content-Length: 18\r\n\r\n"
|
||||
b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n'
|
||||
)
|
||||
|
||||
s = requests.Session()
|
||||
close_server = threading.Event()
|
||||
server = TLSServer(
|
||||
handler=response_handler,
|
||||
wait_to_close_event=close_server,
|
||||
requests_to_handle=3,
|
||||
cert_chain="tests/certs/expired/server/server.pem",
|
||||
keyfile="tests/certs/expired/server/server.key",
|
||||
)
|
||||
|
||||
with server as (host, port):
|
||||
url = f"https://{host}:{port}"
|
||||
r1 = s.get(url, verify=False)
|
||||
assert r1.status_code == 200
|
||||
|
||||
# Has right trust bundle, but certificate expired
|
||||
with pytest.raises(requests.exceptions.SSLError):
|
||||
s.get(url, verify="tests/certs/expired/ca/ca.crt")
|
||||
|
||||
close_server.set()
|
||||
assert 2 == len(s.adapters["https://"].poolmanager.pools)
|
||||
|
||||
def test_different_connection_pool_for_tls_settings_verify_bundle_unexpired_cert(
|
||||
self,
|
||||
):
|
||||
def response_handler(sock):
|
||||
consume_socket_content(sock, timeout=0.5)
|
||||
sock.send(
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
b"Content-Length: 18\r\n\r\n"
|
||||
b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n'
|
||||
)
|
||||
|
||||
s = requests.Session()
|
||||
close_server = threading.Event()
|
||||
server = TLSServer(
|
||||
handler=response_handler,
|
||||
wait_to_close_event=close_server,
|
||||
requests_to_handle=3,
|
||||
cert_chain="tests/certs/valid/server/server.pem",
|
||||
keyfile="tests/certs/valid/server/server.key",
|
||||
)
|
||||
|
||||
with server as (host, port):
|
||||
url = f"https://{host}:{port}"
|
||||
r1 = s.get(url, verify=False)
|
||||
assert r1.status_code == 200
|
||||
|
||||
r2 = s.get(url, verify="tests/certs/valid/ca/ca.crt")
|
||||
assert r2.status_code == 200
|
||||
|
||||
close_server.set()
|
||||
assert 2 == len(s.adapters["https://"].poolmanager.pools)
|
||||
|
||||
def test_different_connection_pool_for_mtls_settings(self):
|
||||
client_cert = None
|
||||
|
||||
def response_handler(sock):
|
||||
nonlocal client_cert
|
||||
client_cert = sock.getpeercert()
|
||||
consume_socket_content(sock, timeout=0.5)
|
||||
sock.send(
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
b"Content-Length: 18\r\n\r\n"
|
||||
b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n'
|
||||
)
|
||||
|
||||
s = requests.Session()
|
||||
close_server = threading.Event()
|
||||
server = TLSServer(
|
||||
handler=response_handler,
|
||||
wait_to_close_event=close_server,
|
||||
requests_to_handle=2,
|
||||
cert_chain="tests/certs/expired/server/server.pem",
|
||||
keyfile="tests/certs/expired/server/server.key",
|
||||
mutual_tls=True,
|
||||
cacert="tests/certs/expired/ca/ca.crt",
|
||||
)
|
||||
|
||||
cert = (
|
||||
"tests/certs/mtls/client/client.pem",
|
||||
"tests/certs/mtls/client/client.key",
|
||||
)
|
||||
with server as (host, port):
|
||||
url = f"https://{host}:{port}"
|
||||
r1 = s.get(url, verify=False, cert=cert)
|
||||
assert r1.status_code == 200
|
||||
with pytest.raises(requests.exceptions.SSLError):
|
||||
s.get(url, cert=cert)
|
||||
close_server.set()
|
||||
|
||||
assert client_cert is not None
|
||||
|
||||
|
||||
def test_json_decode_errors_are_serializable_deserializable():
|
||||
json_decode_error = requests.exceptions.JSONDecodeError(
|
||||
"Extra data",
|
||||
'{"responseCode":["706"],"data":null}{"responseCode":["706"],"data":null}',
|
||||
36,
|
||||
)
|
||||
deserialized_error = pickle.loads(pickle.dumps(json_decode_error))
|
||||
assert repr(json_decode_error) == repr(deserialized_error)
|
||||
|
||||
@@ -5,6 +5,7 @@ import tarfile
|
||||
import zipfile
|
||||
from collections import deque
|
||||
from io import BytesIO
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -751,13 +752,13 @@ def test_should_bypass_proxies(url, expected, monkeypatch):
|
||||
("http://user:pass@hostname:5000", "hostname"),
|
||||
),
|
||||
)
|
||||
def test_should_bypass_proxies_pass_only_hostname(url, expected, mocker):
|
||||
def test_should_bypass_proxies_pass_only_hostname(url, expected):
|
||||
"""The proxy_bypass function should be called with a hostname or IP without
|
||||
a port number or auth credentials.
|
||||
"""
|
||||
proxy_bypass = mocker.patch("requests.utils.proxy_bypass")
|
||||
should_bypass_proxies(url, no_proxy=None)
|
||||
proxy_bypass.assert_called_once_with(expected)
|
||||
with mock.patch("requests.utils.proxy_bypass") as proxy_bypass:
|
||||
should_bypass_proxies(url, no_proxy=None)
|
||||
proxy_bypass.assert_called_once_with(expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -923,3 +924,35 @@ def test_set_environ_raises_exception():
|
||||
raise Exception("Expected exception")
|
||||
|
||||
assert "Expected exception" in str(exception.value)
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "nt", reason="Test only on Windows")
|
||||
def test_should_bypass_proxies_win_registry_ProxyOverride_value(monkeypatch):
|
||||
"""Tests for function should_bypass_proxies to check if proxy
|
||||
can be bypassed or not with Windows ProxyOverride registry value ending with a semicolon.
|
||||
"""
|
||||
import winreg
|
||||
|
||||
class RegHandle:
|
||||
def Close(self):
|
||||
pass
|
||||
|
||||
ie_settings = RegHandle()
|
||||
|
||||
def OpenKey(key, subkey):
|
||||
return ie_settings
|
||||
|
||||
def QueryValueEx(key, value_name):
|
||||
if key is ie_settings:
|
||||
if value_name == "ProxyEnable":
|
||||
return [1]
|
||||
elif value_name == "ProxyOverride":
|
||||
return [
|
||||
"192.168.*;127.0.0.1;localhost.localdomain;172.16.1.1;<-loopback>;"
|
||||
]
|
||||
|
||||
monkeypatch.setenv("NO_PROXY", "")
|
||||
monkeypatch.setenv("no_proxy", "")
|
||||
monkeypatch.setattr(winreg, "OpenKey", OpenKey)
|
||||
monkeypatch.setattr(winreg, "QueryValueEx", QueryValueEx)
|
||||
assert should_bypass_proxies("http://example.com/", None) is False
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import select
|
||||
import socket
|
||||
import ssl
|
||||
import threading
|
||||
|
||||
|
||||
@@ -132,3 +133,44 @@ class Server(threading.Thread):
|
||||
self._close_server_sock_ignore_errors()
|
||||
self.join()
|
||||
return False # allow exceptions to propagate
|
||||
|
||||
|
||||
class TLSServer(Server):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
handler=None,
|
||||
host="localhost",
|
||||
port=0,
|
||||
requests_to_handle=1,
|
||||
wait_to_close_event=None,
|
||||
cert_chain=None,
|
||||
keyfile=None,
|
||||
mutual_tls=False,
|
||||
cacert=None,
|
||||
):
|
||||
super().__init__(
|
||||
handler=handler,
|
||||
host=host,
|
||||
port=port,
|
||||
requests_to_handle=requests_to_handle,
|
||||
wait_to_close_event=wait_to_close_event,
|
||||
)
|
||||
self.cert_chain = cert_chain
|
||||
self.keyfile = keyfile
|
||||
self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
self.ssl_context.load_cert_chain(self.cert_chain, keyfile=self.keyfile)
|
||||
self.mutual_tls = mutual_tls
|
||||
self.cacert = cacert
|
||||
if mutual_tls:
|
||||
# For simplicity, we're going to assume that the client cert is
|
||||
# issued by the same CA as our Server certificate
|
||||
self.ssl_context.verify_mode = ssl.CERT_OPTIONAL
|
||||
self.ssl_context.load_verify_locations(self.cacert)
|
||||
|
||||
def _create_socket_and_bind(self):
|
||||
sock = socket.socket()
|
||||
sock = self.ssl_context.wrap_socket(sock, server_side=True)
|
||||
sock.bind((self.host, self.port))
|
||||
sock.listen()
|
||||
return sock
|
||||
|
||||
4
tox.ini
4
tox.ini
@@ -1,5 +1,5 @@
|
||||
[tox]
|
||||
envlist = py{37,38,39,310,311}-{default, use_chardet_on_py3}
|
||||
envlist = py{38,39,310,311,312}-{default, use_chardet_on_py3}
|
||||
|
||||
[testenv]
|
||||
deps = -rrequirements-dev.txt
|
||||
@@ -7,7 +7,7 @@ extras =
|
||||
security
|
||||
socks
|
||||
commands =
|
||||
pytest tests
|
||||
pytest {posargs:tests}
|
||||
|
||||
[testenv:default]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user