Skip to content
Merged

Dev #25

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
112 changes: 92 additions & 20 deletions modules/analyze.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,73 @@ const now = require('performance-now');
const { isURL } = require('validator');
const makeHttpRequest = require('./httpRequest');
const performDnsLookup = require('./dnsLookup');
const checkSSLCertificate = require('./sslCheck');

function analyzeURL(req, res) {
try {
if (!req.body.url) {
throw new Error('URL is missing');
}

const urlToAnalyze = req.body.url;
let urlToAnalyze = req.body.url.trim();
const originalUrl = urlToAnalyze;
const parsedUrl = url.parse(urlToAnalyze);
const hadProtocol = !!parsedUrl.protocol;

// If no protocol, try HTTPS first
if (!hadProtocol) {
urlToAnalyze = `https://${urlToAnalyze}`;
}

// Validate the URL
if (!isURL(urlToAnalyze)) {
throw new Error('Invalid URL');
}

const hostname = url.parse(urlToAnalyze).hostname;
const isHttps = urlToAnalyze.startsWith('https://');

// Try the request
makeHttpRequest(urlToAnalyze, {}, (responseDetails, latencyMs, httpRequestError) => {
// If HTTPS failed and we added the protocol (no protocol originally), try HTTP as fallback
if (httpRequestError && isHttps && !hadProtocol) {
const httpUrl = urlToAnalyze.replace('https://', 'http://');
return makeHttpRequest(httpUrl, {}, (httpResponseDetails, httpLatencyMs, httpError) => {
if (httpError || !httpResponseDetails) {
return res.json({
isUp: false,
ipAddress: null,
uptime: 0,
latencyMs: 0,
dnsLookupMs: 0,
statusCode: null,
protocol: 'http',
sslInfo: null
});
}
processResponse(httpResponseDetails, httpLatencyMs, hostname, 'http', res);
});
}

if (httpRequestError || !responseDetails) {
console.error(`Failed to request ${urlToAnalyze}: ${httpRequestError || 'Unknown error'}`);
return res.json({ isUp: false, ipAddress: null, uptime: 0, latencyMs: 0, dnsLookupMs: 0 });
return res.json({
isUp: false,
ipAddress: null,
uptime: 0,
latencyMs: 0,
dnsLookupMs: 0,
statusCode: null,
protocol: isHttps ? 'https' : 'http',
sslInfo: null
});
}

processResponse(responseDetails, latencyMs, hostname, isHttps ? 'https' : 'http', res);
});

const isUp =
responseDetails.statusCode >= 200 && responseDetails.statusCode < 400;
function processResponse(responseDetails, latencyMs, hostname, protocol, res) {
const isUp = responseDetails.statusCode >= 200 && responseDetails.statusCode < 400;
if (!isUp) {
return res.json({
isUp: false,
Expand All @@ -35,31 +80,58 @@ function analyzeURL(req, res) {
latencyMs,
dnsLookupMs: 0,
statusCode: responseDetails.statusCode,
protocol: protocol,
sslInfo: null
});
}

performDnsLookup(hostname, ({ ipAddress, lookupMs, error: dnsLookupError }) => {
if (dnsLookupError) {
console.error(`Failed to lookup IP address for ${urlToAnalyze}: ${dnsLookupError}`);
console.error(`Failed to lookup IP address: ${dnsLookupError}`);
return res.status(500).json({ error: 'Internal server error' });
}

// Calculate uptime baseline on successful check
const uptime = 100;

console.log(
`isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}`
);
return res.json({
isUp: true,
ipAddress,
uptime,
latencyMs,
dnsLookupMs: lookupMs,
statusCode: responseDetails.statusCode,
});
// Check SSL certificate if HTTPS
if (protocol === 'https') {
checkSSLCertificate(hostname, 443, (sslError, sslInfo) => {
const uptime = 100;
const response = {
isUp: true,
ipAddress,
uptime,
latencyMs,
dnsLookupMs: lookupMs,
statusCode: responseDetails.statusCode,
protocol: protocol,
sslInfo: sslInfo
};

console.log(
`isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}, sslInfo: ${sslInfo ? JSON.stringify(sslInfo) : 'null'}`
);
return res.json(response);
});
} else {
// HTTP - no SSL info
const uptime = 100;
const response = {
isUp: true,
ipAddress,
uptime,
latencyMs,
dnsLookupMs: lookupMs,
statusCode: responseDetails.statusCode,
protocol: protocol,
sslInfo: null
};

console.log(
`isUp: ${isUp}, ipAddress: ${ipAddress}, uptime: ${uptime}%, latencyMs: ${latencyMs}, dnsLookupMs: ${lookupMs}, protocol: ${protocol}`
);
return res.json(response);
}
});
});
}
} catch (error) {
console.error(`Error analyzing URL: ${error.message}`);
return res.status(400).json({ error: error.message });
Expand Down
60 changes: 60 additions & 0 deletions modules/sslCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const https = require('https');
const tls = require('tls');

function checkSSLCertificate(hostname, port = 443, callback) {
let certRetrieved = false;

// Use tls.connect for more reliable certificate retrieval
const socket = tls.connect({
host: hostname,
port: port,
rejectUnauthorized: false, // We want to check even if cert is invalid
servername: hostname, // Important for SNI
}, () => {
try {
const cert = socket.getPeerCertificate(true);

if (cert && cert.valid_to) {
certRetrieved = true;
const expirationDate = new Date(cert.valid_to);
const now = new Date();
const daysUntilExpiry = Math.ceil((expirationDate - now) / (1000 * 60 * 60 * 24));

callback(null, {
expirationDate: expirationDate.toISOString(),
daysUntilExpiry: daysUntilExpiry,
isValid: expirationDate > now,
issuer: cert.issuer ? (cert.issuer.CN || JSON.stringify(cert.issuer)) : null,
subject: cert.subject ? (cert.subject.CN || hostname) : hostname,
});
} else {
callback(null, null);
}
} catch (err) {
callback(null, null);
} finally {
if (!socket.destroyed) {
socket.end();
}
}
});

socket.on('error', (error) => {
if (!certRetrieved) {
// If we can't get the certificate, return null (might be HTTP or connection issue)
callback(null, null);
}
});

socket.on('timeout', () => {
if (!certRetrieved) {
socket.destroy();
callback(null, null);
}
});

socket.setTimeout(5000);
}

module.exports = checkSSLCertificate;

Loading