diff --git a/AUTHORS b/AUTHORS index e3c8c39..c3a4fcf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,5 +17,6 @@ Patches and Suggestions - Flavio Percoco - Radomir Stevanovic (http://github.com/randomir) - Steven Honson +- Bob Carroll @rcarz - Cory Benfield (Lukasa) - Matt Robenolt (https://github.com/mattrobenolt) diff --git a/README.md b/README.md index a0d13e2..00fff52 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Freely hosted in [HTTP](http://httpbin.org) & - [`/status/:code`](http://httpbin.org/status/418) Returns given HTTP Status code. - [`/response-headers?key=val`](http://httpbin.org/response-headers?Content-Type=text/plain;%20charset=UTF-8&Server=httpbin) Returns given response headers. - [`/redirect/:n`](http://httpbin.org/redirect/6) 302 Redirects *n* times. +- [`/redirect-to?url=foo`](http://httpbin.org/redirect-to?url=http://example.com/) 302 Redirects to the *foo* URL. - [`/relative-redirect/:n`](http://httpbin.org/relative-redirect/6) 302 Relative redirects *n* times. - [`/cookies`](http://httpbin.org/cookies) Returns cookie data. - [`/cookies/set?name=value`](http://httpbin.org/cookies/set?k1=v1&k2=v2) Sets one or more simple cookies. diff --git a/httpbin/core.py b/httpbin/core.py index d232180..0a3cb71 100644 --- a/httpbin/core.py +++ b/httpbin/core.py @@ -17,6 +17,7 @@ from flask import Flask, Response, request, render_template, redirect, jsonify, from raven.contrib.flask import Sentry from werkzeug.datastructures import WWWAuthenticate from werkzeug.http import http_date +from werkzeug.wrappers import BaseResponse from . import filters from .helpers import get_headers, status_code, get_dict, check_basic_auth, check_digest_auth, H, ROBOT_TXT, ANGRY_ASCII @@ -34,6 +35,9 @@ ENV_COOKIES = ( '__utmb' ) +# Prevent WSGI from correcting the casing of the Location header +BaseResponse.autocorrect_location_header = False + app = Flask(__name__) # Setup error collection @@ -160,6 +164,22 @@ def redirect_n_times(n): return redirect('/redirect/{0}'.format(n - 1)) +@app.route('/redirect-to') +def redirect_to(): + """302 Redirects to the given URL.""" + + args = CaseInsensitiveDict(request.args.items()) + + # We need to build the response manually and convert to UTF-8 to prevent + # werkzeug from "fixing" the URL. This endpoint should set the Location + # header to the exact string supplied. + response = app.make_response('') + response.status_code = 302 + response.headers['Location'] = args['url'].encode('utf-8') + + return response + + @app.route('/relative-redirect/') def relative_redirect_n_times(n): """301 Redirects n times.""" diff --git a/httpbin/templates/httpbin.1.html b/httpbin/templates/httpbin.1.html index e51a572..5b6eca4 100644 --- a/httpbin/templates/httpbin.1.html +++ b/httpbin/templates/httpbin.1.html @@ -20,6 +20,7 @@
  • /status/:code Returns given HTTP Status code.
  • /response-headers?key=val Returns given response headers.
  • /redirect/:n 302 Redirects n times.
  • +
  • /redirect-to?url=foo 302 Redirects to the foo URL.
  • /relative-redirect/:n 302 Relative redirects n times.
  • /cookies Returns cookie data.
  • /cookies/set?name=value Sets one or more simple cookies.