diff --git a/lnt/server/ui/globals.py b/lnt/server/ui/globals.py --- a/lnt/server/ui/globals.py +++ b/lnt/server/ui/globals.py @@ -3,6 +3,7 @@ """ import flask +from flask import Response import lnt.server.ui.util @@ -34,6 +35,18 @@ except Exception: return False +class fixed_location_response(Response): + autocorrect_location_header = False + +def v4_redirect(*args, **kwargs): + """ + Like redirect but can be used to allow relative URL redirection. + Flask's default response makes URLs absolute before putting in the + Location header due to adhering to a typo the out-dated RFC 2616. + + The RFC was fixed by Flask still doesn't allow it by default. + """ + return flask.redirect(*args, Response=fixed_location_response, **kwargs) def register(env): # Add some normal Python builtins which can be useful in templates. @@ -43,4 +56,5 @@ env.globals.update( db_url_for=db_url_for, v4_url_for=v4_url_for, + v4_redirect=v4_redirect, v4_url_available=v4_url_available) diff --git a/lnt/server/ui/regression_views.py b/lnt/server/ui/regression_views.py --- a/lnt/server/ui/regression_views.py +++ b/lnt/server/ui/regression_views.py @@ -16,7 +16,7 @@ from lnt.server.ui.decorators import v4_route import lnt.server.reporting.analysis -from lnt.server.ui.globals import v4_url_for +from lnt.server.ui.globals import v4_url_for, v4_redirect from lnt.server.ui.views import ts_data from lnt.util import logger @@ -60,7 +60,7 @@ request.form['btn'] == "Create New Regression": regression, _ = new_regression(session, ts, form.field_changes.data) flash("Created " + regression.title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_list", + return v4_redirect(v4_url_for(".v4_regression_list", highlight=regression.id)) if request.method == 'POST' and request.form['btn'] == "Ignore Changes": msg = "Ignoring changes: " @@ -171,7 +171,7 @@ session.commit() flash("Created: " + new_regress.title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_detail", id=new_regress.id)) + return v4_redirect(v4_url_for(".v4_regression_detail", id=new_regress.id)) # Delete requested regressions. if request.method == 'POST' and \ request.form['merge_btn'] == "Delete Regressions": @@ -184,7 +184,7 @@ session.delete(reg) session.commit() flash(' Deleted: '.join(titles), FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_list", state=state_filter)) + return v4_redirect(v4_url_for(".v4_regression_list", state=state_filter)) q = session.query(ts.Regression) title = "All Regressions" @@ -298,7 +298,7 @@ regression_info.state = form.state.data session.commit() flash("Updated " + regression_info.title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_list", + return v4_redirect(v4_url_for(".v4_regression_list", highlight=regression_info.id, state=int(form.edit_state.data))) if request.method == 'POST' and \ @@ -318,7 +318,7 @@ lnt.server.db.fieldchange.rebuild_title(session, ts, regression_info) session.commit() flash("Split " + second_regression.title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_list", + return v4_redirect(v4_url_for(".v4_regression_list", highlight=second_regression.id, state=int(form.edit_state.data))) if request.method == 'POST' and request.form['save_btn'] == "Delete": @@ -334,7 +334,7 @@ session.delete(regression_info) session.commit() flash("Deleted " + title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_list", + return v4_redirect(v4_url_for(".v4_regression_list", state=int(form.edit_state.data))) form.field_changes.choices = list() form.state.default = regression_info.state @@ -475,4 +475,4 @@ logger.info("Manually created new regressions: {}".format(regression.id)) flash("Created " + regression.title, FLASH_SUCCESS) - return redirect(v4_url_for(".v4_regression_detail", id=regression.id)) + return v4_redirect(v4_url_for(".v4_regression_detail", id=regression.id)) diff --git a/lnt/server/ui/views.py b/lnt/server/ui/views.py --- a/lnt/server/ui/views.py +++ b/lnt/server/ui/views.py @@ -40,7 +40,7 @@ from lnt.server.reporting.analysis import ComparisonResult, calc_geomean from lnt.server.ui import util from lnt.server.ui.decorators import frontend, db_route, v4_route -from lnt.server.ui.globals import db_url_for, v4_url_for +from lnt.server.ui.globals import db_url_for, v4_url_for, v4_redirect from lnt.server.ui.util import FLASH_DANGER, FLASH_SUCCESS, FLASH_INFO from lnt.server.ui.util import PrecomputedCR from lnt.server.ui.util import baseline_key, convert_revision @@ -72,7 +72,7 @@ @frontend.route('/favicon.ico') def favicon_ico(): - return redirect(url_for('.static', filename='favicon.ico')) + return v4_redirect(url_for('.static', filename='favicon.ico')) @frontend.route('/select_db') @@ -91,7 +91,7 @@ else: if '/' in path[1:]: new_path += "/" + path.split("/", 2)[2] - return redirect(request.script_root + new_path) + return v4_redirect(request.script_root + new_path) ##### # Per-Database Routes @@ -188,7 +188,7 @@ """Compatibility url that hardcodes testsuite to 'nts'""" if request.method == 'GET': g.testsuite_name = 'nts' - return redirect(v4_url_for('.v4_submitRun')) + return v4_redirect(v4_url_for('.v4_submitRun')) # This route doesn't know the testsuite to use. We have some defaults/ # autodetection for old submissions, but really you should use the full @@ -269,7 +269,7 @@ .filter(ts.Run.machine_id == machine_id) \ .order_by(ts.Run.start_time.desc()) \ .first() - return redirect(v4_url_for('.v4_run', id=run.id, **request.args)) + return v4_redirect(v4_url_for('.v4_run', id=run.id, **request.args)) @v4_route("/machine//compare") @@ -288,7 +288,7 @@ .order_by(ts.Run.start_time.desc()) \ .first() - return redirect(v4_url_for('.v4_run', id=machine_1_run.id, + return v4_redirect(v4_url_for('.v4_run', id=machine_1_run.id, compare_to=machine_2_run.id)) @@ -468,7 +468,7 @@ # If we found one, redirect to it's report. if matched_run is not None: - return redirect(db_url_for(".v4_run", testsuite_name=tag, + return v4_redirect(db_url_for(".v4_run", testsuite_name=tag, id=matched_run.id)) # Otherwise, report an error. @@ -602,7 +602,7 @@ session.commit() flash("Baseline {} updated.".format(baseline.name), FLASH_SUCCESS) - return redirect(v4_url_for(".v4_order", id=id)) + return v4_redirect(v4_url_for(".v4_order", id=id)) try: baseline = session.query(ts.Baseline) \ @@ -650,7 +650,7 @@ flash("Baseline set to " + base.name, FLASH_SUCCESS) flask.session[baseline_key(ts.name)] = id - return redirect(get_redirect_target()) + return v4_redirect(get_redirect_target()) @v4_route("/all_orders") @@ -697,7 +697,7 @@ run.machine.id, test_id, value) plot_number += 1 - return redirect(v4_url_for(".v4_graph", **args)) + return v4_redirect(v4_url_for(".v4_graph", **args)) BaselineLegendItem = namedtuple('BaselineLegendItem', 'name id') @@ -741,7 +741,7 @@ kwargs.update(request.args) graph_url = v4_url_for('.v4_graph', **kwargs) - return redirect(graph_url) + return v4_redirect(graph_url) @v4_route("/graph") @@ -1421,7 +1421,7 @@ extra_args.pop("month", None) extra_args.pop("day", None) - return redirect(v4_url_for(".v4_daily_report", + return v4_redirect(v4_url_for(".v4_daily_report", year=date.year, month=date.month, day=date.day, **extra_args)) @@ -1482,7 +1482,7 @@ flask.json.dump(config, f, indent=2) # Redirect to the summary report. - return redirect(db_url_for(".v4_summary_report")) + return v4_redirect(db_url_for(".v4_summary_report")) config_path = get_summary_config_path() if os.path.exists(config_path):