1'use strict' 2 3const Dispatcher = require('./dispatcher') 4const { 5 ClientDestroyedError, 6 ClientClosedError, 7 InvalidArgumentError 8} = require('./core/errors') 9const { kDestroy, kClose, kDispatch, kInterceptors } = require('./core/symbols') 10 11const kDestroyed = Symbol('destroyed') 12const kClosed = Symbol('closed') 13const kOnDestroyed = Symbol('onDestroyed') 14const kOnClosed = Symbol('onClosed') 15const kInterceptedDispatch = Symbol('Intercepted Dispatch') 16 17class DispatcherBase extends Dispatcher { 18 constructor () { 19 super() 20 21 this[kDestroyed] = false 22 this[kOnDestroyed] = null 23 this[kClosed] = false 24 this[kOnClosed] = [] 25 } 26 27 get destroyed () { 28 return this[kDestroyed] 29 } 30 31 get closed () { 32 return this[kClosed] 33 } 34 35 get interceptors () { 36 return this[kInterceptors] 37 } 38 39 set interceptors (newInterceptors) { 40 if (newInterceptors) { 41 for (let i = newInterceptors.length - 1; i >= 0; i--) { 42 const interceptor = this[kInterceptors][i] 43 if (typeof interceptor !== 'function') { 44 throw new InvalidArgumentError('interceptor must be an function') 45 } 46 } 47 } 48 49 this[kInterceptors] = newInterceptors 50 } 51 52 close (callback) { 53 if (callback === undefined) { 54 return new Promise((resolve, reject) => { 55 this.close((err, data) => { 56 return err ? reject(err) : resolve(data) 57 }) 58 }) 59 } 60 61 if (typeof callback !== 'function') { 62 throw new InvalidArgumentError('invalid callback') 63 } 64 65 if (this[kDestroyed]) { 66 queueMicrotask(() => callback(new ClientDestroyedError(), null)) 67 return 68 } 69 70 if (this[kClosed]) { 71 if (this[kOnClosed]) { 72 this[kOnClosed].push(callback) 73 } else { 74 queueMicrotask(() => callback(null, null)) 75 } 76 return 77 } 78 79 this[kClosed] = true 80 this[kOnClosed].push(callback) 81 82 const onClosed = () => { 83 const callbacks = this[kOnClosed] 84 this[kOnClosed] = null 85 for (let i = 0; i < callbacks.length; i++) { 86 callbacks[i](null, null) 87 } 88 } 89 90 // Should not error. 91 this[kClose]() 92 .then(() => this.destroy()) 93 .then(() => { 94 queueMicrotask(onClosed) 95 }) 96 } 97 98 destroy (err, callback) { 99 if (typeof err === 'function') { 100 callback = err 101 err = null 102 } 103 104 if (callback === undefined) { 105 return new Promise((resolve, reject) => { 106 this.destroy(err, (err, data) => { 107 return err ? /* istanbul ignore next: should never error */ reject(err) : resolve(data) 108 }) 109 }) 110 } 111 112 if (typeof callback !== 'function') { 113 throw new InvalidArgumentError('invalid callback') 114 } 115 116 if (this[kDestroyed]) { 117 if (this[kOnDestroyed]) { 118 this[kOnDestroyed].push(callback) 119 } else { 120 queueMicrotask(() => callback(null, null)) 121 } 122 return 123 } 124 125 if (!err) { 126 err = new ClientDestroyedError() 127 } 128 129 this[kDestroyed] = true 130 this[kOnDestroyed] = this[kOnDestroyed] || [] 131 this[kOnDestroyed].push(callback) 132 133 const onDestroyed = () => { 134 const callbacks = this[kOnDestroyed] 135 this[kOnDestroyed] = null 136 for (let i = 0; i < callbacks.length; i++) { 137 callbacks[i](null, null) 138 } 139 } 140 141 // Should not error. 142 this[kDestroy](err).then(() => { 143 queueMicrotask(onDestroyed) 144 }) 145 } 146 147 [kInterceptedDispatch] (opts, handler) { 148 if (!this[kInterceptors] || this[kInterceptors].length === 0) { 149 this[kInterceptedDispatch] = this[kDispatch] 150 return this[kDispatch](opts, handler) 151 } 152 153 let dispatch = this[kDispatch].bind(this) 154 for (let i = this[kInterceptors].length - 1; i >= 0; i--) { 155 dispatch = this[kInterceptors][i](dispatch) 156 } 157 this[kInterceptedDispatch] = dispatch 158 return dispatch(opts, handler) 159 } 160 161 dispatch (opts, handler) { 162 if (!handler || typeof handler !== 'object') { 163 throw new InvalidArgumentError('handler must be an object') 164 } 165 166 try { 167 if (!opts || typeof opts !== 'object') { 168 throw new InvalidArgumentError('opts must be an object.') 169 } 170 171 if (this[kDestroyed] || this[kOnDestroyed]) { 172 throw new ClientDestroyedError() 173 } 174 175 if (this[kClosed]) { 176 throw new ClientClosedError() 177 } 178 179 return this[kInterceptedDispatch](opts, handler) 180 } catch (err) { 181 if (typeof handler.onError !== 'function') { 182 throw new InvalidArgumentError('invalid onError method') 183 } 184 185 handler.onError(err) 186 187 return false 188 } 189 } 190} 191 192module.exports = DispatcherBase 193