Skip to content

Commit 4d1f581

Browse files
committed
Merge branch 'hotfix/0.0.2.1-fix-redirect-protocol'
2 parents e05f446 + 0bbcb79 commit 4d1f581

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

HISTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33

44
- 0.0.2
55
Added responder.redirect().
6+
7+
- 0.0.2.1-fix-redirect-protocol
8+
Adding http/https protocol to redirect urls.
9+
Redirect url full paths require a leading slash.

lib/server/responder.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ function is_relative_path(url) {
88
return url.indexOf('.') === 0;
99
}
1010

11+
function is_https(req) {
12+
return req && req.headers['x-forwarded-proto'] === 'https';
13+
}
14+
15+
function prepend_http_protocol(url, req) {
16+
if (is_https(req)) {
17+
return 'https://' + url;
18+
} else {
19+
return 'http://' + url;
20+
}
21+
}
22+
1123
var responder = {
1224
error: function (res, err, next) {
1325
next(err);
@@ -82,9 +94,13 @@ var responder = {
8294
if (is_relative_path(url)) {
8395
url = url.replace(/^./, '');
8496
url = host + req.path + url;
97+
} else if (url.match(/^\//)) {
98+
url = host + url; // a bit hacky
8599
} else {
86-
url = host + '/' + url;
100+
return responder.error(res, new restify.InternalError('Full-path redirect url requires leading slash (/)'), next);
87101
}
102+
103+
url = prepend_http_protocol(url, req);
88104
}
89105

90106
res.header('Content-Type', content_type);

test/server/responder.functional.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ var methods = {
2121
},
2222

2323
redirect_to_full_path: function (req, res, next) {
24+
var args = {url: '/foo/bar'};
25+
return responder.redirect(req, res, args, next);
26+
},
27+
28+
redirect_to_full_path_without_leading_slash: function (req, res, next) {
2429
var args = {url: 'foo/bar'};
2530
return responder.redirect(req, res, args, next);
2631
},
@@ -56,6 +61,12 @@ var routes = [
5661
func: methods.redirect_to_full_path,
5762
middleware: []
5863
},
64+
{
65+
method: "get",
66+
url: "/test/redirects/full_path_without_leading_slash",
67+
func: methods.redirect_to_full_path_without_leading_slash,
68+
middleware: []
69+
},
5970
{
6071
method: "get",
6172
url: "/test/redirects/relative_path",
@@ -123,19 +134,29 @@ describe("functional - server/responder.js", function () {
123134
context("when full path passed in without host and port", function () {
124135
it("redirects to the correct URL using request's host and port", function (done) {
125136
http_client.get('/test/redirects/full_path', function (err, result, raw_res) {
126-
var expected_location = http.host + ':' + http.port + '/foo/bar';
137+
var expected_location = 'http://' + http.host + ':' + http.port + '/foo/bar';
127138
assert.equal(raw_res.headers.location, expected_location);
128139
assert.equal(raw_res.statusCode, 302);
129140
done();
130141
});
131142
});
132143
});
133144

145+
context("when full path passed in without leading slash", function () {
146+
it("returns Internal error", function (done) {
147+
http_client.get('/test/redirects/full_path_without_leading_slash', function (err, result, raw_res) {
148+
assert.equal(err.code, 'Internal');
149+
assert.ok(err.message.match(/leading slash/));
150+
done();
151+
});
152+
});
153+
});
154+
134155
context("when relative path passed in", function () {
135156
it("redirects to the correct URL using request's host and port", function (done) {
136157
var starting_path = '/test/redirects/relative_path';
137158
http_client.get(starting_path, function (err, result, raw_res) {
138-
var expected_location = http.host + ':' + http.port + starting_path + '/foo/bar';
159+
var expected_location = 'http://' + http.host + ':' + http.port + starting_path + '/foo/bar';
139160
assert.equal(raw_res.headers.location, expected_location);
140161
assert.equal(raw_res.statusCode, 302);
141162
done();

test/server/responder.unit.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
var assert = require('assert');
22
var sinon = require('sinon');
3+
var support = require('../support');
34
var responder = main.server.responder;
45

56
var fake_res = {
67
header: function () {},
78
status: function () {},
8-
json: function () {}
9+
json: function () {},
10+
send: function () {}
911
};
1012

11-
var fake_req = {};
13+
var fake_req = {
14+
headers: {
15+
host: 'local.foo.com'
16+
}
17+
};
1218

1319
var fake_next = function () {};
1420

@@ -78,7 +84,7 @@ describe("responder.js", function () {
7884
next();
7985
});
8086

81-
responder.redirect(fake_res, fake_req, null, fake_next);
87+
responder.redirect(fake_req, fake_res, null, fake_next);
8288

8389
assert.ok(responder.error.called);
8490

@@ -94,13 +100,27 @@ describe("responder.js", function () {
94100
});
95101

96102
delete args.url;
97-
responder.redirect(fake_res, fake_req, args, fake_next);
103+
responder.redirect(fake_req, fake_res, args, fake_next);
98104

99105
assert.ok(responder.error.called);
100106

101107
responder.error.restore();
102108
done();
103109
});
104110
});
111+
112+
context("when request's protocol is https", function() {
113+
sinon.stub(fake_res, 'header');
114+
115+
var https_req = support.shallow_clone(fake_req);
116+
https_req.headers['x-forwarded-proto'] = 'https';
117+
var args = {url: '/foo/bar'};
118+
119+
responder.redirect(https_req, fake_res, args, fake_next);
120+
121+
assert.ok(fake_res.header.calledWith('Location', "https://local.foo.com/foo/bar"));
122+
123+
fake_res.header.restore();
124+
});
105125
});
106126
});

0 commit comments

Comments
 (0)