api-connect.js 2.5 KB

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