UNPKG

undici

Version:

An HTTP/1.1 client, written from scratch for Node.js

116 lines (91 loc) 2.91 kB
'use strict' const { InvalidArgumentError, SocketError } = require('../core/errors') const { AsyncResource } = require('node:async_hooks') const assert = require('node:assert') const util = require('../core/util') const { kHTTP2Stream } = require('../core/symbols') const { addSignal, removeSignal } = require('./abort-signal') class UpgradeHandler extends AsyncResource { constructor (opts, callback) { if (!opts || typeof opts !== 'object') { throw new InvalidArgumentError('invalid opts') } if (typeof callback !== 'function') { throw new InvalidArgumentError('invalid callback') } const { signal, opaque, responseHeaders } = opts if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') } super('UNDICI_UPGRADE') this.responseHeaders = responseHeaders || null this.opaque = opaque || null this.callback = callback this.abort = null this.context = null addSignal(this, signal) } onRequestStart (controller, context) { if (this.reason) { controller.abort(this.reason) return } assert(this.callback) this.abort = (reason) => controller.abort(reason) this.context = context } onResponseStart () { throw new SocketError('bad upgrade', null) } onRequestUpgrade (controller, statusCode, headers, socket) { assert(socket[kHTTP2Stream] === true ? statusCode === 200 : statusCode === 101) const { callback, opaque, context } = this removeSignal(this) this.callback = null const rawHeaders = controller?.rawHeaders const responseHeaders = this.responseHeaders === 'raw' ? (Array.isArray(rawHeaders) ? util.parseRawHeaders(rawHeaders) : []) : headers this.runInAsyncScope(callback, null, null, { headers: responseHeaders, socket, opaque, context }) } onResponseError (_controller, err) { const { callback, opaque } = this removeSignal(this) if (callback) { this.callback = null queueMicrotask(() => { this.runInAsyncScope(callback, null, err, { opaque }) }) } } } function upgrade (opts, callback) { if (callback === undefined) { return new Promise((resolve, reject) => { upgrade.call(this, opts, (err, data) => { return err ? reject(err) : resolve(data) }) }) } try { const upgradeHandler = new UpgradeHandler(opts, callback) const upgradeOpts = { ...opts, method: opts.method || 'GET', upgrade: opts.protocol || 'Websocket' } this.dispatch(upgradeOpts, upgradeHandler) } catch (err) { if (typeof callback !== 'function') { throw err } const opaque = opts?.opaque queueMicrotask(() => callback(err, { opaque })) } } module.exports = upgrade