forwardchannelrequestpool.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2013 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A pool of forward channel requests to enable real-time
  16. * messaging from the client to server.
  17. *
  18. * @visibility {:internal}
  19. */
  20. goog.provide('goog.labs.net.webChannel.ForwardChannelRequestPool');
  21. goog.require('goog.array');
  22. goog.require('goog.string');
  23. goog.require('goog.structs.Set');
  24. goog.scope(function() {
  25. /** @suppress {missingRequire} type checking only */
  26. var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
  27. /**
  28. * This class represents the state of all forward channel requests.
  29. *
  30. * @param {number=} opt_maxPoolSize The maximum pool size.
  31. *
  32. * @constructor
  33. * @final
  34. */
  35. goog.labs.net.webChannel.ForwardChannelRequestPool = function(opt_maxPoolSize) {
  36. /**
  37. * THe max pool size as configured.
  38. *
  39. * @private {number}
  40. */
  41. this.maxPoolSizeConfigured_ = opt_maxPoolSize ||
  42. goog.labs.net.webChannel.ForwardChannelRequestPool.MAX_POOL_SIZE_;
  43. /**
  44. * The current size limit of the request pool. This limit is meant to be
  45. * read-only after the channel is fully opened.
  46. *
  47. * If SPDY is enabled, set it to the max pool size, which is also
  48. * configurable.
  49. *
  50. * @private {number}
  51. */
  52. this.maxSize_ = ForwardChannelRequestPool.isSpdyEnabled_() ?
  53. this.maxPoolSizeConfigured_ :
  54. 1;
  55. /**
  56. * The container for all the pending request objects.
  57. *
  58. * @private {goog.structs.Set<ChannelRequest>}
  59. */
  60. this.requestPool_ = null;
  61. if (this.maxSize_ > 1) {
  62. this.requestPool_ = new goog.structs.Set();
  63. }
  64. /**
  65. * The single request object when the pool size is limited to one.
  66. *
  67. * @private {ChannelRequest}
  68. */
  69. this.request_ = null;
  70. };
  71. var ForwardChannelRequestPool =
  72. goog.labs.net.webChannel.ForwardChannelRequestPool;
  73. /**
  74. * The default size limit of the request pool.
  75. *
  76. * @private {number}
  77. */
  78. ForwardChannelRequestPool.MAX_POOL_SIZE_ = 10;
  79. /**
  80. * @return {boolean} True if SPDY is enabled for the current page using
  81. * chrome specific APIs.
  82. * @private
  83. */
  84. ForwardChannelRequestPool.isSpdyEnabled_ = function() {
  85. return !!(
  86. goog.global.chrome && goog.global.chrome.loadTimes &&
  87. goog.global.chrome.loadTimes() &&
  88. goog.global.chrome.loadTimes().wasFetchedViaSpdy);
  89. };
  90. /**
  91. * Once we know the client protocol (from the handshake), check if we need
  92. * enable the request pool accordingly. This is more robust than using
  93. * browser-internal APIs (specific to Chrome).
  94. *
  95. * @param {string} clientProtocol The client protocol
  96. */
  97. ForwardChannelRequestPool.prototype.applyClientProtocol = function(
  98. clientProtocol) {
  99. if (this.requestPool_) {
  100. return;
  101. }
  102. if (goog.string.contains(clientProtocol, 'spdy') ||
  103. goog.string.contains(clientProtocol, 'quic') ||
  104. goog.string.contains(clientProtocol, 'h2')) {
  105. this.maxSize_ = this.maxPoolSizeConfigured_;
  106. this.requestPool_ = new goog.structs.Set();
  107. if (this.request_) {
  108. this.addRequest(this.request_);
  109. this.request_ = null;
  110. }
  111. }
  112. };
  113. /**
  114. * @return {boolean} True if the pool is full.
  115. */
  116. ForwardChannelRequestPool.prototype.isFull = function() {
  117. if (this.request_) {
  118. return true;
  119. }
  120. if (this.requestPool_) {
  121. return this.requestPool_.getCount() >= this.maxSize_;
  122. }
  123. return false;
  124. };
  125. /**
  126. * @return {number} The current size limit.
  127. */
  128. ForwardChannelRequestPool.prototype.getMaxSize = function() {
  129. return this.maxSize_;
  130. };
  131. /**
  132. * @return {number} The number of pending requests in the pool.
  133. */
  134. ForwardChannelRequestPool.prototype.getRequestCount = function() {
  135. if (this.request_) {
  136. return 1;
  137. }
  138. if (this.requestPool_) {
  139. return this.requestPool_.getCount();
  140. }
  141. return 0;
  142. };
  143. /**
  144. * @param {ChannelRequest} req The channel request.
  145. * @return {boolean} True if the request is a included inside the pool.
  146. */
  147. ForwardChannelRequestPool.prototype.hasRequest = function(req) {
  148. if (this.request_) {
  149. return this.request_ == req;
  150. }
  151. if (this.requestPool_) {
  152. return this.requestPool_.contains(req);
  153. }
  154. return false;
  155. };
  156. /**
  157. * Adds a new request to the pool.
  158. *
  159. * @param {!ChannelRequest} req The new channel request.
  160. */
  161. ForwardChannelRequestPool.prototype.addRequest = function(req) {
  162. if (this.requestPool_) {
  163. this.requestPool_.add(req);
  164. } else {
  165. this.request_ = req;
  166. }
  167. };
  168. /**
  169. * Removes the given request from the pool.
  170. *
  171. * @param {ChannelRequest} req The channel request.
  172. * @return {boolean} Whether the request has been removed from the pool.
  173. */
  174. ForwardChannelRequestPool.prototype.removeRequest = function(req) {
  175. if (this.request_ && this.request_ == req) {
  176. this.request_ = null;
  177. return true;
  178. }
  179. if (this.requestPool_ && this.requestPool_.contains(req)) {
  180. this.requestPool_.remove(req);
  181. return true;
  182. }
  183. return false;
  184. };
  185. /**
  186. * Clears the pool and cancel all the pending requests.
  187. */
  188. ForwardChannelRequestPool.prototype.cancel = function() {
  189. if (this.request_) {
  190. this.request_.cancel();
  191. this.request_ = null;
  192. return;
  193. }
  194. if (this.requestPool_ && !this.requestPool_.isEmpty()) {
  195. goog.array.forEach(
  196. this.requestPool_.getValues(), function(val) { val.cancel(); });
  197. this.requestPool_.clear();
  198. }
  199. };
  200. /**
  201. * @return {boolean} Whether there are any pending requests.
  202. */
  203. ForwardChannelRequestPool.prototype.hasPendingRequest = function() {
  204. return (this.request_ != null) ||
  205. (this.requestPool_ != null && !this.requestPool_.isEmpty());
  206. };
  207. /**
  208. * Cancels all pending requests and force the completion of channel requests.
  209. *
  210. * Need go through the standard onRequestComplete logic to expose the max-retry
  211. * failure in the standard way.
  212. *
  213. * @param {function(!ChannelRequest)} onComplete The completion callback.
  214. * @return {boolean} true if any request has been forced to complete.
  215. */
  216. ForwardChannelRequestPool.prototype.forceComplete = function(onComplete) {
  217. if (this.request_ != null) {
  218. this.request_.cancel();
  219. onComplete(this.request_);
  220. return true;
  221. }
  222. if (this.requestPool_ && !this.requestPool_.isEmpty()) {
  223. goog.array.forEach(this.requestPool_.getValues(), function(val) {
  224. val.cancel();
  225. onComplete(val);
  226. });
  227. return true;
  228. }
  229. return false;
  230. };
  231. }); // goog.scope