mirror of
				https://github.com/1disk/edp445.git
				synced 2024-08-14 22:47:02 +00:00 
			
		
		
		
	Changed alot of things.
This commit is contained in:
		
							parent
							
								
									a5a0523e5a
								
							
						
					
					
						commit
						3513d5390c
					
				
					 2016 changed files with 336930 additions and 9 deletions
				
			
		
							
								
								
									
										607
									
								
								node_modules/ws/lib/receiver.js
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										607
									
								
								node_modules/ws/lib/receiver.js
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,607 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| const { Writable } = require('stream'); | ||||
| 
 | ||||
| const PerMessageDeflate = require('./permessage-deflate'); | ||||
| const { | ||||
|   BINARY_TYPES, | ||||
|   EMPTY_BUFFER, | ||||
|   kStatusCode, | ||||
|   kWebSocket | ||||
| } = require('./constants'); | ||||
| const { concat, toArrayBuffer, unmask } = require('./buffer-util'); | ||||
| const { isValidStatusCode, isValidUTF8 } = require('./validation'); | ||||
| 
 | ||||
| const GET_INFO = 0; | ||||
| const GET_PAYLOAD_LENGTH_16 = 1; | ||||
| const GET_PAYLOAD_LENGTH_64 = 2; | ||||
| const GET_MASK = 3; | ||||
| const GET_DATA = 4; | ||||
| const INFLATING = 5; | ||||
| 
 | ||||
| /** | ||||
|  * HyBi Receiver implementation. | ||||
|  * | ||||
|  * @extends Writable | ||||
|  */ | ||||
| class Receiver extends Writable { | ||||
|   /** | ||||
|    * Creates a Receiver instance. | ||||
|    * | ||||
|    * @param {String} [binaryType=nodebuffer] The type for binary data | ||||
|    * @param {Object} [extensions] An object containing the negotiated extensions | ||||
|    * @param {Boolean} [isServer=false] Specifies whether to operate in client or | ||||
|    *     server mode | ||||
|    * @param {Number} [maxPayload=0] The maximum allowed message length | ||||
|    */ | ||||
|   constructor(binaryType, extensions, isServer, maxPayload) { | ||||
|     super(); | ||||
| 
 | ||||
|     this._binaryType = binaryType || BINARY_TYPES[0]; | ||||
|     this[kWebSocket] = undefined; | ||||
|     this._extensions = extensions || {}; | ||||
|     this._isServer = !!isServer; | ||||
|     this._maxPayload = maxPayload | 0; | ||||
| 
 | ||||
|     this._bufferedBytes = 0; | ||||
|     this._buffers = []; | ||||
| 
 | ||||
|     this._compressed = false; | ||||
|     this._payloadLength = 0; | ||||
|     this._mask = undefined; | ||||
|     this._fragmented = 0; | ||||
|     this._masked = false; | ||||
|     this._fin = false; | ||||
|     this._opcode = 0; | ||||
| 
 | ||||
|     this._totalPayloadLength = 0; | ||||
|     this._messageLength = 0; | ||||
|     this._fragments = []; | ||||
| 
 | ||||
|     this._state = GET_INFO; | ||||
|     this._loop = false; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Implements `Writable.prototype._write()`. | ||||
|    * | ||||
|    * @param {Buffer} chunk The chunk of data to write | ||||
|    * @param {String} encoding The character encoding of `chunk` | ||||
|    * @param {Function} cb Callback | ||||
|    * @private | ||||
|    */ | ||||
|   _write(chunk, encoding, cb) { | ||||
|     if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); | ||||
| 
 | ||||
|     this._bufferedBytes += chunk.length; | ||||
|     this._buffers.push(chunk); | ||||
|     this.startLoop(cb); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Consumes `n` bytes from the buffered data. | ||||
|    * | ||||
|    * @param {Number} n The number of bytes to consume | ||||
|    * @return {Buffer} The consumed bytes | ||||
|    * @private | ||||
|    */ | ||||
|   consume(n) { | ||||
|     this._bufferedBytes -= n; | ||||
| 
 | ||||
|     if (n === this._buffers[0].length) return this._buffers.shift(); | ||||
| 
 | ||||
|     if (n < this._buffers[0].length) { | ||||
|       const buf = this._buffers[0]; | ||||
|       this._buffers[0] = buf.slice(n); | ||||
|       return buf.slice(0, n); | ||||
|     } | ||||
| 
 | ||||
|     const dst = Buffer.allocUnsafe(n); | ||||
| 
 | ||||
|     do { | ||||
|       const buf = this._buffers[0]; | ||||
|       const offset = dst.length - n; | ||||
| 
 | ||||
|       if (n >= buf.length) { | ||||
|         dst.set(this._buffers.shift(), offset); | ||||
|       } else { | ||||
|         dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); | ||||
|         this._buffers[0] = buf.slice(n); | ||||
|       } | ||||
| 
 | ||||
|       n -= buf.length; | ||||
|     } while (n > 0); | ||||
| 
 | ||||
|     return dst; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Starts the parsing loop. | ||||
|    * | ||||
|    * @param {Function} cb Callback | ||||
|    * @private | ||||
|    */ | ||||
|   startLoop(cb) { | ||||
|     let err; | ||||
|     this._loop = true; | ||||
| 
 | ||||
|     do { | ||||
|       switch (this._state) { | ||||
|         case GET_INFO: | ||||
|           err = this.getInfo(); | ||||
|           break; | ||||
|         case GET_PAYLOAD_LENGTH_16: | ||||
|           err = this.getPayloadLength16(); | ||||
|           break; | ||||
|         case GET_PAYLOAD_LENGTH_64: | ||||
|           err = this.getPayloadLength64(); | ||||
|           break; | ||||
|         case GET_MASK: | ||||
|           this.getMask(); | ||||
|           break; | ||||
|         case GET_DATA: | ||||
|           err = this.getData(cb); | ||||
|           break; | ||||
|         default: | ||||
|           // `INFLATING`
 | ||||
|           this._loop = false; | ||||
|           return; | ||||
|       } | ||||
|     } while (this._loop); | ||||
| 
 | ||||
|     cb(err); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Reads the first two bytes of a frame. | ||||
|    * | ||||
|    * @return {(RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   getInfo() { | ||||
|     if (this._bufferedBytes < 2) { | ||||
|       this._loop = false; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const buf = this.consume(2); | ||||
| 
 | ||||
|     if ((buf[0] & 0x30) !== 0x00) { | ||||
|       this._loop = false; | ||||
|       return error( | ||||
|         RangeError, | ||||
|         'RSV2 and RSV3 must be clear', | ||||
|         true, | ||||
|         1002, | ||||
|         'WS_ERR_UNEXPECTED_RSV_2_3' | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     const compressed = (buf[0] & 0x40) === 0x40; | ||||
| 
 | ||||
|     if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { | ||||
|       this._loop = false; | ||||
|       return error( | ||||
|         RangeError, | ||||
|         'RSV1 must be clear', | ||||
|         true, | ||||
|         1002, | ||||
|         'WS_ERR_UNEXPECTED_RSV_1' | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     this._fin = (buf[0] & 0x80) === 0x80; | ||||
|     this._opcode = buf[0] & 0x0f; | ||||
|     this._payloadLength = buf[1] & 0x7f; | ||||
| 
 | ||||
|     if (this._opcode === 0x00) { | ||||
|       if (compressed) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'RSV1 must be clear', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_UNEXPECTED_RSV_1' | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       if (!this._fragmented) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'invalid opcode 0', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_INVALID_OPCODE' | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       this._opcode = this._fragmented; | ||||
|     } else if (this._opcode === 0x01 || this._opcode === 0x02) { | ||||
|       if (this._fragmented) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           `invalid opcode ${this._opcode}`, | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_INVALID_OPCODE' | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       this._compressed = compressed; | ||||
|     } else if (this._opcode > 0x07 && this._opcode < 0x0b) { | ||||
|       if (!this._fin) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'FIN must be set', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_EXPECTED_FIN' | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       if (compressed) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'RSV1 must be clear', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_UNEXPECTED_RSV_1' | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       if (this._payloadLength > 0x7d) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           `invalid payload length ${this._payloadLength}`, | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' | ||||
|         ); | ||||
|       } | ||||
|     } else { | ||||
|       this._loop = false; | ||||
|       return error( | ||||
|         RangeError, | ||||
|         `invalid opcode ${this._opcode}`, | ||||
|         true, | ||||
|         1002, | ||||
|         'WS_ERR_INVALID_OPCODE' | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (!this._fin && !this._fragmented) this._fragmented = this._opcode; | ||||
|     this._masked = (buf[1] & 0x80) === 0x80; | ||||
| 
 | ||||
|     if (this._isServer) { | ||||
|       if (!this._masked) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'MASK must be set', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_EXPECTED_MASK' | ||||
|         ); | ||||
|       } | ||||
|     } else if (this._masked) { | ||||
|       this._loop = false; | ||||
|       return error( | ||||
|         RangeError, | ||||
|         'MASK must be clear', | ||||
|         true, | ||||
|         1002, | ||||
|         'WS_ERR_UNEXPECTED_MASK' | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; | ||||
|     else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; | ||||
|     else return this.haveLength(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets extended payload length (7+16). | ||||
|    * | ||||
|    * @return {(RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   getPayloadLength16() { | ||||
|     if (this._bufferedBytes < 2) { | ||||
|       this._loop = false; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     this._payloadLength = this.consume(2).readUInt16BE(0); | ||||
|     return this.haveLength(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets extended payload length (7+64). | ||||
|    * | ||||
|    * @return {(RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   getPayloadLength64() { | ||||
|     if (this._bufferedBytes < 8) { | ||||
|       this._loop = false; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const buf = this.consume(8); | ||||
|     const num = buf.readUInt32BE(0); | ||||
| 
 | ||||
|     //
 | ||||
|     // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
 | ||||
|     // if payload length is greater than this number.
 | ||||
|     //
 | ||||
|     if (num > Math.pow(2, 53 - 32) - 1) { | ||||
|       this._loop = false; | ||||
|       return error( | ||||
|         RangeError, | ||||
|         'Unsupported WebSocket frame: payload length > 2^53 - 1', | ||||
|         false, | ||||
|         1009, | ||||
|         'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH' | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); | ||||
|     return this.haveLength(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Payload length has been read. | ||||
|    * | ||||
|    * @return {(RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   haveLength() { | ||||
|     if (this._payloadLength && this._opcode < 0x08) { | ||||
|       this._totalPayloadLength += this._payloadLength; | ||||
|       if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { | ||||
|         this._loop = false; | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'Max payload size exceeded', | ||||
|           false, | ||||
|           1009, | ||||
|           'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (this._masked) this._state = GET_MASK; | ||||
|     else this._state = GET_DATA; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Reads mask bytes. | ||||
|    * | ||||
|    * @private | ||||
|    */ | ||||
|   getMask() { | ||||
|     if (this._bufferedBytes < 4) { | ||||
|       this._loop = false; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     this._mask = this.consume(4); | ||||
|     this._state = GET_DATA; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Reads data bytes. | ||||
|    * | ||||
|    * @param {Function} cb Callback | ||||
|    * @return {(Error|RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   getData(cb) { | ||||
|     let data = EMPTY_BUFFER; | ||||
| 
 | ||||
|     if (this._payloadLength) { | ||||
|       if (this._bufferedBytes < this._payloadLength) { | ||||
|         this._loop = false; | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       data = this.consume(this._payloadLength); | ||||
|       if (this._masked) unmask(data, this._mask); | ||||
|     } | ||||
| 
 | ||||
|     if (this._opcode > 0x07) return this.controlMessage(data); | ||||
| 
 | ||||
|     if (this._compressed) { | ||||
|       this._state = INFLATING; | ||||
|       this.decompress(data, cb); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     if (data.length) { | ||||
|       //
 | ||||
|       // This message is not compressed so its lenght is the sum of the payload
 | ||||
|       // length of all fragments.
 | ||||
|       //
 | ||||
|       this._messageLength = this._totalPayloadLength; | ||||
|       this._fragments.push(data); | ||||
|     } | ||||
| 
 | ||||
|     return this.dataMessage(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Decompresses data. | ||||
|    * | ||||
|    * @param {Buffer} data Compressed data | ||||
|    * @param {Function} cb Callback | ||||
|    * @private | ||||
|    */ | ||||
|   decompress(data, cb) { | ||||
|     const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; | ||||
| 
 | ||||
|     perMessageDeflate.decompress(data, this._fin, (err, buf) => { | ||||
|       if (err) return cb(err); | ||||
| 
 | ||||
|       if (buf.length) { | ||||
|         this._messageLength += buf.length; | ||||
|         if (this._messageLength > this._maxPayload && this._maxPayload > 0) { | ||||
|           return cb( | ||||
|             error( | ||||
|               RangeError, | ||||
|               'Max payload size exceeded', | ||||
|               false, | ||||
|               1009, | ||||
|               'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' | ||||
|             ) | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         this._fragments.push(buf); | ||||
|       } | ||||
| 
 | ||||
|       const er = this.dataMessage(); | ||||
|       if (er) return cb(er); | ||||
| 
 | ||||
|       this.startLoop(cb); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Handles a data message. | ||||
|    * | ||||
|    * @return {(Error|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   dataMessage() { | ||||
|     if (this._fin) { | ||||
|       const messageLength = this._messageLength; | ||||
|       const fragments = this._fragments; | ||||
| 
 | ||||
|       this._totalPayloadLength = 0; | ||||
|       this._messageLength = 0; | ||||
|       this._fragmented = 0; | ||||
|       this._fragments = []; | ||||
| 
 | ||||
|       if (this._opcode === 2) { | ||||
|         let data; | ||||
| 
 | ||||
|         if (this._binaryType === 'nodebuffer') { | ||||
|           data = concat(fragments, messageLength); | ||||
|         } else if (this._binaryType === 'arraybuffer') { | ||||
|           data = toArrayBuffer(concat(fragments, messageLength)); | ||||
|         } else { | ||||
|           data = fragments; | ||||
|         } | ||||
| 
 | ||||
|         this.emit('message', data); | ||||
|       } else { | ||||
|         const buf = concat(fragments, messageLength); | ||||
| 
 | ||||
|         if (!isValidUTF8(buf)) { | ||||
|           this._loop = false; | ||||
|           return error( | ||||
|             Error, | ||||
|             'invalid UTF-8 sequence', | ||||
|             true, | ||||
|             1007, | ||||
|             'WS_ERR_INVALID_UTF8' | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         this.emit('message', buf.toString()); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     this._state = GET_INFO; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Handles a control message. | ||||
|    * | ||||
|    * @param {Buffer} data Data to handle | ||||
|    * @return {(Error|RangeError|undefined)} A possible error | ||||
|    * @private | ||||
|    */ | ||||
|   controlMessage(data) { | ||||
|     if (this._opcode === 0x08) { | ||||
|       this._loop = false; | ||||
| 
 | ||||
|       if (data.length === 0) { | ||||
|         this.emit('conclude', 1005, ''); | ||||
|         this.end(); | ||||
|       } else if (data.length === 1) { | ||||
|         return error( | ||||
|           RangeError, | ||||
|           'invalid payload length 1', | ||||
|           true, | ||||
|           1002, | ||||
|           'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' | ||||
|         ); | ||||
|       } else { | ||||
|         const code = data.readUInt16BE(0); | ||||
| 
 | ||||
|         if (!isValidStatusCode(code)) { | ||||
|           return error( | ||||
|             RangeError, | ||||
|             `invalid status code ${code}`, | ||||
|             true, | ||||
|             1002, | ||||
|             'WS_ERR_INVALID_CLOSE_CODE' | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         const buf = data.slice(2); | ||||
| 
 | ||||
|         if (!isValidUTF8(buf)) { | ||||
|           return error( | ||||
|             Error, | ||||
|             'invalid UTF-8 sequence', | ||||
|             true, | ||||
|             1007, | ||||
|             'WS_ERR_INVALID_UTF8' | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         this.emit('conclude', code, buf.toString()); | ||||
|         this.end(); | ||||
|       } | ||||
|     } else if (this._opcode === 0x09) { | ||||
|       this.emit('ping', data); | ||||
|     } else { | ||||
|       this.emit('pong', data); | ||||
|     } | ||||
| 
 | ||||
|     this._state = GET_INFO; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports = Receiver; | ||||
| 
 | ||||
| /** | ||||
|  * Builds an error object. | ||||
|  * | ||||
|  * @param {function(new:Error|RangeError)} ErrorCtor The error constructor | ||||
|  * @param {String} message The error message | ||||
|  * @param {Boolean} prefix Specifies whether or not to add a default prefix to | ||||
|  *     `message` | ||||
|  * @param {Number} statusCode The status code | ||||
|  * @param {String} errorCode The exposed error code | ||||
|  * @return {(Error|RangeError)} The error | ||||
|  * @private | ||||
|  */ | ||||
| function error(ErrorCtor, message, prefix, statusCode, errorCode) { | ||||
|   const err = new ErrorCtor( | ||||
|     prefix ? `Invalid WebSocket frame: ${message}` : message | ||||
|   ); | ||||
| 
 | ||||
|   Error.captureStackTrace(err, error); | ||||
|   err.code = errorCode; | ||||
|   err[kStatusCode] = statusCode; | ||||
|   return err; | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue