181 lines
4.4 KiB
JavaScript
181 lines
4.4 KiB
JavaScript
|
const exec = require('child_process').execFileSync
|
||
|
const path = require('path')
|
||
|
const { URL } = require('url')
|
||
|
const _fetch = require('node-fetch')
|
||
|
const shared = require('./shared')
|
||
|
|
||
|
function fetch (resource, init) {
|
||
|
const request = []
|
||
|
|
||
|
if (resource instanceof fetch.Request) {
|
||
|
request.push(...shared.serializeRequest(resource))
|
||
|
} else if (resource instanceof URL) {
|
||
|
request.push(resource.href, {})
|
||
|
} else {
|
||
|
request.push(resource, {})
|
||
|
}
|
||
|
|
||
|
Object.assign(request[1], init)
|
||
|
|
||
|
request[1].headers = new _fetch.Headers(request[1].headers)
|
||
|
|
||
|
if (request[1].body) {
|
||
|
const contentType = extractContentType(request)
|
||
|
if (contentType && !request[1].headers.get('content-type')) { request[1].headers.append('content-type', contentType) }
|
||
|
request[1].body = shared.parseBody(init.body).toString('base64')
|
||
|
}
|
||
|
|
||
|
request[1].headers = shared.serializeHeaders(request[1].headers)
|
||
|
|
||
|
// TODO credentials
|
||
|
|
||
|
const response = JSON.parse(sendMessage(request))
|
||
|
if ('headers' in response[1]) {
|
||
|
return shared.deserializeResponse(fetch, ...response)
|
||
|
} else {
|
||
|
throw shared.deserializeError(fetch, ...response)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function sendMessage (message) {
|
||
|
return exec(process.execPath, [path.join(__dirname, 'worker.js')], {
|
||
|
windowsHide: true,
|
||
|
maxBuffer: Infinity,
|
||
|
input: JSON.stringify(message),
|
||
|
shell: false
|
||
|
}).toString()
|
||
|
}
|
||
|
|
||
|
function extractContentType (input) {
|
||
|
const request = new _fetch.Request(...input)
|
||
|
return request.headers.get('content-type') || undefined
|
||
|
}
|
||
|
|
||
|
const _body = Symbol('bodyBuffer')
|
||
|
const _bodyError = Symbol('bodyError')
|
||
|
|
||
|
class SyncRequest extends _fetch.Request {
|
||
|
constructor (resource, init = {}) {
|
||
|
const buffer = shared.parseBody(init.body)
|
||
|
|
||
|
super(resource, init)
|
||
|
defineBuffer(this, buffer)
|
||
|
}
|
||
|
|
||
|
clone () {
|
||
|
checkBody(this)
|
||
|
return new SyncRequest(...shared.serializeRequest(this))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class SyncResponse extends _fetch.Response {
|
||
|
constructor (body, init, options = {}) {
|
||
|
const {
|
||
|
buffer = shared.parseBody(body),
|
||
|
bodyError
|
||
|
} = options
|
||
|
|
||
|
super(body, init)
|
||
|
defineBuffer(this, buffer)
|
||
|
if (bodyError) defineBodyError(this, bodyError)
|
||
|
}
|
||
|
|
||
|
clone () {
|
||
|
checkBody(this)
|
||
|
const buffer = Buffer.from(this[_body])
|
||
|
return new SyncResponse(
|
||
|
shared.createStream(buffer),
|
||
|
shared.serializeResponse(this),
|
||
|
{
|
||
|
buffer,
|
||
|
bodyError: this[_bodyError]
|
||
|
}
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Body {
|
||
|
static mixin (proto) {
|
||
|
for (const name of Object.getOwnPropertyNames(Body.prototype)) {
|
||
|
if (name === 'constructor') { continue }
|
||
|
const desc = Object.getOwnPropertyDescriptor(Body.prototype, name)
|
||
|
Object.defineProperty(proto, name, {
|
||
|
...desc,
|
||
|
enumerable: true
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arrayBuffer () {
|
||
|
checkBody(this)
|
||
|
const buf = consumeBody(this)
|
||
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
|
||
|
}
|
||
|
|
||
|
text () {
|
||
|
checkBody(this)
|
||
|
return consumeBody(this).toString()
|
||
|
}
|
||
|
|
||
|
json () {
|
||
|
checkBody(this)
|
||
|
try {
|
||
|
return JSON.parse(consumeBody(this).toString())
|
||
|
} catch (err) {
|
||
|
throw new fetch.FetchError(`invalid json response body at ${this.url} reason: ${err.message}`, 'invalid-json')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer () {
|
||
|
checkBody(this)
|
||
|
return Buffer.from(consumeBody(this))
|
||
|
}
|
||
|
|
||
|
textConverted () {
|
||
|
throw new fetch.FetchError('textConverted not implemented')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function _super (self, method) {
|
||
|
return Object.getPrototypeOf(Object.getPrototypeOf(self))[method].bind(self)
|
||
|
}
|
||
|
|
||
|
function checkBody (body) {
|
||
|
if (body[_bodyError]) {
|
||
|
throw body[_bodyError]
|
||
|
}
|
||
|
if (body.bodyUsed) {
|
||
|
throw new TypeError(`body used already for: ${body.url}`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function consumeBody (body) {
|
||
|
_super(body, 'buffer')().catch(error => console.error(error))
|
||
|
return body[_body] || Buffer.alloc(0)
|
||
|
}
|
||
|
|
||
|
function defineBuffer (body, buffer) {
|
||
|
Object.defineProperty(body, _body, {
|
||
|
value: buffer,
|
||
|
enumerable: false
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function defineBodyError (body, error) {
|
||
|
Object.defineProperty(body, _bodyError, {
|
||
|
value: shared.deserializeError(fetch, ...error),
|
||
|
enumerable: false
|
||
|
})
|
||
|
}
|
||
|
|
||
|
Body.mixin(SyncRequest.prototype)
|
||
|
Body.mixin(SyncResponse.prototype)
|
||
|
Object.defineProperties(SyncRequest.prototype, { clone: { enumerable: true } })
|
||
|
Object.defineProperties(SyncResponse.prototype, { clone: { enumerable: true } })
|
||
|
|
||
|
fetch.Headers = _fetch.Headers
|
||
|
fetch.FetchError = _fetch.FetchError
|
||
|
fetch.Request = SyncRequest
|
||
|
fetch.Response = SyncResponse
|
||
|
module.exports = fetch
|