1'use strict' 2 3const { AsyncResource } = require('async_hooks') 4const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors') 5const util = require('../core/util') 6const { addSignal, removeSignal } = require('./abort-signal') 7 8class ConnectHandler extends AsyncResource { 9 constructor (opts, callback) { 10 if (!opts || typeof opts !== 'object') { 11 throw new InvalidArgumentError('invalid opts') 12 } 13 14 if (typeof callback !== 'function') { 15 throw new InvalidArgumentError('invalid callback') 16 } 17 18 const { signal, opaque, responseHeaders } = opts 19 20 if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { 21 throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') 22 } 23 24 super('UNDICI_CONNECT') 25 26 this.opaque = opaque || null 27 this.responseHeaders = responseHeaders || null 28 this.callback = callback 29 this.abort = null 30 31 addSignal(this, signal) 32 } 33 34 onConnect (abort, context) { 35 if (!this.callback) { 36 throw new RequestAbortedError() 37 } 38 39 this.abort = abort 40 this.context = context 41 } 42 43 onHeaders () { 44 throw new SocketError('bad connect', null) 45 } 46 47 onUpgrade (statusCode, rawHeaders, socket) { 48 const { callback, opaque, context } = this 49 50 removeSignal(this) 51 52 this.callback = null 53 54 let headers = rawHeaders 55 // Indicates is an HTTP2Session 56 if (headers != null) { 57 headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) 58 } 59 60 this.runInAsyncScope(callback, null, null, { 61 statusCode, 62 headers, 63 socket, 64 opaque, 65 context 66 }) 67 } 68 69 onError (err) { 70 const { callback, opaque } = this 71 72 removeSignal(this) 73 74 if (callback) { 75 this.callback = null 76 queueMicrotask(() => { 77 this.runInAsyncScope(callback, null, err, { opaque }) 78 }) 79 } 80 } 81} 82 83function connect (opts, callback) { 84 if (callback === undefined) { 85 return new Promise((resolve, reject) => { 86 connect.call(this, opts, (err, data) => { 87 return err ? reject(err) : resolve(data) 88 }) 89 }) 90 } 91 92 try { 93 const connectHandler = new ConnectHandler(opts, callback) 94 this.dispatch({ ...opts, method: 'CONNECT' }, connectHandler) 95 } catch (err) { 96 if (typeof callback !== 'function') { 97 throw err 98 } 99 const opaque = opts && opts.opaque 100 queueMicrotask(() => callback(err, { opaque })) 101 } 102} 103 104module.exports = connect 105