1 line
12 KiB
JSON
1 line
12 KiB
JSON
{"ast":null,"code":"'use strict';\n\nrequire('./shims');\n\nvar URL = require('url-parse'),\n inherits = require('inherits'),\n JSON3 = require('json3'),\n random = require('./utils/random'),\n escape = require('./utils/escape'),\n urlUtils = require('./utils/url'),\n eventUtils = require('./utils/event'),\n transport = require('./utils/transport'),\n objectUtils = require('./utils/object'),\n browser = require('./utils/browser'),\n log = require('./utils/log'),\n Event = require('./event/event'),\n EventTarget = require('./event/eventtarget'),\n loc = require('./location'),\n CloseEvent = require('./event/close'),\n TransportMessageEvent = require('./event/trans-message'),\n InfoReceiver = require('./info-receiver');\n\nvar debug = function debug() {};\n\nif (process.env.NODE_ENV !== 'production') {\n debug = require('debug')('sockjs-client:main');\n}\n\nvar transports; // follow constructor steps defined at http://dev.w3.org/html5/websockets/#the-websocket-interface\n\nfunction SockJS(url, protocols, options) {\n if (!(this instanceof SockJS)) {\n return new SockJS(url, protocols, options);\n }\n\n if (arguments.length < 1) {\n throw new TypeError(\"Failed to construct 'SockJS: 1 argument required, but only 0 present\");\n }\n\n EventTarget.call(this);\n this.readyState = SockJS.CONNECTING;\n this.extensions = '';\n this.protocol = ''; // non-standard extension\n\n options = options || {};\n\n if (options.protocols_whitelist) {\n log.warn(\"'protocols_whitelist' is DEPRECATED. Use 'transports' instead.\");\n }\n\n this._transportsWhitelist = options.transports;\n this._transportOptions = options.transportOptions || {};\n var sessionId = options.sessionId || 8;\n\n if (typeof sessionId === 'function') {\n this._generateSessionId = sessionId;\n } else if (typeof sessionId === 'number') {\n this._generateSessionId = function () {\n return random.string(sessionId);\n };\n } else {\n throw new TypeError('If sessionId is used in the options, it needs to be a number or a function.');\n }\n\n this._server = options.server || random.numberString(1000); // Step 1 of WS spec - parse and validate the url. Issue #8\n\n var parsedUrl = new URL(url);\n\n if (!parsedUrl.host || !parsedUrl.protocol) {\n throw new SyntaxError(\"The URL '\" + url + \"' is invalid\");\n } else if (parsedUrl.hash) {\n throw new SyntaxError('The URL must not contain a fragment');\n } else if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new SyntaxError(\"The URL's scheme must be either 'http:' or 'https:'. '\" + parsedUrl.protocol + \"' is not allowed.\");\n }\n\n var secure = parsedUrl.protocol === 'https:'; // Step 2 - don't allow secure origin with an insecure protocol\n\n if (loc.protocol === 'https:' && !secure) {\n throw new Error('SecurityError: An insecure SockJS connection may not be initiated from a page loaded over HTTPS');\n } // Step 3 - check port access - no need here\n // Step 4 - parse protocols argument\n\n\n if (!protocols) {\n protocols = [];\n } else if (!Array.isArray(protocols)) {\n protocols = [protocols];\n } // Step 5 - check protocols argument\n\n\n var sortedProtocols = protocols.sort();\n sortedProtocols.forEach(function (proto, i) {\n if (!proto) {\n throw new SyntaxError(\"The protocols entry '\" + proto + \"' is invalid.\");\n }\n\n if (i < sortedProtocols.length - 1 && proto === sortedProtocols[i + 1]) {\n throw new SyntaxError(\"The protocols entry '\" + proto + \"' is duplicated.\");\n }\n }); // Step 6 - convert origin\n\n var o = urlUtils.getOrigin(loc.href);\n this._origin = o ? o.toLowerCase() : null; // remove the trailing slash\n\n parsedUrl.set('pathname', parsedUrl.pathname.replace(/\\/+$/, '')); // store the sanitized url\n\n this.url = parsedUrl.href;\n debug('using url', this.url); // Step 7 - start connection in background\n // obtain server info\n // http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-26\n\n this._urlInfo = {\n nullOrigin: !browser.hasDomain(),\n sameOrigin: urlUtils.isOriginEqual(this.url, loc.href),\n sameScheme: urlUtils.isSchemeEqual(this.url, loc.href)\n };\n this._ir = new InfoReceiver(this.url, this._urlInfo);\n\n this._ir.once('finish', this._receiveInfo.bind(this));\n}\n\ninherits(SockJS, EventTarget);\n\nfunction userSetCode(code) {\n return code === 1000 || code >= 3000 && code <= 4999;\n}\n\nSockJS.prototype.close = function (code, reason) {\n // Step 1\n if (code && !userSetCode(code)) {\n throw new Error('InvalidAccessError: Invalid code');\n } // Step 2.4 states the max is 123 bytes, but we are just checking length\n\n\n if (reason && reason.length > 123) {\n throw new SyntaxError('reason argument has an invalid length');\n } // Step 3.1\n\n\n if (this.readyState === SockJS.CLOSING || this.readyState === SockJS.CLOSED) {\n return;\n } // TODO look at docs to determine how to set this\n\n\n var wasClean = true;\n\n this._close(code || 1000, reason || 'Normal closure', wasClean);\n};\n\nSockJS.prototype.send = function (data) {\n // #13 - convert anything non-string to string\n // TODO this currently turns objects into [object Object]\n if (typeof data !== 'string') {\n data = '' + data;\n }\n\n if (this.readyState === SockJS.CONNECTING) {\n throw new Error('InvalidStateError: The connection has not been established yet');\n }\n\n if (this.readyState !== SockJS.OPEN) {\n return;\n }\n\n this._transport.send(escape.quote(data));\n};\n\nSockJS.version = require('./version');\nSockJS.CONNECTING = 0;\nSockJS.OPEN = 1;\nSockJS.CLOSING = 2;\nSockJS.CLOSED = 3;\n\nSockJS.prototype._receiveInfo = function (info, rtt) {\n debug('_receiveInfo', rtt);\n this._ir = null;\n\n if (!info) {\n this._close(1002, 'Cannot connect to server');\n\n return;\n } // establish a round-trip timeout (RTO) based on the\n // round-trip time (RTT)\n\n\n this._rto = this.countRTO(rtt); // allow server to override url used for the actual transport\n\n this._transUrl = info.base_url ? info.base_url : this.url;\n info = objectUtils.extend(info, this._urlInfo);\n debug('info', info); // determine list of desired and supported transports\n\n var enabledTransports = transports.filterToEnabled(this._transportsWhitelist, info);\n this._transports = enabledTransports.main;\n debug(this._transports.length + ' enabled transports');\n\n this._connect();\n};\n\nSockJS.prototype._connect = function () {\n for (var Transport = this._transports.shift(); Transport; Transport = this._transports.shift()) {\n debug('attempt', Transport.transportName);\n\n if (Transport.needBody) {\n if (!global.document.body || typeof global.document.readyState !== 'undefined' && global.document.readyState !== 'complete' && global.document.readyState !== 'interactive') {\n debug('waiting for body');\n\n this._transports.unshift(Transport);\n\n eventUtils.attachEvent('load', this._connect.bind(this));\n return;\n }\n } // calculate timeout based on RTO and round trips. Default to 5s\n\n\n var timeoutMs = this._rto * Transport.roundTrips || 5000;\n this._transportTimeoutId = setTimeout(this._transportTimeout.bind(this), timeoutMs);\n debug('using timeout', timeoutMs);\n var transportUrl = urlUtils.addPath(this._transUrl, '/' + this._server + '/' + this._generateSessionId());\n var options = this._transportOptions[Transport.transportName];\n debug('transport url', transportUrl);\n var transportObj = new Transport(transportUrl, this._transUrl, options);\n transportObj.on('message', this._transportMessage.bind(this));\n transportObj.once('close', this._transportClose.bind(this));\n transportObj.transportName = Transport.transportName;\n this._transport = transportObj;\n return;\n }\n\n this._close(2000, 'All transports failed', false);\n};\n\nSockJS.prototype._transportTimeout = function () {\n debug('_transportTimeout');\n\n if (this.readyState === SockJS.CONNECTING) {\n if (this._transport) {\n this._transport.close();\n }\n\n this._transportClose(2007, 'Transport timed out');\n }\n};\n\nSockJS.prototype._transportMessage = function (msg) {\n debug('_transportMessage', msg);\n var self = this,\n type = msg.slice(0, 1),\n content = msg.slice(1),\n payload; // first check for messages that don't need a payload\n\n switch (type) {\n case 'o':\n this._open();\n\n return;\n\n case 'h':\n this.dispatchEvent(new Event('heartbeat'));\n debug('heartbeat', this.transport);\n return;\n }\n\n if (content) {\n try {\n payload = JSON3.parse(content);\n } catch (e) {\n debug('bad json', content);\n }\n }\n\n if (typeof payload === 'undefined') {\n debug('empty payload', content);\n return;\n }\n\n switch (type) {\n case 'a':\n if (Array.isArray(payload)) {\n payload.forEach(function (p) {\n debug('message', self.transport, p);\n self.dispatchEvent(new TransportMessageEvent(p));\n });\n }\n\n break;\n\n case 'm':\n debug('message', this.transport, payload);\n this.dispatchEvent(new TransportMessageEvent(payload));\n break;\n\n case 'c':\n if (Array.isArray(payload) && payload.length === 2) {\n this._close(payload[0], payload[1], true);\n }\n\n break;\n }\n};\n\nSockJS.prototype._transportClose = function (code, reason) {\n debug('_transportClose', this.transport, code, reason);\n\n if (this._transport) {\n this._transport.removeAllListeners();\n\n this._transport = null;\n this.transport = null;\n }\n\n if (!userSetCode(code) && code !== 2000 && this.readyState === SockJS.CONNECTING) {\n this._connect();\n\n return;\n }\n\n this._close(code, reason);\n};\n\nSockJS.prototype._open = function () {\n debug('_open', this._transport.transportName, this.readyState);\n\n if (this.readyState === SockJS.CONNECTING) {\n if (this._transportTimeoutId) {\n clearTimeout(this._transportTimeoutId);\n this._transportTimeoutId = null;\n }\n\n this.readyState = SockJS.OPEN;\n this.transport = this._transport.transportName;\n this.dispatchEvent(new Event('open'));\n debug('connected', this.transport);\n } else {\n // The server might have been restarted, and lost track of our\n // connection.\n this._close(1006, 'Server lost session');\n }\n};\n\nSockJS.prototype._close = function (code, reason, wasClean) {\n debug('_close', this.transport, code, reason, wasClean, this.readyState);\n var forceFail = false;\n\n if (this._ir) {\n forceFail = true;\n\n this._ir.close();\n\n this._ir = null;\n }\n\n if (this._transport) {\n this._transport.close();\n\n this._transport = null;\n this.transport = null;\n }\n\n if (this.readyState === SockJS.CLOSED) {\n throw new Error('InvalidStateError: SockJS has already been closed');\n }\n\n this.readyState = SockJS.CLOSING;\n setTimeout(function () {\n this.readyState = SockJS.CLOSED;\n\n if (forceFail) {\n this.dispatchEvent(new Event('error'));\n }\n\n var e = new CloseEvent('close');\n e.wasClean = wasClean || false;\n e.code = code || 1000;\n e.reason = reason;\n this.dispatchEvent(e);\n this.onmessage = this.onclose = this.onerror = null;\n debug('disconnected');\n }.bind(this), 0);\n}; // See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/\n// and RFC 2988.\n\n\nSockJS.prototype.countRTO = function (rtt) {\n // In a local environment, when using IE8/9 and the `jsonp-polling`\n // transport the time needed to establish a connection (the time that pass\n // from the opening of the transport to the call of `_dispatchOpen`) is\n // around 200msec (the lower bound used in the article above) and this\n // causes spurious timeouts. For this reason we calculate a value slightly\n // larger than that used in the article.\n if (rtt > 100) {\n return 4 * rtt; // rto > 400msec\n }\n\n return 300 + rtt; // 300msec < rto <= 400msec\n};\n\nmodule.exports = function (availableTransports) {\n transports = transport(availableTransports);\n\n require('./iframe-bootstrap')(SockJS, availableTransports);\n\n return SockJS;\n};","map":null,"metadata":{},"sourceType":"script"} |