Created
November 25, 2017 19:41
-
-
Save radu-matei/84f12b835fbd0fbe3cbe165a70e13e34 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.signalR = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
class Base64EncodedHubProtocol { | |
constructor(protocol) { | |
this.wrappedProtocol = protocol; | |
this.name = this.wrappedProtocol.name; | |
this.type = 1 /* Text */; | |
} | |
parseMessages(input) { | |
// The format of the message is `size:message;` | |
let pos = input.indexOf(":"); | |
if (pos == -1 || input[input.length - 1] != ';') { | |
throw new Error("Invalid payload."); | |
} | |
let lenStr = input.substring(0, pos); | |
if (!/^[0-9]+$/.test(lenStr)) { | |
throw new Error(`Invalid length: '${lenStr}'`); | |
} | |
let messageSize = parseInt(lenStr, 10); | |
// 2 accounts for ':' after message size and trailing ';' | |
if (messageSize != input.length - pos - 2) { | |
throw new Error("Invalid message size."); | |
} | |
let encodedMessage = input.substring(pos + 1, input.length - 1); | |
// atob/btoa are browsers APIs but they can be polyfilled. If this becomes problematic we can use | |
// base64-js module | |
let s = atob(encodedMessage); | |
let payload = new Uint8Array(s.length); | |
for (let i = 0; i < payload.length; i++) { | |
payload[i] = s.charCodeAt(i); | |
} | |
return this.wrappedProtocol.parseMessages(payload.buffer); | |
} | |
writeMessage(message) { | |
let payload = new Uint8Array(this.wrappedProtocol.writeMessage(message)); | |
let s = ""; | |
for (let i = 0; i < payload.byteLength; i++) { | |
s += String.fromCharCode(payload[i]); | |
} | |
// atob/btoa are browsers APIs but they can be polyfilled. If this becomes problematic we can use | |
// base64-js module | |
let encodedMessage = btoa(s); | |
return `${encodedMessage.length.toString()}:${encodedMessage};`; | |
} | |
} | |
exports.Base64EncodedHubProtocol = Base64EncodedHubProtocol; | |
},{}],2:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
var TextMessageFormat; | |
(function (TextMessageFormat) { | |
const RecordSeparator = String.fromCharCode(0x1e); | |
function write(output) { | |
return `${output}${RecordSeparator}`; | |
} | |
TextMessageFormat.write = write; | |
function parse(input) { | |
if (input[input.length - 1] != RecordSeparator) { | |
throw new Error("Message is incomplete."); | |
} | |
let messages = input.split(RecordSeparator); | |
messages.pop(); | |
return messages; | |
} | |
TextMessageFormat.parse = parse; | |
})(TextMessageFormat = exports.TextMessageFormat || (exports.TextMessageFormat = {})); | |
var BinaryMessageFormat; | |
(function (BinaryMessageFormat) { | |
// The length prefix of binary messages is encoded as VarInt. Read the comment in | |
// the BinaryMessageParser.TryParseMessage for details. | |
function write(output) { | |
// msgpack5 uses returns Buffer instead of Uint8Array on IE10 and some other browser | |
// in which case .byteLength does will be undefined | |
let size = output.byteLength || output.length; | |
let lenBuffer = []; | |
do { | |
let sizePart = size & 0x7f; | |
size = size >> 7; | |
if (size > 0) { | |
sizePart |= 0x80; | |
} | |
lenBuffer.push(sizePart); | |
} while (size > 0); | |
// msgpack5 uses returns Buffer instead of Uint8Array on IE10 and some other browser | |
// in which case .byteLength does will be undefined | |
size = output.byteLength || output.length; | |
let buffer = new Uint8Array(lenBuffer.length + size); | |
buffer.set(lenBuffer, 0); | |
buffer.set(output, lenBuffer.length); | |
return buffer.buffer; | |
} | |
BinaryMessageFormat.write = write; | |
function parse(input) { | |
let result = []; | |
let uint8Array = new Uint8Array(input); | |
const maxLengthPrefixSize = 5; | |
const numBitsToShift = [0, 7, 14, 21, 28]; | |
for (let offset = 0; offset < input.byteLength;) { | |
let numBytes = 0; | |
let size = 0; | |
let byteRead; | |
do { | |
byteRead = uint8Array[offset + numBytes]; | |
size = size | ((byteRead & 0x7f) << (numBitsToShift[numBytes])); | |
numBytes++; | |
} while (numBytes < Math.min(maxLengthPrefixSize, input.byteLength - offset) && (byteRead & 0x80) != 0); | |
if ((byteRead & 0x80) !== 0 && numBytes < maxLengthPrefixSize) { | |
throw new Error("Cannot read message size."); | |
} | |
if (numBytes === maxLengthPrefixSize && byteRead > 7) { | |
throw new Error("Messages bigger than 2GB are not supported."); | |
} | |
if (uint8Array.byteLength >= (offset + numBytes + size)) { | |
// IE does not support .slice() so use subarray | |
result.push(uint8Array.slice | |
? uint8Array.slice(offset + numBytes, offset + numBytes + size) | |
: uint8Array.subarray(offset + numBytes, offset + numBytes + size)); | |
} | |
else { | |
throw new Error("Incomplete message."); | |
} | |
offset = offset + numBytes + size; | |
} | |
return result; | |
} | |
BinaryMessageFormat.parse = parse; | |
})(BinaryMessageFormat = exports.BinaryMessageFormat || (exports.BinaryMessageFormat = {})); | |
},{}],3:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const HttpError_1 = require("./HttpError"); | |
class HttpClient { | |
get(url, headers) { | |
return this.xhr("GET", url, headers); | |
} | |
options(url, headers) { | |
return this.xhr("OPTIONS", url, headers); | |
} | |
post(url, content, headers) { | |
return this.xhr("POST", url, headers, content); | |
} | |
xhr(method, url, headers, content) { | |
return new Promise((resolve, reject) => { | |
let xhr = new XMLHttpRequest(); | |
xhr.open(method, url, true); | |
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); | |
if (headers) { | |
headers.forEach((value, header) => xhr.setRequestHeader(header, value)); | |
} | |
xhr.send(content); | |
xhr.onload = () => { | |
if (xhr.status >= 200 && xhr.status < 300) { | |
resolve(xhr.response || xhr.responseText); | |
} | |
else { | |
reject(new HttpError_1.HttpError(xhr.statusText, xhr.status)); | |
} | |
}; | |
xhr.onerror = () => { | |
reject(new HttpError_1.HttpError(xhr.statusText, xhr.status)); | |
}; | |
}); | |
} | |
} | |
exports.HttpClient = HttpClient; | |
},{"./HttpError":5}],4:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | |
return new (P || (P = Promise))(function (resolve, reject) { | |
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | |
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | |
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | |
step((generator = generator.apply(thisArg, _arguments || [])).next()); | |
}); | |
}; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const Transports_1 = require("./Transports"); | |
const HttpClient_1 = require("./HttpClient"); | |
const ILogger_1 = require("./ILogger"); | |
const Loggers_1 = require("./Loggers"); | |
class HttpConnection { | |
constructor(url, options = {}) { | |
this.features = {}; | |
this.logger = Loggers_1.LoggerFactory.createLogger(options.logging); | |
this.url = this.resolveUrl(url); | |
options = options || {}; | |
this.httpClient = options.httpClient || new HttpClient_1.HttpClient(); | |
this.connectionState = 0 /* Initial */; | |
this.options = options; | |
} | |
start() { | |
return __awaiter(this, void 0, void 0, function* () { | |
if (this.connectionState != 0 /* Initial */) { | |
return Promise.reject(new Error("Cannot start a connection that is not in the 'Initial' state.")); | |
} | |
this.connectionState = 1 /* Connecting */; | |
this.startPromise = this.startInternal(); | |
return this.startPromise; | |
}); | |
} | |
startInternal() { | |
return __awaiter(this, void 0, void 0, function* () { | |
try { | |
let negotiatePayload = yield this.httpClient.options(this.url); | |
let negotiateResponse = JSON.parse(negotiatePayload); | |
this.connectionId = negotiateResponse.connectionId; | |
// the user tries to stop the the connection when it is being started | |
if (this.connectionState == 3 /* Disconnected */) { | |
return; | |
} | |
this.url += (this.url.indexOf("?") == -1 ? "?" : "&") + `id=${this.connectionId}`; | |
this.transport = this.createTransport(this.options.transport, negotiateResponse.availableTransports); | |
this.transport.onreceive = this.onreceive; | |
this.transport.onclose = e => this.stopConnection(true, e); | |
let requestedTransferMode = this.features.transferMode === 2 /* Binary */ | |
? 2 /* Binary */ | |
: 1 /* Text */; | |
this.features.transferMode = yield this.transport.connect(this.url, requestedTransferMode); | |
// only change the state if we were connecting to not overwrite | |
// the state if the connection is already marked as Disconnected | |
this.changeState(1 /* Connecting */, 2 /* Connected */); | |
} | |
catch (e) { | |
this.logger.log(ILogger_1.LogLevel.Error, "Failed to start the connection. " + e); | |
this.connectionState = 3 /* Disconnected */; | |
this.transport = null; | |
throw e; | |
} | |
; | |
}); | |
} | |
createTransport(transport, availableTransports) { | |
if (!transport && availableTransports.length > 0) { | |
transport = Transports_1.TransportType[availableTransports[0]]; | |
} | |
if (transport === Transports_1.TransportType.WebSockets && availableTransports.indexOf(Transports_1.TransportType[transport]) >= 0) { | |
return new Transports_1.WebSocketTransport(this.logger); | |
} | |
if (transport === Transports_1.TransportType.ServerSentEvents && availableTransports.indexOf(Transports_1.TransportType[transport]) >= 0) { | |
return new Transports_1.ServerSentEventsTransport(this.httpClient, this.logger); | |
} | |
if (transport === Transports_1.TransportType.LongPolling && availableTransports.indexOf(Transports_1.TransportType[transport]) >= 0) { | |
return new Transports_1.LongPollingTransport(this.httpClient, this.logger); | |
} | |
if (this.isITransport(transport)) { | |
return transport; | |
} | |
throw new Error("No available transports found."); | |
} | |
isITransport(transport) { | |
return typeof (transport) === "object" && "connect" in transport; | |
} | |
changeState(from, to) { | |
if (this.connectionState == from) { | |
this.connectionState = to; | |
return true; | |
} | |
return false; | |
} | |
send(data) { | |
if (this.connectionState != 2 /* Connected */) { | |
throw new Error("Cannot send data if the connection is not in the 'Connected' State"); | |
} | |
return this.transport.send(data); | |
} | |
stop() { | |
return __awaiter(this, void 0, void 0, function* () { | |
let previousState = this.connectionState; | |
this.connectionState = 3 /* Disconnected */; | |
try { | |
yield this.startPromise; | |
} | |
catch (e) { | |
// this exception is returned to the user as a rejected Promise from the start method | |
} | |
this.stopConnection(/*raiseClosed*/ previousState == 2 /* Connected */); | |
}); | |
} | |
stopConnection(raiseClosed, error) { | |
if (this.transport) { | |
this.transport.stop(); | |
this.transport = null; | |
} | |
this.connectionState = 3 /* Disconnected */; | |
if (raiseClosed && this.onclose) { | |
this.onclose(error); | |
} | |
} | |
resolveUrl(url) { | |
// startsWith is not supported in IE | |
if (url.lastIndexOf("https://", 0) === 0 || url.lastIndexOf("http://", 0) === 0) { | |
return url; | |
} | |
if (typeof window === 'undefined' || !window || !window.document) { | |
throw new Error(`Cannot resolve '${url}'.`); | |
} | |
let parser = window.document.createElement("a"); | |
parser.href = url; | |
let baseUrl = (!parser.protocol || parser.protocol === ":") | |
? `${window.document.location.protocol}//${(parser.host || window.document.location.host)}` | |
: `${parser.protocol}//${parser.host}`; | |
if (!url || url[0] != '/') { | |
url = '/' + url; | |
} | |
let normalizedUrl = baseUrl + url; | |
this.logger.log(ILogger_1.LogLevel.Information, `Normalizing '${url}' to '${normalizedUrl}'`); | |
return normalizedUrl; | |
} | |
} | |
exports.HttpConnection = HttpConnection; | |
},{"./HttpClient":3,"./ILogger":7,"./Loggers":9,"./Transports":11}],5:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
class HttpError extends Error { | |
constructor(errorMessage, statusCode) { | |
super(errorMessage); | |
this.statusCode = statusCode; | |
} | |
} | |
exports.HttpError = HttpError; | |
},{}],6:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | |
return new (P || (P = Promise))(function (resolve, reject) { | |
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | |
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | |
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | |
step((generator = generator.apply(thisArg, _arguments || [])).next()); | |
}); | |
}; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const HttpConnection_1 = require("./HttpConnection"); | |
const Observable_1 = require("./Observable"); | |
const JsonHubProtocol_1 = require("./JsonHubProtocol"); | |
const Formatters_1 = require("./Formatters"); | |
const Base64EncodedHubProtocol_1 = require("./Base64EncodedHubProtocol"); | |
const ILogger_1 = require("./ILogger"); | |
const Loggers_1 = require("./Loggers"); | |
var Transports_1 = require("./Transports"); | |
exports.TransportType = Transports_1.TransportType; | |
var HttpConnection_2 = require("./HttpConnection"); | |
exports.HttpConnection = HttpConnection_2.HttpConnection; | |
var JsonHubProtocol_2 = require("./JsonHubProtocol"); | |
exports.JsonHubProtocol = JsonHubProtocol_2.JsonHubProtocol; | |
var ILogger_2 = require("./ILogger"); | |
exports.LogLevel = ILogger_2.LogLevel; | |
var Loggers_2 = require("./Loggers"); | |
exports.ConsoleLogger = Loggers_2.ConsoleLogger; | |
exports.NullLogger = Loggers_2.NullLogger; | |
class HubConnection { | |
constructor(urlOrConnection, options = {}) { | |
options = options || {}; | |
if (typeof urlOrConnection === "string") { | |
this.connection = new HttpConnection_1.HttpConnection(urlOrConnection, options); | |
} | |
else { | |
this.connection = urlOrConnection; | |
} | |
this.logger = Loggers_1.LoggerFactory.createLogger(options.logging); | |
this.protocol = options.protocol || new JsonHubProtocol_1.JsonHubProtocol(); | |
this.connection.onreceive = (data) => this.processIncomingData(data); | |
this.connection.onclose = (error) => this.connectionClosed(error); | |
this.callbacks = new Map(); | |
this.methods = new Map(); | |
this.closedCallbacks = []; | |
this.id = 0; | |
} | |
processIncomingData(data) { | |
// Parse the messages | |
let messages = this.protocol.parseMessages(data); | |
for (var i = 0; i < messages.length; ++i) { | |
var message = messages[i]; | |
switch (message.type) { | |
case 1 /* Invocation */: | |
this.invokeClientMethod(message); | |
break; | |
case 2 /* Result */: | |
case 3 /* Completion */: | |
let callback = this.callbacks.get(message.invocationId); | |
if (callback != null) { | |
if (message.type == 3 /* Completion */) { | |
this.callbacks.delete(message.invocationId); | |
} | |
callback(message); | |
} | |
break; | |
default: | |
this.logger.log(ILogger_1.LogLevel.Warning, "Invalid message type: " + data); | |
break; | |
} | |
} | |
} | |
invokeClientMethod(invocationMessage) { | |
let methods = this.methods.get(invocationMessage.target.toLowerCase()); | |
if (methods) { | |
methods.forEach(m => m.apply(this, invocationMessage.arguments)); | |
if (!invocationMessage.nonblocking) { | |
// TODO: send result back to the server? | |
} | |
} | |
else { | |
this.logger.log(ILogger_1.LogLevel.Warning, `No client method with the name '${invocationMessage.target}' found.`); | |
} | |
} | |
connectionClosed(error) { | |
let errorCompletionMessage = { | |
type: 3 /* Completion */, | |
invocationId: "-1", | |
error: error ? error.message : "Invocation cancelled due to connection being closed.", | |
}; | |
this.callbacks.forEach(callback => { | |
callback(errorCompletionMessage); | |
}); | |
this.callbacks.clear(); | |
this.closedCallbacks.forEach(c => c.apply(this, [error])); | |
} | |
start() { | |
return __awaiter(this, void 0, void 0, function* () { | |
let requestedTransferMode = (this.protocol.type === 2 /* Binary */) | |
? 2 /* Binary */ | |
: 1 /* Text */; | |
this.connection.features.transferMode = requestedTransferMode; | |
yield this.connection.start(); | |
var actualTransferMode = this.connection.features.transferMode; | |
yield this.connection.send(Formatters_1.TextMessageFormat.write(JSON.stringify({ protocol: this.protocol.name }))); | |
this.logger.log(ILogger_1.LogLevel.Information, `Using HubProtocol '${this.protocol.name}'.`); | |
if (requestedTransferMode === 2 /* Binary */ && actualTransferMode === 1 /* Text */) { | |
this.protocol = new Base64EncodedHubProtocol_1.Base64EncodedHubProtocol(this.protocol); | |
} | |
}); | |
} | |
stop() { | |
return this.connection.stop(); | |
} | |
stream(methodName, ...args) { | |
let invocationDescriptor = this.createInvocation(methodName, args, false); | |
let subject = new Observable_1.Subject(); | |
this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent) => { | |
if (invocationEvent.type === 3 /* Completion */) { | |
let completionMessage = invocationEvent; | |
if (completionMessage.error) { | |
subject.error(new Error(completionMessage.error)); | |
} | |
else if (completionMessage.result) { | |
subject.error(new Error("Server provided a result in a completion response to a streamed invocation.")); | |
} | |
else { | |
// TODO: Log a warning if there's a payload? | |
subject.complete(); | |
} | |
} | |
else { | |
subject.next(invocationEvent.item); | |
} | |
}); | |
let message = this.protocol.writeMessage(invocationDescriptor); | |
this.connection.send(message) | |
.catch(e => { | |
subject.error(e); | |
this.callbacks.delete(invocationDescriptor.invocationId); | |
}); | |
return subject; | |
} | |
send(methodName, ...args) { | |
let invocationDescriptor = this.createInvocation(methodName, args, true); | |
let message = this.protocol.writeMessage(invocationDescriptor); | |
return this.connection.send(message); | |
} | |
invoke(methodName, ...args) { | |
let invocationDescriptor = this.createInvocation(methodName, args, false); | |
let p = new Promise((resolve, reject) => { | |
this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent) => { | |
if (invocationEvent.type === 3 /* Completion */) { | |
let completionMessage = invocationEvent; | |
if (completionMessage.error) { | |
reject(new Error(completionMessage.error)); | |
} | |
else { | |
resolve(completionMessage.result); | |
} | |
} | |
else { | |
reject(new Error("Streaming methods must be invoked using HubConnection.stream")); | |
} | |
}); | |
let message = this.protocol.writeMessage(invocationDescriptor); | |
this.connection.send(message) | |
.catch(e => { | |
reject(e); | |
this.callbacks.delete(invocationDescriptor.invocationId); | |
}); | |
}); | |
return p; | |
} | |
on(methodName, method) { | |
if (!methodName || !method) { | |
return; | |
} | |
methodName = methodName.toLowerCase(); | |
if (!this.methods.has(methodName)) { | |
this.methods.set(methodName, []); | |
} | |
this.methods.get(methodName).push(method); | |
} | |
off(methodName, method) { | |
if (!methodName || !method) { | |
return; | |
} | |
methodName = methodName.toLowerCase(); | |
let handlers = this.methods.get(methodName); | |
if (!handlers) { | |
return; | |
} | |
var removeIdx = handlers.indexOf(method); | |
if (removeIdx != -1) { | |
handlers.splice(removeIdx, 1); | |
} | |
} | |
onclose(callback) { | |
if (callback) { | |
this.closedCallbacks.push(callback); | |
} | |
} | |
createInvocation(methodName, args, nonblocking) { | |
let id = this.id; | |
this.id++; | |
return { | |
type: 1 /* Invocation */, | |
invocationId: id.toString(), | |
target: methodName, | |
arguments: args, | |
nonblocking: nonblocking | |
}; | |
} | |
} | |
exports.HubConnection = HubConnection; | |
},{"./Base64EncodedHubProtocol":1,"./Formatters":2,"./HttpConnection":4,"./ILogger":7,"./JsonHubProtocol":8,"./Loggers":9,"./Observable":10,"./Transports":11}],7:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
var LogLevel; | |
(function (LogLevel) { | |
LogLevel[LogLevel["Trace"] = 0] = "Trace"; | |
LogLevel[LogLevel["Information"] = 1] = "Information"; | |
LogLevel[LogLevel["Warning"] = 2] = "Warning"; | |
LogLevel[LogLevel["Error"] = 3] = "Error"; | |
LogLevel[LogLevel["None"] = 4] = "None"; | |
})(LogLevel = exports.LogLevel || (exports.LogLevel = {})); | |
},{}],8:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const Formatters_1 = require("./Formatters"); | |
class JsonHubProtocol { | |
constructor() { | |
this.name = "json"; | |
this.type = 1 /* Text */; | |
} | |
parseMessages(input) { | |
if (!input) { | |
return []; | |
} | |
// Parse the messages | |
let messages = Formatters_1.TextMessageFormat.parse(input); | |
let hubMessages = []; | |
for (var i = 0; i < messages.length; ++i) { | |
hubMessages.push(JSON.parse(messages[i])); | |
} | |
return hubMessages; | |
} | |
writeMessage(message) { | |
return Formatters_1.TextMessageFormat.write(JSON.stringify(message)); | |
} | |
} | |
exports.JsonHubProtocol = JsonHubProtocol; | |
},{"./Formatters":2}],9:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const ILogger_1 = require("./ILogger"); | |
class NullLogger { | |
log(logLevel, message) { | |
} | |
} | |
exports.NullLogger = NullLogger; | |
class ConsoleLogger { | |
constructor(minimumLogLevel) { | |
this.minimumLogLevel = minimumLogLevel; | |
} | |
log(logLevel, message) { | |
if (logLevel >= this.minimumLogLevel) { | |
console.log(`${ILogger_1.LogLevel[logLevel]}: ${message}`); | |
} | |
} | |
} | |
exports.ConsoleLogger = ConsoleLogger; | |
var LoggerFactory; | |
(function (LoggerFactory) { | |
function createLogger(logging) { | |
if (logging === undefined) { | |
return new ConsoleLogger(ILogger_1.LogLevel.Information); | |
} | |
if (logging === null) { | |
return new NullLogger(); | |
} | |
if (logging.log) { | |
return logging; | |
} | |
return new ConsoleLogger(logging); | |
} | |
LoggerFactory.createLogger = createLogger; | |
})(LoggerFactory = exports.LoggerFactory || (exports.LoggerFactory = {})); | |
},{"./ILogger":7}],10:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
class Subject { | |
constructor() { | |
this.observers = []; | |
} | |
next(item) { | |
for (let observer of this.observers) { | |
observer.next(item); | |
} | |
} | |
error(err) { | |
for (let observer of this.observers) { | |
observer.error(err); | |
} | |
} | |
complete() { | |
for (let observer of this.observers) { | |
observer.complete(); | |
} | |
} | |
subscribe(observer) { | |
this.observers.push(observer); | |
} | |
} | |
exports.Subject = Subject; | |
},{}],11:[function(require,module,exports){ | |
"use strict"; | |
// Copyright (c) .NET Foundation. All rights reserved. | |
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | |
return new (P || (P = Promise))(function (resolve, reject) { | |
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | |
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | |
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | |
step((generator = generator.apply(thisArg, _arguments || [])).next()); | |
}); | |
}; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
const HttpError_1 = require("./HttpError"); | |
const ILogger_1 = require("./ILogger"); | |
var TransportType; | |
(function (TransportType) { | |
TransportType[TransportType["WebSockets"] = 0] = "WebSockets"; | |
TransportType[TransportType["ServerSentEvents"] = 1] = "ServerSentEvents"; | |
TransportType[TransportType["LongPolling"] = 2] = "LongPolling"; | |
})(TransportType = exports.TransportType || (exports.TransportType = {})); | |
class WebSocketTransport { | |
constructor(logger) { | |
this.logger = logger; | |
} | |
connect(url, requestedTransferMode) { | |
return new Promise((resolve, reject) => { | |
url = url.replace(/^http/, "ws"); | |
let webSocket = new WebSocket(url); | |
if (requestedTransferMode == 2 /* Binary */) { | |
webSocket.binaryType = "arraybuffer"; | |
} | |
webSocket.onopen = (event) => { | |
this.logger.log(ILogger_1.LogLevel.Information, `WebSocket connected to ${url}`); | |
this.webSocket = webSocket; | |
resolve(requestedTransferMode); | |
}; | |
webSocket.onerror = (event) => { | |
reject(); | |
}; | |
webSocket.onmessage = (message) => { | |
this.logger.log(ILogger_1.LogLevel.Trace, `(WebSockets transport) data received: ${message.data}`); | |
if (this.onreceive) { | |
this.onreceive(message.data); | |
} | |
}; | |
webSocket.onclose = (event) => { | |
// webSocket will be null if the transport did not start successfully | |
if (this.onclose && this.webSocket) { | |
if (event.wasClean === false || event.code !== 1000) { | |
this.onclose(new Error(`Websocket closed with status code: ${event.code} (${event.reason})`)); | |
} | |
else { | |
this.onclose(); | |
} | |
} | |
}; | |
}); | |
} | |
send(data) { | |
if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) { | |
this.webSocket.send(data); | |
return Promise.resolve(); | |
} | |
return Promise.reject("WebSocket is not in the OPEN state"); | |
} | |
stop() { | |
if (this.webSocket) { | |
this.webSocket.close(); | |
this.webSocket = null; | |
} | |
} | |
} | |
exports.WebSocketTransport = WebSocketTransport; | |
class ServerSentEventsTransport { | |
constructor(httpClient, logger) { | |
this.httpClient = httpClient; | |
this.logger = logger; | |
} | |
connect(url, requestedTransferMode) { | |
if (typeof (EventSource) === "undefined") { | |
Promise.reject("EventSource not supported by the browser."); | |
} | |
this.url = url; | |
return new Promise((resolve, reject) => { | |
let eventSource = new EventSource(this.url); | |
try { | |
eventSource.onmessage = (e) => { | |
if (this.onreceive) { | |
try { | |
this.logger.log(ILogger_1.LogLevel.Trace, `(SSE transport) data received: ${e.data}`); | |
this.onreceive(e.data); | |
} | |
catch (error) { | |
if (this.onclose) { | |
this.onclose(error); | |
} | |
return; | |
} | |
} | |
}; | |
eventSource.onerror = (e) => { | |
reject(); | |
// don't report an error if the transport did not start successfully | |
if (this.eventSource && this.onclose) { | |
this.onclose(new Error(e.message || "Error occurred")); | |
} | |
}; | |
eventSource.onopen = () => { | |
this.logger.log(ILogger_1.LogLevel.Information, `SSE connected to ${this.url}`); | |
this.eventSource = eventSource; | |
// SSE is a text protocol | |
resolve(1 /* Text */); | |
}; | |
} | |
catch (e) { | |
return Promise.reject(e); | |
} | |
}); | |
} | |
send(data) { | |
return __awaiter(this, void 0, void 0, function* () { | |
return send(this.httpClient, this.url, data); | |
}); | |
} | |
stop() { | |
if (this.eventSource) { | |
this.eventSource.close(); | |
this.eventSource = null; | |
} | |
} | |
} | |
exports.ServerSentEventsTransport = ServerSentEventsTransport; | |
class LongPollingTransport { | |
constructor(httpClient, logger) { | |
this.httpClient = httpClient; | |
this.logger = logger; | |
} | |
connect(url, requestedTransferMode) { | |
this.url = url; | |
this.shouldPoll = true; | |
if (requestedTransferMode === 2 /* Binary */ && (typeof new XMLHttpRequest().responseType !== "string")) { | |
// This will work if we fix: https://github.com/aspnet/SignalR/issues/742 | |
throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported."); | |
} | |
this.poll(this.url, requestedTransferMode); | |
return Promise.resolve(requestedTransferMode); | |
} | |
poll(url, transferMode) { | |
if (!this.shouldPoll) { | |
return; | |
} | |
let pollXhr = new XMLHttpRequest(); | |
pollXhr.onload = () => { | |
if (pollXhr.status == 200) { | |
if (this.onreceive) { | |
try { | |
let response = transferMode === 1 /* Text */ | |
? pollXhr.responseText | |
: pollXhr.response; | |
if (response) { | |
this.logger.log(ILogger_1.LogLevel.Trace, `(LongPolling transport) data received: ${response}`); | |
this.onreceive(response); | |
} | |
else { | |
this.logger.log(ILogger_1.LogLevel.Information, "(LongPolling transport) timed out"); | |
} | |
} | |
catch (error) { | |
if (this.onclose) { | |
this.onclose(error); | |
} | |
return; | |
} | |
} | |
this.poll(url, transferMode); | |
} | |
else if (this.pollXhr.status == 204) { | |
if (this.onclose) { | |
this.onclose(); | |
} | |
} | |
else { | |
if (this.onclose) { | |
this.onclose(new HttpError_1.HttpError(pollXhr.statusText, pollXhr.status)); | |
} | |
} | |
}; | |
pollXhr.onerror = () => { | |
if (this.onclose) { | |
// network related error or denied cross domain request | |
this.onclose(new Error("Sending HTTP request failed.")); | |
} | |
}; | |
pollXhr.ontimeout = () => { | |
this.poll(url, transferMode); | |
}; | |
this.pollXhr = pollXhr; | |
this.pollXhr.open("GET", `${url}&_=${Date.now()}`, true); | |
if (transferMode === 2 /* Binary */) { | |
this.pollXhr.responseType = "arraybuffer"; | |
} | |
// TODO: consider making timeout configurable | |
this.pollXhr.timeout = 120000; | |
this.pollXhr.send(); | |
} | |
send(data) { | |
return __awaiter(this, void 0, void 0, function* () { | |
return send(this.httpClient, this.url, data); | |
}); | |
} | |
stop() { | |
this.shouldPoll = false; | |
if (this.pollXhr) { | |
this.pollXhr.abort(); | |
this.pollXhr = null; | |
} | |
} | |
} | |
exports.LongPollingTransport = LongPollingTransport; | |
const headers = new Map(); | |
function send(httpClient, url, data) { | |
return __awaiter(this, void 0, void 0, function* () { | |
yield httpClient.post(url, data, headers); | |
}); | |
} | |
},{"./HttpError":5,"./ILogger":7}]},{},[6])(6) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment