Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions services/endpoints/ping-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
const ping = require('pingman');
const net = require('net');

// Max ICMP packets per check, matching the documented pingCheckCount limit
const MAX_COUNT = 5;

/* Bounds a numeric param, falling back to the default when absent or out of range */
const boundedParam = (value, fallback, max) => {
const num = Math.floor(Number(value));
if (!Number.isFinite(num) || num < 1) return fallback;
return Math.min(num, max);
};

/* Returned if the URL params are not present or correct */
const immediateError = (render, error) => {
render(JSON.stringify({
Expand All @@ -22,16 +32,16 @@ module.exports = (paramStr, render) => {
// Get the url to check from query params
const params = new URLSearchParams(paramStr.slice(paramStr.indexOf('?') + 1));
const host = params.get('host') || '';
const count = Number(params.get('count')) || 2;
const timeout = Number(params.get('timeout')) || 2000;
const count = boundedParam(params.get('count'), 2, MAX_COUNT);
const timeout = boundedParam(params.get('timeout'), 2000, count * 1000);
if (!host || typeof host !== 'string') {
immediateError(render, 'Invalid host given for ping check.');
return;
}
(async () => {
try {
const configuration = {
timeout: Math.round(timeout/1000),
timeout: Math.max(1, Math.round(timeout / 1000)),
numberOfEchos: count,
IPV4: net.isIPv4(host),
IPV6: net.isIPv6(host),
Expand Down
1 change: 1 addition & 0 deletions services/endpoints/status-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const makeRequest = (url, options, render) => {
headers,
maxRedirects,
timeout: 10000,
ignoreResponseBody: true,
httpsAgent: { rejectUnauthorized: !enableInsecure },
validateUrl: validateTargetUrl,
})
Expand Down
62 changes: 35 additions & 27 deletions services/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ function request(config) {
maxRedirects = 5,
timeout = 0,
maxResponseSize = 0,
ignoreResponseBody = false,
httpsAgent,
validateUrl,
} = config;
Expand Down Expand Up @@ -175,6 +176,39 @@ function request(config) {
return;
}

// Resolves (or rejects) the promise, from an already-complete response body
const finish = (raw) => {
let responseData;
try { responseData = JSON.parse(raw); } catch (_) { responseData = raw; }

const response = {
data: responseData,
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
};
// Expose raw request for socket access (non-enumerable, circular refs break stringify)
Object.defineProperty(response, 'request', {
value: req,
enumerable: false,
});

if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(response);
} else {
reject(new RequestError(
`Request failed with status ${res.statusCode}`,
{ response, code: res.statusCode },
));
}
};
// If body not needed, can finish early with just status code
if (ignoreResponseBody) {
finish('');
req.destroy();
return;
}

// Decompress response based on Content-Encoding (matching axios behavior)
let stream = res;
const encoding = (res.headers['content-encoding'] || '').toLowerCase();
Expand Down Expand Up @@ -209,33 +243,7 @@ function request(config) {
});
stream.on('end', () => {
if (aborted) return;
const raw = Buffer.concat(chunks).toString('utf8');
let responseData;
try { responseData = JSON.parse(raw); } catch (_) { responseData = raw; }

const response = {
data: responseData,
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
};
// Expose the raw request object for socket access (status-check.js
// needs this). Defined as non-enumerable so JSON.stringify() skips
// it — the http.ClientRequest has circular socket references that
// would otherwise crash any endpoint forwarding the response.
Object.defineProperty(response, 'request', {
value: req,
enumerable: false,
});

if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(response);
} else {
reject(new RequestError(
`Request failed with status ${res.statusCode}`,
{ response, code: res.statusCode },
));
}
finish(Buffer.concat(chunks).toString('utf8'));
});
});

Expand Down
Loading