trouble-in-terror-town/node_modules/minipass-fetch/lib/request.js
Mikolaj 2bbacbea09 did some more work on networking and removed EOS in favor of LRM
did some more work on networking and removed EOS in favor of Light Reflective Mirror
2022-05-31 15:04:31 +02:00

281 lines
7 KiB
JavaScript

'use strict'
const { URL } = require('url')
const Minipass = require('minipass')
const Headers = require('./headers.js')
const { exportNodeCompatibleHeaders } = Headers
const Body = require('./body.js')
const { clone, extractContentType, getTotalBytes } = Body
const version = require('../package.json').version
const defaultUserAgent =
`minipass-fetch/${version} (+https://github.com/isaacs/minipass-fetch)`
const INTERNALS = Symbol('Request internals')
const isRequest = input =>
typeof input === 'object' && typeof input[INTERNALS] === 'object'
const isAbortSignal = signal => {
const proto = (
signal
&& typeof signal === 'object'
&& Object.getPrototypeOf(signal)
)
return !!(proto && proto.constructor.name === 'AbortSignal')
}
class Request extends Body {
constructor (input, init = {}) {
const parsedURL = isRequest(input) ? new URL(input.url)
: input && input.href ? new URL(input.href)
: new URL(`${input}`)
if (isRequest(input)) {
init = { ...input[INTERNALS], ...init }
} else if (!input || typeof input === 'string') {
input = {}
}
const method = (init.method || input.method || 'GET').toUpperCase()
const isGETHEAD = method === 'GET' || method === 'HEAD'
if ((init.body !== null && init.body !== undefined ||
isRequest(input) && input.body !== null) && isGETHEAD) {
throw new TypeError('Request with GET/HEAD method cannot have body')
}
const inputBody = init.body !== null && init.body !== undefined ? init.body
: isRequest(input) && input.body !== null ? clone(input)
: null
super(inputBody, {
timeout: init.timeout || input.timeout || 0,
size: init.size || input.size || 0,
})
const headers = new Headers(init.headers || input.headers || {})
if (inputBody !== null && inputBody !== undefined &&
!headers.has('Content-Type')) {
const contentType = extractContentType(inputBody)
if (contentType) {
headers.append('Content-Type', contentType)
}
}
const signal = 'signal' in init ? init.signal
: null
if (signal !== null && signal !== undefined && !isAbortSignal(signal)) {
throw new TypeError('Expected signal must be an instanceof AbortSignal')
}
// TLS specific options that are handled by node
const {
ca,
cert,
ciphers,
clientCertEngine,
crl,
dhparam,
ecdhCurve,
family,
honorCipherOrder,
key,
passphrase,
pfx,
rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0',
secureOptions,
secureProtocol,
servername,
sessionIdContext,
} = init
this[INTERNALS] = {
method,
redirect: init.redirect || input.redirect || 'follow',
headers,
parsedURL,
signal,
ca,
cert,
ciphers,
clientCertEngine,
crl,
dhparam,
ecdhCurve,
family,
honorCipherOrder,
key,
passphrase,
pfx,
rejectUnauthorized,
secureOptions,
secureProtocol,
servername,
sessionIdContext,
}
// node-fetch-only options
this.follow = init.follow !== undefined ? init.follow
: input.follow !== undefined ? input.follow
: 20
this.compress = init.compress !== undefined ? init.compress
: input.compress !== undefined ? input.compress
: true
this.counter = init.counter || input.counter || 0
this.agent = init.agent || input.agent
}
get method () {
return this[INTERNALS].method
}
get url () {
return this[INTERNALS].parsedURL.toString()
}
get headers () {
return this[INTERNALS].headers
}
get redirect () {
return this[INTERNALS].redirect
}
get signal () {
return this[INTERNALS].signal
}
clone () {
return new Request(this)
}
get [Symbol.toStringTag] () {
return 'Request'
}
static getNodeRequestOptions (request) {
const parsedURL = request[INTERNALS].parsedURL
const headers = new Headers(request[INTERNALS].headers)
// fetch step 1.3
if (!headers.has('Accept')) {
headers.set('Accept', '*/*')
}
// Basic fetch
if (!/^https?:$/.test(parsedURL.protocol)) {
throw new TypeError('Only HTTP(S) protocols are supported')
}
if (request.signal &&
Minipass.isStream(request.body) &&
typeof request.body.destroy !== 'function') {
throw new Error(
'Cancellation of streamed requests with AbortSignal is not supported')
}
// HTTP-network-or-cache fetch steps 2.4-2.7
const contentLengthValue =
(request.body === null || request.body === undefined) &&
/^(POST|PUT)$/i.test(request.method) ? '0'
: request.body !== null && request.body !== undefined
? getTotalBytes(request)
: null
if (contentLengthValue) {
headers.set('Content-Length', contentLengthValue + '')
}
// HTTP-network-or-cache fetch step 2.11
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')) {
headers.set('Accept-Encoding', 'gzip,deflate')
}
const agent = typeof request.agent === 'function'
? request.agent(parsedURL)
: request.agent
if (!headers.has('Connection') && !agent) {
headers.set('Connection', 'close')
}
// TLS specific options that are handled by node
const {
ca,
cert,
ciphers,
clientCertEngine,
crl,
dhparam,
ecdhCurve,
family,
honorCipherOrder,
key,
passphrase,
pfx,
rejectUnauthorized,
secureOptions,
secureProtocol,
servername,
sessionIdContext,
} = request[INTERNALS]
// HTTP-network fetch step 4.2
// chunked encoding is handled by Node.js
// 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}${parsedURL.search}`,
port: parsedURL.port,
protocol: parsedURL.protocol,
}
return {
...urlProps,
method: request.method,
headers: exportNodeCompatibleHeaders(headers),
agent,
ca,
cert,
ciphers,
clientCertEngine,
crl,
dhparam,
ecdhCurve,
family,
honorCipherOrder,
key,
passphrase,
pfx,
rejectUnauthorized,
secureOptions,
secureProtocol,
servername,
sessionIdContext,
}
}
}
module.exports = Request
Object.defineProperties(Request.prototype, {
method: { enumerable: true },
url: { enumerable: true },
headers: { enumerable: true },
redirect: { enumerable: true },
clone: { enumerable: true },
signal: { enumerable: true },
})