* Make an HTTP GET request and return response body as string * @param {string} url - URL to fetch * @param {number} timeout - Request timeout in milliseconds (default: 15000) * @returns {Promise } Response body as string
(url, timeout = 15000)
| 117 | * @returns {Promise<string>} Response body as string |
| 118 | */ |
| 119 | function httpGet (url, timeout = 15000) { |
| 120 | return new Promise((resolve, reject) => { |
| 121 | const client = url.startsWith('https') ? https : http |
| 122 | |
| 123 | const req = client.get(url, { timeout }, (res) => { |
| 124 | // Handle redirects |
| 125 | if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) { |
| 126 | if (res.headers.location) { |
| 127 | // Handle relative URLs |
| 128 | let redirectUrl = res.headers.location |
| 129 | if (!redirectUrl.startsWith('http://') && !redirectUrl.startsWith('https://')) { |
| 130 | const parsedUrl = new URL(url) |
| 131 | redirectUrl = `${parsedUrl.protocol}//${parsedUrl.host}${redirectUrl}` |
| 132 | } |
| 133 | resolve(httpGet(redirectUrl, timeout)) |
| 134 | return |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | if (res.statusCode !== 200) { |
| 139 | reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage || 'Unknown error'}`)) |
| 140 | return |
| 141 | } |
| 142 | |
| 143 | const chunks = [] |
| 144 | res.on('data', (chunk) => chunks.push(chunk)) |
| 145 | res.on('end', () => { |
| 146 | const buffer = Buffer.concat(chunks) |
| 147 | resolve(buffer.toString()) |
| 148 | }) |
| 149 | res.on('error', reject) |
| 150 | }) |
| 151 | |
| 152 | req.on('error', reject) |
| 153 | req.on('timeout', () => { |
| 154 | req.destroy() |
| 155 | reject(new Error(`Request timeout after ${timeout}ms`)) |
| 156 | }) |
| 157 | }) |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * Extract a tar.gz file to destination directory |