// Copyright 2013 The Closure Library Authors. All Rights Reserved. // // 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. /** * @fileoverview A pool of forward channel requests to enable real-time * messaging from the client to server. * * @visibility {:internal} */ goog.provide('goog.labs.net.webChannel.ForwardChannelRequestPool'); goog.require('goog.array'); goog.require('goog.string'); goog.require('goog.structs.Set'); goog.scope(function() { /** @suppress {missingRequire} type checking only */ var ChannelRequest = goog.labs.net.webChannel.ChannelRequest; /** * This class represents the state of all forward channel requests. * * @param {number=} opt_maxPoolSize The maximum pool size. * * @constructor * @final */ goog.labs.net.webChannel.ForwardChannelRequestPool = function(opt_maxPoolSize) { /** * THe max pool size as configured. * * @private {number} */ this.maxPoolSizeConfigured_ = opt_maxPoolSize || goog.labs.net.webChannel.ForwardChannelRequestPool.MAX_POOL_SIZE_; /** * The current size limit of the request pool. This limit is meant to be * read-only after the channel is fully opened. * * If SPDY is enabled, set it to the max pool size, which is also * configurable. * * @private {number} */ this.maxSize_ = ForwardChannelRequestPool.isSpdyEnabled_() ? this.maxPoolSizeConfigured_ : 1; /** * The container for all the pending request objects. * * @private {goog.structs.Set} */ this.requestPool_ = null; if (this.maxSize_ > 1) { this.requestPool_ = new goog.structs.Set(); } /** * The single request object when the pool size is limited to one. * * @private {ChannelRequest} */ this.request_ = null; }; var ForwardChannelRequestPool = goog.labs.net.webChannel.ForwardChannelRequestPool; /** * The default size limit of the request pool. * * @private {number} */ ForwardChannelRequestPool.MAX_POOL_SIZE_ = 10; /** * @return {boolean} True if SPDY is enabled for the current page using * chrome specific APIs. * @private */ ForwardChannelRequestPool.isSpdyEnabled_ = function() { return !!( goog.global.chrome && goog.global.chrome.loadTimes && goog.global.chrome.loadTimes() && goog.global.chrome.loadTimes().wasFetchedViaSpdy); }; /** * Once we know the client protocol (from the handshake), check if we need * enable the request pool accordingly. This is more robust than using * browser-internal APIs (specific to Chrome). * * @param {string} clientProtocol The client protocol */ ForwardChannelRequestPool.prototype.applyClientProtocol = function( clientProtocol) { if (this.requestPool_) { return; } if (goog.string.contains(clientProtocol, 'spdy') || goog.string.contains(clientProtocol, 'quic') || goog.string.contains(clientProtocol, 'h2')) { this.maxSize_ = this.maxPoolSizeConfigured_; this.requestPool_ = new goog.structs.Set(); if (this.request_) { this.addRequest(this.request_); this.request_ = null; } } }; /** * @return {boolean} True if the pool is full. */ ForwardChannelRequestPool.prototype.isFull = function() { if (this.request_) { return true; } if (this.requestPool_) { return this.requestPool_.getCount() >= this.maxSize_; } return false; }; /** * @return {number} The current size limit. */ ForwardChannelRequestPool.prototype.getMaxSize = function() { return this.maxSize_; }; /** * @return {number} The number of pending requests in the pool. */ ForwardChannelRequestPool.prototype.getRequestCount = function() { if (this.request_) { return 1; } if (this.requestPool_) { return this.requestPool_.getCount(); } return 0; }; /** * @param {ChannelRequest} req The channel request. * @return {boolean} True if the request is a included inside the pool. */ ForwardChannelRequestPool.prototype.hasRequest = function(req) { if (this.request_) { return this.request_ == req; } if (this.requestPool_) { return this.requestPool_.contains(req); } return false; }; /** * Adds a new request to the pool. * * @param {!ChannelRequest} req The new channel request. */ ForwardChannelRequestPool.prototype.addRequest = function(req) { if (this.requestPool_) { this.requestPool_.add(req); } else { this.request_ = req; } }; /** * Removes the given request from the pool. * * @param {ChannelRequest} req The channel request. * @return {boolean} Whether the request has been removed from the pool. */ ForwardChannelRequestPool.prototype.removeRequest = function(req) { if (this.request_ && this.request_ == req) { this.request_ = null; return true; } if (this.requestPool_ && this.requestPool_.contains(req)) { this.requestPool_.remove(req); return true; } return false; }; /** * Clears the pool and cancel all the pending requests. */ ForwardChannelRequestPool.prototype.cancel = function() { if (this.request_) { this.request_.cancel(); this.request_ = null; return; } if (this.requestPool_ && !this.requestPool_.isEmpty()) { goog.array.forEach( this.requestPool_.getValues(), function(val) { val.cancel(); }); this.requestPool_.clear(); } }; /** * @return {boolean} Whether there are any pending requests. */ ForwardChannelRequestPool.prototype.hasPendingRequest = function() { return (this.request_ != null) || (this.requestPool_ != null && !this.requestPool_.isEmpty()); }; /** * Cancels all pending requests and force the completion of channel requests. * * Need go through the standard onRequestComplete logic to expose the max-retry * failure in the standard way. * * @param {function(!ChannelRequest)} onComplete The completion callback. * @return {boolean} true if any request has been forced to complete. */ ForwardChannelRequestPool.prototype.forceComplete = function(onComplete) { if (this.request_ != null) { this.request_.cancel(); onComplete(this.request_); return true; } if (this.requestPool_ && !this.requestPool_.isEmpty()) { goog.array.forEach(this.requestPool_.getValues(), function(val) { val.cancel(); onComplete(val); }); return true; } return false; }; }); // goog.scope