electron-fetch
Advanced tools
Comparing version 1.8.0 to 1.9.0-0
@@ -7,2 +7,6 @@ | ||
## v1.9.0 | ||
- Fix handling of invalid headers (thanks wheezard) | ||
- Update dependencies | ||
## v1.8.0 | ||
@@ -9,0 +13,0 @@ - Fix typings for FetchError |
@@ -1384,132 +1384,137 @@ import { parse, format, resolve } from 'url'; | ||
req.on('response', res => { | ||
clearTimeout(reqTimeout); | ||
try { | ||
clearTimeout(reqTimeout); | ||
if (request.signal) { | ||
request.signal.removeEventListener('abort', abortRequest); | ||
} // handle redirect | ||
if (request.signal) { | ||
request.signal.removeEventListener('abort', abortRequest); | ||
} // handle redirect | ||
if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') { | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
return; | ||
} | ||
if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') { | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
return; | ||
} | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
return; | ||
} | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
return; | ||
} | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
return; | ||
} // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
return; | ||
} // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect | ||
if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { | ||
request.method = 'GET'; | ||
request.body = null; | ||
request.headers.delete('content-length'); | ||
} | ||
if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { | ||
request.method = 'GET'; | ||
request.body = null; | ||
request.headers.delete('content-length'); | ||
} | ||
request.counter++; | ||
resolve$1(fetch(resolve(request.url, res.headers.location), request)); | ||
return; | ||
} // normalize location header for manual redirect mode | ||
request.counter++; | ||
resolve$1(fetch(resolve(request.url, res.headers.location), request)); | ||
return; | ||
} // normalize location header for manual redirect mode | ||
const headers = new Headers(); | ||
const headers = new Headers(); | ||
for (var _i = 0, _Object$keys = Object.keys(res.headers); _i < _Object$keys.length; _i++) { | ||
const name = _Object$keys[_i]; | ||
for (var _i = 0, _Object$keys = Object.keys(res.headers); _i < _Object$keys.length; _i++) { | ||
const name = _Object$keys[_i]; | ||
if (Array.isArray(res.headers[name])) { | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(res.headers[name]), _step2; !(_step2 = _iterator2()).done;) { | ||
const val = _step2.value; | ||
headers.append(name, val); | ||
if (Array.isArray(res.headers[name])) { | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(res.headers[name]), _step2; !(_step2 = _iterator2()).done;) { | ||
const val = _step2.value; | ||
headers.append(name, val); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
} | ||
} | ||
if (request.redirect === 'manual' && headers.has('location')) { | ||
headers.set('location', resolve(request.url, headers.get('location'))); | ||
} // prepare response | ||
if (request.redirect === 'manual' && headers.has('location')) { | ||
headers.set('location', resolve(request.url, headers.get('location'))); | ||
} // prepare response | ||
let body = new PassThrough(); | ||
res.on('error', err => body.emit('error', err)); | ||
res.pipe(body); | ||
body.on('error', cancelRequest); | ||
body.on('cancel-request', cancelRequest); | ||
let body = new PassThrough(); | ||
res.on('error', err => body.emit('error', err)); | ||
res.pipe(body); | ||
body.on('error', cancelRequest); | ||
body.on('cancel-request', cancelRequest); | ||
const abortBody = () => { | ||
res.destroy(); | ||
res.emit('error', new FetchError('request aborted', 'abort')); // separated from the `.destroy()` because somehow Node's IncomingMessage streams do not emit errors on destroy | ||
}; | ||
const abortBody = () => { | ||
res.destroy(); | ||
res.emit('error', new FetchError('request aborted', 'abort')); // separated from the `.destroy()` because somehow Node's IncomingMessage streams do not emit errors on destroy | ||
}; | ||
if (request.signal) { | ||
request.signal.addEventListener('abort', abortBody); | ||
res.on('end', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
res.on('error', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
} | ||
if (request.signal) { | ||
request.signal.addEventListener('abort', abortBody); | ||
res.on('end', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
res.on('error', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
} | ||
const responseOptions = { | ||
url: request.url, | ||
status: res.statusCode, | ||
statusText: res.statusMessage, | ||
headers: headers, | ||
size: request.size, | ||
timeout: request.timeout, | ||
useElectronNet: request.useElectronNet, | ||
useSessionCookies: request.useSessionCookies | ||
}; // HTTP-network fetch step 16.1.2 | ||
const responseOptions = { | ||
url: request.url, | ||
status: res.statusCode, | ||
statusText: res.statusMessage, | ||
headers, | ||
size: request.size, | ||
timeout: request.timeout, | ||
useElectronNet: request.useElectronNet, | ||
useSessionCookies: request.useSessionCookies | ||
}; // HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); // HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. running on Electron/net module (it manages it for us) | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
const codings = headers.get('Content-Encoding'); // HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. running on Electron/net module (it manages it for us) | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
if (!request.useElectronNet && request.method !== 'HEAD' && codings !== null && res.statusCode !== 204 && res.statusCode !== 304) { | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
// /!\ This is disabled for now, because it seems broken in recent node | ||
// const zlibOptions = { | ||
// flush: zlib.Z_SYNC_FLUSH, | ||
// finishFlush: zlib.Z_SYNC_FLUSH | ||
// } | ||
if (codings === 'gzip' || codings === 'x-gzip') { | ||
// for gzip | ||
body = body.pipe(zlib.createGunzip()); | ||
} else if (codings === 'deflate' || codings === 'x-deflate') { | ||
// for deflate | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new PassThrough()); | ||
return raw.once('data', chunk => { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(zlib.createInflate()); | ||
} else { | ||
body = body.pipe(zlib.createInflateRaw()); | ||
} | ||
if (!request.useElectronNet && request.method !== 'HEAD' && codings !== null && res.statusCode !== 204 && res.statusCode !== 304) { | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
// /!\ This is disabled for now, because it seems broken in recent node | ||
// const zlibOptions = { | ||
// flush: zlib.Z_SYNC_FLUSH, | ||
// finishFlush: zlib.Z_SYNC_FLUSH | ||
// } | ||
if (codings === 'gzip' || codings === 'x-gzip') { | ||
// for gzip | ||
body = body.pipe(zlib.createGunzip()); | ||
} else if (codings === 'deflate' || codings === 'x-deflate') { | ||
// for deflate | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new PassThrough()); | ||
return raw.once('data', chunk => { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(zlib.createInflate()); | ||
} else { | ||
body = body.pipe(zlib.createInflateRaw()); | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve$1(response); | ||
}); | ||
const response = new Response(body, responseOptions); | ||
resolve$1(response); | ||
}); | ||
} | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve$1(response); | ||
} catch (error) { | ||
reject(new FetchError(`Invalid response: ${error.message}`, 'invalid-response')); | ||
cancelRequest(); | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve$1(response); | ||
}); | ||
@@ -1516,0 +1521,0 @@ writeToStream(req, request); |
215
lib/index.js
@@ -1412,132 +1412,137 @@ 'use strict'; | ||
req.on('response', res => { | ||
clearTimeout(reqTimeout); | ||
try { | ||
clearTimeout(reqTimeout); | ||
if (request.signal) { | ||
request.signal.removeEventListener('abort', abortRequest); | ||
} // handle redirect | ||
if (request.signal) { | ||
request.signal.removeEventListener('abort', abortRequest); | ||
} // handle redirect | ||
if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') { | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
return; | ||
} | ||
if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') { | ||
if (request.redirect === 'error') { | ||
reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); | ||
return; | ||
} | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
return; | ||
} | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); | ||
return; | ||
} | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
return; | ||
} // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect | ||
if (!res.headers.location) { | ||
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); | ||
return; | ||
} // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect | ||
if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { | ||
request.method = 'GET'; | ||
request.body = null; | ||
request.headers.delete('content-length'); | ||
} | ||
if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { | ||
request.method = 'GET'; | ||
request.body = null; | ||
request.headers.delete('content-length'); | ||
} | ||
request.counter++; | ||
resolve(fetch(url.resolve(request.url, res.headers.location), request)); | ||
return; | ||
} // normalize location header for manual redirect mode | ||
request.counter++; | ||
resolve(fetch(url.resolve(request.url, res.headers.location), request)); | ||
return; | ||
} // normalize location header for manual redirect mode | ||
const headers = new Headers(); | ||
const headers = new Headers(); | ||
for (var _i = 0, _Object$keys = Object.keys(res.headers); _i < _Object$keys.length; _i++) { | ||
const name = _Object$keys[_i]; | ||
for (var _i = 0, _Object$keys = Object.keys(res.headers); _i < _Object$keys.length; _i++) { | ||
const name = _Object$keys[_i]; | ||
if (Array.isArray(res.headers[name])) { | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(res.headers[name]), _step2; !(_step2 = _iterator2()).done;) { | ||
const val = _step2.value; | ||
headers.append(name, val); | ||
if (Array.isArray(res.headers[name])) { | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(res.headers[name]), _step2; !(_step2 = _iterator2()).done;) { | ||
const val = _step2.value; | ||
headers.append(name, val); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
} | ||
} else { | ||
headers.append(name, res.headers[name]); | ||
} | ||
} | ||
if (request.redirect === 'manual' && headers.has('location')) { | ||
headers.set('location', url.resolve(request.url, headers.get('location'))); | ||
} // prepare response | ||
if (request.redirect === 'manual' && headers.has('location')) { | ||
headers.set('location', url.resolve(request.url, headers.get('location'))); | ||
} // prepare response | ||
let body = new Stream.PassThrough(); | ||
res.on('error', err => body.emit('error', err)); | ||
res.pipe(body); | ||
body.on('error', cancelRequest); | ||
body.on('cancel-request', cancelRequest); | ||
let body = new Stream.PassThrough(); | ||
res.on('error', err => body.emit('error', err)); | ||
res.pipe(body); | ||
body.on('error', cancelRequest); | ||
body.on('cancel-request', cancelRequest); | ||
const abortBody = () => { | ||
res.destroy(); | ||
res.emit('error', new FetchError('request aborted', 'abort')); // separated from the `.destroy()` because somehow Node's IncomingMessage streams do not emit errors on destroy | ||
}; | ||
const abortBody = () => { | ||
res.destroy(); | ||
res.emit('error', new FetchError('request aborted', 'abort')); // separated from the `.destroy()` because somehow Node's IncomingMessage streams do not emit errors on destroy | ||
}; | ||
if (request.signal) { | ||
request.signal.addEventListener('abort', abortBody); | ||
res.on('end', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
res.on('error', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
} | ||
if (request.signal) { | ||
request.signal.addEventListener('abort', abortBody); | ||
res.on('end', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
res.on('error', () => { | ||
request.signal.removeEventListener('abort', abortBody); | ||
}); | ||
} | ||
const responseOptions = { | ||
url: request.url, | ||
status: res.statusCode, | ||
statusText: res.statusMessage, | ||
headers: headers, | ||
size: request.size, | ||
timeout: request.timeout, | ||
useElectronNet: request.useElectronNet, | ||
useSessionCookies: request.useSessionCookies | ||
}; // HTTP-network fetch step 16.1.2 | ||
const responseOptions = { | ||
url: request.url, | ||
status: res.statusCode, | ||
statusText: res.statusMessage, | ||
headers, | ||
size: request.size, | ||
timeout: request.timeout, | ||
useElectronNet: request.useElectronNet, | ||
useSessionCookies: request.useSessionCookies | ||
}; // HTTP-network fetch step 16.1.2 | ||
const codings = headers.get('Content-Encoding'); // HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. running on Electron/net module (it manages it for us) | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
const codings = headers.get('Content-Encoding'); // HTTP-network fetch step 16.1.3: handle content codings | ||
// in following scenarios we ignore compression support | ||
// 1. running on Electron/net module (it manages it for us) | ||
// 2. HEAD request | ||
// 3. no Content-Encoding header | ||
// 4. no content response (204) | ||
// 5. content not modified response (304) | ||
if (!request.useElectronNet && request.method !== 'HEAD' && codings !== null && res.statusCode !== 204 && res.statusCode !== 304) { | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
// /!\ This is disabled for now, because it seems broken in recent node | ||
// const zlibOptions = { | ||
// flush: zlib.Z_SYNC_FLUSH, | ||
// finishFlush: zlib.Z_SYNC_FLUSH | ||
// } | ||
if (codings === 'gzip' || codings === 'x-gzip') { | ||
// for gzip | ||
body = body.pipe(zlib__namespace.createGunzip()); | ||
} else if (codings === 'deflate' || codings === 'x-deflate') { | ||
// for deflate | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new Stream.PassThrough()); | ||
return raw.once('data', chunk => { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(zlib__namespace.createInflate()); | ||
} else { | ||
body = body.pipe(zlib__namespace.createInflateRaw()); | ||
} | ||
if (!request.useElectronNet && request.method !== 'HEAD' && codings !== null && res.statusCode !== 204 && res.statusCode !== 304) { | ||
// Be less strict when decoding compressed responses, since sometimes | ||
// servers send slightly invalid responses that are still accepted | ||
// by common browsers. | ||
// Always using Z_SYNC_FLUSH is what cURL does. | ||
// /!\ This is disabled for now, because it seems broken in recent node | ||
// const zlibOptions = { | ||
// flush: zlib.Z_SYNC_FLUSH, | ||
// finishFlush: zlib.Z_SYNC_FLUSH | ||
// } | ||
if (codings === 'gzip' || codings === 'x-gzip') { | ||
// for gzip | ||
body = body.pipe(zlib__namespace.createGunzip()); | ||
} else if (codings === 'deflate' || codings === 'x-deflate') { | ||
// for deflate | ||
// handle the infamous raw deflate response from old servers | ||
// a hack for old IIS and Apache servers | ||
const raw = res.pipe(new Stream.PassThrough()); | ||
return raw.once('data', chunk => { | ||
// see http://stackoverflow.com/questions/37519828 | ||
if ((chunk[0] & 0x0F) === 0x08) { | ||
body = body.pipe(zlib__namespace.createInflate()); | ||
} else { | ||
body = body.pipe(zlib__namespace.createInflateRaw()); | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve(response); | ||
}); | ||
const response = new Response(body, responseOptions); | ||
resolve(response); | ||
}); | ||
} | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve(response); | ||
} catch (error) { | ||
reject(new FetchError(`Invalid response: ${error.message}`, 'invalid-response')); | ||
cancelRequest(); | ||
} | ||
const response = new Response(body, responseOptions); | ||
resolve(response); | ||
}); | ||
@@ -1544,0 +1549,0 @@ writeToStream(req, request); |
{ | ||
"name": "electron-fetch", | ||
"version": "1.8.0", | ||
"version": "1.9.0-0", | ||
"description": "A light-weight module that brings window.fetch to electron's background process", | ||
@@ -45,5 +45,5 @@ "main": "lib/index.js", | ||
"devDependencies": { | ||
"@babel/core": "^7.17.8", | ||
"@babel/preset-env": "^7.16.11", | ||
"@babel/register": "^7.17.7", | ||
"@babel/core": "^7.18.6", | ||
"@babel/preset-env": "^7.18.6", | ||
"@babel/register": "^7.18.6", | ||
"abortcontroller-polyfill": "^1.7.3", | ||
@@ -57,3 +57,3 @@ "babel-eslint": "^10.1.0", | ||
"cross-env": "^7.0.3", | ||
"electron": "^17.1.2", | ||
"electron": "^19.0.8", | ||
"electron-mocha": "^11.0.2", | ||
@@ -64,3 +64,3 @@ "form-data": "^4.0.0", | ||
"istanbul-lib-coverage": "^3.2.0", | ||
"mocha": "^9.2.2", | ||
"mocha": "^10.0.0", | ||
"nyc": "^15.1.0", | ||
@@ -71,8 +71,8 @@ "parted": "^0.1.1", | ||
"resumer": "0.0.0", | ||
"rollup": "^2.70.1", | ||
"rollup": "^2.76.0", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"standard": "^16.0.4", | ||
"standard": "^17.0.0", | ||
"stoppable": "^1.1.0", | ||
"ts-node": "^10.7.0", | ||
"typescript": "^4.6.2", | ||
"ts-node": "^10.8.2", | ||
"typescript": "^4.7.4", | ||
"whatwg-url": "^11.0.0", | ||
@@ -79,0 +79,0 @@ "xvfb-maybe": "^0.2.1" |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
116192
2682
1