[
  {
    "path": ".dockerignore",
    "content": "Dockerfile\n.git\n"
  },
  {
    "path": ".gitignore",
    "content": "env/\nbuild/\ndist/\n.eggs/\n.workon\n.epio-app\n*.pyc\n.tox\n*.egg-info\n*.swp\n.vscode/\n"
  },
  {
    "path": ".travis.yml",
    "content": "dist: trusty\nlanguage: python\n\nsudo: false\nmatrix:\n    include:\n        - python: 2.7\n          env: TOXENV=py27\n        - python: 3.6\n          env: TOXENV=py36\n        - python: 3.7\n          env: TOXENV=py37\n          dist: xenial\n          sudo: true\n\ninstall:\n    - travis_retry pip install tox\n\nscript:\n    - tox\n"
  },
  {
    "path": "AUTHORS",
    "content": "HttpBin is written and maintained by Kenneth Reitz and\nvarious contributors:\n\nDevelopment Lead\n````````````````\n\n- Kenneth Reitz <_@kennethreitz.com>\n\n\nPatches and Suggestions\n```````````````````````\n\n- Zbigniew Siciarz\n- Andrey Petrov\n- Lispython\n- Kyle Conroy\n- Flavio Percoco\n- Radomir Stevanovic (http://github.com/randomir)\n- Steven Honson\n- Bob Carroll <bob.carroll@alum.rit.edu> @rcarz\n- Cory Benfield (Lukasa) <cory@lukasa.co.uk>\n- Matt Robenolt (https://github.com/mattrobenolt)\n- Dave Challis (https://github.com/davechallis)\n- Florian Bruhin (https://github.com/The-Compiler)\n- Brett Randall (https://github.com/javabrett)\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ubuntu:18.04\n\nLABEL name=\"httpbin\"\nLABEL version=\"0.9.2\"\nLABEL description=\"A simple HTTP service.\"\nLABEL org.kennethreitz.vendor=\"Kenneth Reitz\"\n\nENV LC_ALL=C.UTF-8\nENV LANG=C.UTF-8\n\nRUN apt update -y && apt install python3-pip git -y && pip3 install --no-cache-dir pipenv\n\nADD Pipfile Pipfile.lock /httpbin/\nWORKDIR /httpbin\nRUN /bin/bash -c \"pip3 install --no-cache-dir -r <(pipenv lock -r)\"\n\nADD . /httpbin\nRUN pip3 install --no-cache-dir /httpbin\n\nEXPOSE 80\n\nCMD [\"gunicorn\", \"-b\", \"0.0.0.0:80\", \"httpbin:app\", \"-k\", \"gevent\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "ISC License\n\nCopyright (c) 2017 Kenneth Reitz.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include httpbin/VERSION README.md LICENSE AUTHORS test_httpbin.py\nrecursive-include httpbin/templates *\nrecursive-include httpbin/static *\n"
  },
  {
    "path": "Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\n\n[packages]\ngunicorn = \"*\"\ndecorator = \"*\"\nbrotlipy = \"*\"\ngevent = \"*\"\nFlask = \"*\"\nmeinheld = \"*\"\nwerkzeug = \">=0.14.1\"\nsix = \"*\"\nflasgger = \"*\"\npyyaml = {git = \"https://github.com/yaml/pyyaml.git\"}\n\n[dev-packages]\nrope = \"*\"\n"
  },
  {
    "path": "Procfile",
    "content": "web: gunicorn httpbin:app -k gevent\n"
  },
  {
    "path": "README.md",
    "content": "# httpbin(1): HTTP Request & Response Service\n\n\nA [Kenneth Reitz](http://kennethreitz.org/bitcoin) Project.\n\n![ice cream](http://farm1.staticflickr.com/572/32514669683_4daf2ab7bc_k_d.jpg)\n\nRun locally:\n```sh\ndocker pull kennethreitz/httpbin\ndocker run -p 80:80 kennethreitz/httpbin\n```\n\nSee http://httpbin.org for more information.\n\n## Officially Deployed at:\n\n- http://httpbin.org\n- https://httpbin.org\n- https://hub.docker.com/r/kennethreitz/httpbin/\n\n\n## SEE ALSO\n\n- http://requestb.in\n- http://python-requests.org\n- https://grpcb.in/\n\n## Build Status\n\n[![Build Status](https://travis-ci.org/requests/httpbin.svg?branch=master)](https://travis-ci.org/requests/httpbin)\n"
  },
  {
    "path": "app.json",
    "content": "{\n  \"name\": \"httpbin\",\n  \"description\": \"HTTP Request & Response Service, written in Python + Flask.\",\n  \"repository\": \"https://github.com/requests/httpbin\",\n  \"website\": \"https://httpbin.org\",\n  \"logo\": \"https://s3.amazonaws.com/f.cl.ly/items/333Y191Z2C0G2J3m3Y0b/httpbin.svg\",\n  \"keywords\": [\"http\", \"rest\", \"API\", \"testing\", \"integration\", \"python\", \"flask\"],\n  \"addons\": \"sentry\"\n}\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '2'\nservices:\n    httpbin:\n      build: '.'\n      ports:\n        - '80:80'"
  },
  {
    "path": "httpbin/VERSION",
    "content": "0.9.2\n"
  },
  {
    "path": "httpbin/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom .core import *\n"
  },
  {
    "path": "httpbin/core.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nhttpbin.core\n~~~~~~~~~~~~\n\nThis module provides the core HttpBin experience.\n\"\"\"\n\nimport base64\nimport json\nimport os\nimport random\nimport time\nimport uuid\nimport argparse\n\nfrom flask import (\n    Flask,\n    Response,\n    request,\n    render_template,\n    redirect,\n    jsonify as flask_jsonify,\n    make_response,\n    url_for,\n    abort,\n)\nfrom six.moves import range as xrange\nfrom werkzeug.datastructures import WWWAuthenticate, MultiDict\nfrom werkzeug.http import http_date\nfrom werkzeug.wrappers import BaseResponse\nfrom werkzeug.http import parse_authorization_header\nfrom flasgger import Swagger, NO_SANITIZER\n\nfrom . import filters\nfrom .helpers import (\n    get_headers,\n    status_code,\n    get_dict,\n    get_request_range,\n    check_basic_auth,\n    check_digest_auth,\n    secure_cookie,\n    H,\n    ROBOT_TXT,\n    ANGRY_ASCII,\n    parse_multi_value_header,\n    next_stale_after_value,\n    digest_challenge_response,\n)\nfrom .utils import weighted_choice\nfrom .structures import CaseInsensitiveDict\n\nwith open(\n    os.path.join(os.path.realpath(os.path.dirname(__file__)), \"VERSION\")\n) as version_file:\n    version = version_file.read().strip()\n\nENV_COOKIES = (\n    \"_gauges_unique\",\n    \"_gauges_unique_year\",\n    \"_gauges_unique_month\",\n    \"_gauges_unique_day\",\n    \"_gauges_unique_hour\",\n    \"__utmz\",\n    \"__utma\",\n    \"__utmb\",\n)\n\n\ndef jsonify(*args, **kwargs):\n    response = flask_jsonify(*args, **kwargs)\n    if not response.data.endswith(b\"\\n\"):\n        response.data += b\"\\n\"\n    return response\n\n\n# Prevent WSGI from correcting the casing of the Location header\nBaseResponse.autocorrect_location_header = False\n\n# Find the correct template folder when running from a different location\ntmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"templates\")\n\napp = Flask(__name__, template_folder=tmpl_dir)\napp.debug = bool(os.environ.get(\"DEBUG\"))\napp.config[\"JSONIFY_PRETTYPRINT_REGULAR\"] = True\n\napp.add_template_global(\"HTTPBIN_TRACKING\" in os.environ, name=\"tracking_enabled\")\n\napp.config[\"SWAGGER\"] = {\"title\": \"httpbin.org\", \"uiversion\": 3}\n\ntemplate = {\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"title\": \"httpbin.org\",\n        \"description\": (\n            \"A simple HTTP Request & Response Service.\"\n            \"<br/> <br/> <b>Run locally: </b> <code>$ docker run -p 80:80 kennethreitz/httpbin</code>\"\n        ),\n        \"contact\": {\n            \"responsibleOrganization\": \"Kenneth Reitz\",\n            \"responsibleDeveloper\": \"Kenneth Reitz\",\n            \"email\": \"me@kennethreitz.org\",\n            \"url\": \"https://kennethreitz.org\",\n        },\n        # \"termsOfService\": \"http://me.com/terms\",\n        \"version\": version,\n    },\n    \"host\": \"httpbin.org\",  # overrides localhost:5000\n    \"basePath\": \"/\",  # base bash for blueprint registration\n    \"schemes\": [\"https\"],\n    \"protocol\": \"https\",\n    \"tags\": [\n        {\n            \"name\": \"HTTP Methods\",\n            \"description\": \"Testing different HTTP verbs\",\n            # 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}\n        },\n        {\"name\": \"Auth\", \"description\": \"Auth methods\"},\n        {\n            \"name\": \"Status codes\",\n            \"description\": \"Generates responses with given status code\",\n        },\n        {\"name\": \"Request inspection\", \"description\": \"Inspect the request data\"},\n        {\n            \"name\": \"Response inspection\",\n            \"description\": \"Inspect the response data like caching and headers\",\n        },\n        {\n            \"name\": \"Response formats\",\n            \"description\": \"Returns responses in different data formats\",\n        },\n        {\"name\": \"Dynamic data\", \"description\": \"Generates random and dynamic data\"},\n        {\"name\": \"Cookies\", \"description\": \"Creates, reads and deletes Cookies\"},\n        {\"name\": \"Images\", \"description\": \"Returns different image formats\"},\n        {\"name\": \"Redirects\", \"description\": \"Returns different redirect responses\"},\n        {\n            \"name\": \"Anything\",\n            \"description\": \"Returns anything that is passed to request\",\n        },\n    ],\n}\n\nswagger_config = {\n    \"headers\": [],\n    \"specs\": [\n        {\n            \"endpoint\": \"spec\",\n            \"route\": \"/spec.json\",\n            \"rule_filter\": lambda rule: True,  # all in\n            \"model_filter\": lambda tag: True,  # all in\n        }\n    ],\n    \"static_url_path\": \"/flasgger_static\",\n    # \"static_folder\": \"static\",  # must be set by user\n    \"swagger_ui\": True,\n    \"specs_route\": \"/\",\n}\n\nswagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)\n\n# Set up Bugsnag exception tracking, if desired. To use Bugsnag, install the\n# Bugsnag Python client with the command \"pip install bugsnag\", and set the\n# environment variable BUGSNAG_API_KEY. You can also optionally set\n# BUGSNAG_RELEASE_STAGE.\nif os.environ.get(\"BUGSNAG_API_KEY\") is not None:\n    try:\n        import bugsnag\n        import bugsnag.flask\n\n        release_stage = os.environ.get(\"BUGSNAG_RELEASE_STAGE\") or \"production\"\n        bugsnag.configure(\n            api_key=os.environ.get(\"BUGSNAG_API_KEY\"),\n            project_root=os.path.dirname(os.path.abspath(__file__)),\n            use_ssl=True,\n            release_stage=release_stage,\n            ignore_classes=[\"werkzeug.exceptions.NotFound\"],\n        )\n        bugsnag.flask.handle_exceptions(app)\n    except:\n        app.logger.warning(\"Unable to initialize Bugsnag exception handling.\")\n\n# -----------\n# Middlewares\n# -----------\n\"\"\"\nhttps://github.com/kennethreitz/httpbin/issues/340\nAdds a middleware to provide chunked request encoding support running under\ngunicorn only.\nWerkzeug required environ 'wsgi.input_terminated' to be set otherwise it\nempties the input request stream.\n- gunicorn seems to support input_terminated but does not add the environ,\n  so we add it here.\n- flask will hang and does not seem to properly terminate the request, so\n  we explicitly deny chunked requests.\n\"\"\"\n\n\n@app.before_request\ndef before_request():\n    if request.environ.get(\"HTTP_TRANSFER_ENCODING\", \"\").lower() == \"chunked\":\n        server = request.environ.get(\"SERVER_SOFTWARE\", \"\")\n        if server.lower().startswith(\"gunicorn/\"):\n            if \"wsgi.input_terminated\" in request.environ:\n                app.logger.debug(\n                    \"environ wsgi.input_terminated already set, keeping: %s\"\n                    % request.environ[\"wsgi.input_terminated\"]\n                )\n            else:\n                request.environ[\"wsgi.input_terminated\"] = 1\n        else:\n            abort(501, \"Chunked requests are not supported for server %s\" % server)\n\n\n@app.after_request\ndef set_cors_headers(response):\n    response.headers[\"Access-Control-Allow-Origin\"] = request.headers.get(\"Origin\", \"*\")\n    response.headers[\"Access-Control-Allow-Credentials\"] = \"true\"\n\n    if request.method == \"OPTIONS\":\n        # Both of these headers are only used for the \"preflight request\"\n        # http://www.w3.org/TR/cors/#access-control-allow-methods-response-header\n        response.headers[\n            \"Access-Control-Allow-Methods\"\n        ] = \"GET, POST, PUT, DELETE, PATCH, OPTIONS\"\n        response.headers[\"Access-Control-Max-Age\"] = \"3600\"  # 1 hour cache\n        if request.headers.get(\"Access-Control-Request-Headers\") is not None:\n            response.headers[\"Access-Control-Allow-Headers\"] = request.headers[\n                \"Access-Control-Request-Headers\"\n            ]\n    return response\n\n\n# ------\n# Routes\n# ------\n\n\n@app.route(\"/legacy\")\ndef view_landing_page():\n    \"\"\"Generates Landing Page in legacy layout.\"\"\"\n    return render_template(\"index.html\")\n\n\n@app.route(\"/html\")\ndef view_html_page():\n    \"\"\"Returns a simple HTML document.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - text/html\n    responses:\n      200:\n        description: An HTML page.\n    \"\"\"\n\n    return render_template(\"moby.html\")\n\n\n@app.route(\"/robots.txt\")\ndef view_robots_page():\n    \"\"\"Returns some robots.txt rules.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - text/plain\n    responses:\n      200:\n        description: Robots file\n    \"\"\"\n\n    response = make_response()\n    response.data = ROBOT_TXT\n    response.content_type = \"text/plain\"\n    return response\n\n\n@app.route(\"/deny\")\ndef view_deny_page():\n    \"\"\"Returns page denied by robots.txt rules.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - text/plain\n    responses:\n      200:\n        description: Denied message\n    \"\"\"\n    response = make_response()\n    response.data = ANGRY_ASCII\n    response.content_type = \"text/plain\"\n    return response\n    # return \"YOU SHOULDN'T BE HERE\"\n\n\n@app.route(\"/ip\")\ndef view_origin():\n    \"\"\"Returns the requester's IP Address.\n    ---\n    tags:\n      - Request inspection\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The Requester's IP Address.\n    \"\"\"\n\n    return jsonify(origin=request.headers.get(\"X-Forwarded-For\", request.remote_addr))\n\n\n@app.route(\"/uuid\")\ndef view_uuid():\n    \"\"\"Return a UUID4.\n    ---\n    tags:\n      - Dynamic data\n    produces:\n      - application/json\n    responses:\n      200:\n        description: A UUID4.\n    \"\"\"\n\n    return jsonify(uuid=str(uuid.uuid4()))\n\n\n@app.route(\"/headers\")\ndef view_headers():\n    \"\"\"Return the incoming request's HTTP headers.\n    ---\n    tags:\n      - Request inspection\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's headers.\n    \"\"\"\n\n    return jsonify(get_dict('headers'))\n\n\n@app.route(\"/user-agent\")\ndef view_user_agent():\n    \"\"\"Return the incoming requests's User-Agent header.\n    ---\n    tags:\n      - Request inspection\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's User-Agent header.\n    \"\"\"\n\n    headers = get_headers()\n\n    return jsonify({\"user-agent\": headers[\"user-agent\"]})\n\n\n@app.route(\"/get\", methods=(\"GET\",))\ndef view_get():\n    \"\"\"The request's query parameters.\n    ---\n    tags:\n      - HTTP Methods\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's query parameters.\n    \"\"\"\n\n    return jsonify(get_dict(\"url\", \"args\", \"headers\", \"origin\"))\n\n\n@app.route(\"/anything\", methods=[\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"TRACE\"])\n@app.route(\n    \"/anything/<path:anything>\",\n    methods=[\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"TRACE\"],\n)\ndef view_anything(anything=None):\n    \"\"\"Returns anything passed in request data.\n    ---\n    tags:\n      - Anything\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Anything passed in request\n    \"\"\"\n\n    return jsonify(\n        get_dict(\n            \"url\",\n            \"args\",\n            \"headers\",\n            \"origin\",\n            \"method\",\n            \"form\",\n            \"data\",\n            \"files\",\n            \"json\",\n        )\n    )\n\n\n@app.route(\"/post\", methods=(\"POST\",))\ndef view_post():\n    \"\"\"The request's POST parameters.\n    ---\n    tags:\n      - HTTP Methods\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's POST parameters.\n    \"\"\"\n\n    return jsonify(\n        get_dict(\"url\", \"args\", \"form\", \"data\", \"origin\", \"headers\", \"files\", \"json\")\n    )\n\n\n@app.route(\"/put\", methods=(\"PUT\",))\ndef view_put():\n    \"\"\"The request's PUT parameters.\n    ---\n    tags:\n      - HTTP Methods\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's PUT parameters.\n    \"\"\"\n\n    return jsonify(\n        get_dict(\"url\", \"args\", \"form\", \"data\", \"origin\", \"headers\", \"files\", \"json\")\n    )\n\n\n@app.route(\"/patch\", methods=(\"PATCH\",))\ndef view_patch():\n    \"\"\"The request's PATCH parameters.\n    ---\n    tags:\n      - HTTP Methods\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's PATCH parameters.\n    \"\"\"\n\n    return jsonify(\n        get_dict(\"url\", \"args\", \"form\", \"data\", \"origin\", \"headers\", \"files\", \"json\")\n    )\n\n\n@app.route(\"/delete\", methods=(\"DELETE\",))\ndef view_delete():\n    \"\"\"The request's DELETE parameters.\n    ---\n    tags:\n      - HTTP Methods\n    produces:\n      - application/json\n    responses:\n      200:\n        description: The request's DELETE parameters.\n    \"\"\"\n\n    return jsonify(\n        get_dict(\"url\", \"args\", \"form\", \"data\", \"origin\", \"headers\", \"files\", \"json\")\n    )\n\n\n@app.route(\"/gzip\")\n@filters.gzip\ndef view_gzip_encoded_content():\n    \"\"\"Returns GZip-encoded data.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - application/json\n    responses:\n      200:\n        description: GZip-encoded data.\n    \"\"\"\n\n    return jsonify(get_dict(\"origin\", \"headers\", method=request.method, gzipped=True))\n\n\n@app.route(\"/deflate\")\n@filters.deflate\ndef view_deflate_encoded_content():\n    \"\"\"Returns Deflate-encoded data.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Defalte-encoded data.\n    \"\"\"\n\n    return jsonify(get_dict(\"origin\", \"headers\", method=request.method, deflated=True))\n\n\n@app.route(\"/brotli\")\n@filters.brotli\ndef view_brotli_encoded_content():\n    \"\"\"Returns Brotli-encoded data.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Brotli-encoded data.\n    \"\"\"\n\n    return jsonify(get_dict(\"origin\", \"headers\", method=request.method, brotli=True))\n\n\n@app.route(\"/redirect/<int:n>\")\ndef redirect_n_times(n):\n    \"\"\"302 Redirects n times.\n    ---\n    tags:\n      - Redirects\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - text/html\n    responses:\n      302:\n        description: A redirection.\n    \"\"\"\n    assert n > 0\n\n    absolute = request.args.get(\"absolute\", \"false\").lower() == \"true\"\n\n    if n == 1:\n        return redirect(url_for(\"view_get\", _external=absolute))\n\n    if absolute:\n        return _redirect(\"absolute\", n, True)\n    else:\n        return _redirect(\"relative\", n, False)\n\n\ndef _redirect(kind, n, external):\n    return redirect(\n        url_for(\"{0}_redirect_n_times\".format(kind), n=n - 1, _external=external)\n    )\n\n\n@app.route(\"/redirect-to\", methods=[\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"TRACE\"])\ndef redirect_to():\n    \"\"\"302/3XX Redirects to the given URL.\n    ---\n    tags:\n      - Redirects\n    produces:\n      - text/html\n    get:\n      parameters:\n        - in: query\n          name: url\n          type: string\n          required: true\n        - in: query\n          name: status_code\n          type: int\n    post:\n      consumes:\n        - application/x-www-form-urlencoded\n      parameters:\n        - in: formData\n          name: url\n          type: string\n          required: true\n        - in: formData\n          name: status_code\n          type: int\n          required: false\n    patch:\n      consumes:\n        - application/x-www-form-urlencoded\n      parameters:\n        - in: formData\n          name: url\n          type: string\n          required: true\n        - in: formData\n          name: status_code\n          type: int\n          required: false\n    put:\n      consumes:\n        - application/x-www-form-urlencoded\n      parameters:\n        - in: formData\n          name: url\n          type: string\n          required: true\n        - in: formData\n          name: status_code\n          type: int\n          required: false\n    responses:\n      302:\n        description: A redirection.\n    \"\"\"\n\n    args_dict = request.args.items()\n    args = CaseInsensitiveDict(args_dict)\n\n    # We need to build the response manually and convert to UTF-8 to prevent\n    # werkzeug from \"fixing\" the URL. This endpoint should set the Location\n    # header to the exact string supplied.\n    response = app.make_response(\"\")\n    response.status_code = 302\n    if \"status_code\" in args:\n        status_code = int(args[\"status_code\"])\n        if status_code >= 300 and status_code < 400:\n            response.status_code = status_code\n    response.headers[\"Location\"] = args[\"url\"].encode(\"utf-8\")\n\n    return response\n\n\n@app.route(\"/relative-redirect/<int:n>\")\ndef relative_redirect_n_times(n):\n    \"\"\"Relatively 302 Redirects n times.\n    ---\n    tags:\n      - Redirects\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - text/html\n    responses:\n      302:\n        description: A redirection.\n    \"\"\"\n\n    assert n > 0\n\n    response = app.make_response(\"\")\n    response.status_code = 302\n\n    if n == 1:\n        response.headers[\"Location\"] = url_for(\"view_get\")\n        return response\n\n    response.headers[\"Location\"] = url_for(\"relative_redirect_n_times\", n=n - 1)\n    return response\n\n\n@app.route(\"/absolute-redirect/<int:n>\")\ndef absolute_redirect_n_times(n):\n    \"\"\"Absolutely 302 Redirects n times.\n    ---\n    tags:\n      - Redirects\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - text/html\n    responses:\n      302:\n        description: A redirection.\n    \"\"\"\n\n    assert n > 0\n\n    if n == 1:\n        return redirect(url_for(\"view_get\", _external=True))\n\n    return _redirect(\"absolute\", n, True)\n\n\n@app.route(\"/stream/<int:n>\")\ndef stream_n_messages(n):\n    \"\"\"Stream n JSON responses\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Streamed JSON responses.\n    \"\"\"\n    response = get_dict(\"url\", \"args\", \"headers\", \"origin\")\n    n = min(n, 100)\n\n    def generate_stream():\n        for i in range(n):\n            response[\"id\"] = i\n            yield json.dumps(response) + \"\\n\"\n\n    return Response(generate_stream(), headers={\"Content-Type\": \"application/json\"})\n\n\n@app.route(\n    \"/status/<codes>\", methods=[\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"TRACE\"]\n)\ndef view_status_code(codes):\n    \"\"\"Return status code or random status code if more than one are given\n    ---\n    tags:\n      - Status codes\n    parameters:\n      - in: path\n        name: codes\n    produces:\n      - text/plain\n    responses:\n      100:\n        description: Informational responses\n      200:\n        description: Success\n      300:\n        description: Redirection\n      400:\n        description: Client Errors\n      500:\n        description: Server Errors\n    \"\"\"\n\n    if \",\" not in codes:\n        try:\n            code = int(codes)\n        except ValueError:\n            return Response(\"Invalid status code\", status=400)\n        return status_code(code)\n\n    choices = []\n    for choice in codes.split(\",\"):\n        if \":\" not in choice:\n            code = choice\n            weight = 1\n        else:\n            code, weight = choice.split(\":\")\n\n        try:\n            choices.append((int(code), float(weight)))\n        except ValueError:\n            return Response(\"Invalid status code\", status=400)\n\n    code = weighted_choice(choices)\n\n    return status_code(code)\n\n\n@app.route(\"/response-headers\", methods=[\"GET\", \"POST\"])\ndef response_headers():\n    \"\"\"Returns a set of response headers from the query string.\n    ---\n    tags:\n      - Response inspection\n    parameters:\n      - in: query\n        name: freeform\n        explode: true\n        allowEmptyValue: true\n        schema:\n          type: object\n          additionalProperties:\n            type: string\n        style: form\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Response headers\n    \"\"\"\n    # Pending swaggerUI update\n    # https://github.com/swagger-api/swagger-ui/issues/3850\n    headers = MultiDict(request.args.items(multi=True))\n    response = jsonify(list(headers.lists()))\n\n    while True:\n        original_data = response.data\n        d = {}\n        for key in response.headers.keys():\n            value = response.headers.get_all(key)\n            if len(value) == 1:\n                value = value[0]\n            d[key] = value\n        response = jsonify(d)\n        for key, value in headers.items(multi=True):\n            response.headers.add(key, value)\n        response_has_changed = response.data != original_data\n        if not response_has_changed:\n            break\n    return response\n\n\n@app.route(\"/cookies\")\ndef view_cookies(hide_env=True):\n    \"\"\"Returns cookie data.\n    ---\n    tags:\n      - Cookies\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Set cookies.\n    \"\"\"\n\n    cookies = dict(request.cookies.items())\n\n    if hide_env and (\"show_env\" not in request.args):\n        for key in ENV_COOKIES:\n            try:\n                del cookies[key]\n            except KeyError:\n                pass\n\n    return jsonify(cookies=cookies)\n\n\n@app.route(\"/forms/post\")\ndef view_forms_post():\n    \"\"\"Simple HTML form.\"\"\"\n\n    return render_template(\"forms-post.html\")\n\n\n@app.route(\"/cookies/set/<name>/<value>\")\ndef set_cookie(name, value):\n    \"\"\"Sets a cookie and redirects to cookie list.\n    ---\n    tags:\n      - Cookies\n    parameters:\n      - in: path\n        name: name\n        type: string\n      - in: path\n        name: value\n        type: string\n    produces:\n      - text/plain\n    responses:\n      200:\n        description: Set cookies and redirects to cookie list.\n    \"\"\"\n\n    r = app.make_response(redirect(url_for(\"view_cookies\")))\n    r.set_cookie(key=name, value=value, secure=secure_cookie())\n\n    return r\n\n\n@app.route(\"/cookies/set\")\ndef set_cookies():\n    \"\"\"Sets cookie(s) as provided by the query string and redirects to cookie list.\n    ---\n    tags:\n      - Cookies\n    parameters:\n      - in: query\n        name: freeform\n        explode: true\n        allowEmptyValue: true\n        schema:\n          type: object\n          additionalProperties:\n            type: string\n        style: form\n    produces:\n      - text/plain\n    responses:\n      200:\n        description: Redirect to cookie list\n    \"\"\"\n\n    cookies = dict(request.args.items())\n    r = app.make_response(redirect(url_for(\"view_cookies\")))\n    for key, value in cookies.items():\n        r.set_cookie(key=key, value=value, secure=secure_cookie())\n\n    return r\n\n\n@app.route(\"/cookies/delete\")\ndef delete_cookies():\n    \"\"\"Deletes cookie(s) as provided by the query string and redirects to cookie list.\n    ---\n    tags:\n      - Cookies\n    parameters:\n      - in: query\n        name: freeform\n        explode: true\n        allowEmptyValue: true\n        schema:\n          type: object\n          additionalProperties:\n            type: string\n        style: form\n    produces:\n      - text/plain\n    responses:\n      200:\n        description: Redirect to cookie list\n    \"\"\"\n\n    cookies = dict(request.args.items())\n    r = app.make_response(redirect(url_for(\"view_cookies\")))\n    for key, value in cookies.items():\n        r.delete_cookie(key=key)\n\n    return r\n\n\n@app.route(\"/basic-auth/<user>/<passwd>\")\ndef basic_auth(user=\"user\", passwd=\"passwd\"):\n    \"\"\"Prompts the user for authorization using HTTP Basic Auth.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: path\n        name: user\n        type: string\n      - in: path\n        name: passwd\n        type: string\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      401:\n        description: Unsuccessful authentication.\n    \"\"\"\n\n    if not check_basic_auth(user, passwd):\n        return status_code(401)\n\n    return jsonify(authenticated=True, user=user)\n\n\n@app.route(\"/hidden-basic-auth/<user>/<passwd>\")\ndef hidden_basic_auth(user=\"user\", passwd=\"passwd\"):\n    \"\"\"Prompts the user for authorization using HTTP Basic Auth.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: path\n        name: user\n        type: string\n      - in: path\n        name: passwd\n        type: string\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      404:\n        description: Unsuccessful authentication.\n    \"\"\"\n\n    if not check_basic_auth(user, passwd):\n        return status_code(404)\n    return jsonify(authenticated=True, user=user)\n\n\n@app.route(\"/bearer\")\ndef bearer_auth():\n    \"\"\"Prompts the user for authorization using bearer authentication.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: header\n        name: Authorization\n        schema:\n          type: string\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      401:\n        description: Unsuccessful authentication.\n    \"\"\"\n    authorization = request.headers.get(\"Authorization\")\n    if not (authorization and authorization.startswith(\"Bearer \")):\n        response = app.make_response(\"\")\n        response.headers[\"WWW-Authenticate\"] = \"Bearer\"\n        response.status_code = 401\n        return response\n    slice_start = len(\"Bearer \")\n    token = authorization[slice_start:]\n\n    return jsonify(authenticated=True, token=token)\n\n\n@app.route(\"/digest-auth/<qop>/<user>/<passwd>\")\ndef digest_auth_md5(qop=None, user=\"user\", passwd=\"passwd\"):\n    \"\"\"Prompts the user for authorization using Digest Auth.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: path\n        name: qop\n        type: string\n        description: auth or auth-int\n      - in: path\n        name: user\n        type: string\n      - in: path\n        name: passwd\n        type: string\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      401:\n        description: Unsuccessful authentication.\n    \"\"\"\n    return digest_auth(qop, user, passwd, \"MD5\", \"never\")\n\n\n@app.route(\"/digest-auth/<qop>/<user>/<passwd>/<algorithm>\")\ndef digest_auth_nostale(qop=None, user=\"user\", passwd=\"passwd\", algorithm=\"MD5\"):\n    \"\"\"Prompts the user for authorization using Digest Auth + Algorithm.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: path\n        name: qop\n        type: string\n        description: auth or auth-int\n      - in: path\n        name: user\n        type: string\n      - in: path\n        name: passwd\n        type: string\n      - in: path\n        name: algorithm\n        type: string\n        description: MD5, SHA-256, SHA-512\n        default: MD5\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      401:\n        description: Unsuccessful authentication.\n    \"\"\"\n    return digest_auth(qop, user, passwd, algorithm, \"never\")\n\n\n@app.route(\"/digest-auth/<qop>/<user>/<passwd>/<algorithm>/<stale_after>\")\ndef digest_auth(\n    qop=None, user=\"user\", passwd=\"passwd\", algorithm=\"MD5\", stale_after=\"never\"\n):\n    \"\"\"Prompts the user for authorization using Digest Auth + Algorithm.\n    allow settings the stale_after argument.\n    ---\n    tags:\n      - Auth\n    parameters:\n      - in: path\n        name: qop\n        type: string\n        description: auth or auth-int\n      - in: path\n        name: user\n        type: string\n      - in: path\n        name: passwd\n        type: string\n      - in: path\n        name: algorithm\n        type: string\n        description: MD5, SHA-256, SHA-512\n        default: MD5\n      - in: path\n        name: stale_after\n        type: string\n        default: never\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Sucessful authentication.\n      401:\n        description: Unsuccessful authentication.\n    \"\"\"\n    require_cookie_handling = request.args.get(\"require-cookie\", \"\").lower() in (\n        \"1\",\n        \"t\",\n        \"true\",\n    )\n    if algorithm not in (\"MD5\", \"SHA-256\", \"SHA-512\"):\n        algorithm = \"MD5\"\n\n    if qop not in (\"auth\", \"auth-int\"):\n        qop = None\n\n    authorization = request.headers.get(\"Authorization\")\n    credentials = None\n    if authorization:\n        credentials = parse_authorization_header(authorization)\n\n    if (\n        not authorization\n        or not credentials\n        or credentials.type.lower() != \"digest\"\n        or (require_cookie_handling and \"Cookie\" not in request.headers)\n    ):\n        response = digest_challenge_response(app, qop, algorithm)\n        response.set_cookie(\"stale_after\", value=stale_after)\n        response.set_cookie(\"fake\", value=\"fake_value\")\n        return response\n\n    if require_cookie_handling and request.cookies.get(\"fake\") != \"fake_value\":\n        response = jsonify({\"errors\": [\"missing cookie set on challenge\"]})\n        response.set_cookie(\"fake\", value=\"fake_value\")\n        response.status_code = 403\n        return response\n\n    current_nonce = credentials.get(\"nonce\")\n\n    stale_after_value = None\n    if \"stale_after\" in request.cookies:\n        stale_after_value = request.cookies.get(\"stale_after\")\n\n    if (\n        \"last_nonce\" in request.cookies\n        and current_nonce == request.cookies.get(\"last_nonce\")\n        or stale_after_value == \"0\"\n    ):\n        response = digest_challenge_response(app, qop, algorithm, True)\n        response.set_cookie(\"stale_after\", value=stale_after)\n        response.set_cookie(\"last_nonce\", value=current_nonce)\n        response.set_cookie(\"fake\", value=\"fake_value\")\n        return response\n\n    if not check_digest_auth(user, passwd):\n        response = digest_challenge_response(app, qop, algorithm, False)\n        response.set_cookie(\"stale_after\", value=stale_after)\n        response.set_cookie(\"last_nonce\", value=current_nonce)\n        response.set_cookie(\"fake\", value=\"fake_value\")\n        return response\n\n    response = jsonify(authenticated=True, user=user)\n    response.set_cookie(\"fake\", value=\"fake_value\")\n    if stale_after_value:\n        response.set_cookie(\n            \"stale_after\", value=next_stale_after_value(stale_after_value)\n        )\n\n    return response\n\n\n@app.route(\"/delay/<delay>\", methods=[\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"TRACE\"])\ndef delay_response(delay):\n    \"\"\"Returns a delayed response (max of 10 seconds).\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: delay\n        type: int\n    produces:\n      - application/json\n    responses:\n      200:\n        description: A delayed response.\n    \"\"\"\n    delay = min(float(delay), 10)\n\n    time.sleep(delay)\n\n    return jsonify(\n        get_dict(\"url\", \"args\", \"form\", \"data\", \"origin\", \"headers\", \"files\")\n    )\n\n\n@app.route(\"/drip\")\ndef drip():\n    \"\"\"Drips data over a duration after an optional initial delay.\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: query\n        name: duration\n        type: number\n        description: The amount of time (in seconds) over which to drip each byte\n        default: 2\n        required: false\n      - in: query\n        name: numbytes\n        type: integer\n        description: The number of bytes to respond with\n        default: 10\n        required: false\n      - in: query\n        name: code\n        type: integer\n        description: The response code that will be returned\n        default: 200\n        required: false\n      - in: query\n        name: delay\n        type: number\n        description: The amount of time (in seconds) to delay before responding\n        default: 2\n        required: false\n    produces:\n      - application/octet-stream\n    responses:\n      200:\n        description: A dripped response.\n    \"\"\"\n    args = CaseInsensitiveDict(request.args.items())\n    duration = float(args.get(\"duration\", 2))\n    numbytes = min(int(args.get(\"numbytes\", 10)), (10 * 1024 * 1024))  # set 10MB limit\n    code = int(args.get(\"code\", 200))\n\n    if numbytes <= 0:\n        response = Response(\"number of bytes must be positive\", status=400)\n        return response\n\n    delay = float(args.get(\"delay\", 0))\n    if delay > 0:\n        time.sleep(delay)\n\n    pause = duration / numbytes\n\n    def generate_bytes():\n        for i in xrange(numbytes):\n            yield b\"*\"\n            time.sleep(pause)\n\n    response = Response(\n        generate_bytes(),\n        headers={\n            \"Content-Type\": \"application/octet-stream\",\n            \"Content-Length\": str(numbytes),\n        },\n    )\n\n    response.status_code = code\n\n    return response\n\n\n@app.route(\"/base64/<value>\")\ndef decode_base64(value):\n    \"\"\"Decodes base64url-encoded string.\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: value\n        type: string\n        default: SFRUUEJJTiBpcyBhd2Vzb21l\n    produces:\n      - text/html\n    responses:\n      200:\n        description: Decoded base64 content.\n    \"\"\"\n    encoded = value.encode(\"utf-8\")  # base64 expects binary string as input\n    try:\n        return base64.urlsafe_b64decode(encoded).decode(\"utf-8\")\n    except:\n        return \"Incorrect Base64 data try: SFRUUEJJTiBpcyBhd2Vzb21l\"\n\n\n@app.route(\"/cache\", methods=(\"GET\",))\ndef cache():\n    \"\"\"Returns a 304 if an If-Modified-Since header or If-None-Match is present. Returns the same as a GET otherwise.\n    ---\n    tags:\n      - Response inspection\n    parameters:\n      - in: header\n        name: If-Modified-Since\n      - in: header\n        name: If-None-Match\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Cached response\n      304:\n        description: Modified\n\n    \"\"\"\n    is_conditional = request.headers.get(\"If-Modified-Since\") or request.headers.get(\n        \"If-None-Match\"\n    )\n\n    if is_conditional is None:\n        response = view_get()\n        response.headers[\"Last-Modified\"] = http_date()\n        response.headers[\"ETag\"] = uuid.uuid4().hex\n        return response\n    else:\n        return status_code(304)\n\n\n@app.route(\"/etag/<etag>\", methods=(\"GET\",))\ndef etag(etag):\n    \"\"\"Assumes the resource has the given etag and responds to If-None-Match and If-Match headers appropriately.\n    ---\n    tags:\n      - Response inspection\n    parameters:\n      - in: header\n        name: If-None-Match\n      - in: header\n        name: If-Match\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Normal response\n      412:\n        description: match\n\n    \"\"\"\n    if_none_match = parse_multi_value_header(request.headers.get(\"If-None-Match\"))\n    if_match = parse_multi_value_header(request.headers.get(\"If-Match\"))\n\n    if if_none_match:\n        if etag in if_none_match or \"*\" in if_none_match:\n            response = status_code(304)\n            response.headers[\"ETag\"] = etag\n            return response\n    elif if_match:\n        if etag not in if_match and \"*\" not in if_match:\n            return status_code(412)\n\n    # Special cases don't apply, return normal response\n    response = view_get()\n    response.headers[\"ETag\"] = etag\n    return response\n\n\n@app.route(\"/cache/<int:value>\")\ndef cache_control(value):\n    \"\"\"Sets a Cache-Control header for n seconds.\n    ---\n    tags:\n      - Response inspection\n    parameters:\n      - in: path\n        name: value\n        type: integer\n    produces:\n      - application/json\n    responses:\n      200:\n        description: Cache control set\n    \"\"\"\n    response = view_get()\n    response.headers[\"Cache-Control\"] = \"public, max-age={0}\".format(value)\n    return response\n\n\n@app.route(\"/encoding/utf8\")\ndef encoding():\n    \"\"\"Returns a UTF-8 encoded body.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - text/html\n    responses:\n      200:\n        description: Encoded UTF-8 content.\n    \"\"\"\n\n    return render_template(\"UTF-8-demo.txt\")\n\n\n@app.route(\"/bytes/<int:n>\")\ndef random_bytes(n):\n    \"\"\"Returns n random bytes generated with given seed\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - application/octet-stream\n    responses:\n      200:\n        description: Bytes.\n    \"\"\"\n\n    n = min(n, 100 * 1024)  # set 100KB limit\n\n    params = CaseInsensitiveDict(request.args.items())\n    if \"seed\" in params:\n        random.seed(int(params[\"seed\"]))\n\n    response = make_response()\n\n    # Note: can't just use os.urandom here because it ignores the seed\n    response.data = bytearray(random.randint(0, 255) for i in range(n))\n    response.content_type = \"application/octet-stream\"\n    return response\n\n\n@app.route(\"/stream-bytes/<int:n>\")\ndef stream_random_bytes(n):\n    \"\"\"Streams n random bytes generated with given seed, at given chunk size per packet.\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: n\n        type: int\n    produces:\n      - application/octet-stream\n    responses:\n      200:\n        description: Bytes.\n    \"\"\"\n    n = min(n, 100 * 1024)  # set 100KB limit\n\n    params = CaseInsensitiveDict(request.args.items())\n    if \"seed\" in params:\n        random.seed(int(params[\"seed\"]))\n\n    if \"chunk_size\" in params:\n        chunk_size = max(1, int(params[\"chunk_size\"]))\n    else:\n        chunk_size = 10 * 1024\n\n    def generate_bytes():\n        chunks = bytearray()\n\n        for i in xrange(n):\n            chunks.append(random.randint(0, 255))\n            if len(chunks) == chunk_size:\n                yield (bytes(chunks))\n                chunks = bytearray()\n\n        if chunks:\n            yield (bytes(chunks))\n\n    headers = {\"Content-Type\": \"application/octet-stream\"}\n\n    return Response(generate_bytes(), headers=headers)\n\n\n@app.route(\"/range/<int:numbytes>\")\ndef range_request(numbytes):\n    \"\"\"Streams n random bytes generated with given seed, at given chunk size per packet.\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: numbytes\n        type: int\n    produces:\n      - application/octet-stream\n    responses:\n      200:\n        description: Bytes.\n    \"\"\"\n\n    if numbytes <= 0 or numbytes > (100 * 1024):\n        response = Response(\n            headers={\"ETag\": \"range%d\" % numbytes, \"Accept-Ranges\": \"bytes\"}\n        )\n        response.status_code = 404\n        response.data = \"number of bytes must be in the range (0, 102400]\"\n        return response\n\n    params = CaseInsensitiveDict(request.args.items())\n    if \"chunk_size\" in params:\n        chunk_size = max(1, int(params[\"chunk_size\"]))\n    else:\n        chunk_size = 10 * 1024\n\n    duration = float(params.get(\"duration\", 0))\n    pause_per_byte = duration / numbytes\n\n    request_headers = get_headers()\n    first_byte_pos, last_byte_pos = get_request_range(request_headers, numbytes)\n    range_length = (last_byte_pos + 1) - first_byte_pos\n\n    if (\n        first_byte_pos > last_byte_pos\n        or first_byte_pos not in xrange(0, numbytes)\n        or last_byte_pos not in xrange(0, numbytes)\n    ):\n        response = Response(\n            headers={\n                \"ETag\": \"range%d\" % numbytes,\n                \"Accept-Ranges\": \"bytes\",\n                \"Content-Range\": \"bytes */%d\" % numbytes,\n                \"Content-Length\": \"0\",\n            }\n        )\n        response.status_code = 416\n        return response\n\n    def generate_bytes():\n        chunks = bytearray()\n\n        for i in xrange(first_byte_pos, last_byte_pos + 1):\n\n            # We don't want the resource to change across requests, so we need\n            # to use a predictable data generation function\n            chunks.append(ord(\"a\") + (i % 26))\n            if len(chunks) == chunk_size:\n                yield (bytes(chunks))\n                time.sleep(pause_per_byte * chunk_size)\n                chunks = bytearray()\n\n        if chunks:\n            time.sleep(pause_per_byte * len(chunks))\n            yield (bytes(chunks))\n\n    content_range = \"bytes %d-%d/%d\" % (first_byte_pos, last_byte_pos, numbytes)\n    response_headers = {\n        \"Content-Type\": \"application/octet-stream\",\n        \"ETag\": \"range%d\" % numbytes,\n        \"Accept-Ranges\": \"bytes\",\n        \"Content-Length\": str(range_length),\n        \"Content-Range\": content_range,\n    }\n\n    response = Response(generate_bytes(), headers=response_headers)\n\n    if (first_byte_pos == 0) and (last_byte_pos == (numbytes - 1)):\n        response.status_code = 200\n    else:\n        response.status_code = 206\n\n    return response\n\n\n@app.route(\"/links/<int:n>/<int:offset>\")\ndef link_page(n, offset):\n    \"\"\"Generate a page containing n links to other pages which do the same.\n    ---\n    tags:\n      - Dynamic data\n    parameters:\n      - in: path\n        name: n\n        type: int\n      - in: path\n        name: offset\n        type: int\n    produces:\n      - text/html\n    responses:\n      200:\n        description: HTML links.\n    \"\"\"\n    n = min(max(1, n), 200)  # limit to between 1 and 200 links\n\n    link = \"<a href='{0}'>{1}</a> \"\n\n    html = [\"<html><head><title>Links</title></head><body>\"]\n    for i in xrange(n):\n        if i == offset:\n            html.append(\"{0} \".format(i))\n        else:\n            html.append(link.format(url_for(\"link_page\", n=n, offset=i), i))\n    html.append(\"</body></html>\")\n\n    return \"\".join(html)\n\n\n@app.route(\"/links/<int:n>\")\ndef links(n):\n    \"\"\"Redirect to first links page.\"\"\"\n    return redirect(url_for(\"link_page\", n=n, offset=0))\n\n\n@app.route(\"/image\")\ndef image():\n    \"\"\"Returns a simple image of the type suggest by the Accept header.\n    ---\n    tags:\n      - Images\n    produces:\n      - image/webp\n      - image/svg+xml\n      - image/jpeg\n      - image/png\n      - image/*\n    responses:\n      200:\n        description: An image.\n    \"\"\"\n\n    headers = get_headers()\n    if \"accept\" not in headers:\n        return image_png()  # Default media type to png\n\n    accept = headers[\"accept\"].lower()\n\n    if \"image/webp\" in accept:\n        return image_webp()\n    elif \"image/svg+xml\" in accept:\n        return image_svg()\n    elif \"image/jpeg\" in accept:\n        return image_jpeg()\n    elif \"image/png\" in accept or \"image/*\" in accept:\n        return image_png()\n    else:\n        return status_code(406)  # Unsupported media type\n\n\n@app.route(\"/image/png\")\ndef image_png():\n    \"\"\"Returns a simple PNG image.\n    ---\n    tags:\n      - Images\n    produces:\n      - image/png\n    responses:\n      200:\n        description: A PNG image.\n    \"\"\"\n    data = resource(\"images/pig_icon.png\")\n    return Response(data, headers={\"Content-Type\": \"image/png\"})\n\n\n@app.route(\"/image/jpeg\")\ndef image_jpeg():\n    \"\"\"Returns a simple JPEG image.\n    ---\n    tags:\n      - Images\n    produces:\n      - image/jpeg\n    responses:\n      200:\n        description: A JPEG image.\n    \"\"\"\n    data = resource(\"images/jackal.jpg\")\n    return Response(data, headers={\"Content-Type\": \"image/jpeg\"})\n\n\n@app.route(\"/image/webp\")\ndef image_webp():\n    \"\"\"Returns a simple WEBP image.\n    ---\n    tags:\n      - Images\n    produces:\n      - image/webp\n    responses:\n      200:\n        description: A WEBP image.\n    \"\"\"\n    data = resource(\"images/wolf_1.webp\")\n    return Response(data, headers={\"Content-Type\": \"image/webp\"})\n\n\n@app.route(\"/image/svg\")\ndef image_svg():\n    \"\"\"Returns a simple SVG image.\n    ---\n    tags:\n      - Images\n    produces:\n      - image/svg+xml\n    responses:\n      200:\n        description: An SVG image.\n    \"\"\"\n    data = resource(\"images/svg_logo.svg\")\n    return Response(data, headers={\"Content-Type\": \"image/svg+xml\"})\n\n\ndef resource(filename):\n    path = os.path.join(tmpl_dir, filename)\n    with open(path, \"rb\") as f:\n      return f.read()\n\n\n@app.route(\"/xml\")\ndef xml():\n    \"\"\"Returns a simple XML document.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - application/xml\n    responses:\n      200:\n        description: An XML document.\n    \"\"\"\n    response = make_response(render_template(\"sample.xml\"))\n    response.headers[\"Content-Type\"] = \"application/xml\"\n    return response\n\n\n@app.route(\"/json\")\ndef a_json_endpoint():\n    \"\"\"Returns a simple JSON document.\n    ---\n    tags:\n      - Response formats\n    produces:\n      - application/json\n    responses:\n      200:\n        description: An JSON document.\n    \"\"\"\n    return flask_jsonify(\n        slideshow={\n            \"title\": \"Sample Slide Show\",\n            \"date\": \"date of publication\",\n            \"author\": \"Yours Truly\",\n            \"slides\": [\n                {\"type\": \"all\", \"title\": \"Wake up to WonderWidgets!\"},\n                {\n                    \"type\": \"all\",\n                    \"title\": \"Overview\",\n                    \"items\": [\n                        \"Why <em>WonderWidgets</em> are great\",\n                        \"Who <em>buys</em> WonderWidgets\",\n                    ],\n                },\n            ],\n        }\n    )\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--port\", type=int, default=5000)\n    parser.add_argument(\"--host\", default=\"127.0.0.1\")\n    args = parser.parse_args()\n    app.run(port=args.port, host=args.host)\n"
  },
  {
    "path": "httpbin/filters.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nhttpbin.filters\n~~~~~~~~~~~~~~~\n\nThis module provides response filter decorators.\n\"\"\"\n\nimport gzip as gzip2\nimport zlib\n\nimport brotli as _brotli\n\nfrom six import BytesIO\nfrom decimal import Decimal\nfrom time import time as now\n\nfrom decorator import decorator\nfrom flask import Flask, Response\n\n\napp = Flask(__name__)\n\n\n@decorator\ndef x_runtime(f, *args, **kwargs):\n    \"\"\"X-Runtime Flask Response Decorator.\"\"\"\n\n    _t0 = now()\n    r = f(*args, **kwargs)\n    _t1 = now()\n    r.headers['X-Runtime'] = '{0}s'.format(Decimal(str(_t1 - _t0)))\n\n    return r\n\n\n@decorator\ndef gzip(f, *args, **kwargs):\n    \"\"\"GZip Flask Response Decorator.\"\"\"\n\n    data = f(*args, **kwargs)\n\n    if isinstance(data, Response):\n        content = data.data\n    else:\n        content = data\n\n    gzip_buffer = BytesIO()\n    gzip_file = gzip2.GzipFile(\n        mode='wb',\n        compresslevel=4,\n        fileobj=gzip_buffer\n    )\n    gzip_file.write(content)\n    gzip_file.close()\n\n    gzip_data = gzip_buffer.getvalue()\n\n    if isinstance(data, Response):\n        data.data = gzip_data\n        data.headers['Content-Encoding'] = 'gzip'\n        data.headers['Content-Length'] = str(len(data.data))\n\n        return data\n\n    return gzip_data\n\n\n@decorator\ndef deflate(f, *args, **kwargs):\n    \"\"\"Deflate Flask Response Decorator.\"\"\"\n\n    data = f(*args, **kwargs)\n\n    if isinstance(data, Response):\n        content = data.data\n    else:\n        content = data\n\n    deflater = zlib.compressobj()\n    deflated_data = deflater.compress(content)\n    deflated_data += deflater.flush()\n\n    if isinstance(data, Response):\n        data.data = deflated_data\n        data.headers['Content-Encoding'] = 'deflate'\n        data.headers['Content-Length'] = str(len(data.data))\n\n        return data\n\n    return deflated_data\n\n\n@decorator\ndef brotli(f, *args, **kwargs):\n    \"\"\"Brotli Flask Response Decorator\"\"\"\n\n    data = f(*args, **kwargs)\n\n    if isinstance(data, Response):\n        content = data.data\n    else:\n        content = data\n\n    deflated_data = _brotli.compress(content)\n\n    if isinstance(data, Response):\n        data.data = deflated_data\n        data.headers['Content-Encoding'] = 'br'\n        data.headers['Content-Length'] = str(len(data.data))\n\n        return data\n\n    return deflated_data\n"
  },
  {
    "path": "httpbin/helpers.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nhttpbin.helpers\n~~~~~~~~~~~~~~~\n\nThis module provides helper functions for httpbin.\n\"\"\"\n\nimport json\nimport base64\nimport re\nimport time\nimport os\nfrom hashlib import md5, sha256, sha512\nfrom werkzeug.http import parse_authorization_header\nfrom werkzeug.datastructures import WWWAuthenticate\n\nfrom flask import request, make_response\nfrom six.moves.urllib.parse import urlparse, urlunparse\n\n\nfrom .structures import CaseInsensitiveDict\n\n\nASCII_ART = \"\"\"\n    -=[ teapot ]=-\n\n       _...._\n     .'  _ _ `.\n    | .\"` ^ `\". _,\n    \\_;`\"---\"`|//\n      |       ;/\n      \\_     _/\n        `\\\"\\\"\\\"`\n\"\"\"\n\nREDIRECT_LOCATION = '/redirect/1'\n\nENV_HEADERS = (\n    'X-Varnish',\n    'X-Request-Start',\n    'X-Heroku-Queue-Depth',\n    'X-Real-Ip',\n    'X-Forwarded-Proto',\n    'X-Forwarded-Protocol',\n    'X-Forwarded-Ssl',\n    'X-Heroku-Queue-Wait-Time',\n    'X-Forwarded-For',\n    'X-Heroku-Dynos-In-Use',\n    'X-Forwarded-Protocol',\n    'X-Forwarded-Port',\n    'X-Request-Id',\n    'Via',\n    'Total-Route-Time',\n    'Connect-Time'\n)\n\nROBOT_TXT = \"\"\"User-agent: *\nDisallow: /deny\n\"\"\"\n\nACCEPTED_MEDIA_TYPES = [\n    'image/webp',\n    'image/svg+xml',\n    'image/jpeg',\n    'image/png',\n    'image/*'\n]\n\nANGRY_ASCII =\"\"\"\n          .-''''''-.\n        .' _      _ '.\n       /   O      O   \\\\\n      :                :\n      |                |\n      :       __       :\n       \\  .-\"`  `\"-.  /\n        '.          .'\n          '-......-'\n     YOU SHOULDN'T BE HERE\n\"\"\"\n\n\ndef json_safe(string, content_type='application/octet-stream'):\n    \"\"\"Returns JSON-safe version of `string`.\n\n    If `string` is a Unicode string or a valid UTF-8, it is returned unmodified,\n    as it can safely be encoded to JSON string.\n\n    If `string` contains raw/binary data, it is Base64-encoded, formatted and\n    returned according to \"data\" URL scheme (RFC2397). Since JSON is not\n    suitable for binary data, some additional encoding was necessary; \"data\"\n    URL scheme was chosen for its simplicity.\n    \"\"\"\n    try:\n        string = string.decode('utf-8')\n        json.dumps(string)\n        return string\n    except (ValueError, TypeError):\n        return b''.join([\n            b'data:',\n            content_type.encode('utf-8'),\n            b';base64,',\n            base64.b64encode(string)\n        ]).decode('utf-8')\n\n\ndef get_files():\n    \"\"\"Returns files dict from request context.\"\"\"\n\n    files = dict()\n\n    for k, v in request.files.items():\n        content_type = request.files[k].content_type or 'application/octet-stream'\n        val = json_safe(v.read(), content_type)\n        if files.get(k):\n            if not isinstance(files[k], list):\n                files[k] = [files[k]]\n            files[k].append(val)\n        else:\n            files[k] = val\n\n    return files\n\n\ndef get_headers(hide_env=True):\n    \"\"\"Returns headers dict from request context.\"\"\"\n\n    headers = dict(request.headers.items())\n\n    if hide_env and ('show_env' not in request.args):\n        for key in ENV_HEADERS:\n            try:\n                del headers[key]\n            except KeyError:\n                pass\n\n    return CaseInsensitiveDict(headers.items())\n\n\ndef semiflatten(multi):\n    \"\"\"Convert a MutiDict into a regular dict. If there are more than one value\n    for a key, the result will have a list of values for the key. Otherwise it\n    will have the plain value.\"\"\"\n    if multi:\n        result = multi.to_dict(flat=False)\n        for k, v in result.items():\n            if len(v) == 1:\n                result[k] = v[0]\n        return result\n    else:\n        return multi\n\ndef get_url(request):\n    \"\"\"\n    Since we might be hosted behind a proxy, we need to check the\n    X-Forwarded-Proto, X-Forwarded-Protocol, or X-Forwarded-SSL headers\n    to find out what protocol was used to access us.\n    \"\"\"\n    protocol = request.headers.get('X-Forwarded-Proto') or request.headers.get('X-Forwarded-Protocol')\n    if protocol is None and request.headers.get('X-Forwarded-Ssl') == 'on':\n        protocol = 'https'\n    if protocol is None:\n        return request.url\n    url = list(urlparse(request.url))\n    url[0] = protocol\n    return urlunparse(url)\n\n\ndef get_dict(*keys, **extras):\n    \"\"\"Returns request dict of given keys.\"\"\"\n\n    _keys = ('url', 'args', 'form', 'data', 'origin', 'headers', 'files', 'json', 'method')\n\n    assert all(map(_keys.__contains__, keys))\n    data = request.data\n    form = semiflatten(request.form)\n\n    try:\n        _json = json.loads(data.decode('utf-8'))\n    except (ValueError, TypeError):\n        _json = None\n\n    d = dict(\n        url=get_url(request),\n        args=semiflatten(request.args),\n        form=form,\n        data=json_safe(data),\n        origin=request.headers.get('X-Forwarded-For', request.remote_addr),\n        headers=get_headers(),\n        files=get_files(),\n        json=_json,\n        method=request.method,\n    )\n\n    out_d = dict()\n\n    for key in keys:\n        out_d[key] = d.get(key)\n\n    out_d.update(extras)\n\n    return out_d\n\n\ndef status_code(code):\n    \"\"\"Returns response object of given status code.\"\"\"\n\n    redirect = dict(headers=dict(location=REDIRECT_LOCATION))\n\n    code_map = {\n        301: redirect,\n        302: redirect,\n        303: redirect,\n        304: dict(data=''),\n        305: redirect,\n        307: redirect,\n        401: dict(headers={'WWW-Authenticate': 'Basic realm=\"Fake Realm\"'}),\n        402: dict(\n            data='Fuck you, pay me!',\n            headers={\n                'x-more-info': 'http://vimeo.com/22053820'\n            }\n        ),\n        406: dict(data=json.dumps({\n                'message': 'Client did not request a supported media type.',\n                'accept': ACCEPTED_MEDIA_TYPES\n            }),\n            headers={\n                'Content-Type': 'application/json'\n            }),\n        407: dict(headers={'Proxy-Authenticate': 'Basic realm=\"Fake Realm\"'}),\n        418: dict(  # I'm a teapot!\n            data=ASCII_ART,\n            headers={\n                'x-more-info': 'http://tools.ietf.org/html/rfc2324'\n            }\n        ),\n\n    }\n\n    r = make_response()\n    r.status_code = code\n\n    if code in code_map:\n\n        m = code_map[code]\n\n        if 'data' in m:\n            r.data = m['data']\n        if 'headers' in m:\n            r.headers = m['headers']\n\n    return r\n\n\ndef check_basic_auth(user, passwd):\n    \"\"\"Checks user authentication using HTTP Basic Auth.\"\"\"\n\n    auth = request.authorization\n    return auth and auth.username == user and auth.password == passwd\n\n\n\n# Digest auth helpers\n# qop is a quality of protection\n\ndef H(data, algorithm):\n    if algorithm == 'SHA-256':\n        return sha256(data).hexdigest()\n    elif algorithm == 'SHA-512':\n        return sha512(data).hexdigest()\n    else:\n        return md5(data).hexdigest()\n\n\ndef HA1(realm, username, password, algorithm):\n    \"\"\"Create HA1 hash by realm, username, password\n\n    HA1 = md5(A1) = MD5(username:realm:password)\n    \"\"\"\n    if not realm:\n        realm = u''\n    return H(b\":\".join([username.encode('utf-8'),\n                           realm.encode('utf-8'),\n                           password.encode('utf-8')]), algorithm)\n\n\ndef HA2(credentials, request, algorithm):\n    \"\"\"Create HA2 md5 hash\n\n    If the qop directive's value is \"auth\" or is unspecified, then HA2:\n        HA2 = md5(A2) = MD5(method:digestURI)\n    If the qop directive's value is \"auth-int\" , then HA2 is\n        HA2 = md5(A2) = MD5(method:digestURI:MD5(entityBody))\n    \"\"\"\n    if credentials.get(\"qop\") == \"auth\" or credentials.get('qop') is None:\n        return H(b\":\".join([request['method'].encode('utf-8'), request['uri'].encode('utf-8')]), algorithm)\n    elif credentials.get(\"qop\") == \"auth-int\":\n        for k in 'method', 'uri', 'body':\n            if k not in request:\n                raise ValueError(\"%s required\" % k)\n        A2 = b\":\".join([request['method'].encode('utf-8'),\n                        request['uri'].encode('utf-8'),\n                        H(request['body'], algorithm).encode('utf-8')])\n        return H(A2, algorithm)\n    raise ValueError\n\n\ndef response(credentials, password, request):\n    \"\"\"Compile digest auth response\n\n    If the qop directive's value is \"auth\" or \"auth-int\" , then compute the response as follows:\n       RESPONSE = MD5(HA1:nonce:nonceCount:clienNonce:qop:HA2)\n    Else if the qop directive is unspecified, then compute the response as follows:\n       RESPONSE = MD5(HA1:nonce:HA2)\n\n    Arguments:\n    - `credentials`: credentials dict\n    - `password`: request user password\n    - `request`: request dict\n    \"\"\"\n    response = None\n    algorithm = credentials.get('algorithm')\n    HA1_value = HA1(\n        credentials.get('realm'),\n        credentials.get('username'),\n        password,\n        algorithm\n    )\n    HA2_value = HA2(credentials, request, algorithm)\n    if credentials.get('qop') is None:\n        response = H(b\":\".join([\n            HA1_value.encode('utf-8'),\n            credentials.get('nonce', '').encode('utf-8'),\n            HA2_value.encode('utf-8')\n        ]), algorithm)\n    elif credentials.get('qop') == 'auth' or credentials.get('qop') == 'auth-int':\n        for k in 'nonce', 'nc', 'cnonce', 'qop':\n            if k not in credentials:\n                raise ValueError(\"%s required for response H\" % k)\n        response = H(b\":\".join([HA1_value.encode('utf-8'),\n                               credentials.get('nonce').encode('utf-8'),\n                               credentials.get('nc').encode('utf-8'),\n                               credentials.get('cnonce').encode('utf-8'),\n                               credentials.get('qop').encode('utf-8'),\n                               HA2_value.encode('utf-8')]), algorithm)\n    else:\n        raise ValueError(\"qop value are wrong\")\n\n    return response\n\n\ndef check_digest_auth(user, passwd):\n    \"\"\"Check user authentication using HTTP Digest auth\"\"\"\n\n    if request.headers.get('Authorization'):\n        credentials = parse_authorization_header(request.headers.get('Authorization'))\n        if not credentials:\n            return\n        request_uri = request.script_root + request.path\n        if request.query_string:\n            request_uri +=  '?' + request.query_string\n        response_hash = response(credentials, passwd, dict(uri=request_uri,\n                                                           body=request.data,\n                                                           method=request.method))\n        if credentials.get('response') == response_hash:\n            return True\n    return False\n\ndef secure_cookie():\n    \"\"\"Return true if cookie should have secure attribute\"\"\"\n    return request.environ['wsgi.url_scheme'] == 'https'\n\ndef __parse_request_range(range_header_text):\n    \"\"\" Return a tuple describing the byte range requested in a GET request\n    If the range is open ended on the left or right side, then a value of None\n    will be set.\n    RFC7233: http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7233.html#header.range\n    Examples:\n      Range : bytes=1024-\n      Range : bytes=10-20\n      Range : bytes=-999\n    \"\"\"\n\n    left = None\n    right = None\n\n    if not range_header_text:\n        return left, right\n\n    range_header_text = range_header_text.strip()\n    if not range_header_text.startswith('bytes'):\n        return left, right\n\n    components = range_header_text.split(\"=\")\n    if len(components) != 2:\n        return left, right\n\n    components = components[1].split(\"-\")\n\n    try:\n        right = int(components[1])\n    except:\n        pass\n\n    try:\n        left = int(components[0])\n    except:\n        pass\n\n    return left, right\n\ndef get_request_range(request_headers, upper_bound):\n    first_byte_pos, last_byte_pos = __parse_request_range(request_headers['range'])\n\n    if first_byte_pos is None and last_byte_pos is None:\n        # Request full range\n        first_byte_pos = 0\n        last_byte_pos = upper_bound - 1\n    elif first_byte_pos is None:\n        # Request the last X bytes\n        first_byte_pos = max(0, upper_bound - last_byte_pos)\n        last_byte_pos = upper_bound - 1\n    elif last_byte_pos is None:\n        # Request the last X bytes\n        last_byte_pos = upper_bound - 1\n\n    return first_byte_pos, last_byte_pos\n\ndef parse_multi_value_header(header_str):\n    \"\"\"Break apart an HTTP header string that is potentially a quoted, comma separated list as used in entity headers in RFC2616.\"\"\"\n    parsed_parts = []\n    if header_str:\n        parts = header_str.split(',')\n        for part in parts:\n            match = re.search('\\s*(W/)?\\\"?([^\"]*)\\\"?\\s*', part)\n            if match is not None:\n                parsed_parts.append(match.group(2))\n    return parsed_parts\n\n\ndef next_stale_after_value(stale_after):\n    try:\n        stal_after_count = int(stale_after) - 1\n        return str(stal_after_count)\n    except ValueError:\n        return 'never'\n\n\ndef digest_challenge_response(app, qop, algorithm, stale = False):\n    response = app.make_response('')\n    response.status_code = 401\n\n    # RFC2616 Section4.2: HTTP headers are ASCII.  That means\n    # request.remote_addr was originally ASCII, so I should be able to\n    # encode it back to ascii.  Also, RFC2617 says about nonces: \"The\n    # contents of the nonce are implementation dependent\"\n    nonce = H(b''.join([\n        getattr(request, 'remote_addr', u'').encode('ascii'),\n        b':',\n        str(time.time()).encode('ascii'),\n        b':',\n        os.urandom(10)\n    ]), algorithm)\n    opaque = H(os.urandom(10), algorithm)\n\n    auth = WWWAuthenticate(\"digest\")\n    auth.set_digest('me@kennethreitz.com', nonce, opaque=opaque,\n                    qop=('auth', 'auth-int') if qop is None else (qop,), algorithm=algorithm)\n    auth.stale = stale\n    response.headers['WWW-Authenticate'] = auth.to_header()\n    return response\n"
  },
  {
    "path": "httpbin/structures.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nhttpbin.structures\n~~~~~~~~~~~~~~~~~~~\n\nData structures that power httpbin.\n\"\"\"\n\n\nclass CaseInsensitiveDict(dict):\n    \"\"\"Case-insensitive Dictionary for headers.\n\n    For example, ``headers['content-encoding']`` will return the\n    value of a ``'Content-Encoding'`` response header.\n    \"\"\"\n\n    def _lower_keys(self):\n        return [k.lower() for k in self.keys()]\n\n    def __contains__(self, key):\n        return key.lower() in self._lower_keys()\n\n    def __getitem__(self, key):\n        # We allow fall-through here, so values default to None\n        if key in self:\n            return list(self.items())[self._lower_keys().index(key.lower())][1]\n"
  },
  {
    "path": "httpbin/templates/UTF-8-demo.txt",
    "content": "<h1>Unicode Demo</h1>\n\n<p>Taken from <a\nhref=\"http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt\">http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt</a></p>\n\n<pre>\n\nUTF-8 encoded sample plain-text file\n‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n\nMarkus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25\n\n\nThe ASCII compatible UTF-8 encoding used in this plain-text file\nis defined in Unicode, ISO 10646-1, and RFC 2279.\n\n\nUsing Unicode/UTF-8, you can write in emails and source code things such as\n\nMathematics and sciences:\n\n  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫\n                                            ⎪⎢⎜│a²+b³ ⎟⎥⎪\n  ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪\n                                            ⎪⎢⎜⎷ c₈   ⎟⎥⎪\n  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬\n                                            ⎪⎢⎜ ∞     ⎟⎥⎪\n  ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪\n                                            ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪\n  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭\n\nLinguistics and dictionaries:\n\n  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn\n  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]\n\nAPL:\n\n  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈\n\nNicer typography in plain text files:\n\n  ╔══════════════════════════════════════════╗\n  ║                                          ║\n  ║   • ‘single’ and “double” quotes         ║\n  ║                                          ║\n  ║   • Curly apostrophes: “We’ve been here” ║\n  ║                                          ║\n  ║   • Latin-1 apostrophe and accents: '´`  ║\n  ║                                          ║\n  ║   • ‚deutsche‘ „Anführungszeichen“       ║\n  ║                                          ║\n  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║\n  ║                                          ║\n  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║\n  ║                      ╭─────────╮         ║\n  ║   • the euro symbol: │ 14.95 € │         ║\n  ║                      ╰─────────╯         ║\n  ╚══════════════════════════════════════════╝\n\nCombining characters:\n\n  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑\n\nGreek (in Polytonic):\n\n  The Greek anthem:\n\n  Σὲ γνωρίζω ἀπὸ τὴν κόψη\n  τοῦ σπαθιοῦ τὴν τρομερή,\n  σὲ γνωρίζω ἀπὸ τὴν ὄψη\n  ποὺ μὲ βία μετράει τὴ γῆ.\n\n  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη\n  τῶν ῾Ελλήνων τὰ ἱερά\n  καὶ σὰν πρῶτα ἀνδρειωμένη\n  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!\n\n  From a speech of Demosthenes in the 4th century BC:\n\n  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,\n  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς\n  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ\n  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿\n  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ\n  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν\n  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,\n  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν\n  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον\n  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι\n  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν\n  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους\n  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ\n  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ\n  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς\n  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.\n\n  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς\n\nGeorgian:\n\n  From a Unicode conference invitation:\n\n  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო\n  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,\n  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს\n  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,\n  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება\n  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,\n  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.\n\nRussian:\n\n  From a Unicode conference invitation:\n\n  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по\n  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.\n  Конференция соберет широкий круг экспертов по  вопросам глобального\n  Интернета и Unicode, локализации и интернационализации, воплощению и\n  применению Unicode в различных операционных системах и программных\n  приложениях, шрифтах, верстке и многоязычных компьютерных системах.\n\nThai (UCS Level 2):\n\n  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese\n  classic 'San Gua'):\n\n  [----------------------------|------------------------]\n    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่\n  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา\n    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา\n  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ\n    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ\n  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ\n    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้\n  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ\n\n  (The above is a two-column text. If combining characters are handled\n  correctly, the lines of the second column should be aligned with the\n  | character above.)\n\nEthiopian:\n\n  Proverbs in the Amharic language:\n\n  ሰማይ አይታረስ ንጉሥ አይከሰስ።\n  ብላ ካለኝ እንደአባቴ በቆመጠኝ።\n  ጌጥ ያለቤቱ ቁምጥና ነው።\n  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።\n  የአፍ ወለምታ በቅቤ አይታሽም።\n  አይጥ በበላ ዳዋ ተመታ።\n  ሲተረጉሙ ይደረግሙ።\n  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።\n  ድር ቢያብር አንበሳ ያስር።\n  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።\n  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።\n  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።\n  ሥራ ከመፍታት ልጄን ላፋታት።\n  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።\n  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።\n  ተንጋሎ ቢተፉ ተመልሶ ባፉ።\n  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።\n  እግርህን በፍራሽህ ልክ ዘርጋ።\n\nRunes:\n\n  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ\n\n  (Old English, which transcribed into Latin reads 'He cwaeth that he\n  bude thaem lande northweardum with tha Westsae.' and means 'He said\n  that he lived in the northern land near the Western Sea.')\n\nBraille:\n\n  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌\n\n  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞\n  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎\n  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂\n  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙\n  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑\n  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲\n\n  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\n\n  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹\n  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞\n  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕\n  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹\n  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎\n  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎\n  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳\n  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞\n  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\n\n  (The first couple of paragraphs of \"A Christmas Carol\" by Dickens)\n\nCompact font selection example text:\n\n  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\n  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\n  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\n  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ ﬁ�⑀₂ἠḂӥẄɐː⍎אԱა\n\nGreetings in various languages:\n\n  Hello world, Καλημέρα κόσμε, コンニチハ\n\nBox drawing alignment tests:                                          █\n                                                                      ▉\n  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳\n  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳\n  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳\n  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\n  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎\n  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏\n  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█\n                                               ▝▀▘▙▄▟\n\n</pre>\n"
  },
  {
    "path": "httpbin/templates/flasgger/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <title>{{ title }}</title>\n    <link href=\"https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700\"\n        rel=\"stylesheet\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{url_for('flasgger.static', filename='swagger-ui.css')}}\">\n    <link rel=\"icon\" type=\"image/png\" href=\"{{url_for('static', filename='favicon.ico')}}\" sizes=\"64x64 32x32 16x16\" />\n    <style>\n        html {\n            box-sizing: border-box;\n            overflow: -moz-scrollbars-vertical;\n            overflow-y: scroll;\n        }\n\n        *,\n        *:before,\n        *:after {\n            box-sizing: inherit;\n        }\n\n        body {\n            margin: 0;\n            background: #fafafa;\n        }\n    </style>\n</head>\n\n<body>\n    <a href=\"https://github.com/requests/httpbin\" class=\"github-corner\" aria-label=\"View source on Github\">\n        <svg width=\"80\" height=\"80\" viewBox=\"0 0 250 250\" style=\"fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;\"\n            aria-hidden=\"true\">\n            <path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path>\n            <path d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\"\n                fill=\"currentColor\" style=\"transform-origin: 130px 106px;\" class=\"octo-arm\"></path>\n            <path d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\"\n                fill=\"currentColor\" class=\"octo-body\"></path>\n        </svg>\n    </a>\n    <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"position:absolute;width:0;height:0\">\n        <defs>\n            <symbol viewBox=\"0 0 20 20\" id=\"unlocked\">\n                <path d=\"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z\"></path>\n            </symbol>\n\n            <symbol viewBox=\"0 0 20 20\" id=\"locked\">\n                <path d=\"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z\"\n                />\n            </symbol>\n\n            <symbol viewBox=\"0 0 20 20\" id=\"close\">\n                <path d=\"M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z\"\n                />\n            </symbol>\n\n            <symbol viewBox=\"0 0 20 20\" id=\"large-arrow\">\n                <path d=\"M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z\"\n                />\n            </symbol>\n\n            <symbol viewBox=\"0 0 20 20\" id=\"large-arrow-down\">\n                <path d=\"M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z\"\n                />\n            </symbol>\n\n\n            <symbol viewBox=\"0 0 24 24\" id=\"jump-to\">\n                <path d=\"M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z\" />\n            </symbol>\n\n            <symbol viewBox=\"0 0 24 24\" id=\"expand\">\n                <path d=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\" />\n            </symbol>\n\n        </defs>\n    </svg>\n\n\n    <div id=\"swagger-ui\">\n        <div data-reactroot=\"\" class=\"swagger-ui\">\n            <div>\n                <div class=\"information-container wrapper\">\n                    <section class=\"block col-12\">\n                        <div class=\"info\">\n                            <hgroup class=\"main\">\n                                <h2 class=\"title\">httpbin.org\n                                    <small>\n                                        <pre class=\"version\">0.9.2</pre>\n                                    </small>\n                                </h2>\n                                <pre class=\"base-url\">[ Base URL: httpbin.org/ ]</pre>\n                            </hgroup>\n                            <div class=\"description\">\n                                <div class=\"markdown\">\n                                    <p>A simple HTTP Request &amp; Response Service.\n                                        <br>\n                                        <br>\n                                        <b>Run locally: </b>\n                                        <code>$ docker run -p 80:80 kennethreitz/httpbin</code>\n                                    </p>\n                                </div>\n                            </div>\n                            <div>\n                                <div>\n                                    <a href=\"https://kennethreitz.org\" target=\"_blank\">the developer - Website</a>\n                                </div>\n                                <a href=\"mailto:me@kennethreitz.org\">Send email to the developer</a>\n                            </div>\n                        </div>\n                        <!-- ADDS THE LOADER SPINNER -->\n                        <div class=\"loading-container\">\n                            <div class=\"loading\"></div>\n                        </div>\n\n                    </section>\n                </div>\n            </div>\n        </div>\n    </div>\n\n\n    <div class='swagger-ui'>\n        <div class=\"wrapper\">\n            <section class=\"clear\">\n                <span style=\"float: right;\">\n                    [Powered by\n                    <a target=\"_blank\" href=\"https://github.com/rochacbruno/flasgger\">Flasgger</a>]\n                    <br>\n                </span>\n            </section>\n        </div>\n    </div>\n\n\n\n    <script src=\"{{url_for('flasgger.static', filename='swagger-ui-bundle.js')}}\"> </script>\n    <script src=\"{{url_for('flasgger.static', filename='swagger-ui-standalone-preset.js')}}\"> </script>\n    <script src='{{url_for('flasgger.static', filename='')}}lib/jquery.min.js' type='text/javascript'></script>\n    <script>\n\n        window.onload = function () {\n            {% if config.JWT_AUTH_URL_RULE %}\n            // JWT token holder\n            var jwt_token;\n            {% endif %}\n\n            fetch(\"{{ specs[0]['url'] }}\")\n                .then(function (response) {\n                    response.json()\n                        .then(function (json) {\n                            var current_protocol = window.location.protocol.slice(0, -1);\n                            if (json.schemes[0] != current_protocol) {\n                                // Switches scheme to the current in use\n                                var other_protocol = json.schemes[0];\n                                json.schemes[0] = current_protocol;\n                                json.schemes[1] = other_protocol;\n\n                            }\n                            json.host = window.location.host;  // sets the current host\n\n                            const ui = SwaggerUIBundle({\n                                spec: json,\n                                validatorUrl: null,\n                                dom_id: '#swagger-ui',\n                                deepLinking: true,\n                                jsonEditor: true,\n                                docExpansion: \"none\",\n                                apisSorter: \"alpha\",\n                                //operationsSorter: \"alpha\",\n                                presets: [\n                                    SwaggerUIBundle.presets.apis,\n                                    // yay ES6 modules ↘\n                                    Array.isArray(SwaggerUIStandalonePreset) ? SwaggerUIStandalonePreset : SwaggerUIStandalonePreset.default\n                                ],\n                                plugins: [\n                                    SwaggerUIBundle.plugins.DownloadUrl\n                                ],\n            {% if config.JWT_AUTH_URL_RULE %}\n            requestInterceptor: function (request) {\n                                if (jwt_token) {\n                                    request.headers.Authorization = \"Bearer \" + jwt_token;\n                                }\n\n                                return request;\n                            },\n                            responseInterceptor: function (response) {\n                                var tokenField = 'jwt-token';\n                                var headers = response.headers;\n\n                                if (headers.hasOwnProperty(tokenField)) {\n                                    jwt_token = headers[tokenField];\n                                }\n\n                                return response;\n                            },\n            {% endif %}\n            // layout: \"StandaloneLayout\"  // uncomment to enable the green top header\n        })\n\n        window.ui = ui\n\n        // uncomment to rename the top brand if layout is enabled\n        // $(\".topbar-wrapper .link span\").replaceWith(\"<span>httpbin</span>\");\n        })\n    })\n}\n    </script> {% if tracking_enabled %} {% include 'trackingscripts.html' %} {% endif %} {% include 'footer.html' %}\n</body>\n\n</html>\n"
  },
  {
    "path": "httpbin/templates/footer.html",
    "content": "<div class='swagger-ui'>\n    <div class=\"wrapper\">\n        <section class=\"block col-12 block-desktop col-12-desktop\">\n            <div>\n\n                <h2>Other Utilities</h2>\n\n                <ul>\n                    <li>\n                        <a href=\"{{url_for('view_forms_post')}}\">HTML form</a> that posts to /post /forms/post</li>\n                </ul>\n\n                <br />\n                <br />\n            </div>\n        </section>\n    </div>\n</div>\n"
  },
  {
    "path": "httpbin/templates/forms-post.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n  </head>\n  <body>\n  <!-- Example form from HTML5 spec http://www.w3.org/TR/html5/forms.html#writing-a-form's-user-interface -->\n  <form method=\"post\" action=\"{{ url_for('view_post') }}\">\n   <p><label>Customer name: <input name=\"custname\"></label></p>\n   <p><label>Telephone: <input type=tel name=\"custtel\"></label></p>\n   <p><label>E-mail address: <input type=email name=\"custemail\"></label></p>\n   <fieldset>\n    <legend> Pizza Size </legend>\n    <p><label> <input type=radio name=size value=\"small\"> Small </label></p>\n    <p><label> <input type=radio name=size value=\"medium\"> Medium </label></p>\n    <p><label> <input type=radio name=size value=\"large\"> Large </label></p>\n   </fieldset>\n   <fieldset>\n    <legend> Pizza Toppings </legend>\n    <p><label> <input type=checkbox name=\"topping\" value=\"bacon\"> Bacon </label></p>\n    <p><label> <input type=checkbox name=\"topping\" value=\"cheese\"> Extra Cheese </label></p>\n    <p><label> <input type=checkbox name=\"topping\" value=\"onion\"> Onion </label></p>\n    <p><label> <input type=checkbox name=\"topping\" value=\"mushroom\"> Mushroom </label></p>\n   </fieldset>\n   <p><label>Preferred delivery time: <input type=time min=\"11:00\" max=\"21:00\" step=\"900\" name=\"delivery\"></label></p>\n   <p><label>Delivery instructions: <textarea name=\"comments\"></textarea></label></p>\n   <p><button>Submit order</button></p>\n  </form>\n  </body>\n</html>\n"
  },
  {
    "path": "httpbin/templates/httpbin.1.html",
    "content": "<div class='mp'>\n<h1>httpbin(1): HTTP Request &amp; Response Service</h1>\n<p>Freely hosted in <a href=\"http://httpbin.org\">HTTP</a>, <a href=\"https://httpbin.org\">HTTPS</a>, &amp; <a href=\"http://eu.httpbin.org/\">EU</a> flavors by <a href=\"http://kennethreitz.org/bitcoin\">Kenneth Reitz</a> &amp; <a href=\"https://www.heroku.com/python\">Heroku</a>.</p>\n\n<h2 id=\"BONUSPOINTS\">BONUSPOINTS</h2>\n\n<ul>\n<li><a href=\"https://now.httpbin.org/\" data-bare-link=\"true\"><code>now.httpbin.org</code></a> The current time, in a variety of formats.</li>\n</ul>\n\n<h2 id=\"ENDPOINTS\">ENDPOINTS</h2>\n\n<ul>\n<li><a href=\"{{ url_for('view_landing_page') }}\" data-bare-link=\"true\"><code>/</code></a> This page.</li>\n<li><a href=\"{{ url_for('view_origin') }}\" data-bare-link=\"true\"><code>/ip</code></a> Returns Origin IP.</li>\n<li><a href=\"{{ url_for('view_uuid') }}\" data-bare-link=\"true\"><code>/uuid</code></a> Returns UUID4.</li>\n<li><a href=\"{{ url_for('view_user_agent') }}\" data-bare-link=\"true\"><code>/user-agent</code></a> Returns user-agent.</li>\n<li><a href=\"{{ url_for('view_headers') }}\" data-bare-link=\"true\"><code>/headers</code></a> Returns header dict.</li>\n<li><a href=\"{{ url_for('view_get') }}\" data-bare-link=\"true\"><code>/get</code></a> Returns GET data.</li>\n<li><code>/post</code> Returns POST data.</li>\n<li><code>/patch</code> Returns PATCH data.</li>\n<li><code>/put</code> Returns PUT data.</li>\n<li><code>/delete</code> Returns DELETE data</li>\n<li><a href=\"{{ url_for('view_anything') }}\" data-bare-link=\"true\"><code>/anything</code></a> Returns request data, including method used.</li>\n<li><code>/anything/:anything</code> Returns request data, including the URL.</li>\n<li><a href=\"{{ url_for('decode_base64', value='aGVsbG8gd29ybGQNCg==') }}\"><code>/base64/:value</code></a> Decodes base64url-encoded string.</li>\n<li><a href=\"{{ url_for('encoding') }}\"><code>/encoding/utf8</code></a> Returns page containing UTF-8 data.</li>\n<li><a href=\"{{ url_for('view_gzip_encoded_content') }}\" data-bare-link=\"true\"><code>/gzip</code></a> Returns gzip-encoded data.</li>\n<li><a href=\"{{ url_for('view_deflate_encoded_content') }}\" data-bare-link=\"true\"><code>/deflate</code></a> Returns deflate-encoded data.</li>\n<li><a href=\"{{ url_for('view_brotli_encoded_content') }}\" data-bare-link=\"true\"><code>/brotli</code></a> Returns brotli-encoded data.</li>\n<li><a href=\"{{ url_for('view_status_code', codes='418') }}\"><code>/status/:code</code></a> Returns given HTTP Status code.</li>\n<li><a href=\"{{ url_for('response_headers', **{'Content-Type': 'text/plain; charset=UTF-8', 'Server': 'httpbin'}) }}\"><code>/response-headers?key=val</code></a> Returns given response headers.</li>\n<li><a href=\"{{ url_for('redirect_n_times', n=6) }}\"><code>/redirect/:n</code></a> 302 Redirects <em>n</em> times.</li>\n<li><a href=\"{{ url_for('redirect_to', url='http://example.com/') }}\"><code>/redirect-to?url=foo</code></a> 302 Redirects to the <em>foo</em> URL.</li>\n<li><a href=\"{{ url_for('redirect_to', url='http://example.com/', status_code=307) }}\"><code>/redirect-to?url=foo&status_code=307</code></a> 307 Redirects to the <em>foo</em> URL.</li>\n<li><a href=\"{{ url_for('relative_redirect_n_times', n=6) }}\"><code>/relative-redirect/:n</code></a> 302 Relative redirects <em>n</em> times.</li>\n<li><a href=\"{{ url_for('absolute_redirect_n_times', n=6) }}\"><code>/absolute-redirect/:n</code></a> 302 Absolute redirects <em>n</em> times.</li>\n<li><a href=\"{{ url_for('view_cookies') }}\" data-bare-link=\"true\"><code>/cookies</code></a> Returns cookie data.</li>\n<li><a href=\"{{ url_for('set_cookies', k1='v1', k2='v2') }}\"><code>/cookies/set?name=value</code></a> Sets one or more simple cookies.</li>\n<li><a href=\"{{ url_for('delete_cookies', k1='', k2='') }}\"><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>\n<li><a href=\"{{ url_for('basic_auth', user='user', passwd='passwd') }}\"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>\n<li><a href=\"{{ url_for('hidden_basic_auth', user='user', passwd='passwd') }}\"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>\n<li><a href=\"{{ url_for('digest_auth', qop='auth', user='user', passwd='passwd', algorithm='MD5', stale_after='never') }}\"><code>/digest-auth/:qop/:user/:passwd/:algorithm</code></a> Challenges HTTP Digest Auth.</li>\n<li><a href=\"{{ url_for('digest_auth', qop='auth', user='user', passwd='passwd', algorithm='MD5', stale_after='never') }}\"><code>/digest-auth/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>\n<li><a href=\"{{ url_for('stream_n_messages', n=20) }}\"><code>/stream/:n</code></a> Streams <em>min(n, 100)</em> lines.</li>\n<li><a href=\"{{ url_for('delay_response', delay=3) }}\"><code>/delay/:n</code></a> Delays responding for <em>min(n, 10)</em> seconds.</li>\n<li><a href=\"{{ url_for('drip', numbytes=5, duration=5, code=200) }}\"><code>/drip?numbytes=n&amp;duration=s&amp;delay=s&amp;code=code</code></a> Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code.</li>\n<li><a href=\"{{ url_for('range_request', numbytes=1024) }}\"><code>/range/1024?duration=s&amp;chunk_size=code</code></a> Streams <em>n</em> bytes, and allows specifying a <em>Range</em> header to select a subset of the data. Accepts a <em>chunk_size</em> and request <em>duration</em> parameter.</li>\n<li><a href=\"{{ url_for('view_html_page') }}\" data-bare-link=\"true\"><code>/html</code></a> Renders an HTML Page.</li>\n<li><a href=\"{{ url_for('view_robots_page') }}\" data-bare-link=\"true\"><code>/robots.txt</code></a> Returns some robots.txt rules.</li>\n<li><a href=\"{{ url_for('view_deny_page') }}\" data-bare-link=\"true\"><code>/deny</code></a> Denied by robots.txt file.</li>\n<li><a href=\"{{ url_for('cache') }}\" data-bare-link=\"true\"><code>/cache</code></a> Returns 200 unless an If-Modified-Since or If-None-Match header is provided, when it returns a 304.</li>\n<li><a href=\"{{ url_for('etag', etag='etag') }}\"><code>/etag/:etag</code></a> Assumes the resource has the given etag and responds to If-None-Match header with a 200 or 304 and If-Match with a 200 or 412 as appropriate.</li>\n<li><a href=\"{{ url_for('cache_control', value=60) }}\"><code>/cache/:n</code></a> Sets a Cache-Control header for <em>n</em> seconds.</li>\n<li><a href=\"{{ url_for('random_bytes', n=1024) }}\"><code>/bytes/:n</code></a> Generates <em>n</em> random bytes of binary data, accepts optional <em>seed</em> integer parameter.</li>\n<li><a href=\"{{ url_for('stream_random_bytes', n=1024) }}\"><code>/stream-bytes/:n</code></a> Streams <em>n</em> random bytes of binary data in chunked encoding, accepts optional <em>seed</em> and <em>chunk_size</em> integer parameters.</li>\n<li><a href=\"{{ url_for('links', n=10) }}\"><code>/links/:n</code></a> Returns page containing <em>n</em> HTML links.</li>\n<li><a href=\"{{ url_for('image') }}\"><code>/image</code></a> Returns page containing an image based on sent Accept header.</li>\n<li><a href=\"{{ url_for('image_png') }}\"><code>/image/png</code></a> Returns a PNG image.</li>\n<li><a href=\"{{ url_for('image_jpeg') }}\"><code>/image/jpeg</code></a> Returns a JPEG image.</li>\n<li><a href=\"{{ url_for('image_webp') }}\"><code>/image/webp</code></a> Returns a WEBP image.</li>\n<li><a href=\"{{ url_for('image_svg') }}\"><code>/image/svg</code></a> Returns a SVG image.</li>\n<li><a href=\"{{ url_for('view_forms_post') }}\" data-bare-link=\"true\"><code>/forms/post</code></a> HTML form that submits to <em>/post</em></li>\n<li><a href=\"{{ url_for('xml') }}\" data-bare-link=\"true\"><code>/xml</code></a> Returns some XML</li>\n</ul>\n\n<h2 id=\"DESCRIPTION\">DESCRIPTION</h2>\n\n<p>Testing an HTTP Library can become difficult sometimes. <a href=\"http://requestb.in\">RequestBin</a> is fantastic for testing POST requests, but doesn't let you control the response. This exists to cover all kinds of HTTP scenarios. Additional endpoints are being considered.</p>\n\n<p>All endpoint responses are JSON-encoded.</p>\n\n<h2 id=\"EXAMPLES\">EXAMPLES</h2>\n\n<h3 id=\"-curl-http-httpbin-org-ip\">$ curl http://httpbin.org/ip</h3>\n\n<pre><code>{\"origin\": \"24.127.96.129\"}\n</code></pre>\n\n<h3 id=\"-curl-http-httpbin-org-user-agent\">$ curl http://httpbin.org/user-agent</h3>\n\n<pre><code>{\"user-agent\": \"curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3\"}\n</code></pre>\n\n<h3 id=\"-curl-http-httpbin-org-get\">$ curl http://httpbin.org/get</h3>\n\n<pre><code>{\n   \"args\": {},\n   \"headers\": {\n      \"Accept\": \"*/*\",\n      \"Connection\": \"close\",\n      \"Content-Length\": \"\",\n      \"Content-Type\": \"\",\n      \"Host\": \"httpbin.org\",\n      \"User-Agent\": \"curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3\"\n   },\n   \"origin\": \"24.127.96.129\",\n   \"url\": \"http://httpbin.org/get\"\n}\n</code></pre>\n\n<h3 id=\"-curl-I-http-httpbin-org-status-418\">$ curl -I http://httpbin.org/status/418</h3>\n\n<pre><code>HTTP/1.1 418 I'M A TEAPOT\nServer: nginx/0.7.67\nDate: Mon, 13 Jun 2011 04:25:38 GMT\nConnection: close\nx-more-info: http://tools.ietf.org/html/rfc2324\nContent-Length: 135\n</code></pre>\n\n<h3 id=\"-curl-https-httpbin-org-get-show_env-1\">$ curl https://httpbin.org/get?show_env=1</h3>\n\n<pre><code>{\n  \"headers\": {\n    \"Content-Length\": \"\",\n    \"Accept-Language\": \"en-US,en;q=0.8\",\n    \"Accept-Encoding\": \"gzip,deflate,sdch\",\n    \"X-Forwarded-Port\": \"443\",\n    \"X-Forwarded-For\": \"109.60.101.240\",\n    \"Host\": \"httpbin.org\",\n    \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n    \"User-Agent\": \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11\",\n    \"X-Request-Start\": \"1350053933441\",\n    \"Accept-Charset\": \"ISO-8859-1,utf-8;q=0.7,*;q=0.3\",\n    \"Connection\": \"keep-alive\",\n    \"X-Forwarded-Proto\": \"https\",\n    \"Cookie\": \"_gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1; _gauges_unique_hour=1\",\n    \"Content-Type\": \"\"\n  },\n  \"args\": {\n    \"show_env\": \"1\"\n  },\n  \"origin\": \"109.60.101.240\",\n  \"url\": \"http://httpbin.org/get?show_env=1\"\n}\n</code></pre>\n\n<h2 id=\"Installing-and-running-from-PyPI\">Installing and running from PyPI</h2>\n\n<p>You can install httpbin as a library from PyPI and run it as a WSGI app.  For example, using Gunicorn:</p>\n\n<pre><code class=\"bash\">$ pip install httpbin\n$ gunicorn httpbin:app\n</code></pre>\n\n\n<h2 id=\"AUTHOR\">AUTHOR</h2>\n\n<p>A <a href=\"http://kennethreitz.com/\">Kenneth Reitz</a> project.</p>\n<p>BTC: <a href=\"https://www.kennethreitz.org/bitcoin\"><code>1Me2iXTJ91FYZhrGvaGaRDCBtnZ4KdxCug</code></a></p>\n\n<h2 id=\"SEE-ALSO\">SEE ALSO</h2>\n\n<p><a href=\"https://www.hurl.it\">Hurl.it</a> - Make HTTP requests.</p>\n<p><a href=\"http://requestb.in\">RequestBin</a> - Inspect HTTP requests.</p>\n<p><a href=\"http://python-requests.org\" data-bare-link=\"true\">http://python-requests.org</a></p>\n\n</div>\n"
  },
  {
    "path": "httpbin/templates/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta http-equiv='content-type' value='text/html;charset=utf8'>\n  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>\n  <title>httpbin(1): HTTP Client Testing Service</title>\n  <style type='text/css' media='all'>\n    /* style: man */\n\n    body#manpage {\n      margin: 0;\n    }\n\n    .mp {\n      max-width: 100ex;\n      padding: 0 9ex 1ex 4ex;\n    }\n\n    .mp p,\n    .mp pre,\n    .mp ul,\n    .mp ol,\n    .mp dl {\n      margin: 0 0 20px 0;\n    }\n\n    .mp h2 {\n      margin: 10px 0 0 0;\n    }\n\n    .mp>p,\n    .mp>pre,\n    .mp>ul,\n    .mp>ol,\n    .mp>dl {\n      margin-left: 8ex;\n    }\n\n    .mp h3 {\n      margin: 0 0 0 4ex;\n    }\n\n    .mp dt {\n      margin: 0;\n      clear: left;\n    }\n\n    .mp dt.flush {\n      float: left;\n      width: 8ex;\n    }\n\n    .mp dd {\n      margin: 0 0 0 9ex;\n    }\n\n    .mp h1,\n    .mp h2,\n    .mp h3,\n    .mp h4 {\n      clear: left;\n    }\n\n    .mp pre {\n      margin-bottom: 20px;\n    }\n\n    .mp pre+h2,\n    .mp pre+h3 {\n      margin-top: 22px;\n    }\n\n    .mp h2+pre,\n    .mp h3+pre {\n      margin-top: 5px;\n    }\n\n    .mp img {\n      display: block;\n      margin: auto;\n    }\n\n    .mp h1.man-title {\n      display: none;\n    }\n\n    .mp,\n    .mp code,\n    .mp pre,\n    .mp tt,\n    .mp kbd,\n    .mp samp,\n    .mp h3,\n    .mp h4 {\n      font-family: monospace;\n      font-size: 14px;\n      line-height: 1.42857142857143;\n    }\n\n    .mp h2 {\n      font-size: 16px;\n      line-height: 1.25;\n    }\n\n    .mp h1 {\n      font-size: 20px;\n      line-height: 2;\n    }\n\n    .mp {\n      text-align: justify;\n      background: #fff;\n    }\n\n    .mp,\n    .mp code,\n    .mp pre,\n    .mp pre code,\n    .mp tt,\n    .mp kbd,\n    .mp samp {\n      color: #131211;\n    }\n\n    .mp h1,\n    .mp h2,\n    .mp h3,\n    .mp h4 {\n      color: #030201;\n    }\n\n    .mp u {\n      text-decoration: underline;\n    }\n\n    .mp code,\n    .mp strong,\n    .mp b {\n      font-weight: bold;\n      color: #131211;\n    }\n\n    .mp em,\n    .mp var {\n      font-style: italic;\n      color: #232221;\n      text-decoration: none;\n    }\n\n    .mp a,\n    .mp a:link,\n    .mp a:hover,\n    .mp a code,\n    .mp a pre,\n    .mp a tt,\n    .mp a kbd,\n    .mp a samp {\n      color: #0000ff;\n    }\n\n    .mp b.man-ref {\n      font-weight: normal;\n      color: #434241;\n    }\n\n    .mp pre {\n      padding: 0 4ex;\n    }\n\n    .mp pre code {\n      font-weight: normal;\n      color: #434241;\n    }\n\n    .mp h2+pre,\n    h3+pre {\n      padding-left: 0;\n    }\n\n    ol.man-decor,\n    ol.man-decor li {\n      margin: 3px 0 10px 0;\n      padding: 0;\n      float: left;\n      width: 33%;\n      list-style-type: none;\n      text-transform: uppercase;\n      color: #999;\n      letter-spacing: 1px;\n    }\n\n    ol.man-decor {\n      width: 100%;\n    }\n\n    ol.man-decor li.tl {\n      text-align: left;\n    }\n\n    ol.man-decor li.tc {\n      text-align: center;\n      letter-spacing: 4px;\n    }\n\n    ol.man-decor li.tr {\n      text-align: right;\n      float: right;\n    }\n  </style>\n  <style type='text/css' media='all'>\n    /* style: 80c */\n\n    .mp {\n      max-width: 86ex\n    }\n\n    ul {\n      list-style: None;\n      margin-left: 1em !important\n    }\n\n    .man-navigation {\n      left: 101ex\n    }\n\n    .scheme-container {\n      display: none !important;\n    }\n  </style>\n</head>\n\n<body id='manpage'>\n  <a href=\"https://github.com/requests/httpbin\" class=\"github-corner\" aria-label=\"View source on Github\">\n    <svg width=\"80\" height=\"80\" viewBox=\"0 0 250 250\" style=\"fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;\"\n      aria-hidden=\"true\">\n      <path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path>\n      <path d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\"\n        fill=\"currentColor\" style=\"transform-origin: 130px 106px;\" class=\"octo-arm\"></path>\n      <path d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\"\n        fill=\"currentColor\" class=\"octo-body\"></path>\n    </svg>\n  </a>\n\n\n\n  {% include 'httpbin.1.html' %} {% if tracking_enabled %} {% include 'trackingscripts.html' %} {% endif %}\n\n</body>\n\n</html>\n"
  },
  {
    "path": "httpbin/templates/moby.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n  </head>\n  <body>\n      <h1>Herman Melville - Moby-Dick</h1>\n\n      <div>\n        <p>\n          Availing himself of the mild, summer-cool weather that now reigned in these latitudes, and in preparation for the peculiarly active pursuits shortly to be anticipated, Perth, the begrimed, blistered old blacksmith, had not removed his portable forge to the hold again, after concluding his contributory work for Ahab's leg, but still retained it on deck, fast lashed to ringbolts by the foremast; being now almost incessantly invoked by the headsmen, and harpooneers, and bowsmen to do some little job for them; altering, or repairing, or new shaping their various weapons and boat furniture. Often he would be surrounded by an eager circle, all waiting to be served; holding boat-spades, pike-heads, harpoons, and lances, and jealously watching his every sooty movement, as he toiled. Nevertheless, this old man's was a patient hammer wielded by a patient arm. No murmur, no impatience, no petulance did come from him. Silent, slow, and solemn; bowing over still further his chronically broken back, he toiled away, as if toil were life itself, and the heavy beating of his hammer the heavy beating of his heart. And so it was.—Most miserable! A peculiar walk in this old man, a certain slight but painful appearing yawing in his gait, had at an early period of the voyage excited the curiosity of the mariners. And to the importunity of their persisted questionings he had finally given in; and so it came to pass that every one now knew the shameful story of his wretched fate. Belated, and not innocently, one bitter winter's midnight, on the road running between two country towns, the blacksmith half-stupidly felt the deadly numbness stealing over him, and sought refuge in a leaning, dilapidated barn. The issue was, the loss of the extremities of both feet. Out of this revelation, part by part, at last came out the four acts of the gladness, and the one long, and as yet uncatastrophied fifth act of the grief of his life's drama. He was an old man, who, at the age of nearly sixty, had postponedly encountered that thing in sorrow's technicals called ruin. He had been an artisan of famed excellence, and with plenty to do; owned a house and garden; embraced a youthful, daughter-like, loving wife, and three blithe, ruddy children; every Sunday went to a cheerful-looking church, planted in a grove. But one night, under cover of darkness, and further concealed in a most cunning disguisement, a desperate burglar slid into his happy home, and robbed them all of everything. And darker yet to tell, the blacksmith himself did ignorantly conduct this burglar into his family's heart. It was the Bottle Conjuror! Upon the opening of that fatal cork, forth flew the fiend, and shrivelled up his home. Now, for prudent, most wise, and economic reasons, the blacksmith's shop was in the basement of his dwelling, but with a separate entrance to it; so that always had the young and loving healthy wife listened with no unhappy nervousness, but with vigorous pleasure, to the stout ringing of her young-armed old husband's hammer; whose reverberations, muffled by passing through the floors and walls, came up to her, not unsweetly, in her nursery; and so, to stout Labor's iron lullaby, the blacksmith's infants were rocked to slumber. Oh, woe on woe! Oh, Death, why canst thou not sometimes be timely? Hadst thou taken this old blacksmith to thyself ere his full ruin came upon him, then had the young widow had a delicious grief, and her orphans a truly venerable, legendary sire to dream of in their after years; and all of them a care-killing competency.\n        </p>\n      </div>\n  </body>\n</html>\n"
  },
  {
    "path": "httpbin/templates/sample.xml",
    "content": "<?xml version='1.0' encoding='us-ascii'?>\n\n<!--  A SAMPLE set of slides  -->\n\n<slideshow \n    title=\"Sample Slide Show\"\n    date=\"Date of publication\"\n    author=\"Yours Truly\"\n    >\n\n    <!-- TITLE SLIDE -->\n    <slide type=\"all\">\n      <title>Wake up to WonderWidgets!</title>\n    </slide>\n\n    <!-- OVERVIEW -->\n    <slide type=\"all\">\n        <title>Overview</title>\n        <item>Why <em>WonderWidgets</em> are great</item>\n        <item/>\n        <item>Who <em>buys</em> WonderWidgets</item>\n    </slide>\n\n</slideshow>\n"
  },
  {
    "path": "httpbin/templates/trackingscripts.html",
    "content": "{#\n\tplace tracking scripts (like Google Analytics) here\n#}\n\n<script type=\"text/javascript\">\n  var _gauges = _gauges || [];\n  (function() {\n    var t   = document.createElement('script');\n    t.type  = 'text/javascript';\n    t.async = true;\n    t.id    = 'gauges-tracker';\n    t.setAttribute('data-site-id', '58cb2e71c88d9043ac01d000');\n    t.setAttribute('data-track-path', 'https://track.gaug.es/track.gif');\n    t.src = 'https://d36ee2fcip1434.cloudfront.net/track.js';\n    var s = document.getElementsByTagName('script')[0];\n    s.parentNode.insertBefore(t, s);\n  })();\n</script>"
  },
  {
    "path": "httpbin/utils.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nhttpbin.utils\n~~~~~~~~~~~~~~~\n\nUtility functions.\n\"\"\"\n\nimport random\nimport bisect\n\n\ndef weighted_choice(choices):\n    \"\"\"Returns a value from choices chosen by weighted random selection\n\n    choices should be a list of (value, weight) tuples.\n\n    eg. weighted_choice([('val1', 5), ('val2', 0.3), ('val3', 1)])\n\n    \"\"\"\n    values, weights = zip(*choices)\n    total = 0\n    cum_weights = []\n    for w in weights:\n        total += w\n        cum_weights.append(total)\n    x = random.uniform(0, total)\n    i = bisect.bisect(cum_weights, x)\n    return values[i]\n"
  },
  {
    "path": "now.json",
    "content": "{\n    \"name\": \"httpbin\",\n    \"regions\": [\n        \"all\"\n    ],\n    \"alias\": [\n        \"httpbin.org\"\n    ],\n    \"type\": \"docker\"\n}\n"
  },
  {
    "path": "runtime.txt",
    "content": "python-3.6.5"
  },
  {
    "path": "setup.cfg",
    "content": "[bdist_wheel]\nuniversal = 1\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup, find_packages\nimport os\nimport io\n\n\nwith open(os.path.join(os.path.realpath(os.path.dirname(__file__)), 'httpbin', 'VERSION')) as version_file:\n    version = version_file.read().strip()\n\nsetup(\n    name=\"httpbin\",\n    version=version,\n    description=\"HTTP Request and Response Service\",\n    long_description=\"A simple HTTP Request & Response Service, written in Python + Flask.\",\n\n    # The project URL.\n    url='https://github.com/requests/httpbin',\n\n    # Author details\n    author='Kenneth Reitz',\n    author_email='me@kennethreitz.org',\n\n    # Choose your license\n    license='MIT',\n\n    classifiers=[\n         'Development Status :: 5 - Production/Stable',\n         'Intended Audience :: Developers',\n         'Natural Language :: English',\n         'License :: OSI Approved :: MIT License',\n         'Programming Language :: Python',\n         'Programming Language :: Python :: 2.7',\n         'Programming Language :: Python :: 3.6',\n    ],\n    test_suite=\"test_httpbin\",\n    packages=find_packages(),\n    include_package_data = True, # include files listed in MANIFEST.in\n    install_requires=[\n        'Flask', 'MarkupSafe', 'decorator', 'itsdangerous', 'six', 'brotlipy',\n        'raven[flask]', 'werkzeug>=0.14.1', 'gevent', 'flasgger'\n    ],\n)\n"
  },
  {
    "path": "test_httpbin.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nimport os\nimport base64\nimport unittest\nimport contextlib\nimport six\nimport json\nfrom werkzeug.http import parse_dict_header\nfrom hashlib import md5, sha256, sha512\nfrom six import BytesIO\n\nimport httpbin\nfrom httpbin.helpers import parse_multi_value_header\n\n\n@contextlib.contextmanager\ndef _setenv(key, value):\n    \"\"\"Context manager to set an environment variable temporarily.\"\"\"\n    old_value = os.environ.get(key, None)\n    if value is None:\n        os.environ.pop(key, None)\n    else:\n        os.environ[key] = value\n\n    yield\n\n    if old_value is None:\n        os.environ.pop(key, None)\n    else:\n        os.environ[key] = value\n\n\n\ndef _string_to_base64(string):\n    \"\"\"Encodes string to utf-8 and then base64\"\"\"\n    utf8_encoded = string.encode('utf-8')\n    return base64.urlsafe_b64encode(utf8_encoded)\n\ndef _hash(data, algorithm):\n    \"\"\"Encode binary data according to specified algorithm, use MD5 by default\"\"\"\n    if algorithm == 'SHA-256':\n        return sha256(data).hexdigest()\n    elif algorithm == 'SHA-512':\n        return sha512(data).hexdigest()\n    else:\n        return md5(data).hexdigest()\n\ndef _make_digest_auth_header(username, password, method, uri, nonce,\n                             realm=None, opaque=None, algorithm=None,\n                             qop=None, cnonce=None, nc=None, body=None):\n    \"\"\"Compile a digest authentication header string.\n\n    Arguments:\n    - `nonce`: nonce string, received within \"WWW-Authenticate\" header\n    - `realm`: realm string, received within \"WWW-Authenticate\" header\n    - `opaque`: opaque string, received within \"WWW-Authenticate\" header\n    - `algorithm`: type of hashing algorithm, used by the client\n    - `qop`: type of quality-of-protection, used by the client\n    - `cnonce`: client nonce, required if qop is \"auth\" or \"auth-int\"\n    - `nc`: client nonce count, required if qop is \"auth\" or \"auth-int\"\n    - `body`: body of the outgoing request (bytes), used if qop is \"auth-int\"\n    \"\"\"\n\n    assert username\n    assert password\n    assert nonce\n    assert method\n    assert uri\n    assert algorithm in ('MD5', 'SHA-256', 'SHA-512', None)\n\n    a1 = ':'.join([username, realm or '', password])\n    ha1 = _hash(a1.encode('utf-8'), algorithm)\n\n    a2 = ':'.join([method, uri])\n    if qop == 'auth-int':\n        a2 = ':'.join([a2, _hash(body or b'', algorithm)])\n    ha2 = _hash(a2.encode('utf-8'), algorithm)\n\n    a3 = ':'.join([ha1, nonce])\n    if qop in ('auth', 'auth-int'):\n        assert cnonce\n        assert nc\n        a3 = ':'.join([a3, nc, cnonce, qop])\n\n    a3 = ':'.join([a3, ha2])\n    auth_response = _hash(a3.encode('utf-8'), algorithm)\n\n    auth_header = \\\n        'Digest username=\"{0}\", response=\"{1}\", uri=\"{2}\", nonce=\"{3}\"'\\\n            .format(username, auth_response, uri, nonce)\n\n    # 'realm' and 'opaque' should be returned unchanged, even if empty\n    if realm != None:\n        auth_header += ', realm=\"{0}\"'.format(realm)\n    if opaque != None:\n        auth_header += ', opaque=\"{0}\"'.format(opaque)\n\n    if algorithm:\n        auth_header += ', algorithm=\"{0}\"'.format(algorithm)\n    if cnonce:\n        auth_header += ', cnonce=\"{0}\"'.format(cnonce)\n    if nc:\n        auth_header += ', nc={0}'.format(nc)\n    if qop:\n        auth_header += ', qop={0}'.format(qop)\n\n    return auth_header\n\nclass HttpbinTestCase(unittest.TestCase):\n    \"\"\"Httpbin tests\"\"\"\n\n    def setUp(self):\n        httpbin.app.debug = True\n        self.app = httpbin.app.test_client()\n\n    def test_index(self):   \n        response = self.app.get('/', headers={'User-Agent': 'test'})\n        self.assertEqual(response.status_code, 200)\n \n    def get_data(self, response):\n        if 'get_data' in dir(response):\n            return response.get_data()\n        else:\n            return response.data\n\n    def test_response_headers_simple(self):\n        supported_verbs = ['get', 'post']\n        for verb in supported_verbs:\n            method = getattr(self.app, verb)\n            response = method('/response-headers?animal=dog')\n            self.assertEqual(response.status_code, 200)\n            self.assertEqual(response.headers.get_all('animal'), ['dog'])\n            assert json.loads(response.data.decode('utf-8'))['animal'] == 'dog'\n\n    def test_response_headers_multi(self):\n        supported_verbs = ['get', 'post']\n        for verb in supported_verbs:\n            method = getattr(self.app, verb)\n            response = method('/response-headers?animal=dog&animal=cat')\n            self.assertEqual(response.status_code, 200)\n            self.assertEqual(response.headers.get_all('animal'), ['dog', 'cat'])\n            assert json.loads(response.data.decode('utf-8'))['animal'] == ['dog', 'cat']\n\n    def test_get(self):\n        response = self.app.get('/get', headers={'User-Agent': 'test'})\n        self.assertEqual(response.status_code, 200)\n        data = json.loads(response.data.decode('utf-8'))\n        self.assertEqual(data['args'], {})\n        self.assertEqual(data['headers']['Host'], 'localhost')\n        self.assertEqual(data['headers']['Content-Length'], '0')\n        self.assertEqual(data['headers']['User-Agent'], 'test')\n        # self.assertEqual(data['origin'], None)\n        self.assertEqual(data['url'], 'http://localhost/get')\n        self.assertTrue(response.data.endswith(b'\\n'))\n\n    def test_anything(self):\n        response = self.app.get('/anything')\n        self.assertEqual(response.status_code, 200)\n        response = self.app.get('/anything/foo/bar')\n        self.assertEqual(response.status_code, 200)\n        data = json.loads(response.data.decode('utf-8'))\n        self.assertEqual(data['args'], {})\n        self.assertEqual(data['headers']['Host'], 'localhost')\n        self.assertEqual(data['headers']['Content-Length'], '0')\n        self.assertEqual(data['url'], 'http://localhost/anything/foo/bar')\n        self.assertEqual(data['method'], 'GET')\n        self.assertTrue(response.data.endswith(b'\\n'))\n\n    def test_base64(self):\n        greeting = u'Здравствуй, мир!'\n        b64_encoded = _string_to_base64(greeting)\n        response = self.app.get(b'/base64/' + b64_encoded)\n        content = response.data.decode('utf-8')\n        self.assertEqual(greeting, content)\n\n    def test_post_binary(self):\n        response = self.app.post('/post',\n                                 data=b'\\x01\\x02\\x03\\x81\\x82\\x83',\n                                 content_type='application/octet-stream')\n        self.assertEqual(response.status_code, 200)\n\n    def test_post_body_text(self):\n        with open('httpbin/core.py') as f:\n            response = self.app.post('/post', data={\"file\": f.read()})\n        self.assertEqual(response.status_code, 200)\n\n    def test_post_body_binary(self):\n        response = self.app.post(\n            '/post',\n            data={\"file\": b'\\x01\\x02\\x03\\x81\\x82\\x83'})\n        self.assertEqual(response.status_code, 200)\n\n    def test_post_body_unicode(self):\n        response = self.app.post('/post', data=u'оживлённым'.encode('utf-8'))\n        self.assertEqual(json.loads(response.data.decode('utf-8'))['data'], u'оживлённым')\n\n    def test_post_file_with_missing_content_type_header(self):\n        # I built up the form data manually here because I couldn't find a way\n        # to convince the werkzeug test client to send files without the\n        # content-type of the file set.\n        data = '--bound\\r\\nContent-Disposition: form-data; name=\"media\"; '\n        data += 'filename=\"test.bin\"\\r\\n\\r\\n\\xa5\\xc6\\n--bound--\\r\\n'\n        response = self.app.post(\n            '/post',\n            content_type='multipart/form-data; boundary=bound',\n            data=data,\n        )\n        self.assertEqual(response.status_code, 200)\n\n    \"\"\"\n    This is currently a sort of negative-test.\n    We validate that when running Flask-only server that\n    Transfer-Encoding: chunked requests are unsupported and\n    we return 501 Not Implemented\n    \"\"\"\n    def test_post_chunked(self):\n        data = '{\"animal\":\"dog\"}'\n        response = self.app.post(\n            '/post',\n            content_type='application/json',\n            headers=[('Transfer-Encoding', 'chunked')],\n            data=data,\n        )\n        self.assertEqual(response.status_code, 501)\n        #self.assertEqual(response.status_code, 200)\n        #self.assertEqual(json.loads(response.data.decode('utf-8'))['data'], '{\"animal\":\"dog\"}')\n        #self.assertEqual(json.loads(response.data.decode('utf-8'))['json'], {\"animal\": \"dog\"})\n\n    def test_set_cors_headers_after_request(self):\n        response = self.app.get('/get')\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Origin'), '*'\n        )\n\n    def test_set_cors_credentials_headers_after_auth_request(self):\n        response = self.app.get('/basic-auth/foo/bar')\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Credentials'), 'true'\n        )\n\n    def test_set_cors_headers_after_request_with_request_origin(self):\n        response = self.app.get('/get', headers={'Origin': 'origin'})\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Origin'), 'origin'\n        )\n\n    def test_set_cors_headers_with_options_verb(self):\n        response = self.app.open('/get', method='OPTIONS')\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Origin'), '*'\n        )\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Credentials'), 'true'\n        )\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Methods'),\n            'GET, POST, PUT, DELETE, PATCH, OPTIONS'\n        )\n        self.assertEqual(\n            response.headers.get('Access-Control-Max-Age'), '3600'\n        )\n        # FIXME should we add any extra headers?\n        self.assertNotIn(\n            'Access-Control-Allow-Headers', response.headers\n        )\n    def test_set_cors_allow_headers(self):\n        response = self.app.open('/get', method='OPTIONS', headers={'Access-Control-Request-Headers': 'X-Test-Header'})\n        self.assertEqual(\n            response.headers.get('Access-Control-Allow-Headers'), 'X-Test-Header'\n        )\n\n    def test_headers(self):\n        headers = {\n            \"Accept\": \"*/*\",\n            \"Host\": \"localhost:1234\",\n            \"User-Agent\": \"curl/7.54.0\",\n            \"Via\": \"bar\"\n        }\n        response = self.app.get('/headers', headers=headers)\n        self.assertEqual(response.status_code, 200)\n        self.assertTrue({'Accept', 'Host', 'User-Agent'}.issubset(set(response.json['headers'].keys())))\n        self.assertNotIn('Via', response.json)\n\n    def test_headers_show_env(self):\n        headers = {\n            \"Accept\": \"*/*\",\n            \"Host\": \"localhost:1234\",\n            \"User-Agent\": \"curl/7.54.0\",\n            \"Via\": \"bar\"\n        }\n        response = self.app.get('/headers?show_env=true', headers=headers)\n        self.assertEqual(response.status_code, 200)\n        self.assertTrue({'Accept', 'Host', 'User-Agent', 'Via'}.issubset(set(response.json['headers'].keys())))\n\n    def test_user_agent(self):\n        response = self.app.get(\n            '/user-agent', headers={'User-Agent': 'test'}\n        )\n        self.assertIn('test', response.data.decode('utf-8'))\n        self.assertEqual(response.status_code, 200)\n\n    def test_gzip(self):\n        response = self.app.get('/gzip')\n        self.assertEqual(response.status_code, 200)\n\n    def test_brotli(self):\n        response = self.app.get('/brotli')\n        self.assertEqual(response.status_code, 200)\n\n    def test_bearer_auth(self):\n        token = 'abcd1234'\n        response = self.app.get(\n            '/bearer',\n            headers={'Authorization': 'Bearer ' + token}\n        )\n        self.assertEqual(response.status_code, 200)\n        assert json.loads(response.data.decode('utf-8'))['token'] == token\n\n    def test_bearer_auth_with_wrong_authorization_type(self):\n        \"\"\"Sending an non-Bearer Authorization header to /bearer should return a 401\"\"\"\n        auth_headers = (\n            ('Authorization', 'Basic 1234abcd'),\n            ('Authorization', ''),\n            ('',  '')\n        )\n        for header in auth_headers:\n            response = self.app.get(\n                '/bearer',\n                headers={header[0]: header[1]}\n            )\n            self.assertEqual(response.status_code, 401)\n\n    def test_bearer_auth_with_missing_token(self):\n        \"\"\"Sending an 'Authorization: Bearer' header with no token to /bearer should return a 401\"\"\"\n        response = self.app.get(\n            '/bearer',\n            headers={'Authorization': 'Bearer'}\n        )\n        self.assertEqual(response.status_code, 401)\n\n    def test_digest_auth_with_wrong_password(self):\n        auth_header = 'Digest username=\"user\",realm=\"wrong\",nonce=\"wrong\",uri=\"/digest-auth/user/passwd/MD5\",response=\"wrong\",opaque=\"wrong\"'\n        response = self.app.get(\n            '/digest-auth/auth/user/passwd/MD5',\n            environ_base={\n                # httpbin's digest auth implementation uses the remote addr to\n                # build the nonce\n                'REMOTE_ADDR': '127.0.0.1',\n            },\n            headers={\n                'Authorization': auth_header,\n            }\n        )\n        self.assertTrue('Digest' in response.headers.get('WWW-Authenticate'))\n        self.assertEqual(response.status_code, 401)\n\n    def test_digest_auth(self):\n        \"\"\"Test different combinations of digest auth parameters\"\"\"\n        username = 'user'\n        password = 'passwd'\n        for qop in None, 'auth', 'auth-int',:\n            for algorithm in None, 'MD5', 'SHA-256', 'SHA-512':\n                for body in None, b'', b'request payload':\n                    for stale_after in (None, 1, 4) if algorithm else (None,) :\n                        self._test_digest_auth(username, password, qop, algorithm, body, stale_after)\n\n    def test_digest_auth_with_wrong_authorization_type(self):\n        \"\"\"Sending an non-digest Authorization header to /digest-auth should return a 401\"\"\"\n        auth_headers = (\n            ('Authorization', 'Basic 1234abcd'),\n            ('Authorization', ''),\n            ('',  '')\n        )\n        for header in auth_headers:\n            response = self.app.get(\n                '/digest-auth/auth/myname/mysecret',\n                headers={header[0]: header[1]}\n            )\n            self.assertEqual(response.status_code, 401)\n\n    def _test_digest_auth(self, username, password, qop, algorithm=None, body=None, stale_after=None):\n        uri = self._digest_auth_create_uri(username, password, qop, algorithm, stale_after)\n\n        unauthorized_response = self._test_digest_auth_first_challenge(uri)\n\n        header = unauthorized_response.headers.get('WWW-Authenticate')\n\n        authorized_response, nonce = self._test_digest_response_for_auth_request(header, username, password, qop, uri, body)\n        self.assertEqual(authorized_response.status_code, 200)\n\n        if None == stale_after :\n            return\n\n        # test stale after scenerio\n        self._digest_auth_stale_after_check(header, username, password, uri, body, qop, stale_after)\n\n    def _test_digest_auth_first_challenge(self, uri):\n        unauthorized_response = self.app.get(\n            uri,\n            environ_base={\n                # digest auth uses the remote addr to build the nonce\n                'REMOTE_ADDR': '127.0.0.1',\n            }\n        )\n        # make sure it returns a 401\n        self.assertEqual(unauthorized_response.status_code, 401)\n        return unauthorized_response\n\n    def _digest_auth_create_uri(self, username, password, qop, algorithm, stale_after):\n        uri = '/digest-auth/{0}/{1}/{2}'.format(qop or 'wrong-qop', username, password)\n        if algorithm:\n            uri += '/' + algorithm\n        if stale_after:\n            uri += '/{0}'.format(stale_after)\n        return uri\n\n    def _digest_auth_stale_after_check(self, header, username, password, uri, body, qop, stale_after):\n        for nc in range(2, stale_after + 1):\n            authorized_response, nonce = self._test_digest_response_for_auth_request(header, username, password, qop, uri, \\\n                                                                              body, nc)\n            self.assertEqual(authorized_response.status_code, 200)\n        stale_response, nonce = self._test_digest_response_for_auth_request(header, username, password, qop, uri, \\\n                                                                     body, stale_after + 1)\n        self.assertEqual(stale_response.status_code, 401)\n        header = stale_response.headers.get('WWW-Authenticate')\n        self.assertIn('stale=TRUE', header)\n\n    def _test_digest_response_for_auth_request(self, header, username, password, qop, uri, body, nc=1, nonce=None):\n        auth_type, auth_info = header.split(None, 1)\n        self.assertEqual(auth_type, 'Digest')\n\n        d = parse_dict_header(auth_info)\n\n        nonce = nonce or d['nonce']\n        realm = d['realm']\n        opaque = d['opaque']\n        if qop :\n            self.assertIn(qop, [x.strip() for x in d['qop'].split(',')], 'Challenge should contains expected qop')\n        algorithm = d['algorithm']\n\n        cnonce, nc = (_hash(os.urandom(10), \"MD5\"), '{:08}'.format(nc)) if qop in ('auth', 'auth-int') else (None, None)\n\n        auth_header = _make_digest_auth_header(\n            username, password, 'GET', uri, nonce, realm, opaque, algorithm, qop, cnonce, nc, body)\n\n        # make second request\n        return self.app.get(\n            uri,\n            environ_base={\n                # httpbin's digest auth implementation uses the remote addr to\n                # build the nonce\n                'REMOTE_ADDR': '127.0.0.1',\n            },\n            headers={\n                'Authorization': auth_header,\n            },\n            data=body\n        ), nonce\n\n    def test_digest_auth_wrong_pass(self):\n        \"\"\"Test different combinations of digest auth parameters\"\"\"\n        username = 'user'\n        password = 'passwd'\n        for qop in None, 'auth', 'auth-int',:\n            for algorithm in None, 'MD5', 'SHA-256', 'SHA-512':\n                for body in None, b'', b'request payload':\n                    self._test_digest_auth_wrong_pass(username, password, qop, algorithm, body, 3)\n\n    def _test_digest_auth_wrong_pass(self, username, password, qop, algorithm=None, body=None, stale_after=None):\n        uri = self._digest_auth_create_uri(username, password, qop, algorithm, stale_after)\n        unauthorized_response = self._test_digest_auth_first_challenge(uri)\n\n        header = unauthorized_response.headers.get('WWW-Authenticate')\n\n        wrong_pass_response, nonce = self._test_digest_response_for_auth_request(header, username, \"wrongPassword\", qop, uri, body)\n        self.assertEqual(wrong_pass_response.status_code, 401)\n        header = wrong_pass_response.headers.get('WWW-Authenticate')\n        self.assertNotIn('stale=TRUE', header)\n\n        reused_nonce_response, nonce =  self._test_digest_response_for_auth_request(header, username, password, qop, uri, \\\n                                                                              body, nonce=nonce)\n        self.assertEqual(reused_nonce_response.status_code, 401)\n        header = reused_nonce_response.headers.get('WWW-Authenticate')\n        self.assertIn('stale=TRUE', header)\n\n    def test_drip(self):\n        response = self.app.get('/drip?numbytes=400&duration=2&delay=1')\n        self.assertEqual(response.content_length, 400)\n        self.assertEqual(len(self.get_data(response)), 400)\n        self.assertEqual(response.status_code, 200)\n\n    def test_drip_with_invalid_numbytes(self):\n        for bad_num in -1, 0:\n            uri = '/drip?numbytes={0}&duration=2&delay=1'.format(bad_num)\n            response = self.app.get(uri)\n            self.assertEqual(response.status_code, 400)\n\n    def test_drip_with_custom_code(self):\n        response = self.app.get('/drip?numbytes=400&duration=2&code=500')\n        self.assertEqual(response.content_length, 400)\n        self.assertEqual(len(self.get_data(response)), 400)\n        self.assertEqual(response.status_code, 500)\n\n    def test_get_bytes(self):\n        response = self.app.get('/bytes/1024')\n        self.assertEqual(len(self.get_data(response)), 1024)\n        self.assertEqual(response.status_code, 200)\n\n    def test_bytes_with_seed(self):\n        response = self.app.get('/bytes/10?seed=0')\n        # The RNG changed in python3, so even though we are\n        # setting the seed, we can't expect the value to be the\n        # same across both interpreters.\n        if six.PY3:\n            self.assertEqual(\n                response.data, b'\\xc5\\xd7\\x14\\x84\\xf8\\xcf\\x9b\\xf4\\xb7o'\n            )\n        else:\n            self.assertEqual(\n                response.data, b'\\xd8\\xc2kB\\x82g\\xc8Mz\\x95'\n            )\n\n    def test_stream_bytes(self):\n        response = self.app.get('/stream-bytes/1024')\n        self.assertEqual(len(self.get_data(response)), 1024)\n        self.assertEqual(response.status_code, 200)\n\n    def test_stream_bytes_with_seed(self):\n        response = self.app.get('/stream-bytes/10?seed=0')\n        # The RNG changed in python3, so even though we are\n        # setting the seed, we can't expect the value to be the\n        # same across both interpreters.\n        if six.PY3:\n            self.assertEqual(\n                response.data, b'\\xc5\\xd7\\x14\\x84\\xf8\\xcf\\x9b\\xf4\\xb7o'\n            )\n        else:\n            self.assertEqual(\n                response.data, b'\\xd8\\xc2kB\\x82g\\xc8Mz\\x95'\n            )\n\n    def test_delete_endpoint_returns_body(self):\n        response = self.app.delete(\n            '/delete',\n            data={'name': 'kevin'},\n            content_type='application/x-www-form-urlencoded'\n        )\n        form_data = json.loads(response.data.decode('utf-8'))['form']\n        self.assertEqual(form_data, {'name': 'kevin'})\n\n    def test_methods__to_status_endpoint(self):\n        methods = [\n            'GET',\n            'HEAD',\n            'POST',\n            'PUT',\n            'DELETE',\n            'PATCH',\n            'TRACE',\n        ]\n        for m in methods:\n            response = self.app.open(path='/status/418', method=m)\n            self.assertEqual(response.status_code, 418)\n\n    def test_status_endpoint_invalid_code(self):\n        response = self.app.get(path='/status/4!9')\n        self.assertEqual(response.status_code, 400)\n\n    def test_status_endpoint_invalid_codes(self):\n        response = self.app.get(path='/status/200,402,foo')\n        self.assertEqual(response.status_code, 400)\n\n    def test_xml_endpoint(self):\n        response = self.app.get(path='/xml')\n        self.assertEqual(\n            response.headers.get('Content-Type'), 'application/xml'\n        )\n\n    def test_x_forwarded_proto(self):\n        response = self.app.get(path='/get', headers={\n            'X-Forwarded-Proto':'https'\n        })\n        assert json.loads(response.data.decode('utf-8'))['url'].startswith('https://')\n\n    def test_redirect_n_higher_than_1(self):\n        response = self.app.get('/redirect/5')\n        self.assertEqual(\n            response.headers.get('Location'), '/relative-redirect/4'\n        )\n\n    def test_redirect_to_post(self):\n        response = self.app.post('/redirect-to?url=/post&status_code=307',\n                                 data=b'\\x01\\x02\\x03\\x81\\x82\\x83',\n                                 content_type='application/octet-stream')\n        self.assertEqual(response.status_code, 307)\n        self.assertEqual(\n            response.headers.get('Location'), '/post'\n        )\n\n    def test_redirect_absolute_param_n_higher_than_1(self):\n        response = self.app.get('/redirect/5?absolute=true')\n        self.assertEqual(\n            response.headers.get('Location'), 'http://localhost/absolute-redirect/4'\n        )\n\n    def test_redirect_n_equals_to_1(self):\n        response = self.app.get('/redirect/1')\n        self.assertEqual(response.status_code, 302)\n        self.assertEqual(\n            response.headers.get('Location'), '/get'\n        )\n\n    def test_relative_redirect_n_equals_to_1(self):\n        response = self.app.get('/relative-redirect/1')\n        self.assertEqual(\n            response.headers.get('Location'), '/get'\n        )\n\n    def test_relative_redirect_n_higher_than_1(self):\n        response = self.app.get('/relative-redirect/7')\n        self.assertEqual(response.status_code, 302)\n        self.assertEqual(\n            response.headers.get('Location'), '/relative-redirect/6'\n        )\n\n    def test_absolute_redirect_n_higher_than_1(self):\n        response = self.app.get('/absolute-redirect/5')\n        self.assertEqual(\n            response.headers.get('Location'), 'http://localhost/absolute-redirect/4'\n        )\n\n    def test_absolute_redirect_n_equals_to_1(self):\n        response = self.app.get('/absolute-redirect/1')\n        self.assertEqual(response.status_code, 302)\n        self.assertEqual(\n            response.headers.get('Location'), 'http://localhost/get'\n        )\n\n    def test_request_range(self):\n        response1 = self.app.get('/range/1234')\n        self.assertEqual(response1.status_code, 200)\n        self.assertEqual(response1.headers.get('ETag'), 'range1234')\n        self.assertEqual(response1.headers.get('Content-range'), 'bytes 0-1233/1234')\n        self.assertEqual(response1.headers.get('Accept-ranges'), 'bytes')\n        self.assertEqual(len(self.get_data(response1)), 1234)\n\n        response2 = self.app.get('/range/1234')\n        self.assertEqual(response2.status_code, 200)\n        self.assertEqual(response2.headers.get('ETag'), 'range1234')\n        self.assertEqual(self.get_data(response1), self.get_data(response2))\n\n    def test_request_range_with_parameters(self):\n        response = self.app.get(\n            '/range/100?duration=1.5&chunk_size=5',\n            headers={ 'Range': 'bytes=10-24' }\n        )\n\n        self.assertEqual(response.status_code, 206)\n        self.assertEqual(response.headers.get('ETag'), 'range100')\n        self.assertEqual(response.headers.get('Content-range'), 'bytes 10-24/100')\n        self.assertEqual(response.headers.get('Accept-ranges'), 'bytes')\n        self.assertEqual(response.headers.get('Content-Length'), '15')\n        self.assertEqual(self.get_data(response), 'klmnopqrstuvwxy'.encode('utf8'))\n\n    def test_request_range_first_15_bytes(self):\n        response = self.app.get(\n            '/range/1000',\n            headers={ 'Range': 'bytes=0-15' }\n        )\n\n        self.assertEqual(response.status_code, 206)\n        self.assertEqual(response.headers.get('ETag'), 'range1000')\n        self.assertEqual(self.get_data(response), 'abcdefghijklmnop'.encode('utf8'))\n        self.assertEqual(response.headers.get('Content-range'), 'bytes 0-15/1000')\n\n    def test_request_range_open_ended_last_6_bytes(self):\n        response = self.app.get(\n            '/range/26',\n            headers={ 'Range': 'bytes=20-' }\n        )\n\n        self.assertEqual(response.status_code, 206)\n        self.assertEqual(response.headers.get('ETag'), 'range26')\n        self.assertEqual(self.get_data(response), 'uvwxyz'.encode('utf8'))\n        self.assertEqual(response.headers.get('Content-range'), 'bytes 20-25/26')\n        self.assertEqual(response.headers.get('Content-Length'), '6')\n\n    def test_request_range_suffix(self):\n        response = self.app.get(\n            '/range/26',\n            headers={ 'Range': 'bytes=-5' }\n        )\n\n        self.assertEqual(response.status_code, 206)\n        self.assertEqual(response.headers.get('ETag'), 'range26')\n        self.assertEqual(self.get_data(response), 'vwxyz'.encode('utf8'))\n        self.assertEqual(response.headers.get('Content-range'), 'bytes 21-25/26')\n        self.assertEqual(response.headers.get('Content-Length'), '5')\n\n    def test_request_out_of_bounds(self):\n        response = self.app.get(\n            '/range/26',\n            headers={ 'Range': 'bytes=10-5',\n            }\n        )\n\n        self.assertEqual(response.status_code, 416)\n        self.assertEqual(response.headers.get('ETag'), 'range26')\n        self.assertEqual(len(self.get_data(response)), 0)\n        self.assertEqual(response.headers.get('Content-range'), 'bytes */26')\n        self.assertEqual(response.headers.get('Content-Length'), '0')\n\n        response = self.app.get(\n            '/range/26',\n            headers={ 'Range': 'bytes=32-40',\n            }\n        )\n\n        self.assertEqual(response.status_code, 416)\n        response = self.app.get(\n            '/range/26',\n            headers={ 'Range': 'bytes=0-40',\n            }\n        )\n        self.assertEqual(response.status_code, 416)\n\n    def test_etag_if_none_match_matches(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-None-Match': 'abc' }\n        )\n        self.assertEqual(response.status_code, 304)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_none_match_matches_list(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-None-Match': '\"123\", \"abc\"' }\n        )\n        self.assertEqual(response.status_code, 304)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_none_match_matches_star(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-None-Match': '*' }\n        )\n        self.assertEqual(response.status_code, 304)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_none_match_w_prefix(self):\n        response = self.app.get(\n            '/etag/c3piozzzz',\n            headers={ 'If-None-Match': 'W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"' }\n        )\n        self.assertEqual(response.status_code, 304)\n        self.assertEqual(response.headers.get('ETag'), 'c3piozzzz')\n\n    def test_etag_if_none_match_has_no_match(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-None-Match': '123' }\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_match_matches(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-Match': 'abc' }\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_match_matches_list(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-Match': '\"123\", \"abc\"' }\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_match_matches_star(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-Match': '*' }\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_etag_if_match_has_no_match(self):\n        response = self.app.get(\n            '/etag/abc',\n            headers={ 'If-Match': '123' }\n        )\n        self.assertEqual(response.status_code, 412)\n        self.assertNotIn('ETag', response.headers)\n\n    def test_etag_with_no_headers(self):\n        response = self.app.get(\n            '/etag/abc'\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.headers.get('ETag'), 'abc')\n\n    def test_parse_multi_value_header(self):\n        self.assertEqual(parse_multi_value_header('xyzzy'), [ \"xyzzy\" ])\n        self.assertEqual(parse_multi_value_header('\"xyzzy\"'), [ \"xyzzy\" ])\n        self.assertEqual(parse_multi_value_header('W/\"xyzzy\"'), [ \"xyzzy\" ])\n        self.assertEqual(parse_multi_value_header('\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"'), [ \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\" ])\n        self.assertEqual(parse_multi_value_header('W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"'), [ \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\" ])\n        self.assertEqual(parse_multi_value_header('*'), [ \"*\" ])\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py27,py36,py37\n\n[testenv]\ncommands=python test_httpbin.py\n\n[testenv:release]\nskipdist = true\nusedevelop = false\ndeps =\n    twine>=1.6.0\n    wheel\ncommands =\n    python setup.py sdist bdist_wheel\n    twine upload --skip-existing dist/*\n"
  }
]