From ac6bef5219e6db94b92f78d88b6d7d748139967f Mon Sep 17 00:00:00 2001
From: Martin fl0w Iwanowski <martin@iwanowski.se>
Date: Wed, 27 Dec 2017 10:56:47 +0100
Subject: [PATCH 1/2] response.redirect: extract special-cased back

---
 lib/context.js        |  1 +
 lib/response.js       | 25 ++++++++++++++++++++++++-
 test/response/back.js | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+), 1 deletion(-)
 create mode 100644 test/response/back.js

diff --git a/lib/context.js b/lib/context.js
index a537cb9dd..274b5d558 100644
--- a/lib/context.js
+++ b/lib/context.js
@@ -165,6 +165,7 @@ delegate(proto, 'response')
   .method('set')
   .method('append')
   .method('flushHeaders')
+  .method('back')
   .access('status')
   .access('message')
   .access('body')
diff --git a/lib/response.js b/lib/response.js
index fb6b897a9..957922b55 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -18,6 +18,7 @@ const assert = require('assert');
 const extname = require('path').extname;
 const vary = require('vary');
 const only = require('only');
+const deprecate = require('depd')('koa');
 
 /**
  * Prototype.
@@ -258,7 +259,11 @@ module.exports = {
 
   redirect(url, alt) {
     // location
-    if ('back' == url) url = this.ctx.get('Referrer') || alt || '/';
+    if ('back' == url) {
+      deprecate('Special-cased string "back" through redirect will be removed in v3,' +
+                'consider migrating usage to ctx.back() instead.');
+      url = this.ctx.get('Referrer') || alt || '/';
+    }
     this.set('Location', url);
 
     // status
@@ -277,6 +282,24 @@ module.exports = {
     this.body = `Redirecting to ${url}.`;
   },
 
+  /**
+   * Perform a special-cased "back" to provide Referrer support.
+   * When Referrer is not present, `alt` or "/" is used.
+   *
+   * Examples:
+   *
+   *    ctx.back()
+   *    ctx.back('/index.html')
+   *
+   * @param {String} [alt]
+   * @api public
+   */
+
+  back(alt) {
+    const url = this.ctx.get('Referrer') || alt || '/';
+    this.redirect(url);
+  },
+
   /**
    * Set Content-Disposition header to "attachment" with optional `filename`.
    *
diff --git a/test/response/back.js b/test/response/back.js
new file mode 100644
index 000000000..e6c30c378
--- /dev/null
+++ b/test/response/back.js
@@ -0,0 +1,33 @@
+
+'use strict';
+
+const assert = require('assert');
+const context = require('../helpers/context');
+
+describe('ctx.back([alt])', () => {
+  it('should redirect to Referrer', () => {
+    const ctx = context();
+    ctx.req.headers.referrer = '/login';
+    ctx.back();
+    assert.equal(ctx.response.header.location, '/login');
+  });
+
+  it('should redirect to Referer', () => {
+    const ctx = context();
+    ctx.req.headers.referer = '/login';
+    ctx.back();
+    assert.equal(ctx.response.header.location, '/login');
+  });
+
+  it('should default to alt', () => {
+    const ctx = context();
+    ctx.back('/index.html');
+    assert.equal(ctx.response.header.location, '/index.html');
+  });
+
+  it('should default redirect to /', () => {
+    const ctx = context();
+    ctx.back();
+    assert.equal(ctx.response.header.location, '/');
+  });
+});

From 5cdd01c65fabc5955954cf3ea1e7fe9d5d6dfe30 Mon Sep 17 00:00:00 2001
From: Martin fl0w Iwanowski <martin@iwanowski.se>
Date: Wed, 14 Feb 2018 13:05:01 +0100
Subject: [PATCH 2/2] remove deprecated string "back" in redirect

---
 lib/response.js           | 15 +--------------
 test/response/redirect.js | 28 ----------------------------
 2 files changed, 1 insertion(+), 42 deletions(-)

diff --git a/lib/response.js b/lib/response.js
index 957922b55..d2eddce6e 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -18,7 +18,6 @@ const assert = require('assert');
 const extname = require('path').extname;
 const vary = require('vary');
 const only = require('only');
-const deprecate = require('depd')('koa');
 
 /**
  * Prototype.
@@ -241,29 +240,17 @@ module.exports = {
   /**
    * Perform a 302 redirect to `url`.
    *
-   * The string "back" is special-cased
-   * to provide Referrer support, when Referrer
-   * is not present `alt` or "/" is used.
-   *
    * Examples:
    *
-   *    this.redirect('back');
-   *    this.redirect('back', '/index.html');
    *    this.redirect('/login');
    *    this.redirect('http://google.com');
    *
    * @param {String} url
-   * @param {String} [alt]
    * @api public
    */
 
-  redirect(url, alt) {
+  redirect(url) {
     // location
-    if ('back' == url) {
-      deprecate('Special-cased string "back" through redirect will be removed in v3,' +
-                'consider migrating usage to ctx.back() instead.');
-      url = this.ctx.get('Referrer') || alt || '/';
-    }
     this.set('Location', url);
 
     // status
diff --git a/test/response/redirect.js b/test/response/redirect.js
index 619300fbc..ccc0b3f0b 100644
--- a/test/response/redirect.js
+++ b/test/response/redirect.js
@@ -12,34 +12,6 @@ describe('ctx.redirect(url)', () => {
     assert.equal(ctx.status, 302);
   });
 
-  describe('with "back"', () => {
-    it('should redirect to Referrer', () => {
-      const ctx = context();
-      ctx.req.headers.referrer = '/login';
-      ctx.redirect('back');
-      assert.equal(ctx.response.header.location, '/login');
-    });
-
-    it('should redirect to Referer', () => {
-      const ctx = context();
-      ctx.req.headers.referer = '/login';
-      ctx.redirect('back');
-      assert.equal(ctx.response.header.location, '/login');
-    });
-
-    it('should default to alt', () => {
-      const ctx = context();
-      ctx.redirect('back', '/index.html');
-      assert.equal(ctx.response.header.location, '/index.html');
-    });
-
-    it('should default redirect to /', () => {
-      const ctx = context();
-      ctx.redirect('back');
-      assert.equal(ctx.response.header.location, '/');
-    });
-  });
-
   describe('when html is accepted', () => {
     it('should respond with html', () => {
       const ctx = context();