minipass-fetch
Advanced tools
Comparing version 1.4.1 to 2.0.0
@@ -21,6 +21,6 @@ 'use strict' | ||
? Buffer.from(element.buffer, element.byteOffset, element.byteLength) | ||
: element instanceof ArrayBuffer ? Buffer.from(element) | ||
: element instanceof Blob ? element[BUFFER] | ||
: typeof element === 'string' ? Buffer.from(element) | ||
: Buffer.from(String(element)) | ||
: element instanceof ArrayBuffer ? Buffer.from(element) | ||
: element instanceof Blob ? element[BUFFER] | ||
: typeof element === 'string' ? Buffer.from(element) | ||
: Buffer.from(String(element)) | ||
size += buffer.length | ||
@@ -27,0 +27,0 @@ buffers.push(buffer) |
106
lib/body.js
@@ -6,3 +6,3 @@ 'use strict' | ||
const Blob = require('./blob.js') | ||
const {BUFFER} = Blob | ||
const { BUFFER } = Blob | ||
const FetchError = require('./fetch-error.js') | ||
@@ -28,6 +28,6 @@ | ||
? Buffer.from(bodyArg) | ||
: ArrayBuffer.isView(bodyArg) | ||
? Buffer.from(bodyArg.buffer, bodyArg.byteOffset, bodyArg.byteLength) | ||
: Minipass.isStream(bodyArg) ? bodyArg | ||
: Buffer.from(String(bodyArg)) | ||
: ArrayBuffer.isView(bodyArg) | ||
? Buffer.from(bodyArg.buffer, bodyArg.byteOffset, bodyArg.byteLength) | ||
: Minipass.isStream(bodyArg) ? bodyArg | ||
: Buffer.from(String(bodyArg)) | ||
@@ -99,10 +99,12 @@ this[INTERNALS] = { | ||
[CONSUME_BODY] () { | ||
if (this[INTERNALS].disturbed) | ||
if (this[INTERNALS].disturbed) { | ||
return Promise.reject(new TypeError(`body used already for: ${ | ||
this.url}`)) | ||
} | ||
this[INTERNALS].disturbed = true | ||
if (this[INTERNALS].error) | ||
if (this[INTERNALS].error) { | ||
return Promise.reject(this[INTERNALS].error) | ||
} | ||
@@ -114,4 +116,5 @@ // body is null | ||
if (Buffer.isBuffer(this.body)) | ||
if (Buffer.isBuffer(this.body)) { | ||
return Promise.resolve(this.body) | ||
} | ||
@@ -121,4 +124,5 @@ const upstream = isBlob(this.body) ? this.body.stream() : this.body | ||
/* istanbul ignore if: should never happen */ | ||
if (!Minipass.isStream(upstream)) | ||
if (!Minipass.isStream(upstream)) { | ||
return Promise.resolve(Buffer.alloc(0)) | ||
} | ||
@@ -140,3 +144,3 @@ const stream = this.size && upstream instanceof MinipassSized ? upstream | ||
// though we expect it'll get cleared eventually. | ||
if (resTimeout) { | ||
if (resTimeout && resTimeout.unref) { | ||
resTimeout.unref() | ||
@@ -161,11 +165,12 @@ } | ||
// request was aborted, reject with this Error | ||
if (er.name === 'AbortError' || er.name === 'FetchError') | ||
if (er.name === 'AbortError' || er.name === 'FetchError') { | ||
throw er | ||
else if (er.name === 'RangeError') | ||
} else if (er.name === 'RangeError') { | ||
throw new FetchError(`Could not create Buffer from response body for ${ | ||
this.url}: ${er.message}`, 'system', er) | ||
else | ||
} else { | ||
// other errors, such as incorrect content-encoding or content-length | ||
throw new FetchError(`Invalid response body while trying to fetch ${ | ||
this.url}: ${er.message}`, 'system', er) | ||
} | ||
}) | ||
@@ -175,4 +180,5 @@ } | ||
static clone (instance) { | ||
if (instance.bodyUsed) | ||
if (instance.bodyUsed) { | ||
throw new Error('cannot clone body after it is used') | ||
} | ||
@@ -201,4 +207,5 @@ const body = instance.body | ||
return p2 | ||
} else | ||
} else { | ||
return instance.body | ||
} | ||
} | ||
@@ -211,34 +218,34 @@ | ||
? 'application/x-www-form-urlencoded;charset=UTF-8' | ||
: isBlob(body) ? body.type || null | ||
: Buffer.isBuffer(body) ? null | ||
: Object.prototype.toString.call(body) === '[object ArrayBuffer]' ? null | ||
: ArrayBuffer.isView(body) ? null | ||
: typeof body.getBoundary === 'function' | ||
? `multipart/form-data;boundary=${body.getBoundary()}` | ||
: Minipass.isStream(body) ? null | ||
: 'text/plain;charset=UTF-8' | ||
: isBlob(body) ? body.type || null | ||
: Buffer.isBuffer(body) ? null | ||
: Object.prototype.toString.call(body) === '[object ArrayBuffer]' ? null | ||
: ArrayBuffer.isView(body) ? null | ||
: typeof body.getBoundary === 'function' | ||
? `multipart/form-data;boundary=${body.getBoundary()}` | ||
: Minipass.isStream(body) ? null | ||
: 'text/plain;charset=UTF-8' | ||
} | ||
static getTotalBytes (instance) { | ||
const {body} = instance | ||
const { body } = instance | ||
return (body === null || body === undefined) ? 0 | ||
: isBlob(body) ? body.size | ||
: Buffer.isBuffer(body) ? body.length | ||
: body && typeof body.getLengthSync === 'function' && ( | ||
: isBlob(body) ? body.size | ||
: Buffer.isBuffer(body) ? body.length | ||
: body && typeof body.getLengthSync === 'function' && ( | ||
// detect form data input from form-data module | ||
body._lengthRetrievers && | ||
/* istanbul ignore next */ body._lengthRetrievers.length == 0 || // 1.x | ||
/* istanbul ignore next */ body._lengthRetrievers.length === 0 || // 1.x | ||
body.hasKnownLength && body.hasKnownLength()) // 2.x | ||
? body.getLengthSync() | ||
: null | ||
? body.getLengthSync() | ||
: null | ||
} | ||
static writeToStream (dest, instance) { | ||
const {body} = instance | ||
const { body } = instance | ||
if (body === null || body === undefined) | ||
if (body === null || body === undefined) { | ||
dest.end() | ||
else if (Buffer.isBuffer(body) || typeof body === 'string') | ||
} else if (Buffer.isBuffer(body) || typeof body === 'string') { | ||
dest.end(body) | ||
else { | ||
} else { | ||
// body is stream or blob | ||
@@ -259,6 +266,5 @@ const stream = isBlob(body) ? body.stream() : body | ||
json: { enumerable: true }, | ||
text: { enumerable: true } | ||
text: { enumerable: true }, | ||
}) | ||
const isURLSearchParams = obj => | ||
@@ -288,22 +294,24 @@ // Duck-typing as a necessary condition. | ||
const convertBody = (buffer, headers) => { | ||
/* istanbul ignore if */ | ||
if (typeof convert !== 'function') | ||
if (typeof convert !== 'function') { | ||
throw new Error('The package `encoding` must be installed to use the textConverted() function') | ||
} | ||
const ct = headers && headers.get('content-type') | ||
let charset = 'utf-8' | ||
let res, str | ||
let res | ||
// header | ||
if (ct) | ||
if (ct) { | ||
res = /charset=([^;]*)/i.exec(ct) | ||
} | ||
// no charset in content type, peek at response body for at most 1024 bytes | ||
str = buffer.slice(0, 1024).toString() | ||
const str = buffer.slice(0, 1024).toString() | ||
// html5 | ||
if (!res && str) | ||
if (!res && str) { | ||
res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str) | ||
} | ||
@@ -316,13 +324,16 @@ // html4 | ||
res = /<meta[\s]+?content=(['"])(.+?)\1[\s]+?http-equiv=(['"])content-type\3/i.exec(str) | ||
if (res) | ||
res.pop() // drop last quote | ||
if (res) { | ||
res.pop() | ||
} // drop last quote | ||
} | ||
if (res) | ||
if (res) { | ||
res = /charset=(.*)/i.exec(res.pop()) | ||
} | ||
} | ||
// xml | ||
if (!res && str) | ||
if (!res && str) { | ||
res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str) | ||
} | ||
@@ -335,4 +346,5 @@ // found charset | ||
// ref: https://hsivonen.fi/encoding-menu/ | ||
if (charset === 'gb2312' || charset === 'gbk') | ||
if (charset === 'gb2312' || charset === 'gbk') { | ||
charset = 'gb18030' | ||
} | ||
} | ||
@@ -339,0 +351,0 @@ |
@@ -8,4 +8,5 @@ 'use strict' | ||
// pick up code, expected, path, ... | ||
if (systemError) | ||
if (systemError) { | ||
Object.assign(this, systemError) | ||
} | ||
@@ -12,0 +13,0 @@ this.errno = this.code |
'use strict' | ||
const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/ | ||
const invalidTokenRegex = /[^^_`a-zA-Z\-0-9!#$%&'*+.|~]/ | ||
const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/ | ||
@@ -7,4 +7,5 @@ | ||
name = `${name}` | ||
if (invalidTokenRegex.test(name) || name === '') | ||
if (invalidTokenRegex.test(name) || name === '') { | ||
throw new TypeError(`${name} is not a legal HTTP header name`) | ||
} | ||
} | ||
@@ -14,4 +15,5 @@ | ||
value = `${value}` | ||
if (invalidHeaderCharRegex.test(value)) | ||
if (invalidHeaderCharRegex.test(value)) { | ||
throw new TypeError(`${value} is not a legal HTTP header value`) | ||
} | ||
} | ||
@@ -22,4 +24,5 @@ | ||
for (const key in map) { | ||
if (key.toLowerCase() === name) | ||
if (key.toLowerCase() === name) { | ||
return key | ||
} | ||
} | ||
@@ -45,4 +48,5 @@ return undefined | ||
// no-op | ||
if (init === undefined || init === null) | ||
if (init === undefined || init === null) { | ||
return | ||
} | ||
@@ -52,4 +56,5 @@ if (typeof init === 'object') { | ||
if (method !== null && method !== undefined) { | ||
if (typeof method !== 'function') | ||
if (typeof method !== 'function') { | ||
throw new TypeError('Header pairs must be iterable') | ||
} | ||
@@ -61,7 +66,9 @@ // sequence<sequence<ByteString>> | ||
if (typeof pair !== 'object' || | ||
typeof pair[Symbol.iterator] !== 'function') | ||
typeof pair[Symbol.iterator] !== 'function') { | ||
throw new TypeError('Each header pair must be iterable') | ||
} | ||
const arrPair = Array.from(pair) | ||
if (arrPair.length !== 2) | ||
if (arrPair.length !== 2) { | ||
throw new TypeError('Each header pair must be a name/value tuple') | ||
} | ||
pairs.push(arrPair) | ||
@@ -79,4 +86,5 @@ } | ||
} | ||
} else | ||
} else { | ||
throw new TypeError('Provided initializer must be an object') | ||
} | ||
} | ||
@@ -88,4 +96,5 @@ | ||
const key = find(this[MAP], name) | ||
if (key === undefined) | ||
if (key === undefined) { | ||
return null | ||
} | ||
@@ -120,6 +129,7 @@ return this[MAP][key].join(', ') | ||
const key = find(this[MAP], name) | ||
if (key !== undefined) | ||
if (key !== undefined) { | ||
this[MAP][key].push(value) | ||
else | ||
} else { | ||
this[MAP][name] = [value] | ||
} | ||
} | ||
@@ -137,4 +147,5 @@ | ||
const key = find(this[MAP], name) | ||
if (key !== undefined) | ||
if (key !== undefined) { | ||
delete this[MAP][key] | ||
} | ||
} | ||
@@ -154,3 +165,3 @@ | ||
[Symbol.iterator]() { | ||
[Symbol.iterator] () { | ||
return new HeadersIterator(this, 'key+value') | ||
@@ -173,4 +184,5 @@ } | ||
const hostHeaderKey = find(headers[MAP], 'Host') | ||
if (hostHeaderKey !== undefined) | ||
if (hostHeaderKey !== undefined) { | ||
obj[hostHeaderKey] = obj[hostHeaderKey][0] | ||
} | ||
@@ -183,17 +195,21 @@ return obj | ||
for (const name of Object.keys(obj)) { | ||
if (invalidTokenRegex.test(name)) | ||
if (invalidTokenRegex.test(name)) { | ||
continue | ||
} | ||
if (Array.isArray(obj[name])) { | ||
for (const val of obj[name]) { | ||
if (invalidHeaderCharRegex.test(val)) | ||
if (invalidHeaderCharRegex.test(val)) { | ||
continue | ||
} | ||
if (headers[MAP][name] === undefined) | ||
if (headers[MAP][name] === undefined) { | ||
headers[MAP][name] = [val] | ||
else | ||
} else { | ||
headers[MAP][name].push(val) | ||
} | ||
} | ||
} else if (!invalidHeaderCharRegex.test(obj[name])) | ||
} else if (!invalidHeaderCharRegex.test(obj[name])) { | ||
headers[MAP][name] = [obj[name]] | ||
} | ||
} | ||
@@ -240,4 +256,5 @@ return headers | ||
/* istanbul ignore if: should be impossible */ | ||
if (!this || Object.getPrototypeOf(this) !== HeadersIterator.prototype) | ||
if (!this || Object.getPrototypeOf(this) !== HeadersIterator.prototype) { | ||
throw new TypeError('Value of `this` is not a HeadersIterator') | ||
} | ||
@@ -244,0 +261,0 @@ const { target, kind, index } = this[INTERNAL] |
175
lib/index.js
'use strict' | ||
const Url = require('url') | ||
const { URL } = require('url') | ||
const http = require('http') | ||
@@ -18,4 +18,2 @@ const https = require('https') | ||
const resolveUrl = Url.resolve | ||
const fetch = (url, opts) => { | ||
@@ -32,3 +30,3 @@ if (/^data:/.test(url)) { | ||
'Content-Length': data.length, | ||
} | ||
}, | ||
})) | ||
@@ -66,4 +64,5 @@ } catch (er) { | ||
if (signal && signal.aborted) | ||
if (signal && signal.aborted) { | ||
return abort() | ||
} | ||
@@ -77,4 +76,5 @@ const abortAndFinalize = () => { | ||
req.abort() | ||
if (signal) | ||
if (signal) { | ||
signal.removeEventListener('abort', abortAndFinalize) | ||
} | ||
clearTimeout(reqTimeout) | ||
@@ -86,4 +86,5 @@ } | ||
if (signal) | ||
if (signal) { | ||
signal.addEventListener('abort', abortAndFinalize) | ||
} | ||
@@ -113,4 +114,5 @@ let reqTimeout = null | ||
// istanbul ignore next | ||
if (req.res) | ||
if (req.res) { | ||
req.res.emit('error', er) | ||
} | ||
reject(new FetchError(`request to ${request.url} failed, reason: ${ | ||
@@ -133,89 +135,79 @@ er.message}`, 'system', er)) | ||
const locationURL = location === null ? null | ||
: resolveUrl(request.url, location) | ||
: (new URL(location, request.url)).toString() | ||
// HTTP fetch step 5.5 | ||
switch (request.redirect) { | ||
case 'error': | ||
reject(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${ | ||
request.url}`, 'no-redirect')) | ||
if (request.redirect === 'error') { | ||
reject(new FetchError('uri requested responds with a redirect, ' + | ||
`redirect mode is set to error: ${request.url}`, 'no-redirect')) | ||
finalize() | ||
return | ||
} else if (request.redirect === 'manual') { | ||
// node-fetch-specific step: make manual redirect a bit easier to | ||
// use by setting the Location header value to the resolved URL. | ||
if (locationURL !== null) { | ||
// handle corrupted header | ||
try { | ||
headers.set('Location', locationURL) | ||
} catch (err) { | ||
/* istanbul ignore next: nodejs server prevent invalid | ||
response headers, we can't test this through normal | ||
request */ | ||
reject(err) | ||
} | ||
} | ||
} else if (request.redirect === 'follow' && locationURL !== null) { | ||
// HTTP-redirect fetch step 5 | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${ | ||
request.url}`, 'max-redirect')) | ||
finalize() | ||
return | ||
} | ||
case 'manual': | ||
// node-fetch-specific step: make manual redirect a bit easier to | ||
// use by setting the Location header value to the resolved URL. | ||
if (locationURL !== null) { | ||
// handle corrupted header | ||
try { | ||
headers.set('Location', locationURL) | ||
} catch (err) { | ||
/* istanbul ignore next: nodejs server prevent invalid | ||
response headers, we can't test this through normal | ||
request */ | ||
reject(err) | ||
} | ||
} | ||
break | ||
// HTTP-redirect fetch step 9 | ||
if (res.statusCode !== 303 && | ||
request.body && | ||
getTotalBytes(request) === null) { | ||
reject(new FetchError( | ||
'Cannot follow redirect with body being a readable stream', | ||
'unsupported-redirect' | ||
)) | ||
finalize() | ||
return | ||
} | ||
case 'follow': | ||
// HTTP-redirect fetch step 2 | ||
if (locationURL === null) { | ||
break | ||
} | ||
// Update host due to redirection | ||
request.headers.set('host', (new URL(locationURL)).host) | ||
// HTTP-redirect fetch step 5 | ||
if (request.counter >= request.follow) { | ||
reject(new FetchError(`maximum redirect reached at: ${ | ||
request.url}`, 'max-redirect')) | ||
finalize() | ||
return | ||
} | ||
// HTTP-redirect fetch step 6 (counter increment) | ||
// Create a new Request object. | ||
const requestOpts = { | ||
headers: new Headers(request.headers), | ||
follow: request.follow, | ||
counter: request.counter + 1, | ||
agent: request.agent, | ||
compress: request.compress, | ||
method: request.method, | ||
body: request.body, | ||
signal: request.signal, | ||
timeout: request.timeout, | ||
} | ||
// HTTP-redirect fetch step 9 | ||
if (res.statusCode !== 303 && | ||
request.body && | ||
getTotalBytes(request) === null) { | ||
reject(new FetchError( | ||
'Cannot follow redirect with body being a readable stream', | ||
'unsupported-redirect' | ||
)) | ||
finalize() | ||
return | ||
} | ||
// HTTP-redirect fetch step 11 | ||
if (res.statusCode === 303 || ( | ||
(res.statusCode === 301 || res.statusCode === 302) && | ||
request.method === 'POST' | ||
)) { | ||
requestOpts.method = 'GET' | ||
requestOpts.body = undefined | ||
requestOpts.headers.delete('content-length') | ||
} | ||
// Update host due to redirection | ||
request.headers.set('host', Url.parse(locationURL).host) | ||
// HTTP-redirect fetch step 6 (counter increment) | ||
// Create a new Request object. | ||
const requestOpts = { | ||
headers: new Headers(request.headers), | ||
follow: request.follow, | ||
counter: request.counter + 1, | ||
agent: request.agent, | ||
compress: request.compress, | ||
method: request.method, | ||
body: request.body, | ||
signal: request.signal, | ||
timeout: request.timeout, | ||
} | ||
// HTTP-redirect fetch step 11 | ||
if (res.statusCode === 303 || ( | ||
(res.statusCode === 301 || res.statusCode === 302) && | ||
request.method === 'POST' | ||
)) { | ||
requestOpts.method = 'GET' | ||
requestOpts.body = undefined | ||
requestOpts.headers.delete('content-length') | ||
} | ||
// HTTP-redirect fetch step 15 | ||
resolve(fetch(new Request(locationURL, requestOpts))) | ||
finalize() | ||
return | ||
// HTTP-redirect fetch step 15 | ||
resolve(fetch(new Request(locationURL, requestOpts))) | ||
finalize() | ||
return | ||
} | ||
} // end if(isRedirect) | ||
// prepare response | ||
@@ -226,2 +218,9 @@ res.once('end', () => | ||
const body = new Minipass() | ||
// if an error occurs, either on the response stream itself, on one of the | ||
// decoder streams, or a response length timeout from the Body class, we | ||
// forward the error through to our internal body stream. If we see an | ||
// error event on that, we call finalize to abort the request and ensure | ||
// we don't leave a socket believing a request is in flight. | ||
// this is difficult to test, so lacks specific coverage. | ||
body.on('error', finalize) | ||
// exceedingly rare that the stream would have an error, | ||
@@ -242,3 +241,3 @@ // but just in case we proxy it to the stream in use. | ||
trailer: new Promise(resolve => | ||
res.on('end', () => resolve(createHeadersLenient(res.trailers)))) | ||
res.on('end', () => resolve(createHeadersLenient(res.trailers)))), | ||
} | ||
@@ -267,3 +266,2 @@ | ||
// Be less strict when decoding compressed responses, since sometimes | ||
@@ -279,3 +277,3 @@ // servers send slightly invalid responses that are still accepted | ||
// for gzip | ||
if (codings == 'gzip' || codings == 'x-gzip') { | ||
if (codings === 'gzip' || codings === 'x-gzip') { | ||
const unzip = new zlib.Gunzip(zlibOptions) | ||
@@ -293,3 +291,3 @@ response = new Response( | ||
// for deflate | ||
if (codings == 'deflate' || codings == 'x-deflate') { | ||
if (codings === 'deflate' || codings === 'x-deflate') { | ||
// handle the infamous raw deflate response from old servers | ||
@@ -312,5 +310,4 @@ // a hack for old IIS and Apache servers | ||
// for br | ||
if (codings == 'br') { | ||
if (codings === 'br') { | ||
// ignoring coverage so tests don't have to fake support (or lack of) for brotli | ||
@@ -317,0 +314,0 @@ // istanbul ignore next |
'use strict' | ||
const Url = require('url') | ||
const { URL } = require('url') | ||
const Minipass = require('minipass') | ||
@@ -15,4 +15,2 @@ const Headers = require('./headers.js') | ||
const { parse: parseUrl, format: formatUrl } = Url | ||
const isRequest = input => | ||
@@ -32,10 +30,11 @@ typeof input === 'object' && typeof input[INTERNALS] === 'object' | ||
constructor (input, init = {}) { | ||
const parsedURL = isRequest(input) ? Url.parse(input.url) | ||
: input && input.href ? Url.parse(input.href) | ||
: Url.parse(`${input}`) | ||
const parsedURL = isRequest(input) ? new URL(input.url) | ||
: input && input.href ? new URL(input.href) | ||
: new URL(`${input}`) | ||
if (isRequest(input)) | ||
if (isRequest(input)) { | ||
init = { ...input[INTERNALS], ...init } | ||
else if (!input || typeof input === 'string') | ||
} else if (!input || typeof input === 'string') { | ||
input = {} | ||
} | ||
@@ -46,4 +45,5 @@ const method = (init.method || input.method || 'GET').toUpperCase() | ||
if ((init.body !== null && init.body !== undefined || | ||
isRequest(input) && input.body !== null) && isGETHEAD) | ||
isRequest(input) && input.body !== null) && isGETHEAD) { | ||
throw new TypeError('Request with GET/HEAD method cannot have body') | ||
} | ||
@@ -64,4 +64,5 @@ const inputBody = init.body !== null && init.body !== undefined ? init.body | ||
const contentType = extractContentType(inputBody) | ||
if (contentType) | ||
if (contentType) { | ||
headers.append('Content-Type', contentType) | ||
} | ||
} | ||
@@ -72,4 +73,5 @@ | ||
if (signal !== null && signal !== undefined && !isAbortSignal(signal)) | ||
if (signal !== null && signal !== undefined && !isAbortSignal(signal)) { | ||
throw new TypeError('Expected signal must be an instanceof AbortSignal') | ||
} | ||
@@ -133,19 +135,19 @@ // TLS specific options that are handled by node | ||
get method() { | ||
get method () { | ||
return this[INTERNALS].method | ||
} | ||
get url() { | ||
return formatUrl(this[INTERNALS].parsedURL) | ||
get url () { | ||
return this[INTERNALS].parsedURL.toString() | ||
} | ||
get headers() { | ||
get headers () { | ||
return this[INTERNALS].headers | ||
} | ||
get redirect() { | ||
get redirect () { | ||
return this[INTERNALS].redirect | ||
} | ||
get signal() { | ||
get signal () { | ||
return this[INTERNALS].signal | ||
@@ -167,11 +169,10 @@ } | ||
// fetch step 1.3 | ||
if (!headers.has('Accept')) | ||
if (!headers.has('Accept')) { | ||
headers.set('Accept', '*/*') | ||
} | ||
// Basic fetch | ||
if (!parsedURL.protocol || !parsedURL.hostname) | ||
throw new TypeError('Only absolute URLs are supported') | ||
if (!/^https?:$/.test(parsedURL.protocol)) | ||
if (!/^https?:$/.test(parsedURL.protocol)) { | ||
throw new TypeError('Only HTTP(S) protocols are supported') | ||
} | ||
@@ -191,14 +192,17 @@ if (request.signal && | ||
? getTotalBytes(request) | ||
: null | ||
: null | ||
if (contentLengthValue) | ||
if (contentLengthValue) { | ||
headers.set('Content-Length', contentLengthValue + '') | ||
} | ||
// HTTP-network-or-cache fetch step 2.11 | ||
if (!headers.has('User-Agent')) | ||
if (!headers.has('User-Agent')) { | ||
headers.set('User-Agent', defaultUserAgent) | ||
} | ||
// HTTP-network-or-cache fetch step 2.15 | ||
if (request.compress && !headers.has('Accept-Encoding')) | ||
if (request.compress && !headers.has('Accept-Encoding')) { | ||
headers.set('Accept-Encoding', 'gzip,deflate') | ||
} | ||
@@ -209,4 +213,5 @@ const agent = typeof request.agent === 'function' | ||
if (!headers.has('Connection') && !agent) | ||
if (!headers.has('Connection') && !agent) { | ||
headers.set('Connection', 'close') | ||
} | ||
@@ -237,4 +242,17 @@ // TLS specific options that are handled by node | ||
// we cannot spread parsedURL directly, so we have to read each property one-by-one | ||
// and map them to the equivalent https?.request() method options | ||
const urlProps = { | ||
auth: parsedURL.username || parsedURL.password | ||
? `${parsedURL.username}:${parsedURL.password}` | ||
: '', | ||
host: parsedURL.host, | ||
hostname: parsedURL.hostname, | ||
path: parsedURL.pathname, | ||
port: parsedURL.port, | ||
protocol: parsedURL.protocol, | ||
} | ||
return { | ||
...parsedURL, | ||
...urlProps, | ||
method: request.method, | ||
@@ -241,0 +259,0 @@ headers: exportNodeCompatibleHeaders(headers), |
@@ -20,4 +20,5 @@ 'use strict' | ||
const contentType = extractContentType(body) | ||
if (contentType) | ||
if (contentType) { | ||
headers.append('Content-Type', contentType) | ||
} | ||
} | ||
@@ -47,3 +48,3 @@ | ||
get ok () { | ||
get ok () { | ||
return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300 | ||
@@ -50,0 +51,0 @@ } |
{ | ||
"name": "minipass-fetch", | ||
"version": "1.4.1", | ||
"version": "2.0.0", | ||
"description": "An implementation of window.fetch in Node.js using Minipass streams", | ||
@@ -12,3 +12,9 @@ "license": "MIT", | ||
"postversion": "npm publish", | ||
"postpublish": "git push origin --follow-tags" | ||
"postpublish": "git push origin --follow-tags", | ||
"lint": "eslint '**/*.js'", | ||
"postlint": "npm-template-check", | ||
"template-copy": "npm-template-copy --force", | ||
"lintfix": "npm run lint -- --fix", | ||
"prepublishOnly": "git push origin --follow-tags", | ||
"posttest": "npm run lint" | ||
}, | ||
@@ -20,6 +26,7 @@ "tap": { | ||
"devDependencies": { | ||
"@ungap/url-search-params": "^0.1.2", | ||
"@npmcli/template-oss": "^2.8.1", | ||
"@ungap/url-search-params": "^0.2.2", | ||
"abort-controller": "^3.0.0", | ||
"abortcontroller-polyfill": "~1.3.0", | ||
"form-data": "^2.5.1", | ||
"abortcontroller-polyfill": "~1.7.3", | ||
"form-data": "^4.0.0", | ||
"parted": "^0.1.1", | ||
@@ -31,5 +38,5 @@ "string-to-arraybuffer": "^1.0.2", | ||
"dependencies": { | ||
"minipass": "^3.1.0", | ||
"minipass": "^3.1.6", | ||
"minipass-sized": "^1.0.3", | ||
"minizlib": "^2.0.0" | ||
"minizlib": "^2.1.2" | ||
}, | ||
@@ -50,8 +57,12 @@ "optionalDependencies": { | ||
"files": [ | ||
"index.js", | ||
"lib/*.js" | ||
"bin", | ||
"lib" | ||
], | ||
"engines": { | ||
"node": ">=8" | ||
"node": "^12.13.0 || ^14.15.0 || >=16" | ||
}, | ||
"author": "GitHub Inc.", | ||
"templateOSS": { | ||
"version": "2.8.1" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
44601
1263
1
9
11
0
Updatedminipass@^3.1.6
Updatedminizlib@^2.1.2