1 | 'use strict'
|
---|
2 |
|
---|
3 | const { AsyncResource } = require('async_hooks')
|
---|
4 | const { InvalidArgumentError, RequestAbortedError, SocketError } = require('../core/errors')
|
---|
5 | const util = require('../core/util')
|
---|
6 | const { addSignal, removeSignal } = require('./abort-signal')
|
---|
7 |
|
---|
8 | class 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 |
|
---|
83 | function 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 |
|
---|
104 | module.exports = connect
|
---|