Skip to content

Commit 660a13f

Browse files
committed
Manually handle 3xx redirects for POST calls
1 parent e01bfee commit 660a13f

File tree

8 files changed

+176
-18
lines changed

8 files changed

+176
-18
lines changed

services/notify-one-challenge.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
}),
1717
res = await request({
1818
method: 'GET',
19-
followAllRedirect: true,
2019
uri: testUrl,
2120
timeout: config.requestTimeout,
2221
resolveWithFullResponse: true
2322
});
2423

24+
console.log(`GET ${testUrl}`);
25+
2526
if (res.statusCode < 200 || res.statusCode > 299 || res.body !== challenge) {
2627
throw new ErrorResponse('Notification Failed');
2728
}

services/notify-one.js

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,31 @@
77
request = require('request-promise-native');
88

99
async function notifyOneRest(apiurl, resourceUrl) {
10-
const res = await request({
11-
method: 'POST',
12-
followAllRedirect: true,
13-
uri: apiurl,
14-
timeout: config.requestTimeout,
15-
form: {
16-
'url': resourceUrl
17-
},
18-
resolveWithFullResponse: true
19-
});
10+
let res;
11+
12+
try {
13+
res = await request({
14+
method: 'POST',
15+
uri: apiurl,
16+
timeout: config.requestTimeout,
17+
form: {
18+
'url': resourceUrl
19+
},
20+
resolveWithFullResponse: true
21+
});
22+
} catch (err) {
23+
if (!err.response) {
24+
throw err;
25+
}
26+
27+
res = err.response;
28+
if (res.statusCode >= 300 || res.statusCode < 400) {
29+
if (res.headers.location) {
30+
const location = new URL(res.headers.location, apiurl);
31+
return notifyOneRest(location.toString(), resourceUrl);
32+
}
33+
}
34+
}
2035

2136
if (res.statusCode < 200 || res.statusCode > 299) {
2237
throw new ErrorResponse('Notification Failed');

services/parse-notify-params.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161

6262
params.urlList = parseUrlList(req.body);
6363

64-
if (undefined === req.body.domain) {
64+
if (null == req.body.domain || '' === req.body.domain) {
6565
parts.client = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
6666
params.diffDomain = false;
6767
} else {

services/ping.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
try {
3434
res = await request({
3535
method: 'GET',
36-
followAllRedirect: true,
3736
uri: resourceUrl,
3837
timeout: config.requestTimeout,
3938
resolveWithFullResponse: true

services/please-notify.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
async function checkresourceUrlStatusCode(resourceUrl) {
1818
return request({
1919
method: 'GET',
20-
followAllRedirect: true,
2120
uri: resourceUrl,
2221
timeout: config.requestTimeout,
2322
resolveWithFullResponse: true

test/mock.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const https = require('https'),
55
textParser = bodyParser.text({ type: '*/xml'}),
66
urlencodedParser = bodyParser.urlencoded({ extended: false }),
77
parseRpcRequest = require('../services/parse-rpc-request'),
8+
querystring = require('querystring'),
89
MOCK_SERVER_DOMAIN = process.env.MOCK_SERVER_DOMAIN,
910
MOCK_SERVER_PORT = process.env.MOCK_SERVER_PORT || 8002,
1011
MOCK_SERVER_URL = process.env.MOCK_SERVER_URL || `http://${MOCK_SERVER_DOMAIN}:${MOCK_SERVER_PORT}`,
@@ -19,9 +20,21 @@ async function restController(req, res) {
1920
if (this.routes[method] && this.routes[method][path]) {
2021
this.requests[method][path].push(req);
2122
let responseBody = this.routes[method][path].responseBody;
22-
res
23-
.status(this.routes[method][path].status)
24-
.send(typeof responseBody === 'function' ? await responseBody(req) : responseBody);
23+
if (300 <= this.routes[method][path].status && 400 > this.routes[method][path].status) {
24+
let location = typeof responseBody === 'function' ? await responseBody(req) : responseBody;
25+
if (0 < Object.keys(req.query).length) {
26+
location += '?' + querystring.stringify(req.query);
27+
}
28+
res
29+
.redirect(
30+
this.routes[method][path].status,
31+
location
32+
);
33+
} else {
34+
res
35+
.status(this.routes[method][path].status)
36+
.send(typeof responseBody === 'function' ? await responseBody(req) : responseBody);
37+
}
2538
} else {
2639
res
2740
.status(501)

test/ping.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ for (const pingProtocol of ['XML-RPC', 'REST']) {
122122
}
123123
});
124124

125-
it.only(`should ping multiple subscribers on same domain`, async function () {
125+
it(`should ping multiple subscribers on same domain`, async function () {
126126
const feedPath = '/rss.xml',
127127
pingPath1 = '/feedupdated1',
128128
pingPath2 = '/feedupdated2'

test/please-notify.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,137 @@ for (const pingProtocol of ['XML-RPC', 'REST']) {
242242
expect(mock.requests.POST).property(pingPath).lengthOf(0, `Should not POST ${pingPath}`);
243243
}
244244
});
245+
246+
});
247+
248+
if ('xml-rpc' === protocol) {
249+
// Not Applicable
250+
continue;
251+
}
252+
253+
describe(`PleaseNotify ${pingProtocol} to ${protocol} via redirect returning ${returnFormat}`, function () {
254+
255+
before(async function () {
256+
await mongodb.before();
257+
await mock.before();
258+
});
259+
260+
after(async function () {
261+
await mongodb.after();
262+
await mock.after();
263+
});
264+
265+
beforeEach(async function () {
266+
await mongodb.beforeEach();
267+
await mock.beforeEach();
268+
});
269+
270+
afterEach(async function () {
271+
await mongodb.afterEach();
272+
await mock.afterEach();
273+
});
274+
275+
it('should accept a pleaseNotify for a redirected subscriber', async function () {
276+
const feedPath = '/rss.xml',
277+
resourceUrl = mock.serverUrl + feedPath;
278+
279+
let pingPath = '/feedupdated',
280+
redirPath = '/redirect',
281+
notifyProcedure = false;
282+
283+
let body = {
284+
domain: mock.serverDomain,
285+
port: 'https-post' === protocol ? mock.secureServerPort : mock.serverPort,
286+
path: redirPath,
287+
notifyProcedure: notifyProcedure,
288+
protocol,
289+
url1: resourceUrl
290+
};
291+
292+
if ('XML-RPC' === pingProtocol) {
293+
body = [
294+
notifyProcedure,
295+
'https-post' === protocol ? mock.secureServerPort : mock.serverPort,
296+
redirPath,
297+
protocol,
298+
[resourceUrl],
299+
mock.serverDomain
300+
];
301+
}
302+
303+
mock.route('GET', feedPath, 200, '<RSS Feed />');
304+
mock.route('GET', redirPath, 302, (req) => { return pingPath; });
305+
mock.route('GET', pingPath, 200, (req) => { return req.query.challenge; });
306+
mock.rpc(notifyProcedure, rpcReturnSuccess(true));
307+
308+
let res = await pleaseNotify(pingProtocol, body, returnFormat);
309+
310+
expect(res).status(200);
311+
312+
if ('XML-RPC' === pingProtocol) {
313+
expect(res.text).xml.equal(rpcReturnSuccess(true));
314+
} else {
315+
if ('JSON' === returnFormat) {
316+
expect(res.body).deep.equal({ success: true, msg: `Thanks for the registration. It worked. When the resource updates we\'ll notify you. Don\'t forget to re-register after 24 hours, your subscription will expire in 25. Keep on truckin!` });
317+
} else {
318+
expect(res.text).xml.equal('<notifyResult success="true" msg="Thanks for the registration. It worked. When the resource updates we\'ll notify you. Don\'t forget to re-register after 24 hours, your subscription will expire in 25. Keep on truckin!"/>');
319+
}
320+
}
321+
322+
expect(mock.requests.GET).property(feedPath).lengthOf(1, `Missing GET ${feedPath}`);
323+
expect(mock.requests.GET).property(pingPath).lengthOf(1, `Missing GET ${pingPath}`);
324+
});
325+
326+
it('should accept a pleaseNotify without domain for a redirected subscriber', async function () {
327+
const feedPath = '/rss.xml',
328+
resourceUrl = mock.serverUrl + feedPath;
329+
330+
let pingPath = '/feedupdated',
331+
redirPath = '/redirect',
332+
notifyProcedure = false;
333+
334+
335+
let body = {
336+
port: 'https-post' === protocol ? mock.secureServerPort : mock.serverPort,
337+
path: redirPath,
338+
notifyProcedure: notifyProcedure,
339+
protocol,
340+
url1: resourceUrl
341+
};
342+
343+
if ('XML-RPC' === pingProtocol) {
344+
body = [
345+
notifyProcedure,
346+
'https-post' === protocol ? mock.secureServerPort : mock.serverPort,
347+
redirPath,
348+
protocol,
349+
[resourceUrl]
350+
];
351+
}
352+
353+
mock.route('GET', feedPath, 200, '<RSS Feed />');
354+
mock.route('POST', redirPath, 302, (req) => { return pingPath; });
355+
mock.route('POST', pingPath, 200, 'Thanks for the update! :-)');
356+
mock.rpc(notifyProcedure, rpcReturnSuccess(true));
357+
358+
let res = await pleaseNotify(pingProtocol, body, returnFormat);
359+
360+
expect(res).status(200);
361+
362+
if ('XML-RPC' === pingProtocol) {
363+
expect(res.text).xml.equal(rpcReturnSuccess(true));
364+
} else {
365+
if ('JSON' === returnFormat) {
366+
expect(res.body).deep.equal({ success: true, msg: `Thanks for the registration. It worked. When the resource updates we\'ll notify you. Don\'t forget to re-register after 24 hours, your subscription will expire in 25. Keep on truckin!` });
367+
} else {
368+
expect(res.text).xml.equal('<notifyResult success="true" msg="Thanks for the registration. It worked. When the resource updates we\'ll notify you. Don\'t forget to re-register after 24 hours, your subscription will expire in 25. Keep on truckin!"/>');
369+
}
370+
}
371+
372+
expect(mock.requests.GET).property(feedPath).lengthOf(1, `Missing GET ${feedPath}`);
373+
expect(mock.requests.POST).property(pingPath).lengthOf(1, `Missing POST ${pingPath}`);
374+
});
375+
245376
});
246377

247378
} // end for pingProtocol

0 commit comments

Comments
 (0)