import Factory from '../utils/factory';
import Util from '../util';
import * as Errors from '../errors';
import * as Collections from '../utils/collections';
import Strategy from './strategy';
import Transport from '../transports/transport';
import StrategyOptions from './strategy_options';
import Handshake from '../connection/handshake';
/** Provides a strategy interface for transports.
*
* @param {String} name
* @param {Number} priority
* @param {Class} transport
* @param {Object} options
*/
export default class TransportStrategy implements Strategy {
name: string;
priority: number;
transport: Transport;
options: StrategyOptions;
constructor(
name: string,
priority: number,
transport: Transport,
options: StrategyOptions
) {
this.name = name;
this.priority = priority;
this.transport = transport;
this.options = options || {};
}
/** Returns whether the transport is supported in the browser.
*
* @returns {Boolean}
*/
isSupported(): boolean {
return this.transport.isSupported({
useTLS: this.options.useTLS
});
}
/** Launches a connection attempt and returns a strategy runner.
*
* @param {Function} callback
* @return {Object} strategy runner
*/
connect(minPriority: number, callback: Function) {
if (!this.isSupported()) {
return failAttempt(new Errors.UnsupportedStrategy(), callback);
} else if (this.priority < minPriority) {
return failAttempt(new Errors.TransportPriorityTooLow(), callback);
}
var connected = false;
var transport = this.transport.createConnection(
this.name,
this.priority,
this.options.key,
this.options
);
var handshake = null;
var onInitialized = function() {
transport.unbind('initialized', onInitialized);
transport.connect();
};
var onOpen = function() {
handshake = Factory.createHandshake(transport, function(result) {
connected = true;
unbindListeners();
callback(null, result);
});
};
var onError = function(error) {
unbindListeners();
callback(error);
};
var onClosed = function() {
unbindListeners();
var serializedTransport;
// The reason for this try/catch block is that on React Native
// the WebSocket object is circular. Therefore transport.socket will
// throw errors upon stringification. Collections.safeJSONStringify
// discards circular references when serializing.
serializedTransport = Collections.safeJSONStringify(transport);
callback(new Errors.TransportClosed(serializedTransport));
};
var unbindListeners = function() {
transport.unbind('initialized', onInitialized);
transport.unbind('open', onOpen);
transport.unbind('error', onError);
transport.unbind('closed', onClosed);
};
transport.bind('initialized', onInitialized);
transport.bind('open', onOpen);
transport.bind('error', onError);
transport.bind('closed', onClosed);
// connect will be called automatically after initialization
transport.initialize();
return {
abort: () => {
if (connected) {
return;
}
unbindListeners();
if (handshake) {
handshake.close();
} else {
transport.close();
}
},
forceMinPriority: p => {
if (connected) {
return;
}
if (this.priority < p) {
if (handshake) {
handshake.close();
} else {
transport.close();
}
}
}
};
}
}
function failAttempt(error: Error, callback: Function) {
Util.defer(function() {
callback(error);
});
return {
abort: function() {},
forceMinPriority: function() {}
};
}
|