Skip to content

Commit e0b575f

Browse files
andrewshellclaude
andcommitted
Replace request/request-promise-native with native fetch API
- Remove deprecated request and request-promise-native dependencies - Update all HTTP client services to use Node.js native fetch API - Implement proper timeout handling with AbortController - Maintain existing error handling and redirect behavior - Convert form data and XML request handling to fetch patterns Services updated: - services/notify-one.js: REST and RPC notification endpoints - services/ping.js: Resource change detection - services/notify-one-challenge.js: Challenge verification - services/please-notify.js: Resource URL validation Benefits: - Remove security vulnerabilities from deprecated packages - Use modern native Node.js APIs - Reduce bundle size and dependency count - Better performance with native implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 759420b commit e0b575f

File tree

6 files changed

+116
-151
lines changed

6 files changed

+116
-151
lines changed

package-lock.json

Lines changed: 0 additions & 76 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
"mongodb": "6.17.0",
3232
"morgan": "^1.10.0",
3333
"nconf": "^0.13.0",
34-
"request": "^2.88.2",
35-
"request-promise-native": "1.0.9",
3634
"sprintf-js": "^1.1.3",
3735
"xml2js": "^0.6.2",
3836
"xmlbuilder": "^15.1.1"

services/notify-one-challenge.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,39 @@ const config = require('../config'),
22
ErrorResponse = require('./error-response'),
33
notifyOne = require('./notify-one'),
44
getRandomPassword = require('./get-random-password'),
5-
querystring = require('querystring'),
6-
request = require('request-promise-native');
5+
querystring = require('querystring');
76

87
async function notifyOneChallengeRest(apiurl, resourceUrl) {
9-
const challenge = getRandomPassword(20),
10-
testUrl = apiurl + '?' + querystring.stringify({
11-
'url': resourceUrl,
12-
'challenge': challenge
13-
}),
14-
res = await request({
8+
const challenge = getRandomPassword(20);
9+
const testUrl = apiurl + '?' + querystring.stringify({
10+
'url': resourceUrl,
11+
'challenge': challenge
12+
});
13+
14+
console.log(`GET ${testUrl}`);
15+
16+
const controller = new AbortController();
17+
const timeoutId = setTimeout(() => controller.abort(), config.requestTimeout);
18+
19+
try {
20+
const res = await fetch(testUrl, {
1521
method: 'GET',
16-
uri: testUrl,
17-
timeout: config.requestTimeout,
18-
resolveWithFullResponse: true
22+
signal: controller.signal
1923
});
2024

21-
console.log(`GET ${testUrl}`);
25+
clearTimeout(timeoutId);
26+
27+
const body = await res.text();
2228

23-
if (res.statusCode < 200 || res.statusCode > 299 || res.body !== challenge) {
24-
throw new ErrorResponse('Notification Failed');
29+
if (res.status < 200 || res.status > 299 || body !== challenge) {
30+
throw new ErrorResponse('Notification Failed');
31+
}
32+
} catch (err) {
33+
clearTimeout(timeoutId);
34+
if (err.name === 'AbortError') {
35+
throw new ErrorResponse('Notification Failed - Timeout');
36+
}
37+
throw err;
2538
}
2639
}
2740

services/notify-one.js

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,49 @@
11
const builder = require('xmlbuilder'),
22
config = require('../config'),
33
ErrorResponse = require('./error-response'),
4-
request = require('request-promise-native'),
54
{ URL } = require('url');
65

76
async function notifyOneRest(apiurl, resourceUrl) {
8-
let res;
7+
const controller = new AbortController();
8+
const timeoutId = setTimeout(() => controller.abort(), config.requestTimeout);
99

1010
try {
11-
res = await request({
11+
const formData = new URLSearchParams();
12+
formData.append('url', resourceUrl);
13+
14+
const res = await fetch(apiurl, {
1215
method: 'POST',
13-
uri: apiurl,
14-
timeout: config.requestTimeout,
15-
form: {
16-
'url': resourceUrl
16+
headers: {
17+
'Content-Type': 'application/x-www-form-urlencoded'
1718
},
18-
resolveWithFullResponse: true
19+
body: formData,
20+
signal: controller.signal,
21+
redirect: 'manual'
1922
});
20-
} catch (err) {
21-
if (!err.response) {
22-
throw err;
23-
}
2423

25-
res = err.response;
26-
if (res.statusCode >= 300 || res.statusCode < 400) {
27-
if (res.headers.location) {
28-
const location = new URL(res.headers.location, apiurl);
29-
return notifyOneRest(location.toString(), resourceUrl);
24+
clearTimeout(timeoutId);
25+
26+
// Handle redirects manually
27+
if (res.status >= 300 && res.status < 400) {
28+
const location = res.headers.get('location');
29+
if (location) {
30+
const redirectUrl = new URL(location, apiurl);
31+
return notifyOneRest(redirectUrl.toString(), resourceUrl);
3032
}
3133
}
32-
}
3334

34-
if (res.statusCode < 200 || res.statusCode > 299) {
35-
throw new ErrorResponse('Notification Failed');
36-
}
35+
if (res.status < 200 || res.status > 299) {
36+
throw new ErrorResponse('Notification Failed');
37+
}
3738

38-
return true;
39+
return true;
40+
} catch (err) {
41+
clearTimeout(timeoutId);
42+
if (err.name === 'AbortError') {
43+
throw new ErrorResponse('Notification Failed - Timeout');
44+
}
45+
throw err;
46+
}
3947
}
4048

4149
async function notifyOneRpc(notifyProcedure, apiurl, resourceUrl) {
@@ -50,22 +58,33 @@ async function notifyOneRpc(notifyProcedure, apiurl, resourceUrl) {
5058
}
5159
}).end({ pretty: true });
5260

53-
let res = await request({
54-
method: 'POST',
55-
uri: apiurl,
56-
timeout: 4000,
57-
body: xmldoc,
58-
resolveWithFullResponse: true,
59-
headers: {
60-
'content-type': 'text/xml'
61+
const controller = new AbortController();
62+
const timeoutId = setTimeout(() => controller.abort(), 4000);
63+
64+
try {
65+
const res = await fetch(apiurl, {
66+
method: 'POST',
67+
headers: {
68+
'Content-Type': 'text/xml'
69+
},
70+
body: xmldoc,
71+
signal: controller.signal
72+
});
73+
74+
clearTimeout(timeoutId);
75+
76+
if (res.status < 200 || res.status > 299) {
77+
throw new ErrorResponse('Notification Failed');
6178
}
62-
});
6379

64-
if (res.statusCode < 200 || res.statusCode > 299) {
65-
throw new ErrorResponse('Notification Failed');
80+
return true;
81+
} catch (err) {
82+
clearTimeout(timeoutId);
83+
if (err.name === 'AbortError') {
84+
throw new ErrorResponse('Notification Failed - Timeout');
85+
}
86+
throw err;
6687
}
67-
68-
return true;
6988
}
7089

7190
function notifyOne(notifyProcedure, apiurl, protocol, resourceUrl) {

services/ping.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const appMessage = require('./app-messages'),
77
moment = require('moment'),
88
mongodb = require('./mongodb'),
99
notifySubscribers = require('./notify-subscribers'),
10-
request = require('request-promise-native'),
1110
sprintf = require('sprintf-js').sprintf;
1211

1312
function checkPingFrequency(resource) {
@@ -26,37 +25,46 @@ function md5Hash(value) {
2625

2726
async function checkForResourceChange(resource, resourceUrl, startticks) {
2827
let res;
28+
let body = '';
29+
30+
const controller = new AbortController();
31+
const timeoutId = setTimeout(() => controller.abort(), config.requestTimeout);
2932

3033
try {
31-
res = await request({
34+
res = await fetch(resourceUrl, {
3235
method: 'GET',
33-
uri: resourceUrl,
34-
timeout: config.requestTimeout,
35-
resolveWithFullResponse: true
36+
signal: controller.signal
3637
});
38+
39+
clearTimeout(timeoutId);
40+
41+
if (res.status >= 200 && res.status <= 299) {
42+
body = await res.text();
43+
}
3744
} catch {
38-
res = { statusCode: 404 };
45+
clearTimeout(timeoutId);
46+
res = { status: 404 };
3947
}
4048

4149
resource.ctChecks += 1;
4250
resource.whenLastCheck = new Date(moment().utc().format());
4351

44-
if (res.statusCode < 200 || res.statusCode > 299) {
52+
if (res.status < 200 || res.status > 299) {
4553
throw new ErrorResponse(sprintf(appMessage.error.ping.readResource, resourceUrl));
4654
}
4755

48-
const hash = md5Hash(res.body);
56+
const hash = md5Hash(body);
4957

5058
if (resource.lastHash !== hash) {
5159
resource.flDirty = true;
52-
} else if (resource.lastSize !== res.body.length) {
60+
} else if (resource.lastSize !== body.length) {
5361
resource.flDirty = true;
5462
} else {
5563
resource.flDirty = false;
5664
}
5765

5866
resource.lastHash = hash;
59-
resource.lastSize = res.body.length;
67+
resource.lastSize = body.length;
6068

6169
await logEvent(
6270
'Ping',

0 commit comments

Comments
 (0)