X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fwebsocket%2Flib%2FWebSocketRequest.js;fp=node_modules%2Fwebsocket%2Flib%2FWebSocketRequest.js;h=0000000000000000000000000000000000000000;hp=f4d96555f9597661244c17265c4e1b144b05cf94;hb=5e96dd57ddd883604e87f62bdddcb111c63a6e1a;hpb=acb5f682a2b75b972710cabd81658f63071324b0 diff --git a/node_modules/websocket/lib/WebSocketRequest.js b/node_modules/websocket/lib/WebSocketRequest.js deleted file mode 100644 index f4d9655..0000000 --- a/node_modules/websocket/lib/WebSocketRequest.js +++ /dev/null @@ -1,524 +0,0 @@ -/************************************************************************ - * Copyright 2010-2015 Brian McKelvey. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ***********************************************************************/ - -var crypto = require('crypto'); -var util = require('util'); -var url = require('url'); -var EventEmitter = require('events').EventEmitter; -var WebSocketConnection = require('./WebSocketConnection'); - -var headerValueSplitRegExp = /,\s*/; -var headerParamSplitRegExp = /;\s*/; -var headerSanitizeRegExp = /[\r\n]/g; -var xForwardedForSeparatorRegExp = /,\s*/; -var separators = [ - '(', ')', '<', '>', '@', - ',', ';', ':', '\\', '\"', - '/', '[', ']', '?', '=', - '{', '}', ' ', String.fromCharCode(9) -]; -var controlChars = [String.fromCharCode(127) /* DEL */]; -for (var i=0; i < 31; i ++) { - /* US-ASCII Control Characters */ - controlChars.push(String.fromCharCode(i)); -} - -var cookieNameValidateRegEx = /([\x00-\x20\x22\x28\x29\x2c\x2f\x3a-\x3f\x40\x5b-\x5e\x7b\x7d\x7f])/; -var cookieValueValidateRegEx = /[^\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]/; -var cookieValueDQuoteValidateRegEx = /^"[^"]*"$/; -var controlCharsAndSemicolonRegEx = /[\x00-\x20\x3b]/g; - -var cookieSeparatorRegEx = /[;,] */; - -var httpStatusDescriptions = { - 100: 'Continue', - 101: 'Switching Protocols', - 200: 'OK', - 201: 'Created', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 307: 'Temporary Redirect', - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 406: 'Not Acceptable', - 407: 'Proxy Authorization Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Long', - 414: 'Request-URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 426: 'Upgrade Required', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported' -}; - -function WebSocketRequest(socket, httpRequest, serverConfig) { - // Superclass Constructor - EventEmitter.call(this); - - this.socket = socket; - this.httpRequest = httpRequest; - this.resource = httpRequest.url; - this.remoteAddress = socket.remoteAddress; - this.remoteAddresses = [this.remoteAddress]; - this.serverConfig = serverConfig; - - // Watch for the underlying TCP socket closing before we call accept - this._socketIsClosing = false; - this._socketCloseHandler = this._handleSocketCloseBeforeAccept.bind(this); - this.socket.on('end', this._socketCloseHandler); - this.socket.on('close', this._socketCloseHandler); - - this._resolved = false; -} - -util.inherits(WebSocketRequest, EventEmitter); - -WebSocketRequest.prototype.readHandshake = function() { - var self = this; - var request = this.httpRequest; - - // Decode URL - this.resourceURL = url.parse(this.resource, true); - - this.host = request.headers['host']; - if (!this.host) { - throw new Error('Client must provide a Host header.'); - } - - this.key = request.headers['sec-websocket-key']; - if (!this.key) { - throw new Error('Client must provide a value for Sec-WebSocket-Key.'); - } - - this.webSocketVersion = parseInt(request.headers['sec-websocket-version'], 10); - - if (!this.webSocketVersion || isNaN(this.webSocketVersion)) { - throw new Error('Client must provide a value for Sec-WebSocket-Version.'); - } - - switch (this.webSocketVersion) { - case 8: - case 13: - break; - default: - var e = new Error('Unsupported websocket client version: ' + this.webSocketVersion + - 'Only versions 8 and 13 are supported.'); - e.httpCode = 426; - e.headers = { - 'Sec-WebSocket-Version': '13' - }; - throw e; - } - - if (this.webSocketVersion === 13) { - this.origin = request.headers['origin']; - } - else if (this.webSocketVersion === 8) { - this.origin = request.headers['sec-websocket-origin']; - } - - // Protocol is optional. - var protocolString = request.headers['sec-websocket-protocol']; - this.protocolFullCaseMap = {}; - this.requestedProtocols = []; - if (protocolString) { - var requestedProtocolsFullCase = protocolString.split(headerValueSplitRegExp); - requestedProtocolsFullCase.forEach(function(protocol) { - var lcProtocol = protocol.toLocaleLowerCase(); - self.requestedProtocols.push(lcProtocol); - self.protocolFullCaseMap[lcProtocol] = protocol; - }); - } - - if (!this.serverConfig.ignoreXForwardedFor && - request.headers['x-forwarded-for']) { - var immediatePeerIP = this.remoteAddress; - this.remoteAddresses = request.headers['x-forwarded-for'] - .split(xForwardedForSeparatorRegExp); - this.remoteAddresses.push(immediatePeerIP); - this.remoteAddress = this.remoteAddresses[0]; - } - - // Extensions are optional. - var extensionsString = request.headers['sec-websocket-extensions']; - this.requestedExtensions = this.parseExtensions(extensionsString); - - // Cookies are optional - var cookieString = request.headers['cookie']; - this.cookies = this.parseCookies(cookieString); -}; - -WebSocketRequest.prototype.parseExtensions = function(extensionsString) { - if (!extensionsString || extensionsString.length === 0) { - return []; - } - var extensions = extensionsString.toLocaleLowerCase().split(headerValueSplitRegExp); - extensions.forEach(function(extension, index, array) { - var params = extension.split(headerParamSplitRegExp); - var extensionName = params[0]; - var extensionParams = params.slice(1); - extensionParams.forEach(function(rawParam, index, array) { - var arr = rawParam.split('='); - var obj = { - name: arr[0], - value: arr[1] - }; - array.splice(index, 1, obj); - }); - var obj = { - name: extensionName, - params: extensionParams - }; - array.splice(index, 1, obj); - }); - return extensions; -}; - -// This function adapted from node-cookie -// https://github.com/shtylman/node-cookie -WebSocketRequest.prototype.parseCookies = function(str) { - // Sanity Check - if (!str || typeof(str) !== 'string') { - return []; - } - - var cookies = []; - var pairs = str.split(cookieSeparatorRegEx); - - pairs.forEach(function(pair) { - var eq_idx = pair.indexOf('='); - if (eq_idx === -1) { - cookies.push({ - name: pair, - value: null - }); - return; - } - - var key = pair.substr(0, eq_idx).trim(); - var val = pair.substr(++eq_idx, pair.length).trim(); - - // quoted values - if ('"' === val[0]) { - val = val.slice(1, -1); - } - - cookies.push({ - name: key, - value: decodeURIComponent(val) - }); - }); - - return cookies; -}; - -WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, cookies) { - this._verifyResolution(); - - // TODO: Handle extensions - - var protocolFullCase; - - if (acceptedProtocol) { - protocolFullCase = this.protocolFullCaseMap[acceptedProtocol.toLocaleLowerCase()]; - if (typeof(protocolFullCase) === 'undefined') { - protocolFullCase = acceptedProtocol; - } - } - else { - protocolFullCase = acceptedProtocol; - } - this.protocolFullCaseMap = null; - - // Create key validation hash - var sha1 = crypto.createHash('sha1'); - sha1.update(this.key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); - var acceptKey = sha1.digest('base64'); - - var response = 'HTTP/1.1 101 Switching Protocols\r\n' + - 'Upgrade: websocket\r\n' + - 'Connection: Upgrade\r\n' + - 'Sec-WebSocket-Accept: ' + acceptKey + '\r\n'; - - if (protocolFullCase) { - // validate protocol - for (var i=0; i < protocolFullCase.length; i++) { - var charCode = protocolFullCase.charCodeAt(i); - var character = protocolFullCase.charAt(i); - if (charCode < 0x21 || charCode > 0x7E || separators.indexOf(character) !== -1) { - this.reject(500); - throw new Error('Illegal character "' + String.fromCharCode(character) + '" in subprotocol.'); - } - } - if (this.requestedProtocols.indexOf(acceptedProtocol) === -1) { - this.reject(500); - throw new Error('Specified protocol was not requested by the client.'); - } - - protocolFullCase = protocolFullCase.replace(headerSanitizeRegExp, ''); - response += 'Sec-WebSocket-Protocol: ' + protocolFullCase + '\r\n'; - } - this.requestedProtocols = null; - - if (allowedOrigin) { - allowedOrigin = allowedOrigin.replace(headerSanitizeRegExp, ''); - if (this.webSocketVersion === 13) { - response += 'Origin: ' + allowedOrigin + '\r\n'; - } - else if (this.webSocketVersion === 8) { - response += 'Sec-WebSocket-Origin: ' + allowedOrigin + '\r\n'; - } - } - - if (cookies) { - if (!Array.isArray(cookies)) { - this.reject(500); - throw new Error('Value supplied for "cookies" argument must be an array.'); - } - var seenCookies = {}; - cookies.forEach(function(cookie) { - if (!cookie.name || !cookie.value) { - this.reject(500); - throw new Error('Each cookie to set must at least provide a "name" and "value"'); - } - - // Make sure there are no \r\n sequences inserted - cookie.name = cookie.name.replace(controlCharsAndSemicolonRegEx, ''); - cookie.value = cookie.value.replace(controlCharsAndSemicolonRegEx, ''); - - if (seenCookies[cookie.name]) { - this.reject(500); - throw new Error('You may not specify the same cookie name twice.'); - } - seenCookies[cookie.name] = true; - - // token (RFC 2616, Section 2.2) - var invalidChar = cookie.name.match(cookieNameValidateRegEx); - if (invalidChar) { - this.reject(500); - throw new Error('Illegal character ' + invalidChar[0] + ' in cookie name'); - } - - // RFC 6265, Section 4.1.1 - // *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) | %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - if (cookie.value.match(cookieValueDQuoteValidateRegEx)) { - invalidChar = cookie.value.slice(1, -1).match(cookieValueValidateRegEx); - } else { - invalidChar = cookie.value.match(cookieValueValidateRegEx); - } - if (invalidChar) { - this.reject(500); - throw new Error('Illegal character ' + invalidChar[0] + ' in cookie value'); - } - - var cookieParts = [cookie.name + '=' + cookie.value]; - - // RFC 6265, Section 4.1.1 - // 'Path=' path-value | - if(cookie.path){ - invalidChar = cookie.path.match(controlCharsAndSemicolonRegEx); - if (invalidChar) { - this.reject(500); - throw new Error('Illegal character ' + invalidChar[0] + ' in cookie path'); - } - cookieParts.push('Path=' + cookie.path); - } - - // RFC 6265, Section 4.1.2.3 - // 'Domain=' subdomain - if (cookie.domain) { - if (typeof(cookie.domain) !== 'string') { - this.reject(500); - throw new Error('Domain must be specified and must be a string.'); - } - invalidChar = cookie.domain.match(controlCharsAndSemicolonRegEx); - if (invalidChar) { - this.reject(500); - throw new Error('Illegal character ' + invalidChar[0] + ' in cookie domain'); - } - cookieParts.push('Domain=' + cookie.domain.toLowerCase()); - } - - // RFC 6265, Section 4.1.1 - //'Expires=' sane-cookie-date | Force Date object requirement by using only epoch - if (cookie.expires) { - if (!(cookie.expires instanceof Date)){ - this.reject(500); - throw new Error('Value supplied for cookie "expires" must be a vaild date object'); - } - cookieParts.push('Expires=' + cookie.expires.toGMTString()); - } - - // RFC 6265, Section 4.1.1 - //'Max-Age=' non-zero-digit *DIGIT - if (cookie.maxage) { - var maxage = cookie.maxage; - if (typeof(maxage) === 'string') { - maxage = parseInt(maxage, 10); - } - if (isNaN(maxage) || maxage <= 0 ) { - this.reject(500); - throw new Error('Value supplied for cookie "maxage" must be a non-zero number'); - } - maxage = Math.round(maxage); - cookieParts.push('Max-Age=' + maxage.toString(10)); - } - - // RFC 6265, Section 4.1.1 - //'Secure;' - if (cookie.secure) { - if (typeof(cookie.secure) !== 'boolean') { - this.reject(500); - throw new Error('Value supplied for cookie "secure" must be of type boolean'); - } - cookieParts.push('Secure'); - } - - // RFC 6265, Section 4.1.1 - //'HttpOnly;' - if (cookie.httponly) { - if (typeof(cookie.httponly) !== 'boolean') { - this.reject(500); - throw new Error('Value supplied for cookie "httponly" must be of type boolean'); - } - cookieParts.push('HttpOnly'); - } - - response += ('Set-Cookie: ' + cookieParts.join(';') + '\r\n'); - }.bind(this)); - } - - // TODO: handle negotiated extensions - // if (negotiatedExtensions) { - // response += 'Sec-WebSocket-Extensions: ' + negotiatedExtensions.join(', ') + '\r\n'; - // } - - // Mark the request resolved now so that the user can't call accept or - // reject a second time. - this._resolved = true; - this.emit('requestResolved', this); - - response += '\r\n'; - - var connection = new WebSocketConnection(this.socket, [], acceptedProtocol, false, this.serverConfig); - connection.webSocketVersion = this.webSocketVersion; - connection.remoteAddress = this.remoteAddress; - connection.remoteAddresses = this.remoteAddresses; - - var self = this; - - if (this._socketIsClosing) { - // Handle case when the client hangs up before we get a chance to - // accept the connection and send our side of the opening handshake. - cleanupFailedConnection(connection); - } - else { - this.socket.write(response, 'ascii', function(error) { - if (error) { - cleanupFailedConnection(connection); - return; - } - - self._removeSocketCloseListeners(); - connection._addSocketEventListeners(); - }); - } - - this.emit('requestAccepted', connection); - return connection; -}; - -WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) { - this._verifyResolution(); - - // Mark the request resolved now so that the user can't call accept or - // reject a second time. - this._resolved = true; - this.emit('requestResolved', this); - - if (typeof(status) !== 'number') { - status = 403; - } - var response = 'HTTP/1.1 ' + status + ' ' + httpStatusDescriptions[status] + '\r\n' + - 'Connection: close\r\n'; - if (reason) { - reason = reason.replace(headerSanitizeRegExp, ''); - response += 'X-WebSocket-Reject-Reason: ' + reason + '\r\n'; - } - - if (extraHeaders) { - for (var key in extraHeaders) { - var sanitizedValue = extraHeaders[key].toString().replace(headerSanitizeRegExp, ''); - var sanitizedKey = key.replace(headerSanitizeRegExp, ''); - response += (sanitizedKey + ': ' + sanitizedValue + '\r\n'); - } - } - - response += '\r\n'; - this.socket.end(response, 'ascii'); - - this.emit('requestRejected', this); -}; - -WebSocketRequest.prototype._handleSocketCloseBeforeAccept = function() { - this._socketIsClosing = true; - this._removeSocketCloseListeners(); -}; - -WebSocketRequest.prototype._removeSocketCloseListeners = function() { - this.socket.removeListener('end', this._socketCloseHandler); - this.socket.removeListener('close', this._socketCloseHandler); -}; - -WebSocketRequest.prototype._verifyResolution = function() { - if (this._resolved) { - throw new Error('WebSocketRequest may only be accepted or rejected one time.'); - } -}; - -function cleanupFailedConnection(connection) { - // Since we have to return a connection object even if the socket is - // already dead in order not to break the API, we schedule a 'close' - // event on the connection object to occur immediately. - process.nextTick(function() { - // WebSocketConnection.CLOSE_REASON_ABNORMAL = 1006 - // Third param: Skip sending the close frame to a dead socket - connection.drop(1006, 'TCP connection lost before handshake completed.', true); - }); -} - -module.exports = WebSocketRequest;