/** * HTTP Utility for Node.js environments * * Handles HTTP requests with fallback from curl to built-in http module */ const { exec } = require('child_process'); const util = require('util'); const http = require('http'); const execAsync = util.promisify(exec); class HttpUtil { static curlAvailable = null; static async checkCurlAvailability() { if (this.curlAvailable !== null) { return this.curlAvailable; } try { await execAsync('which curl'); this.curlAvailable = true; return true; } catch (error) { this.curlAvailable = false; return false; } } static async get(url) { const isCurlAvailable = await this.checkCurlAvailability(); if (isCurlAvailable) { return this.getWithCurl(url); } else { return this.getWithHttp(url); } } static async getWithCurl(url) { try { const { stdout } = await execAsync(`curl -s ${url}`); return JSON.parse(stdout); } catch (error) { throw new Error(`curl request failed: ${error.message}`); } } static async getWithHttp(url) { return new Promise((resolve, reject) => { const urlObj = new URL(url); const req = http.get({ hostname: urlObj.hostname, port: urlObj.port || 80, path: urlObj.pathname + urlObj.search, timeout: 5000 }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const parsedData = JSON.parse(data); resolve(parsedData); } catch (parseError) { reject(new Error(`JSON parse error: ${parseError.message}`)); } }); }); req.on('error', (error) => { reject(new Error(`HTTP request error: ${error.message}`)); }); req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); }); }); } static async post(url, data) { const isCurlAvailable = await this.checkCurlAvailability(); if (isCurlAvailable) { return this.postWithCurl(url, data); } else { return this.postWithHttp(url, data); } } static async postWithCurl(url, data) { try { const jsonData = JSON.stringify(data); const { stdout } = await execAsync(`curl -s -X POST -H "Content-Type: application/json" -d '${jsonData}' ${url}`); return JSON.parse(stdout); } catch (error) { throw new Error(`curl POST request failed: ${error.message}`); } } static async postWithHttp(url, data) { return new Promise((resolve, reject) => { const urlObj = new URL(url); const postData = JSON.stringify(data); const options = { hostname: urlObj.hostname, port: urlObj.port || 80, path: urlObj.pathname + urlObj.search, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }, timeout: 5000 }; const req = http.request(options, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', () => { try { const parsedData = JSON.parse(responseData); resolve(parsedData); } catch (parseError) { reject(new Error(`JSON parse error: ${parseError.message}`)); } }); }); req.on('error', (error) => { reject(new Error(`HTTP request error: ${error.message}`)); }); req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); }); req.write(postData); req.end(); }); } } module.exports = HttpUtil;