[
  {
    "path": ".flake8",
    "content": "[flake8]\ndisable-noqa = True\nmax-line-length = 88\nextend-ignore =\n    E203\n"
  },
  {
    "path": ".github/workflows/main.yaml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - master\n    tags:\n      - v*\n  pull_request:\n\njobs:\n  tox:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13, pypy-3.10]\n        os: [macOS-latest, ubuntu-latest, windows-latest]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Set Up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v5\n        with:\n          python-version: ${{ matrix.python-version }}\n          cache: pip\n          allow-prereleases: true\n          cache-dependency-path: |\n            pyproject.toml\n            setup.cfg\n            setup.py\n\n      - name: Install\n        run: |\n          pip install tox\n\n      - name: tox\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: tox -e py,release\n\n      - name: upload dist\n        uses: actions/upload-artifact@v4\n        with:\n          name: ${{ matrix.os }}_${{ matrix.python-version}}_dist\n          path: dist\n\n  all-successful:\n    # https://github.community/t/is-it-possible-to-require-all-github-actions-tasks-to-pass-without-enumerating-them/117957/4?u=graingert\n    runs-on: ubuntu-latest\n    needs: [tox]\n    permissions:\n      id-token: write\n    steps:\n      - name: Download dists for PyPI\n        uses: actions/download-artifact@v4\n        with:\n          name: ubuntu-latest_3.11_dist\n          path: dist\n\n      - name: Display structure of donwloaded files\n        run: ls -R\n\n      - name: Publish package\n        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')\n        uses: pypa/gh-action-pypi-publish@release/v1\n\n      - name: note that all tests succeeded\n        run: echo \"🎉\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Backup files\n*.~\n*.swp\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\nbin/\nbuild/\ndevelop-eggs/\ndist/\neggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\n.tox/\n.coverage\n.cache\nnosetests.xml\ncoverage.xml\n\n# Translations\n*.mo\n\n# Sphinx documentation\ndocs/_build/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/asottile/pyupgrade\n    rev: v3.4.0\n    hooks:\n      - id: pyupgrade\n        args: ['--py38-plus']\n\n  - repo: https://github.com/psf/black\n    rev: 23.3.0\n    hooks:\n      - id: black\n        args: ['--target-version', 'py38']\n\n  - repo: https://github.com/pycqa/isort\n    rev: 5.12.0\n    hooks:\n      - id: isort\n\n  - repo: https://github.com/pre-commit/pygrep-hooks\n    rev: v1.10.0\n    hooks:\n      - id: python-check-blanket-noqa\n\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.4.0\n    hooks:\n      - id: check-merge-conflict\n      - id: check-toml\n      - id: check-yaml\n      - id: mixed-line-ending\n\n  - repo: https://github.com/pre-commit/mirrors-prettier\n    rev: v3.0.0-alpha.9-for-vscode\n    hooks:\n      - id: prettier\n        args: [--prose-wrap=always, --print-width=88]\n\n  - repo: https://github.com/myint/autoflake\n    rev: v2.1.1\n    hooks:\n      - id: autoflake\n        args:\n          - --in-place\n          - --remove-all-unused-imports\n          - --expand-star-imports\n          - --remove-duplicate-keys\n          - --remove-unused-variables\n\n  - repo: https://github.com/pycqa/flake8\n    rev: 6.0.0\n    hooks:\n      - id: flake8\n        additional_dependencies: [flake8-2020]\n"
  },
  {
    "path": "DESCRIPTION.rst",
    "content": "pytest-httpbin\n==============\n\nhttpbin is an amazing web service for testing HTTP libraries. It has several\ngreat endpoints that can test pretty much everything you need in a HTTP\nlibrary. The only problem is: maybe you don't want to wait for your tests to\ntravel across the Internet and back to make assertions against a remote web\nservice.\n\nEnter pytest-httpbin. Pytest-httpbin creates a pytest \"fixture\" that is\ndependency-injected into your tests. It automatically starts up a HTTP server\nin a separate thread running httpbin and provides your test with the URL in the\nfixture. Check out this example:\n\n.. code-block:: python\n\n    def test_that_my_library_works_kinda_ok(httpbin):\n        assert requests.get(httpbin.url + '/get/').status_code == 200\n\nThis replaces a test that might have looked like this before:\n\n.. code-block:: python\n\n    def test_that_my_library_works_kinda_ok():\n        assert requests.get('http://httpbin.org/get').status_code == 200\n\npytest-httpbin also supports https and includes its own CA cert you can use.\nCheck out `the full documentation`_ on the github page.\n\n.. _the full documentation: https://github.com/kevin1024/pytest-httpbin\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014-2023 Kevin McCarthy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "# If using Python 2.6 or less, then have to include package data, even though\n# it's already declared in setup.py\ninclude pytest_httpbin/certs/*\ninclude DESCRIPTION.rst\nrecursive-include tests *.py\n"
  },
  {
    "path": "README.md",
    "content": "# pytest-httpbin\n\n[![Build Status](https://github.com/kevin1024/pytest-httpbin/actions/workflows/main.yaml/badge.svg)](https://github.com/kevin1024/pytest-httpbin/actions/workflows/main.yaml)\n\n[httpbin](https://httpbin.org/) is an amazing web service for testing HTTP libraries. It\nhas several great endpoints that can test pretty much everything you need in a HTTP\nlibrary. The only problem is: maybe you don't want to wait for your tests to travel\nacross the Internet and back to make assertions against a remote web service (speed),\nand maybe you want to work offline (convenience).\n\nEnter **pytest-httpbin**. Pytest-httpbin creates a\n[pytest fixture](https://pytest.org/latest/fixture.html) that is dependency-injected\ninto your tests. It automatically starts up a HTTP server in a separate thread running\nhttpbin and provides your test with the URL in the fixture. Check out this example:\n\n```python\ndef test_that_my_library_works_kinda_ok(httpbin):\n    assert requests.get(httpbin.url + '/get').status_code == 200\n```\n\nThis replaces a test that might have looked like this before:\n\n```python\ndef test_that_my_library_works_kinda_ok():\n    assert requests.get('http://httpbin.org/get').status_code == 200\n```\n\nIf you're making a lot of requests to httpbin, it can radically speed up your tests.\n\n![demo](http://i.imgur.com/heNOQLP.gif)\n\n# HTTPS support\n\npytest-httpbin also supports HTTPS:\n\n```python\ndef test_that_my_library_works_kinda_ok(httpbin_secure):\n    assert requests.get(httpbin_secure.url + '/get/').status_code == 200\n```\n\nIt's actually starting 2 web servers in separate threads in the background: one HTTP and\none HTTPS. The servers are started on a random port (see below for fixed port support),\non the loopback interface on your machine. Pytest-httpbin includes a self-signed\ncertificate. If your library verifies certificates against a CA (and it should), you'll\nhave to add the CA from pytest-httpbin. The path to the pytest-httpbin CA bundle can by\nfound like this `python -m pytest_httpbin.certs`.\n\nFor example in requests, you can set the `REQUESTS_CA_BUNDLE` python path. You can run\nyour tests like this:\n\n```bash\nREQUESTS_CA_BUNDLE=`python -m pytest_httpbin.certs` py.test tests/\n```\n\n# API of the injected object\n\nThe injected object has the following attributes:\n\n- url\n- port\n- host\n\nand the following methods:\n\n- join(string): Returns the results of calling `urlparse.urljoin` with the url from the\n  injected server automatically applied as the first argument. You supply the second\n  argument\n\nAlso, I defined `__add__` on the object to append to `httpbin.url`. This means you can\ndo stuff like `httpbin + '/get'` instead of `httpbin.url + '/get'`.\n\n## Testing both HTTP and HTTPS endpoints with one test\n\nIf you ever find yourself needing to test both the http and https version of and\nendpoint, you can use the `httpbin_both` funcarg like this:\n\n```python\ndef test_that_my_library_works_kinda_ok(httpbin_both):\n    assert requests.get(httpbin_both.url + '/get/').status_code == 200\n```\n\nThrough the magic of pytest parametrization, this function will actually execute twice:\nonce with an http url and once with an https url.\n\n## Using pytest-httpbin with unittest-style test cases\n\nI have provided 2 additional fixtures to make testing with class-based tests easier. I\nhave also provided a couple decorators that provide some syntactic sugar around the\npytest method of adding the fixtures to class-based tests. Just add the\n`use_class_based_httpbin` and/or `use_class_based_httpbin_secure` class decorators to\nyour class, and then you can access httpbin using self.httpbin and self.httpbin_secure.\n\n```python\nimport pytest_httpbin\n\n@pytest_httpbin.use_class_based_httpbin\n@pytest_httpbin.use_class_based_httpbin_secure\nclass TestClassBassedTests(unittest.TestCase):\n    def test_http(self):\n        assert requests.get(self.httpbin.url + '/get').response\n\n    def test_http_secure(self):\n        assert requests.get(self.httpbin_secure.url + '/get').response\n```\n\n## Running the server on fixed port\n\nSometimes a randomized port can be a problem. Worry not, you can fix the port number to\na desired value with the `HTTPBIN_HTTP_PORT` and `HTTPBIN_HTTPS_PORT` environment\nvariables. If those are defined during pytest plugins are loaded, `httbin` and\n`httpbin_secure` fixtures will run on given ports. You can run your tests like this:\n\n```bash\nHTTPBIN_HTTP_PORT=8080 HTTPBIN_HTTPS_PORT=8443 py.test tests/\n```\n\n## Installation\n\n[![PyPI Version](https://img.shields.io/pypi/v/pytest-httpbin.svg)](https://pypi.org/project/pytest-httpbin/)\n[![Supported Versions](https://img.shields.io/pypi/pyversions/pytest-httpbin.svg)](https://pypi.org/project/pytest-httpbin/)\n\nTo install from [PyPI](https://pypi.org/project/pytest-httpbin/), all you need to do is\nthis:\n\n```bash\npip install pytest-httpbin\n```\n\nand your tests executed by pytest all will have access to the `httpbin` and\n`httpbin_secure` funcargs. Cool right?\n\n## Support and dependencies\n\npytest-httpbin supports Python 3.8+, and pypy. It will automatically\ninstall httpbin and flask when you install it from PyPI.\n\n[httpbin](https://github.com/postmanlabs/httpbin) itself does not support python 2.6 as\nof version 0.6.0, when the Flask-common dependency was added. If you need python 2.6\nsupport pin the httpbin version to 0.5.0\n\n## Running the pytest-httpbin test suite\n\nIf you want to run pytest-httpbin's test suite, you'll need to install requests and\npytest, and then use the ./runtests.sh script.\n\n```bash\npip install pytest\n./runtests.sh\n```\n\nAlso, you can use tox to run the tests on all supported python versions:\n\n```bash\npip install tox\ntox\n```\n\n## Changelog\n\n- 2.1.0\n  - Drop support for Python 3.7 (#85)\n  - Test against PyPy 3.10 (#77)\n  - Add support for CPython 3.13 by regenerating the bundled certificates (#90)\n  - Fix an issue where secure POST requests would fail with a connection reset\n    by peer (#90)\n  - Include a LICENCE\n- 2.0.0\n  - Drop support for Python 2.6, 2.7, 3.4, 3.5 and 3.6 (#68)\n  - Add support for Python 3.7, 3.8, 3.9 and 3.10 (#68)\n  - Avoid deprecation warnings and resource warnings (#71)\n  - Add support for Python 3.11 and 3.12, drop dependency on six (#76)\n- 1.0.2\n  - Switch from travis to github actions\n  - This will be the last release to support Python 2.6, 2.7 or 3.6\n- 1.0.1\n  - httpbin_secure: fix redirect Location to have \"https://\" scheme (#62) - thanks\n    @immerrr\n  - Include regression tests in pypi tarball (#56) - thanks @kmosiejczuk\n- 1.0.0\n  - Update included self-signed cert to include IP address in SAN (See #52). Full\n    version bump because this could be a breaking change for those depending on the\n    certificate missing the IP address in the SAN (as it seems the requests test suite\n    does)\n  - Only use @pytest.fixture decorator once (thanks @hroncok)\n  - Fix a few README typos (thanks @hemberger)\n- 0.3.0\n  - Allow to run httpbin on fixed port using environment variables (thanks @hroncok)\n  - Allow server to be thread.join()ed (thanks @graingert)\n  - Add support for Python 3.6 (thanks @graingert)\n- 0.2.3:\n  - Another attempt to fix #32 (Rare bug, only happens on Travis)\n- 0.2.2:\n  - Fix bug with python3\n- 0.2.1:\n  - Attempt to fix strange, impossible-to-reproduce bug with broken SSL certs that only\n    happens on Travis (#32) [Bad release, breaks py3]\n- 0.2.0:\n  - Remove threaded HTTP server. I built it for Requests, but they deleted their\n    threaded test since it didn't really work very well. The threaded server seems to\n    cause some strange problems with HTTP chunking, so I'll just remove it since nobody\n    is using it (I hope)\n- 0.1.1:\n  - Fix weird hang with SSL on pypy (again)\n- 0.1.0:\n  - Update server to use multithreaded werkzeug server\n- 0.0.7:\n  - Update the certificates (they expired)\n- 0.0.6:\n  - Fix an issue where pypy was hanging when a request was made with an invalid\n    certificate\n- 0.0.5:\n  - Fix broken version parsing in 0.0.4\n- 0.0.4:\n  - **Bad release: Broken version parsing**\n  - Fix `BadStatusLine` error that occurs when sending multiple requests in a single\n    session (PR #16). Thanks @msabramo!\n  - Fix #9 (\"Can't be installed at the same time than pytest?\") (PR #14). Thanks\n    @msabramo!\n  - Add `httpbin_ca_bundle` pytest fixture. With this fixture there is no need to\n    specify the bundle on every request, as it will automatically set\n    `REQUESTS_CA_BUNDLE` if using [requests](https://docs.python-requests.org/). And you\n    don't have to care about where it is located (PR #8). Thanks @t-8ch!\n- 0.0.3: Add a couple test fixtures to make testing old class-based test suites easier\n- 0.0.2: Fixed a couple bugs with the wsgiref server to bring behavior in line with\n  httpbin.org, thanks @jakubroztocil for the bug reports\n- 0.0.1: Initial release\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014-2019 Kevin McCarthy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this\nsoftware and associated documentation files (the \"Software\"), to deal in the Software\nwithout restriction, including without limitation the rights to use, copy, modify,\nmerge, publish, distribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or\nsubstantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\nINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT\nOR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools>=61.2\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"pytest-httpbin\"\ndescription = \"Easily test your HTTP library against a local copy of httpbin\"\nauthors = [{name = \"Kevin McCarthy\", email = \"me@kevinmccarthy.org\"}]\nlicense = {text = \"MIT\"}\nclassifiers = [\n    \"Development Status :: 5 - Production/Stable\",\n    \"Intended Audience :: Developers\",\n    \"Topic :: Software Development :: Testing\",\n    \"Topic :: Software Development :: Libraries\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"Programming Language :: Python :: 3.8\",\n    \"Programming Language :: Python :: 3.9\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n]\nkeywords = [\"pytest-httpbin testing pytest httpbin\"]\nrequires-python = \">=3.8\"\ndependencies = [\"httpbin\"]\ndynamic = [\"version\"]\n\n[project.readme]\nfile = \"DESCRIPTION.rst\"\ncontent-type = \"text/x-rst\"\n\n[project.urls]\nHomepage = \"https://github.com/kevin1024/pytest-httpbin\"\n\n[project.entry-points]\npytest11 = {httpbin = \"pytest_httpbin.plugin\"}\n\n[project.optional-dependencies]\ntest = [\n    \"requests\",\n    \"pytest\",\n]\n\n[tool.setuptools]\ninclude-package-data = true\n\n[tool.setuptools.packages.find]\nexclude = [\n    \"contrib\",\n    \"docs\",\n    \"tests*\",\n]\nnamespaces = false\n\n[tool.setuptools.dynamic]\nversion = {attr = \"pytest_httpbin.version.__version__\"}\n\n[tool.pytest.ini_options]\naddopts = \"--strict-config --strict-markers\"\nfilterwarnings = [\n    \"error\",\n    'ignore:ast\\.(Str|NameConstant) is deprecated:DeprecationWarning:_pytest',\n]\nxfail_strict = true\n\n[tool.tox]\nlegacy_tox_ini = \"\"\"\n    [tox]\n    minversion=3.28.0\n    requires=\n        virtualenv>=20.13.2\n        tox-gh-actions>=2.9.1\n    envlist = py38, py39, py310, pypy3\n\n    [testenv]\n    package = wheel\n    wheel_build_env = .pkg\n    extras = test\n    commands = pytest -v -s {posargs}\n    install_command = python -I -m pip install --use-pep517 {opts} {packages}\n\n    [testenv:release]\n    deps =\n        build\n        twine\n    commands =\n        {envpython} -m release\n        pyproject-build --sdist\n        twine check {toxinidir}/dist/*.*\n\"\"\"\n"
  },
  {
    "path": "pytest_httpbin/__init__.py",
    "content": "import os\n\nimport pytest\n\nfrom .version import __version__\n\nuse_class_based_httpbin = pytest.mark.usefixtures(\"class_based_httpbin\")\nuse_class_based_httpbin_secure = pytest.mark.usefixtures(\"class_based_httpbin_secure\")\n"
  },
  {
    "path": "pytest_httpbin/certs/README.md",
    "content": "generated with 'python -m trustme'\n"
  },
  {
    "path": "pytest_httpbin/certs/client.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIB0TCCAXegAwIBAgIUScnyyX1CI+ywC6GdKol8IIwuGnkwCgYIKoZIzj0EAwIw\nRDEbMBkGA1UECgwSdHJ1c3RtZSB2MS4xLjArZGV2MSUwIwYDVQQLDBxUZXN0aW5n\nIENBICNBdXNVcWJaNG81d3pjb0tCMCAXDTAwMDEwMTAwMDAwMFoYDzMwMDAwMTAx\nMDAwMDAwWjBEMRswGQYDVQQKDBJ0cnVzdG1lIHYxLjEuMCtkZXYxJTAjBgNVBAsM\nHFRlc3RpbmcgQ0EgI0F1c1VxYlo0bzV3emNvS0IwWTATBgcqhkjOPQIBBggqhkjO\nPQMBBwNCAARhrRi78wmZY28t3/y8MTDDCsi7Lzir4WaQm96gf4/9kSolBTFVDUvB\nMkSC7Yged+2bWEzTRERZQLf88uiorUnAo0UwQzAdBgNVHQ4EFgQUHymIBJV4gCrA\nqv+6Q9pSJFtd7PYwEgYDVR0TAQH/BAgwBgEB/wIBCTAOBgNVHQ8BAf8EBAMCAYYw\nCgYIKoZIzj0EAwIDSAAwRQIgLf0sybmdbJoTIgZWrU1k11oecQbdkzh+3jFtNEFn\nzYUCIQCRXjIBDZXtyaywk3DgIggByCQxrrB5vjlnyYTd9vNUSw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "pytest_httpbin/certs/server.key",
    "content": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIPNMu1H1DN9x0VLZNzO3BFp5boEGyc80XFaR1ML18uFRoAoGCCqGSM49\nAwEHoUQDQgAEiNIfYxmsmjemcRRpcd4qP+x1yONFBZZli7CEKxg9j3x5j1OJPeyC\nBQ83kogrxJYLbRjdHUx4VOCEXjffmYhnMA==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "pytest_httpbin/certs/server.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICTDCCAfOgAwIBAgIUZ9rBQX/YRZFcqXCIzOSAd1D0IUcwCgYIKoZIzj0EAwIw\nRDEbMBkGA1UECgwSdHJ1c3RtZSB2MS4xLjArZGV2MSUwIwYDVQQLDBxUZXN0aW5n\nIENBICNBdXNVcWJaNG81d3pjb0tCMCAXDTAwMDEwMTAwMDAwMFoYDzMwMDAwMTAx\nMDAwMDAwWjBGMRswGQYDVQQKDBJ0cnVzdG1lIHYxLjEuMCtkZXYxJzAlBgNVBAsM\nHlRlc3RpbmcgY2VydCAjLVdQNWpjLTllQ0U0S0JxMjBZMBMGByqGSM49AgEGCCqG\nSM49AwEHA0IABIjSH2MZrJo3pnEUaXHeKj/sdcjjRQWWZYuwhCsYPY98eY9TiT3s\nggUPN5KIK8SWC20Y3R1MeFTghF4335mIZzCjgb4wgbswHQYDVR0OBBYEFCO99Ega\nh7pEyFEJVwe09DZzNHDtMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUHymIBJV4\ngCrAqv+6Q9pSJFtd7PYwLwYDVR0RAQH/BCUwI4IJbG9jYWxob3N0hwR/AAABhxAA\nAAAAAAAAAAAAAAAAAAABMA4GA1UdDwEB/wQEAwIFoDAqBgNVHSUBAf8EIDAeBggr\nBgEFBQcDAgYIKwYBBQUHAwEGCCsGAQUFBwMDMAoGCCqGSM49BAMCA0cAMEQCIHB0\nimdD2aQuq4DipTvnFJjmT+w8i3D/Pz8X6bPdkJW/AiATl+m4TW4BE5v1ID3ftDhz\nja8s574nAjDAqcSL7otVpQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "pytest_httpbin/certs.py",
    "content": "\"\"\"\ncerts.py\n~~~~~~~~\n\nThis module returns the preferred default CA certificate bundle.\n\nIf you are packaging pytest-httpbin, e.g., for a Linux distribution or a\nmanaged environment, you can change the definition of where() to return a\nseparately packaged CA bundle.\n\"\"\"\n\nimport os.path\n\n\ndef where():\n    \"\"\"Return the preferred certificate bundle.\"\"\"\n    # vendored bundle inside Requests\n    return os.path.join(os.path.dirname(__file__), \"certs\", \"client.pem\")\n\n\nif __name__ == \"__main__\":\n    print(where())\n"
  },
  {
    "path": "pytest_httpbin/plugin.py",
    "content": "import pytest\nfrom httpbin import app as httpbin_app\n\nfrom . import certs, serve\n\n\n@pytest.fixture(scope=\"session\")\ndef httpbin(request):\n    with serve.Server(application=httpbin_app) as server:\n        yield server\n\n\n@pytest.fixture(scope=\"session\")\ndef httpbin_secure(request):\n    with serve.SecureServer(application=httpbin_app) as server:\n        yield server\n\n\n@pytest.fixture(scope=\"session\", params=[\"http\", \"https\"])\ndef httpbin_both(request, httpbin, httpbin_secure):\n    if request.param == \"http\":\n        return httpbin\n    elif request.param == \"https\":\n        return httpbin_secure\n\n\n@pytest.fixture(scope=\"class\")\ndef class_based_httpbin(request, httpbin):\n    request.cls.httpbin = httpbin\n\n\n@pytest.fixture(scope=\"class\")\ndef class_based_httpbin_secure(request, httpbin_secure):\n    request.cls.httpbin_secure = httpbin_secure\n\n\n@pytest.fixture(scope=\"function\")\ndef httpbin_ca_bundle(monkeypatch):\n    monkeypatch.setenv(\"REQUESTS_CA_BUNDLE\", certs.where())\n"
  },
  {
    "path": "pytest_httpbin/serve.py",
    "content": "import os\nimport ssl\nimport threading\nfrom urllib.parse import urljoin\nfrom wsgiref.handlers import SimpleHandler\nfrom wsgiref.simple_server import WSGIRequestHandler, WSGIServer, make_server\n\nCERT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"certs\")\n\n\nclass ServerHandler(SimpleHandler):\n    server_software = \"Pytest-HTTPBIN/0.1.0\"\n    http_version = \"1.1\"\n\n    def cleanup_headers(self):\n        SimpleHandler.cleanup_headers(self)\n        self.headers[\"Connection\"] = \"Close\"\n\n    def close(self):\n        try:\n            self.request_handler.log_request(\n                self.status.split(\" \", 1)[0], self.bytes_sent\n            )\n        finally:\n            SimpleHandler.close(self)\n\n\nclass Handler(WSGIRequestHandler):\n    def handle(self):\n        \"\"\"Handle a single HTTP request\"\"\"\n\n        self.raw_requestline = self.rfile.readline()\n        if not self.parse_request():  # An error code has been sent, just exit\n            return\n\n        handler = ServerHandler(\n            self.rfile, self.wfile, self.get_stderr(), self.get_environ()\n        )\n        handler.request_handler = self  # backpointer for logging\n        handler.run(self.server.get_app())\n\n    def get_environ(self):\n        \"\"\"\n        wsgiref simple server adds content-type text/plain to everything, this\n        removes it if it's not actually in the headers.\n        \"\"\"\n        # Note: Can't use super since this is an oldstyle class in python 2.x\n        environ = WSGIRequestHandler.get_environ(self).copy()\n        if self.headers.get(\"content-type\") is None:\n            del environ[\"CONTENT_TYPE\"]\n        return environ\n\n\nclass SecureWSGIServer(WSGIServer):\n    def get_request(self):\n        socket, address = super().get_request()\n        try:\n            socket.settimeout(1.0)\n            context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)\n            context.load_cert_chain(\n                os.path.join(CERT_DIR, \"server.pem\"),\n                os.path.join(CERT_DIR, \"server.key\"),\n            )\n            return (\n                context.wrap_socket(\n                    socket, server_side=True, suppress_ragged_eofs=True\n                ),\n                address,\n            )\n        except Exception as e:\n            print(\"pytest-httpbin server hit an exception serving request: %s\" % e)\n            print(\"attempting to ignore so the rest of the tests can run\")\n            raise\n\n    def setup_environ(self):\n        super().setup_environ()\n        self.base_environ[\"HTTPS\"] = \"yes\"\n\n\nclass Server:\n    \"\"\"\n    HTTP server running a WSGI application in its own thread.\n    \"\"\"\n\n    port_envvar = \"HTTPBIN_HTTP_PORT\"\n\n    def __init__(self, host=\"127.0.0.1\", port=0, application=None, **kwargs):\n        self.app = application\n        if self.port_envvar in os.environ:\n            port = int(os.environ[self.port_envvar])\n        self._server = make_server(\n            host, port, self.app, handler_class=Handler, **kwargs\n        )\n        self.host = self._server.server_address[0]\n        self.port = self._server.server_address[1]\n        self.protocol = \"http\"\n\n        self._thread = threading.Thread(\n            name=self.__class__,\n            target=self._server.serve_forever,\n        )\n\n    def __del__(self):\n        if hasattr(self, \"_server\"):\n            self.stop()\n\n    def start(self):\n        self._thread.start()\n\n    def __enter__(self):\n        self.start()\n        return self\n\n    def __exit__(self, *args, **kwargs):\n        self.stop()\n        suppress_exc = self._server.__exit__(*args, **kwargs)\n        self._thread.join()\n        return suppress_exc\n\n    def __add__(self, other):\n        return self.url + other\n\n    def stop(self):\n        self._server.shutdown()\n\n    @property\n    def url(self):\n        return f\"{self.protocol}://{self.host}:{self.port}\"\n\n    def join(self, url, allow_fragments=True):\n        return urljoin(self.url, url, allow_fragments=allow_fragments)\n\n\nclass SecureServer(Server):\n    port_envvar = \"HTTPBIN_HTTPS_PORT\"\n\n    def __init__(self, host=\"127.0.0.1\", port=0, application=None, **kwargs):\n        kwargs[\"server_class\"] = SecureWSGIServer\n        super().__init__(host, port, application, **kwargs)\n        self.protocol = \"https\"\n"
  },
  {
    "path": "pytest_httpbin/version.py",
    "content": "__version__ = \"2.1.0\"\n"
  },
  {
    "path": "release.py",
    "content": "import os\nimport pathlib\nimport shutil\nimport sys\n\n\ndef main():\n    dist = pathlib.Path(__file__).parent / \"dist\"\n    shutil.rmtree(dist, ignore_errors=True)\n    dist.mkdir()\n    shutil.copy(os.environ[\"TOX_PACKAGE\"], dist)\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "runtests.sh",
    "content": "#!/bin/bash\n\npy.test $1 -v -s\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "import pytest\n\n\n@pytest.fixture(autouse=True, scope=\"function\")\ndef httpbin_ca_bundle_autoused(httpbin_ca_bundle):\n    pass\n"
  },
  {
    "path": "tests/test_httpbin.py",
    "content": "import ssl\nimport sys\nimport unittest\nimport urllib.request\n\nimport pytest\nimport requests.exceptions\nimport urllib3\n\nimport pytest_httpbin.certs\n\n\ndef test_httpbin_gets_injected(httpbin):\n    assert httpbin.url\n\n\ndef test_httpbin_accepts_get_requests(httpbin):\n    assert requests.get(httpbin.url + \"/get\").status_code == 200\n\n\ndef test_httpbin_secure_accepts_get_requests(httpbin_secure):\n    assert requests.get(httpbin_secure.url + \"/get\").status_code == 200\n\n\ndef test_httpbin_secure_accepts_lots_of_get_requests(httpbin_secure):\n    for i in range(10):\n        assert requests.get(httpbin_secure.url + \"/get\").status_code == 200\n\n\ndef test_httpbin_accepts_lots_of_get_requests_in_single_session(httpbin):\n    session = requests.Session()\n\n    for i in range(10):\n        assert session.get(httpbin.url + \"/get\").status_code == 200\n\n\ndef test_httpbin_both(httpbin_both):\n    # this test will get called twice, once with an http url, once with an\n    # https url\n    assert requests.get(httpbin_both.url + \"/get\").status_code == 200\n\n\ndef test_httpbin_join(httpbin):\n    assert httpbin.join(\"foo\") == httpbin.url + \"/foo\"\n\n\ndef test_httpbin_str(httpbin):\n    assert httpbin + \"/foo\" == httpbin.url + \"/foo\"\n\n\ndef test_chunked_encoding(httpbin):\n    assert requests.get(httpbin.url + \"/stream/20\").status_code == 200\n\n\n@pytest.mark.xfail(\n    condition=sys.version_info < (3, 8) and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0),\n    reason=\"fails on python3.7 openssl 3+\",\n    raises=requests.exceptions.SSLError,\n)\ndef test_chunked_encoding_secure(httpbin_secure):\n    assert requests.get(httpbin_secure.url + \"/stream/20\").status_code == 200\n\n\n@pytest_httpbin.use_class_based_httpbin\n@pytest_httpbin.use_class_based_httpbin_secure\nclass TestClassBassedTests(unittest.TestCase):\n    def test_http(self):\n        assert requests.get(self.httpbin.url + \"/get\").status_code == 200\n\n    def test_http_secure(self):\n        assert requests.get(self.httpbin_secure.url + \"/get\").status_code == 200\n\n\ndef test_with_urllib2(httpbin_secure):\n    url = httpbin_secure.url + \"/get\"\n    context = ssl.create_default_context(cafile=pytest_httpbin.certs.where())\n    with urllib.request.urlopen(url, context=context) as response:\n        assert response.getcode() == 200\n\n\ndef test_with_urllib3(httpbin_secure):\n    with urllib3.PoolManager(\n        cert_reqs=\"CERT_REQUIRED\",\n        ca_certs=pytest_httpbin.certs.where(),\n    ) as pool:\n        pool.request(\n            \"POST\", httpbin_secure.url + \"/post\", {\"key1\": \"value1\", \"key2\": \"value2\"}\n        )\n"
  },
  {
    "path": "tests/test_server.py",
    "content": "import contextlib\nimport os\nimport re\nimport socket\n\nimport pytest\nimport requests.exceptions\nfrom httpbin import app as httpbin_app\nfrom util import get_raw_http_response\n\nfrom pytest_httpbin import serve\n\n\ndef test_content_type_header_not_automatically_added(httpbin):\n    \"\"\"\n    The server was automatically adding this for some reason, see issue #5\n    \"\"\"\n    resp = requests.get(httpbin + \"/headers\").json()[\"headers\"]\n    assert \"Content-Type\" not in resp\n\n\ndef test_unicode_data(httpbin):\n    \"\"\"\n    UTF-8 was not getting recognized for what it was and being encoded as if it\n    was binary, see issue #7\n    \"\"\"\n    resp = requests.post(\n        httpbin + \"/post\",\n        data=\"оживлённым\".encode(),\n        headers={\n            \"content-type\": \"text/html; charset=utf-8\",\n        },\n    )\n    assert resp.json()[\"data\"] == \"оживлённым\"\n\n\ndef test_server_should_be_http_1_1(httpbin):\n    \"\"\"\n    The server should speak HTTP/1.1 since we live in the future, see issue #6\n    \"\"\"\n    resp = get_raw_http_response(httpbin.host, httpbin.port, \"/get\")\n    assert resp.startswith(b\"HTTP/1.1\")\n\n\ndef test_dont_crash_on_certificate_problems(httpbin_secure):\n    with pytest.raises(requests.exceptions.SSLError):\n        # this request used to hang\n        requests.get(httpbin_secure + \"/get\", verify=True, cert=__file__)\n\n    # and this request would never happen\n    requests.get(\n        httpbin_secure + \"/get\",\n        verify=True,\n    )\n\n\ndef test_dont_crash_on_handshake_timeout(httpbin_secure, capsys):\n    with socket.socket() as sock:\n        sock.connect((httpbin_secure.host, httpbin_secure.port))\n        # this request used to hang\n        assert sock.recv(1) == b\"\"\n\n    assert (\n        re.match(\n            r\"pytest-httpbin server hit an exception serving request:.* The \"\n            \"handshake operation timed out\\nattempting to ignore so the rest \"\n            \"of the tests can run\\n\",\n            capsys.readouterr().out,\n        )\n        is not None\n    )\n\n    # and this request would never happen\n    requests.get(\n        httpbin_secure + \"/get\",\n        verify=True,\n    )\n\n\n@pytest.mark.parametrize(\"protocol\", (\"http\", \"https\"))\ndef test_fixed_port_environment_variables(protocol):\n    \"\"\"\n    Note that we cannot test the fixture here because it is session scoped\n    and was already started. Thus, let's just test a new Server instance.\n    \"\"\"\n    if protocol == \"http\":\n        server_cls = serve.Server\n        envvar = \"HTTPBIN_HTTP_PORT\"\n    elif protocol == \"https\":\n        server_cls = serve.SecureServer\n        envvar = \"HTTPBIN_HTTPS_PORT\"\n    else:\n        raise RuntimeError(f\"Unexpected protocol param: {protocol}\")\n\n    # just have different port to avoid adrress already in use\n    # if the second test run too fast after the first one (happens on pypy)\n    port = 12345 + len(protocol)\n    server = contextlib.nullcontext()\n\n    try:\n        envvar_original = os.environ.get(envvar, None)\n        os.environ[envvar] = str(port)\n        server = server_cls(application=httpbin_app)\n        assert server.port == port\n    finally:\n        # if we don't do this, it blocks:\n        with server:\n            pass\n\n        # restore the original environ:\n        if envvar_original is None:\n            del os.environ[envvar]\n        else:\n            os.environ[envvar] = envvar_original\n\n\ndef test_redirect_location_is_https_for_secure_server(httpbin_secure):\n    assert httpbin_secure.url.startswith(\"https://\")\n    response = requests.get(\n        httpbin_secure + \"/redirect-to?url=/html\", allow_redirects=False\n    )\n    assert response.status_code == 302\n    assert response.headers.get(\"Location\")\n    assert response.headers[\"Location\"] == \"/html\"\n"
  },
  {
    "path": "tests/util.py",
    "content": "import socket\n\n\ndef get_raw_http_response(host, port, path):\n    CRLF = b\"\\r\\n\"\n\n    request = [\n        b\"GET \" + path.encode(\"ascii\") + b\" HTTP/1.1\",\n        b\"Host: \" + host.encode(\"ascii\"),\n        b\"Connection: Close\",\n        b\"\",\n        b\"\",\n    ]\n\n    # Connect to the server\n    with socket.socket() as s:\n        s.connect((host, port))\n\n        # Send an HTTP request\n        s.send(CRLF.join(request))\n\n        # Get the response (in several parts, if necessary)\n        response = b\"\"\n        buffer = s.recv(4096)\n        while buffer:\n            response += buffer\n            buffer = s.recv(4096)\n\n        return response\n"
  }
]