Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: redirect should work even when hostname or protocol is not speci… #1497

Merged
merged 1 commit into from Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/api/response.md
Expand Up @@ -93,6 +93,14 @@ Sets the link header.
* `url` {String} a url to redirect to
* `next` {Function} a callback function
* `options` {Object} an options object to configure a redirect
* `[options.secure]` {Boolean} whether to redirect to http or https
* `[options.hostname]` {String} redirect location's hostname
* `[options.pathname]` {String} redirect location's pathname
* `[options.port]` {String} redirect location's port number
* `[options.query]` {String} redirect location's query string parameters
* `[options.overrideQuery]` {Boolean} if true, `options.query` stomps over any
existing query parameters on current URL. by default, will merge the two.
* `[options.permanent]` {Boolean} if true, sets 301. defaults to 302.

A convenience method for 301/302 redirects. Using this method will
tell restify to stop execution of your handler chain. You can also
Expand Down
11 changes: 7 additions & 4 deletions lib/response.js
Expand Up @@ -599,14 +599,17 @@ Response.prototype.redirect = function redirect(arg1, arg2, arg3) {
};

// start building url based on options.
// first, set protocol.
finalUri.protocol = (secure === true) ? 'https' : 'http';

// then set host
// start with the host
if (opt.hostname) {
finalUri.hostname = opt.hostname;
}

// then set protocol IFF hostname is set - otherwise we end up with
// malformed URL.
if (finalUri.hostname) {
finalUri.protocol = (secure === true) ? 'https' : 'http';
}

// then set current path after the host
if (opt.pathname) {
finalUri.pathname = opt.pathname;
Expand Down
28 changes: 25 additions & 3 deletions test/response.test.js
Expand Up @@ -101,15 +101,14 @@ test('redirect to new relative string url as-is', function (t) {
res.redirect('/1', next);
});

CLIENT.get(join(LOCALHOST, '/20'), function (err, _, res) {
CLIENT.get(join(LOCALHOST, '/20?a=1'), function (err, _, res) {
t.ifError(err);
t.equal(res.statusCode, 302);
t.equal(res.headers.location, '/1');
t.end();
});
});


test('redirect to current url (reload)', function (t) {
SERVER.get('/2', function (req, res, next) {
res.redirect({
Expand Down Expand Up @@ -158,7 +157,6 @@ test('redirect to current url from https -> http', function (t) {
});
});


test('redirect by changing path', function (t) {
SERVER.get('/4', function (req, res, next) {
res.redirect({
Expand All @@ -174,6 +172,30 @@ test('redirect by changing path', function (t) {
});
});

test('GH-1494: redirect should succeed even if req.url does not specify host' +
' or protocol', function (t) {
SERVER.get('/5', function (req, res, next) {
res.redirect({
pathname: '/'
}, next);
});

// use a relative URL here instead of request with full protocol and host.
// this causes node to receive different values for req.url, which affects
// how reconstruction of the redirect URL is done. for example including
// full host will result in a req.url value of:
// http://127.0.0.1:57824/5
// using relative URL results in a req.url value of:
// /5
// this causes a bug as documented in GH-1494
CLIENT.get('/5', function (err, _, res) {
t.ifError(err);
t.equal(res.statusCode, 302);
t.equal(res.headers.location, '/');
t.end();
});
});

test('redirect should add query params', function (t) {
SERVER.get('/5', function (req, res, next) {
res.redirect({
Expand Down