$ docker run -p 80:80 kennethreitz/httpbin"
+ ),
+ "contact": {
+ "responsibleOrganization": "Kenneth Reitz",
+ "responsibleDeveloper": "Kenneth Reitz",
+ "email": "me@kennethreitz.org",
+ "url": "https://kennethreitz.org",
+ },
+ # "termsOfService": "http://me.com/terms",
+ "version": version,
+ },
+ "host": "httpbin.org", # overrides localhost:5000
+ "basePath": "/", # base bash for blueprint registration
+ "schemes": ["https"],
+ "protocol": "https",
+ "tags": [
+ {
+ "name": "HTTP Methods",
+ "description": "Testing different HTTP verbs",
+ # 'externalDocs': {'description': 'Learn more', 'url': 'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'}
+ },
+ {"name": "Auth", "description": "Auth methods"},
+ {
+ "name": "Status codes",
+ "description": "Generates responses with given status code",
+ },
+ {"name": "Request inspection", "description": "Inspect the request data"},
+ {
+ "name": "Response inspection",
+ "description": "Inspect the response data like caching and headers",
+ },
+ {
+ "name": "Response formats",
+ "description": "Returns responses in different data formats",
+ },
+ {"name": "Dynamic data", "description": "Generates random and dynamic data"},
+ {"name": "Cookies", "description": "Creates, reads and deletes Cookies"},
+ {"name": "Images", "description": "Returns different image formats"},
+ {"name": "Redirects", "description": "Returns different redirect responses"},
+ {
+ "name": "Anything",
+ "description": "Returns anything that is passed to request",
+ },
+ ],
+}
+
+swagger_config = {
+ "headers": [],
+ "specs": [
+ {
+ "endpoint": "spec",
+ "route": "/spec.json",
+ "rule_filter": lambda rule: True, # all in
+ "model_filter": lambda tag: True, # all in
+ }
+ ],
+ "static_url_path": "/flasgger_static",
+ # "static_folder": "static", # must be set by user
+ "swagger_ui": True,
+ "specs_route": "/",
+}
+
+swagger = Swagger(app, sanitizer=NO_SANITIZER, template=template, config=swagger_config)
# Set up Bugsnag exception tracking, if desired. To use Bugsnag, install the
# Bugsnag Python client with the command "pip install bugsnag", and set the
@@ -72,11 +168,15 @@ def jsonify(*args, **kwargs):
try:
import bugsnag
import bugsnag.flask
+
release_stage = os.environ.get("BUGSNAG_RELEASE_STAGE") or "production"
- bugsnag.configure(api_key=os.environ.get("BUGSNAG_API_KEY"),
- project_root=os.path.dirname(os.path.abspath(__file__)),
- use_ssl=True, release_stage=release_stage,
- ignore_classes=['werkzeug.exceptions.NotFound'])
+ bugsnag.configure(
+ api_key=os.environ.get("BUGSNAG_API_KEY"),
+ project_root=os.path.dirname(os.path.abspath(__file__)),
+ use_ssl=True,
+ release_stage=release_stage,
+ ignore_classes=["werkzeug.exceptions.NotFound"],
+ )
bugsnag.flask.handle_exceptions(app)
except:
app.logger.warning("Unable to initialize Bugsnag exception handling.")
@@ -95,31 +195,40 @@ def jsonify(*args, **kwargs):
- flask will hang and does not seem to properly terminate the request, so
we explicitly deny chunked requests.
"""
+
+
@app.before_request
def before_request():
- if request.environ.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked':
- server = request.environ.get('SERVER_SOFTWARE', '')
- if server.lower().startswith('gunicorn/'):
- if 'wsgi.input_terminated' in request.environ:
- app.logger.debug("environ wsgi.input_terminated already set, keeping: %s"
- % request.environ['wsgi.input_terminated'])
+ if request.environ.get("HTTP_TRANSFER_ENCODING", "").lower() == "chunked":
+ server = request.environ.get("SERVER_SOFTWARE", "")
+ if server.lower().startswith("gunicorn/"):
+ if "wsgi.input_terminated" in request.environ:
+ app.logger.debug(
+ "environ wsgi.input_terminated already set, keeping: %s"
+ % request.environ["wsgi.input_terminated"]
+ )
else:
- request.environ['wsgi.input_terminated'] = 1
+ request.environ["wsgi.input_terminated"] = 1
else:
abort(501, "Chunked requests are not supported for server %s" % server)
+
@app.after_request
def set_cors_headers(response):
- response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
- response.headers['Access-Control-Allow-Credentials'] = 'true'
+ response.headers["Access-Control-Allow-Origin"] = request.headers.get("Origin", "*")
+ response.headers["Access-Control-Allow-Credentials"] = "true"
- if request.method == 'OPTIONS':
+ if request.method == "OPTIONS":
# Both of these headers are only used for the "preflight request"
# http://www.w3.org/TR/cors/#access-control-allow-methods-response-header
- response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, PATCH, OPTIONS'
- response.headers['Access-Control-Max-Age'] = '3600' # 1 hour cache
- if request.headers.get('Access-Control-Request-Headers') is not None:
- response.headers['Access-Control-Allow-Headers'] = request.headers['Access-Control-Request-Headers']
+ response.headers[
+ "Access-Control-Allow-Methods"
+ ] = "GET, POST, PUT, DELETE, PATCH, OPTIONS"
+ response.headers["Access-Control-Max-Age"] = "3600" # 1 hour cache
+ if request.headers.get("Access-Control-Request-Headers") is not None:
+ response.headers["Access-Control-Allow-Headers"] = request.headers[
+ "Access-Control-Request-Headers"
+ ]
return response
@@ -127,23 +236,41 @@ def set_cors_headers(response):
# Routes
# ------
-@app.route('/')
+
+@app.route("/legacy")
def view_landing_page():
- """Generates Landing Page."""
- tracking_enabled = 'HTTPBIN_TRACKING' in os.environ
- return render_template('index.html', tracking_enabled=tracking_enabled)
+ """Generates Landing Page in legacy layout."""
+ return render_template("index.html")
-@app.route('/html')
+@app.route("/html")
def view_html_page():
- """Simple Html Page"""
+ """Returns a simple HTML document.
+ ---
+ tags:
+ - Response formats
+ produces:
+ - text/html
+ responses:
+ 200:
+ description: An HTML page.
+ """
- return render_template('moby.html')
+ return render_template("moby.html")
-@app.route('/robots.txt')
+@app.route("/robots.txt")
def view_robots_page():
- """Simple Html Page"""
+ """Returns some robots.txt rules.
+ ---
+ tags:
+ - Response formats
+ produces:
+ - text/plain
+ responses:
+ 200:
+ description: Robots file
+ """
response = make_response()
response.data = ROBOT_TXT
@@ -151,9 +278,18 @@ def view_robots_page():
return response
-@app.route('/deny')
+@app.route("/deny")
def view_deny_page():
- """Simple Html Page"""
+ """Returns page denied by robots.txt rules.
+ ---
+ tags:
+ - Response formats
+ produces:
+ - text/plain
+ responses:
+ 200:
+ description: Denied message
+ """
response = make_response()
response.data = ANGRY_ASCII
response.content_type = "text/plain"
@@ -161,227 +297,510 @@ def view_deny_page():
# return "YOU SHOULDN'T BE HERE"
-@app.route('/ip')
+@app.route("/ip")
def view_origin():
- """Returns Origin IP."""
+ """Returns the requester's IP Address.
+ ---
+ tags:
+ - Request inspection
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: The Requester's IP Address.
+ """
- return jsonify(origin=request.headers.get('X-Forwarded-For', request.remote_addr))
+ return jsonify(origin=request.headers.get("X-Forwarded-For", request.remote_addr))
-@app.route('/uuid')
+@app.route("/uuid")
def view_uuid():
- """Returns a UUID."""
+ """Return a UUID4.
+ ---
+ tags:
+ - Dynamic data
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: A UUID4.
+ """
return jsonify(uuid=str(uuid.uuid4()))
-@app.route('/headers')
+@app.route("/headers")
def view_headers():
- """Returns HTTP HEADERS."""
+ """Return the incoming request's HTTP headers.
+ ---
+ tags:
+ - Request inspection
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: The request's headers.
+ """
return jsonify(get_dict('headers'))
-@app.route('/user-agent')
+@app.route("/user-agent")
def view_user_agent():
- """Returns User-Agent."""
+ """Return the incoming requests's User-Agent header.
+ ---
+ tags:
+ - Request inspection
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: The request's User-Agent header.
+ """
headers = get_headers()
- return jsonify({'user-agent': headers['user-agent']})
+ return jsonify({"user-agent": headers["user-agent"]})
-@app.route('/get', methods=('GET',))
+@app.route("/get", methods=("GET",))
def view_get():
- """Returns GET Data."""
-
- return jsonify(get_dict('url', 'args', 'headers', 'origin'))
-
-
-@app.route('/anything', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'TRACE'])
-@app.route('/anything/0.9.2+ +
[ Base URL: httpbin.org/ ]+ +
A simple HTTP Request & Response Service.
+
+
+ Run locally:
+ $ docker run -p 80:80 kennethreitz/httpbin
+
Freely hosted in HTTP, HTTPS, & EU flavors by Kenneth Reitz & Runscope.
+Freely hosted in HTTP, HTTPS, & EU flavors by Kenneth Reitz & Heroku.
+
+
+
-{% include 'httpbin.1.html' %}
-
-{% if tracking_enabled %}
- {% include 'trackingscripts.html' %}
-{% endif %}
+ {% include 'httpbin.1.html' %} {% if tracking_enabled %} {% include 'trackingscripts.html' %} {% endif %}
+
diff --git a/httpbin/templates/trackingscripts.html b/httpbin/templates/trackingscripts.html
index 4fd235ef..080bbcd4 100644
--- a/httpbin/templates/trackingscripts.html
+++ b/httpbin/templates/trackingscripts.html
@@ -1,15 +1,6 @@
{#
place tracking scripts (like Google Analytics) here
#}
-