Adding mapbox-gl branch
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
// Copyright 2006 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 Base TestChannel implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.BaseTestChannel');
|
||||
|
||||
goog.require('goog.labs.net.webChannel.Channel');
|
||||
goog.require('goog.labs.net.webChannel.ChannelRequest');
|
||||
goog.require('goog.labs.net.webChannel.requestStats');
|
||||
goog.require('goog.labs.net.webChannel.requestStats.Stat');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A TestChannel is used during the first part of channel negotiation
|
||||
* with the server to create the channel. It helps us determine whether we're
|
||||
* behind a buffering proxy.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @param {!goog.labs.net.webChannel.Channel} channel The channel
|
||||
* that owns this test channel.
|
||||
* @param {!goog.labs.net.webChannel.WebChannelDebug} channelDebug A
|
||||
* WebChannelDebug instance to use for logging.
|
||||
* @implements {goog.labs.net.webChannel.Channel}
|
||||
*/
|
||||
goog.labs.net.webChannel.BaseTestChannel = function(channel, channelDebug) {
|
||||
/**
|
||||
* The channel that owns this test channel
|
||||
* @private {!goog.labs.net.webChannel.Channel}
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
|
||||
/**
|
||||
* The channel debug to use for logging
|
||||
* @private {!goog.labs.net.webChannel.WebChannelDebug}
|
||||
*/
|
||||
this.channelDebug_ = channelDebug;
|
||||
|
||||
/**
|
||||
* Extra HTTP headers to add to all the requests sent to the server.
|
||||
* @private {Object}
|
||||
*/
|
||||
this.extraHeaders_ = null;
|
||||
|
||||
/**
|
||||
* The test request.
|
||||
* @private {goog.labs.net.webChannel.ChannelRequest}
|
||||
*/
|
||||
this.request_ = null;
|
||||
|
||||
/**
|
||||
* Whether we have received the first result as an intermediate result. This
|
||||
* helps us determine whether we're behind a buffering proxy.
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.receivedIntermediateResult_ = false;
|
||||
|
||||
/**
|
||||
* The time when the test request was started. We use timing in IE as
|
||||
* a heuristic for whether we're behind a buffering proxy.
|
||||
* @private {?number}
|
||||
*/
|
||||
this.startTime_ = null;
|
||||
|
||||
/**
|
||||
* The time for of the first result part. We use timing in IE as a
|
||||
* heuristic for whether we're behind a buffering proxy.
|
||||
* @private {?number}
|
||||
*/
|
||||
this.firstTime_ = null;
|
||||
|
||||
/**
|
||||
* The time for of the last result part. We use timing in IE as a
|
||||
* heuristic for whether we're behind a buffering proxy.
|
||||
* @private {?number}
|
||||
*/
|
||||
this.lastTime_ = null;
|
||||
|
||||
/**
|
||||
* The relative path for test requests.
|
||||
* @private {?string}
|
||||
*/
|
||||
this.path_ = null;
|
||||
|
||||
/**
|
||||
* The last status code received.
|
||||
* @private {number}
|
||||
*/
|
||||
this.lastStatusCode_ = -1;
|
||||
|
||||
/**
|
||||
* A subdomain prefix for using a subdomain in IE for the backchannel
|
||||
* requests.
|
||||
* @private {?string}
|
||||
*/
|
||||
this.hostPrefix_ = null;
|
||||
|
||||
/**
|
||||
* The effective client protocol as indicated by the initial handshake
|
||||
* response via the x-client-wire-protocol header.
|
||||
*
|
||||
* @private {?string}
|
||||
*/
|
||||
this.clientProtocol_ = null;
|
||||
};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var BaseTestChannel = goog.labs.net.webChannel.BaseTestChannel;
|
||||
var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
|
||||
var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
|
||||
var requestStats = goog.labs.net.webChannel.requestStats;
|
||||
var Channel = goog.labs.net.webChannel.Channel;
|
||||
|
||||
|
||||
/**
|
||||
* Enum type for the test channel state machine
|
||||
* @enum {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.State_ = {
|
||||
/**
|
||||
* The state for the TestChannel state machine where we making the
|
||||
* initial call to get the server configured parameters.
|
||||
*/
|
||||
INIT: 0,
|
||||
|
||||
/**
|
||||
* The state for the TestChannel state machine where we're checking to
|
||||
* se if we're behind a buffering proxy.
|
||||
*/
|
||||
CONNECTION_TESTING: 1
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The state of the state machine for this object.
|
||||
*
|
||||
* @private {?BaseTestChannel.State_}
|
||||
*/
|
||||
BaseTestChannel.prototype.state_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Time between chunks in the test connection that indicates that we
|
||||
* are not behind a buffering proxy. This value should be less than or
|
||||
* equals to the time between chunks sent from the server.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_ = 500;
|
||||
|
||||
|
||||
/**
|
||||
* Sets extra HTTP headers to add to all the requests sent to the server.
|
||||
*
|
||||
* @param {Object} extraHeaders The HTTP headers.
|
||||
*/
|
||||
BaseTestChannel.prototype.setExtraHeaders = function(extraHeaders) {
|
||||
this.extraHeaders_ = extraHeaders;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Starts the test channel. This initiates connections to the server.
|
||||
*
|
||||
* @param {string} path The relative uri for the test connection.
|
||||
*/
|
||||
BaseTestChannel.prototype.connect = function(path) {
|
||||
this.path_ = path;
|
||||
var sendDataUri = this.channel_.getForwardChannelUri(this.path_);
|
||||
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_START);
|
||||
this.startTime_ = goog.now();
|
||||
|
||||
// If the channel already has the result of the handshake, then skip it.
|
||||
var handshakeResult = this.channel_.getConnectionState().handshakeResult;
|
||||
if (goog.isDefAndNotNull(handshakeResult)) {
|
||||
this.hostPrefix_ = this.channel_.correctHostPrefix(handshakeResult[0]);
|
||||
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
|
||||
this.checkBufferingProxy_();
|
||||
return;
|
||||
}
|
||||
|
||||
// the first request returns server specific parameters
|
||||
sendDataUri.setParameterValues('MODE', 'init');
|
||||
this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
|
||||
this.request_.setExtraHeaders(this.extraHeaders_);
|
||||
this.request_.xmlHttpGet(sendDataUri, false /* decodeChunks */,
|
||||
null /* hostPrefix */, true /* opt_noClose */);
|
||||
this.state_ = BaseTestChannel.State_.INIT;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Begins the second stage of the test channel where we test to see if we're
|
||||
* behind a buffering proxy. The server sends back a multi-chunked response
|
||||
* with the first chunk containing the content '1' and then two seconds later
|
||||
* sending the second chunk containing the content '2'. Depending on how we
|
||||
* receive the content, we can tell if we're behind a buffering proxy.
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.checkBufferingProxy_ = function() {
|
||||
this.channelDebug_.debug('TestConnection: starting stage 2');
|
||||
|
||||
// If the test result is already available, skip its execution.
|
||||
var bufferingProxyResult =
|
||||
this.channel_.getConnectionState().bufferingProxyResult;
|
||||
if (goog.isDefAndNotNull(bufferingProxyResult)) {
|
||||
this.channelDebug_.debug(
|
||||
'TestConnection: skipping stage 2, precomputed result is ' +
|
||||
bufferingProxyResult ? 'Buffered' : 'Unbuffered');
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
|
||||
if (bufferingProxyResult) { // Buffered/Proxy connection
|
||||
requestStats.notifyStatEvent(requestStats.Stat.PROXY);
|
||||
this.channel_.testConnectionFinished(this, false);
|
||||
} else { // Unbuffered/NoProxy connection
|
||||
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
|
||||
this.channel_.testConnectionFinished(this, true);
|
||||
}
|
||||
return; // Skip the test
|
||||
}
|
||||
this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
|
||||
this.request_.setExtraHeaders(this.extraHeaders_);
|
||||
var recvDataUri = this.channel_.getBackChannelUri(this.hostPrefix_,
|
||||
/** @type {string} */ (this.path_));
|
||||
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
|
||||
if (!ChannelRequest.supportsXhrStreaming()) {
|
||||
recvDataUri.setParameterValues('TYPE', 'html');
|
||||
this.request_.tridentGet(recvDataUri, Boolean(this.hostPrefix_));
|
||||
} else {
|
||||
recvDataUri.setParameterValues('TYPE', 'xmlhttp');
|
||||
this.request_.xmlHttpGet(recvDataUri, false /** decodeChunks */,
|
||||
this.hostPrefix_, false /** opt_noClose */);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.createXhrIo = function(hostPrefix) {
|
||||
return this.channel_.createXhrIo(hostPrefix);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Aborts the test channel.
|
||||
*/
|
||||
BaseTestChannel.prototype.abort = function() {
|
||||
if (this.request_) {
|
||||
this.request_.cancel();
|
||||
this.request_ = null;
|
||||
}
|
||||
this.lastStatusCode_ = -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the test channel is closed. The ChannelRequest object expects
|
||||
* this method to be implemented on its handler.
|
||||
*
|
||||
* @return {boolean} Whether the channel is closed.
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.isClosed = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback from ChannelRequest for when new data is received
|
||||
*
|
||||
* @param {ChannelRequest} req The request object.
|
||||
* @param {string} responseText The text of the response.
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.onRequestData = function(req, responseText) {
|
||||
this.lastStatusCode_ = req.getLastStatusCode();
|
||||
if (this.state_ == BaseTestChannel.State_.INIT) {
|
||||
this.channelDebug_.debug('TestConnection: Got data for stage 1');
|
||||
if (!responseText) {
|
||||
this.channelDebug_.debug('TestConnection: Null responseText');
|
||||
// The server should always send text; something is wrong here
|
||||
this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
/** @preserveTry */
|
||||
try {
|
||||
var respArray = this.channel_.getWireCodec().decodeMessage(responseText);
|
||||
} catch (e) {
|
||||
this.channelDebug_.dumpException(e);
|
||||
this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
|
||||
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
|
||||
if (this.receivedIntermediateResult_) {
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_TWO);
|
||||
this.lastTime_ = goog.now();
|
||||
} else {
|
||||
// '11111' is used instead of '1' to prevent a small amount of buffering
|
||||
// by Safari.
|
||||
if (responseText == '11111') {
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_ONE);
|
||||
this.receivedIntermediateResult_ = true;
|
||||
this.firstTime_ = goog.now();
|
||||
if (this.checkForEarlyNonBuffered_()) {
|
||||
// If early chunk detection is on, and we passed the tests,
|
||||
// assume HTTP_OK, cancel the test and turn on noproxy mode.
|
||||
this.lastStatusCode_ = 200;
|
||||
this.request_.cancel();
|
||||
this.channelDebug_.debug(
|
||||
'Test connection succeeded; using streaming connection');
|
||||
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
|
||||
this.channel_.testConnectionFinished(this, true);
|
||||
}
|
||||
} else {
|
||||
requestStats.notifyStatEvent(
|
||||
requestStats.Stat.TEST_STAGE_TWO_DATA_BOTH);
|
||||
this.firstTime_ = this.lastTime_ = goog.now();
|
||||
this.receivedIntermediateResult_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback from ChannelRequest that indicates a request has completed.
|
||||
*
|
||||
* @param {!ChannelRequest} req The request object.
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.onRequestComplete = function(req) {
|
||||
this.lastStatusCode_ = this.request_.getLastStatusCode();
|
||||
if (!this.request_.getSuccess()) {
|
||||
this.channelDebug_.debug(
|
||||
'TestConnection: request failed, in state ' + this.state_);
|
||||
if (this.state_ == BaseTestChannel.State_.INIT) {
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_FAILED);
|
||||
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_FAILED);
|
||||
}
|
||||
this.channel_.testConnectionFailure(this,
|
||||
/** @type {ChannelRequest.Error} */
|
||||
(this.request_.getLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state_ == BaseTestChannel.State_.INIT) {
|
||||
this.recordClientProtocol_(req);
|
||||
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
|
||||
|
||||
this.channelDebug_.debug(
|
||||
'TestConnection: request complete for initial check');
|
||||
|
||||
this.checkBufferingProxy_();
|
||||
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
|
||||
this.channelDebug_.debug('TestConnection: request complete for stage 2');
|
||||
var goodConn = false;
|
||||
|
||||
if (!ChannelRequest.supportsXhrStreaming()) {
|
||||
// we always get Trident responses in separate calls to
|
||||
// onRequestData, so we have to check the time they came
|
||||
var ms = this.lastTime_ - this.firstTime_;
|
||||
if (ms < 200) {
|
||||
// TODO: need to empirically verify that this number is OK
|
||||
// for slow computers
|
||||
goodConn = false;
|
||||
} else {
|
||||
goodConn = true;
|
||||
}
|
||||
} else {
|
||||
goodConn = this.receivedIntermediateResult_;
|
||||
}
|
||||
|
||||
if (goodConn) {
|
||||
this.channelDebug_.debug(
|
||||
'Test connection succeeded; using streaming connection');
|
||||
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
|
||||
this.channel_.testConnectionFinished(this, true);
|
||||
} else {
|
||||
this.channelDebug_.debug(
|
||||
'Test connection failed; not using streaming');
|
||||
requestStats.notifyStatEvent(requestStats.Stat.PROXY);
|
||||
this.channel_.testConnectionFinished(this, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Record the client protocol header from the initial handshake response.
|
||||
*
|
||||
* @param {!ChannelRequest} req The request object.
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.recordClientProtocol_ = function(req) {
|
||||
var xmlHttp = req.getXhr();
|
||||
if (xmlHttp) {
|
||||
var protocolHeader = xmlHttp.getResponseHeader('x-client-wire-protocol');
|
||||
this.clientProtocol_ = protocolHeader ? protocolHeader : null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {?string} The client protocol as recorded with the init handshake
|
||||
* request.
|
||||
*/
|
||||
BaseTestChannel.prototype.getClientProtocol = function() {
|
||||
return this.clientProtocol_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the last status code received for a request.
|
||||
* @return {number} The last status code received for a request.
|
||||
*/
|
||||
BaseTestChannel.prototype.getLastStatusCode = function() {
|
||||
return this.lastStatusCode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether we should be using secondary domains when the
|
||||
* server instructs us to do so.
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.shouldUseSecondaryDomains = function() {
|
||||
return this.channel_.shouldUseSecondaryDomains();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.isActive = function() {
|
||||
return this.channel_.isActive();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} True if test stage 2 detected a non-buffered
|
||||
* channel early and early no buffering detection is enabled.
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.checkForEarlyNonBuffered_ = function() {
|
||||
var ms = this.firstTime_ - this.startTime_;
|
||||
|
||||
// we always get Trident responses in separate calls to
|
||||
// onRequestData, so we have to check the time that the first came in
|
||||
// and verify that the data arrived before the second portion could
|
||||
// have been sent. For all other browser's we skip the timing test.
|
||||
return ChannelRequest.supportsXhrStreaming() ||
|
||||
ms < BaseTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.getForwardChannelUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.getBackChannelUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.correctHostPrefix = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.createDataUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.testConnectionFinished = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.testConnectionFailure = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.getConnectionState = goog.abstractMethod;
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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 shared interface for WebChannelBase and BaseTestChannel.
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.Channel');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Shared interface between Channel and TestChannel to support callbacks
|
||||
* between WebChannelBase and BaseTestChannel and between Channel and
|
||||
* ChannelRequest.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
goog.labs.net.webChannel.Channel = function() {};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var Channel = goog.labs.net.webChannel.Channel;
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether to use a secondary domain when the server gives us
|
||||
* a host prefix. This allows us to work around browser per-domain
|
||||
* connection limits.
|
||||
*
|
||||
* Currently, we use secondary domains when using Trident's ActiveXObject,
|
||||
* because it supports cross-domain requests out of the box. Note that in IE10
|
||||
* we no longer use ActiveX since it's not supported in Metro mode and IE10
|
||||
* supports XHR streaming.
|
||||
*
|
||||
* If you need to use secondary domains on other browsers and IE10,
|
||||
* you have two choices:
|
||||
* 1) If you only care about browsers that support CORS
|
||||
* (https://developer.mozilla.org/en-US/docs/HTTP_access_control), you
|
||||
* can use {@link #setSupportsCrossDomainXhrs} and set the appropriate
|
||||
* CORS response headers on the server.
|
||||
* 2) Or, override this method in a subclass, and make sure that those
|
||||
* browsers use some messaging mechanism that works cross-domain (e.g
|
||||
* iframes and window.postMessage).
|
||||
*
|
||||
* @return {boolean} Whether to use secondary domains.
|
||||
* @see http://code.google.com/p/closure-library/issues/detail?id=339
|
||||
*/
|
||||
Channel.prototype.shouldUseSecondaryDomains = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Called when creating an XhrIo object. Override in a subclass if
|
||||
* you need to customize the behavior, for example to enable the creation of
|
||||
* XHR's capable of calling a secondary domain. Will also allow calling
|
||||
* a secondary domain if withCredentials (CORS) is enabled.
|
||||
* @param {?string} hostPrefix The host prefix, if we need an XhrIo object
|
||||
* capable of calling a secondary domain.
|
||||
* @return {!goog.net.XhrIo} A new XhrIo object.
|
||||
*/
|
||||
Channel.prototype.createXhrIo = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Callback from ChannelRequest that indicates a request has completed.
|
||||
* @param {!goog.labs.net.webChannel.ChannelRequest} request
|
||||
* The request object.
|
||||
*/
|
||||
Channel.prototype.onRequestComplete = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the channel is closed
|
||||
* @return {boolean} true if the channel is closed.
|
||||
*/
|
||||
Channel.prototype.isClosed = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Callback from ChannelRequest for when new data is received
|
||||
* @param {goog.labs.net.webChannel.ChannelRequest} request
|
||||
* The request object.
|
||||
* @param {string} responseText The text of the response.
|
||||
*/
|
||||
Channel.prototype.onRequestData = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets whether this channel is currently active. This is used to determine the
|
||||
* length of time to wait before retrying. This call delegates to the handler.
|
||||
* @return {boolean} Whether the channel is currently active.
|
||||
*/
|
||||
Channel.prototype.isActive = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Gets the Uri used for the connection that sends data to the server.
|
||||
* @param {string} path The path on the host.
|
||||
* @return {goog.Uri} The forward channel URI.
|
||||
*/
|
||||
Channel.prototype.getForwardChannelUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Gets the Uri used for the connection that receives data from the server.
|
||||
* @param {?string} hostPrefix The host prefix.
|
||||
* @param {string} path The path on the host.
|
||||
* @return {goog.Uri} The back channel URI.
|
||||
*/
|
||||
Channel.prototype.getBackChannelUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Allows the handler to override a host prefix provided by the server. Will
|
||||
* be called whenever the channel has received such a prefix and is considering
|
||||
* its use.
|
||||
* @param {?string} serverHostPrefix The host prefix provided by the server.
|
||||
* @return {?string} The host prefix the client should use.
|
||||
*/
|
||||
Channel.prototype.correctHostPrefix = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Creates a data Uri applying logic for secondary hostprefix, port
|
||||
* overrides, and versioning.
|
||||
* @param {?string} hostPrefix The host prefix.
|
||||
* @param {string} path The path on the host (may be absolute or relative).
|
||||
* @param {number=} opt_overridePort Optional override port.
|
||||
* @return {goog.Uri} The data URI.
|
||||
*/
|
||||
Channel.prototype.createDataUri = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Callback from TestChannel for when the channel is finished.
|
||||
* @param {goog.labs.net.webChannel.BaseTestChannel} testChannel
|
||||
* The TestChannel.
|
||||
* @param {boolean} useChunked Whether we can chunk responses.
|
||||
*/
|
||||
Channel.prototype.testConnectionFinished = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
*
|
||||
* Callback from TestChannel for when the channel has an error.
|
||||
* @param {goog.labs.net.webChannel.BaseTestChannel} testChannel
|
||||
* The TestChannel.
|
||||
* @param {goog.labs.net.webChannel.ChannelRequest.Error} errorCode
|
||||
* The error code of the failure.
|
||||
*/
|
||||
Channel.prototype.testConnectionFailure = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
* Gets the result of previous connectivity tests.
|
||||
*
|
||||
* @return {!goog.labs.net.webChannel.ConnectionState} The connectivity state.
|
||||
*/
|
||||
Channel.prototype.getConnectionState = goog.abstractMethod;
|
||||
}); // goog.scope
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2013 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Closure Unit Tests - goog.labs.net.webChannel.ChannelRequest</title>
|
||||
<script src="../../../base.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
goog.require('goog.labs.net.webChannel.channelRequestTest');
|
||||
</script>
|
||||
<div id="debug" style="font-size: small"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,310 @@
|
||||
// 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 Unit tests for goog.labs.net.webChannel.ChannelRequest.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.channelRequestTest');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.labs.net.webChannel.ChannelRequest');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelDebug');
|
||||
goog.require('goog.labs.net.webChannel.requestStats');
|
||||
goog.require('goog.labs.net.webChannel.requestStats.ServerReachability');
|
||||
goog.require('goog.testing.MockClock');
|
||||
goog.require('goog.testing.PropertyReplacer');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.testing.net.XhrIo');
|
||||
goog.require('goog.testing.recordFunction');
|
||||
|
||||
goog.setTestOnly('goog.labs.net.webChannel.channelRequestTest');
|
||||
|
||||
|
||||
var channelRequest;
|
||||
var mockChannel;
|
||||
var mockClock;
|
||||
var stubs;
|
||||
var xhrIo;
|
||||
var reachabilityEvents;
|
||||
|
||||
|
||||
/**
|
||||
* Time to wait for a network request to time out, before aborting.
|
||||
*/
|
||||
var WATCHDOG_TIME = 2000;
|
||||
|
||||
|
||||
/**
|
||||
* Time to throttle readystatechange events.
|
||||
*/
|
||||
var THROTTLE_TIME = 500;
|
||||
|
||||
|
||||
/**
|
||||
* A really long time - used to make sure no more timeouts will fire.
|
||||
*/
|
||||
var ALL_DAY_MS = 1000 * 60 * 60 * 24;
|
||||
|
||||
|
||||
function setUp() {
|
||||
mockClock = new goog.testing.MockClock();
|
||||
mockClock.install();
|
||||
reachabilityEvents = {};
|
||||
stubs = new goog.testing.PropertyReplacer();
|
||||
|
||||
// Mock out the stat notification code.
|
||||
var notifyServerReachabilityEvent = function(reachabilityType) {
|
||||
if (!reachabilityEvents[reachabilityType]) {
|
||||
reachabilityEvents[reachabilityType] = 0;
|
||||
}
|
||||
reachabilityEvents[reachabilityType]++;
|
||||
};
|
||||
stubs.set(goog.labs.net.webChannel.requestStats,
|
||||
'notifyServerReachabilityEvent', notifyServerReachabilityEvent);
|
||||
}
|
||||
|
||||
|
||||
function tearDown() {
|
||||
stubs.reset();
|
||||
mockClock.uninstall();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a duck-type WebChannelBase that tracks the completed requests.
|
||||
* @constructor
|
||||
* @struct
|
||||
* @final
|
||||
*/
|
||||
function MockWebChannelBase() {
|
||||
this.isClosed = function() {
|
||||
return false;
|
||||
};
|
||||
this.isActive = function() {
|
||||
return true;
|
||||
};
|
||||
this.shouldUseSecondaryDomains = function() {
|
||||
return false;
|
||||
};
|
||||
this.completedRequests = [];
|
||||
this.onRequestComplete = function(request) {
|
||||
this.completedRequests.push(request);
|
||||
};
|
||||
this.onRequestData = function(request, data) {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a real ChannelRequest object, with some modifications for
|
||||
* testability:
|
||||
* <ul>
|
||||
* <li>The channel is a mock channel.
|
||||
* <li>The new watchdogTimeoutCallCount property tracks onWatchDogTimeout_()
|
||||
* calls.
|
||||
* <li>The timeout is set to WATCHDOG_TIME.
|
||||
* </ul>
|
||||
*/
|
||||
function createChannelRequest() {
|
||||
xhrIo = new goog.testing.net.XhrIo();
|
||||
xhrIo.abort = xhrIo.abort || function() {
|
||||
this.active_ = false;
|
||||
};
|
||||
|
||||
// Install mock channel and no-op debug logger.
|
||||
mockChannel = new MockWebChannelBase();
|
||||
channelRequest = new goog.labs.net.webChannel.ChannelRequest(
|
||||
mockChannel,
|
||||
new goog.labs.net.webChannel.WebChannelDebug());
|
||||
|
||||
// Install test XhrIo.
|
||||
mockChannel.createXhrIo = function() {
|
||||
return xhrIo;
|
||||
};
|
||||
|
||||
// Install watchdogTimeoutCallCount.
|
||||
channelRequest.watchdogTimeoutCallCount = 0;
|
||||
channelRequest.originalOnWatchDogTimeout = channelRequest.onWatchDogTimeout_;
|
||||
channelRequest.onWatchDogTimeout_ = function() {
|
||||
channelRequest.watchdogTimeoutCallCount++;
|
||||
return channelRequest.originalOnWatchDogTimeout();
|
||||
};
|
||||
|
||||
channelRequest.setTimeout(WATCHDOG_TIME);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run through the lifecycle of a long lived request, checking that the right
|
||||
* network events are reported.
|
||||
*/
|
||||
function testNetworkEvents() {
|
||||
createChannelRequest();
|
||||
|
||||
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
|
||||
checkReachabilityEvents(1, 0, 0, 0);
|
||||
if (goog.labs.net.webChannel.ChannelRequest.supportsXhrStreaming()) {
|
||||
xhrIo.simulatePartialResponse('17\nI am a BC Message');
|
||||
checkReachabilityEvents(1, 0, 0, 1);
|
||||
xhrIo.simulatePartialResponse('23\nI am another BC Message');
|
||||
checkReachabilityEvents(1, 0, 0, 2);
|
||||
xhrIo.simulateResponse(200, '16\Final BC Message');
|
||||
checkReachabilityEvents(1, 1, 0, 2);
|
||||
} else {
|
||||
xhrIo.simulateResponse(200, '16\Final BC Message');
|
||||
checkReachabilityEvents(1, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test throttling of readystatechange events.
|
||||
*/
|
||||
function testNetworkEvents_throttleReadyStateChange() {
|
||||
createChannelRequest();
|
||||
channelRequest.setReadyStateChangeThrottle(THROTTLE_TIME);
|
||||
|
||||
var recordedHandler =
|
||||
goog.testing.recordFunction(channelRequest.xmlHttpHandler_);
|
||||
stubs.set(channelRequest, 'xmlHttpHandler_', recordedHandler);
|
||||
|
||||
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
|
||||
assertEquals(1, recordedHandler.getCallCount());
|
||||
|
||||
checkReachabilityEvents(1, 0, 0, 0);
|
||||
if (goog.labs.net.webChannel.ChannelRequest.supportsXhrStreaming()) {
|
||||
xhrIo.simulatePartialResponse('17\nI am a BC Message');
|
||||
checkReachabilityEvents(1, 0, 0, 1);
|
||||
assertEquals(3, recordedHandler.getCallCount());
|
||||
|
||||
// Second event should be throttled
|
||||
xhrIo.simulatePartialResponse('23\nI am another BC Message');
|
||||
assertEquals(3, recordedHandler.getCallCount());
|
||||
|
||||
xhrIo.simulatePartialResponse('27\nI am yet another BC Message');
|
||||
assertEquals(3, recordedHandler.getCallCount());
|
||||
mockClock.tick(THROTTLE_TIME);
|
||||
|
||||
checkReachabilityEvents(1, 0, 0, 3);
|
||||
// Only one more call because of throttling.
|
||||
assertEquals(4, recordedHandler.getCallCount());
|
||||
|
||||
xhrIo.simulateResponse(200, '16\Final BC Message');
|
||||
checkReachabilityEvents(1, 1, 0, 3);
|
||||
assertEquals(5, recordedHandler.getCallCount());
|
||||
} else {
|
||||
xhrIo.simulateResponse(200, '16\Final BC Message');
|
||||
checkReachabilityEvents(1, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that the request "completes" with an error when the timeout
|
||||
* expires.
|
||||
*/
|
||||
function testRequestTimeout() {
|
||||
createChannelRequest();
|
||||
|
||||
channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
|
||||
assertEquals(0, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(0, channelRequest.channel_.completedRequests.length);
|
||||
|
||||
// Watchdog timeout.
|
||||
mockClock.tick(WATCHDOG_TIME);
|
||||
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(1, channelRequest.channel_.completedRequests.length);
|
||||
assertFalse(channelRequest.getSuccess());
|
||||
|
||||
// Make sure no more timers are firing.
|
||||
mockClock.tick(ALL_DAY_MS);
|
||||
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(1, channelRequest.channel_.completedRequests.length);
|
||||
|
||||
checkReachabilityEvents(1, 0, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
function testRequestTimeoutWithUnexpectedException() {
|
||||
createChannelRequest();
|
||||
channelRequest.channel_.createXhrIo = goog.functions.error('Weird error');
|
||||
|
||||
try {
|
||||
channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null);
|
||||
fail('Expected error');
|
||||
} catch (e) {
|
||||
assertEquals('Weird error', e.message);
|
||||
}
|
||||
|
||||
assertEquals(0, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(0, channelRequest.channel_.completedRequests.length);
|
||||
|
||||
// Watchdog timeout.
|
||||
mockClock.tick(WATCHDOG_TIME);
|
||||
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(1, channelRequest.channel_.completedRequests.length);
|
||||
assertFalse(channelRequest.getSuccess());
|
||||
|
||||
// Make sure no more timers are firing.
|
||||
mockClock.tick(ALL_DAY_MS);
|
||||
assertEquals(1, channelRequest.watchdogTimeoutCallCount);
|
||||
assertEquals(1, channelRequest.channel_.completedRequests.length);
|
||||
|
||||
checkReachabilityEvents(0, 0, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
function testActiveXBlocked() {
|
||||
createChannelRequest();
|
||||
stubs.set(goog.global, 'ActiveXObject',
|
||||
goog.functions.error('Active X blocked'));
|
||||
|
||||
channelRequest.tridentGet(new goog.Uri('some_uri'), false);
|
||||
assertFalse(channelRequest.getSuccess());
|
||||
assertEquals(
|
||||
goog.labs.net.webChannel.ChannelRequest.Error.ACTIVE_X_BLOCKED,
|
||||
channelRequest.getLastError());
|
||||
|
||||
checkReachabilityEvents(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
function checkReachabilityEvents(reqMade, reqSucceeded, reqFail, backChannel) {
|
||||
var Reachability =
|
||||
goog.labs.net.webChannel.requestStats.ServerReachability;
|
||||
assertEquals(reqMade,
|
||||
reachabilityEvents[Reachability.REQUEST_MADE] || 0);
|
||||
assertEquals(reqSucceeded,
|
||||
reachabilityEvents[Reachability.REQUEST_SUCCEEDED] || 0);
|
||||
assertEquals(reqFail,
|
||||
reachabilityEvents[Reachability.REQUEST_FAILED] || 0);
|
||||
assertEquals(backChannel,
|
||||
reachabilityEvents[Reachability.BACK_CHANNEL_ACTIVITY] || 0);
|
||||
}
|
||||
|
||||
|
||||
function testDuplicatedRandomParams() {
|
||||
createChannelRequest();
|
||||
channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null, true,
|
||||
true /* opt_duplicateRandom */);
|
||||
var z = xhrIo.getLastUri().getParameterValue('zx');
|
||||
var z1 = xhrIo.getLastUri().getParameterValue('zx1');
|
||||
assertTrue(goog.isDefAndNotNull(z));
|
||||
assertTrue(goog.isDefAndNotNull(z1));
|
||||
assertEquals(z1, z);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 This class manages the network connectivity state.
|
||||
*
|
||||
* Some of the connectivity state may be exposed to the client code in future,
|
||||
* e.g. the initial handshake state, in order to save one RTT when a channel
|
||||
* has to be reestablished. TODO(user).
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.ConnectionState');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The connectivity state of the channel.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
*/
|
||||
goog.labs.net.webChannel.ConnectionState = function() {
|
||||
/**
|
||||
* Handshake result.
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
this.handshakeResult = null;
|
||||
|
||||
/**
|
||||
* The result of checking if there is a buffering proxy in the network.
|
||||
* True means the connection is buffered, False means unbuffered,
|
||||
* null means that the result is not available.
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.bufferingProxyResult = null;
|
||||
};
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
// 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() {
|
||||
// type checking only (no require)
|
||||
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<ChannelRequest>}
|
||||
*/
|
||||
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 !!(window.chrome && window.chrome.loadTimes &&
|
||||
window.chrome.loadTimes() && window.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')) {
|
||||
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
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2013 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Closure Unit Tests - goog.labs.net.webChannel.ForwardChannelRequestPool</title>
|
||||
<script src="../../../base.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
goog.require('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
|
||||
</script>
|
||||
<div id="debug" style="font-size: small"></div>
|
||||
</body>
|
||||
</html>
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// 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 Unit tests for
|
||||
* goog.labs.net.webChannel.ForwardChannelRequestPool.
|
||||
* @suppress {accessControls} Private methods are accessed for test purposes.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
|
||||
|
||||
goog.require('goog.labs.net.webChannel.ChannelRequest');
|
||||
goog.require('goog.labs.net.webChannel.ForwardChannelRequestPool');
|
||||
goog.require('goog.testing.PropertyReplacer');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
goog.setTestOnly('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
|
||||
|
||||
|
||||
var propertyReplacer = new goog.testing.PropertyReplacer();
|
||||
var req = new goog.labs.net.webChannel.ChannelRequest(null, null);
|
||||
|
||||
|
||||
function setUp() {
|
||||
}
|
||||
|
||||
|
||||
function tearDown() {
|
||||
propertyReplacer.reset();
|
||||
}
|
||||
|
||||
|
||||
function stubSpdyCheck(spdyEnabled) {
|
||||
propertyReplacer.set(goog.labs.net.webChannel.ForwardChannelRequestPool,
|
||||
'isSpdyEnabled_',
|
||||
function() {
|
||||
return spdyEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function testSpdyEnabled() {
|
||||
stubSpdyCheck(true);
|
||||
|
||||
var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
|
||||
assertFalse(pool.isFull());
|
||||
assertEquals(0, pool.getRequestCount());
|
||||
pool.addRequest(req);
|
||||
assertTrue(pool.hasPendingRequest());
|
||||
assertTrue(pool.hasRequest(req));
|
||||
pool.removeRequest(req);
|
||||
assertFalse(pool.hasPendingRequest());
|
||||
|
||||
for (var i = 0; i < pool.getMaxSize(); i++) {
|
||||
pool.addRequest(new goog.labs.net.webChannel.ChannelRequest(null, null));
|
||||
}
|
||||
assertTrue(pool.isFull());
|
||||
|
||||
// do not fail
|
||||
pool.addRequest(req);
|
||||
assertTrue(pool.isFull());
|
||||
}
|
||||
|
||||
|
||||
function testSpdyNotEnabled() {
|
||||
stubSpdyCheck(false);
|
||||
|
||||
var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
|
||||
assertFalse(pool.isFull());
|
||||
assertEquals(0, pool.getRequestCount());
|
||||
pool.addRequest(req);
|
||||
assertTrue(pool.hasPendingRequest());
|
||||
assertTrue(pool.hasRequest(req));
|
||||
assertTrue(pool.isFull());
|
||||
pool.removeRequest(req);
|
||||
assertFalse(pool.hasPendingRequest());
|
||||
|
||||
// do not fail
|
||||
pool.addRequest(req);
|
||||
assertTrue(pool.isFull());
|
||||
}
|
||||
|
||||
|
||||
function testApplyClientProtocol() {
|
||||
stubSpdyCheck(false);
|
||||
|
||||
var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
|
||||
assertEquals(1, pool.getMaxSize());
|
||||
pool.applyClientProtocol('spdy/3');
|
||||
assertTrue(pool.getMaxSize() > 1);
|
||||
pool.applyClientProtocol('foo-bar'); // no effect
|
||||
assertTrue(pool.getMaxSize() > 1);
|
||||
|
||||
pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
|
||||
assertEquals(1, pool.getMaxSize());
|
||||
pool.applyClientProtocol('quic/x');
|
||||
assertTrue(pool.getMaxSize() > 1);
|
||||
|
||||
stubSpdyCheck(true);
|
||||
|
||||
pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
|
||||
assertTrue(pool.getMaxSize() > 1);
|
||||
pool.applyClientProtocol('foo/3'); // no effect
|
||||
assertTrue(pool.getMaxSize() > 1);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 Utility functions for managing networking, such as
|
||||
* testing network connectivity.
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.netUtils');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelDebug');
|
||||
|
||||
goog.scope(function() {
|
||||
var netUtils = goog.labs.net.webChannel.netUtils;
|
||||
var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
|
||||
|
||||
|
||||
/**
|
||||
* Default timeout to allow for URI pings.
|
||||
* @type {number}
|
||||
*/
|
||||
netUtils.NETWORK_TIMEOUT = 10000;
|
||||
|
||||
|
||||
/**
|
||||
* Pings the network with an image URI to check if an error is a server error
|
||||
* or user's network error.
|
||||
*
|
||||
* The caller needs to add a 'rand' parameter to make sure the response is
|
||||
* not fulfilled by browser cache.
|
||||
*
|
||||
* @param {function(boolean)} callback The function to call back with results.
|
||||
* @param {goog.Uri=} opt_imageUri The URI (of an image) to use for the network
|
||||
* test.
|
||||
*/
|
||||
netUtils.testNetwork = function(callback, opt_imageUri) {
|
||||
var uri = opt_imageUri;
|
||||
if (!uri) {
|
||||
// default google.com image
|
||||
uri = new goog.Uri('//www.google.com/images/cleardot.gif');
|
||||
|
||||
if (!(goog.global.location && goog.global.location.protocol == 'http')) {
|
||||
uri.setScheme('https'); // e.g. chrome-extension
|
||||
}
|
||||
uri.makeUnique();
|
||||
}
|
||||
|
||||
netUtils.testLoadImage(uri.toString(), netUtils.NETWORK_TIMEOUT, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Test loading the given image, retrying if necessary.
|
||||
* @param {string} url URL to the image.
|
||||
* @param {number} timeout Milliseconds before giving up.
|
||||
* @param {function(boolean)} callback Function to call with results.
|
||||
* @param {number} retries The number of times to retry.
|
||||
* @param {number=} opt_pauseBetweenRetriesMS Optional number of milliseconds
|
||||
* between retries - defaults to 0.
|
||||
*/
|
||||
netUtils.testLoadImageWithRetries = function(url, timeout, callback,
|
||||
retries, opt_pauseBetweenRetriesMS) {
|
||||
var channelDebug = new WebChannelDebug();
|
||||
channelDebug.debug('TestLoadImageWithRetries: ' + opt_pauseBetweenRetriesMS);
|
||||
if (retries == 0) {
|
||||
// no more retries, give up
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var pauseBetweenRetries = opt_pauseBetweenRetriesMS || 0;
|
||||
retries--;
|
||||
netUtils.testLoadImage(url, timeout, function(succeeded) {
|
||||
if (succeeded) {
|
||||
callback(true);
|
||||
} else {
|
||||
// try again
|
||||
goog.global.setTimeout(function() {
|
||||
netUtils.testLoadImageWithRetries(url, timeout, callback,
|
||||
retries, pauseBetweenRetries);
|
||||
}, pauseBetweenRetries);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Test loading the given image.
|
||||
* @param {string} url URL to the image.
|
||||
* @param {number} timeout Milliseconds before giving up.
|
||||
* @param {function(boolean)} callback Function to call with results.
|
||||
*/
|
||||
netUtils.testLoadImage = function(url, timeout, callback) {
|
||||
var channelDebug = new WebChannelDebug();
|
||||
channelDebug.debug('TestLoadImage: loading ' + url);
|
||||
var img = new Image();
|
||||
img.onload = goog.partial(netUtils.imageCallback_, channelDebug, img,
|
||||
'TestLoadImage: loaded', true, callback);
|
||||
img.onerror = goog.partial(netUtils.imageCallback_, channelDebug, img,
|
||||
'TestLoadImage: error', false, callback);
|
||||
img.onabort = goog.partial(netUtils.imageCallback_, channelDebug, img,
|
||||
'TestLoadImage: abort', false, callback);
|
||||
img.ontimeout = goog.partial(netUtils.imageCallback_, channelDebug, img,
|
||||
'TestLoadImage: timeout', false, callback);
|
||||
|
||||
goog.global.setTimeout(function() {
|
||||
if (img.ontimeout) {
|
||||
img.ontimeout();
|
||||
}
|
||||
}, timeout);
|
||||
img.src = url;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the image callback with debug and cleanup logic.
|
||||
* @param {!WebChannelDebug} channelDebug The WebChannelDebug object.
|
||||
* @param {!Image} img The image element.
|
||||
* @param {string} debugText The debug text.
|
||||
* @param {boolean} result The result of image loading.
|
||||
* @param {function(boolean)} callback The image callback.
|
||||
* @private
|
||||
*/
|
||||
netUtils.imageCallback_ = function(channelDebug, img, debugText, result,
|
||||
callback) {
|
||||
try {
|
||||
channelDebug.debug(debugText);
|
||||
netUtils.clearImageCallbacks_(img);
|
||||
callback(result);
|
||||
} catch (e) {
|
||||
channelDebug.dumpException(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears handlers to avoid memory leaks.
|
||||
* @param {Image} img The image to clear handlers from.
|
||||
* @private
|
||||
*/
|
||||
netUtils.clearImageCallbacks_ = function(img) {
|
||||
img.onload = null;
|
||||
img.onerror = null;
|
||||
img.onabort = null;
|
||||
img.ontimeout = null;
|
||||
};
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,386 @@
|
||||
// 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 Static utilities for collecting stats associated with
|
||||
* ChannelRequest.
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.requestStats');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.Event');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.ServerReachability');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.ServerReachabilityEvent');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.Stat');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.StatEvent');
|
||||
goog.provide('goog.labs.net.webChannel.requestStats.TimingEvent');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventTarget');
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var requestStats = goog.labs.net.webChannel.requestStats;
|
||||
|
||||
|
||||
/**
|
||||
* Events fired.
|
||||
* @const
|
||||
*/
|
||||
requestStats.Event = {};
|
||||
|
||||
|
||||
/**
|
||||
* Singleton event target for firing stat events
|
||||
* @type {goog.events.EventTarget}
|
||||
* @private
|
||||
*/
|
||||
requestStats.statEventTarget_ = new goog.events.EventTarget();
|
||||
|
||||
|
||||
/**
|
||||
* The type of event that occurs every time some information about how reachable
|
||||
* the server is is discovered.
|
||||
*/
|
||||
requestStats.Event.SERVER_REACHABILITY_EVENT = 'serverreachability';
|
||||
|
||||
|
||||
/**
|
||||
* Types of events which reveal information about the reachability of the
|
||||
* server.
|
||||
* @enum {number}
|
||||
*/
|
||||
requestStats.ServerReachability = {
|
||||
REQUEST_MADE: 1,
|
||||
REQUEST_SUCCEEDED: 2,
|
||||
REQUEST_FAILED: 3,
|
||||
BACK_CHANNEL_ACTIVITY: 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event class for SERVER_REACHABILITY_EVENT.
|
||||
*
|
||||
* @param {goog.events.EventTarget} target The stat event target for
|
||||
the channel.
|
||||
* @param {requestStats.ServerReachability} reachabilityType
|
||||
* The reachability event type.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
requestStats.ServerReachabilityEvent = function(target, reachabilityType) {
|
||||
goog.events.Event.call(this,
|
||||
requestStats.Event.SERVER_REACHABILITY_EVENT, target);
|
||||
|
||||
/**
|
||||
* @type {requestStats.ServerReachability}
|
||||
*/
|
||||
this.reachabilityType = reachabilityType;
|
||||
};
|
||||
goog.inherits(requestStats.ServerReachabilityEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Notify the channel that a particular fine grained network event has occurred.
|
||||
* Should be considered package-private.
|
||||
* @param {requestStats.ServerReachability} reachabilityType
|
||||
* The reachability event type.
|
||||
*/
|
||||
requestStats.notifyServerReachabilityEvent = function(reachabilityType) {
|
||||
var target = requestStats.statEventTarget_;
|
||||
target.dispatchEvent(
|
||||
new requestStats.ServerReachabilityEvent(target, reachabilityType));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stat Event that fires when things of interest happen that may be useful for
|
||||
* applications to know about for stats or debugging purposes.
|
||||
*/
|
||||
requestStats.Event.STAT_EVENT = 'statevent';
|
||||
|
||||
|
||||
/**
|
||||
* Enum that identifies events for statistics that are interesting to track.
|
||||
* @enum {number}
|
||||
*/
|
||||
requestStats.Stat = {
|
||||
/** Event indicating a new connection attempt. */
|
||||
CONNECT_ATTEMPT: 0,
|
||||
|
||||
/** Event indicating a connection error due to a general network problem. */
|
||||
ERROR_NETWORK: 1,
|
||||
|
||||
/**
|
||||
* Event indicating a connection error that isn't due to a general network
|
||||
* problem.
|
||||
*/
|
||||
ERROR_OTHER: 2,
|
||||
|
||||
/** Event indicating the start of test stage one. */
|
||||
TEST_STAGE_ONE_START: 3,
|
||||
|
||||
/** Event indicating the start of test stage two. */
|
||||
TEST_STAGE_TWO_START: 4,
|
||||
|
||||
/** Event indicating the first piece of test data was received. */
|
||||
TEST_STAGE_TWO_DATA_ONE: 5,
|
||||
|
||||
/**
|
||||
* Event indicating that the second piece of test data was received and it was
|
||||
* recieved separately from the first.
|
||||
*/
|
||||
TEST_STAGE_TWO_DATA_TWO: 6,
|
||||
|
||||
/** Event indicating both pieces of test data were received simultaneously. */
|
||||
TEST_STAGE_TWO_DATA_BOTH: 7,
|
||||
|
||||
/** Event indicating stage one of the test request failed. */
|
||||
TEST_STAGE_ONE_FAILED: 8,
|
||||
|
||||
/** Event indicating stage two of the test request failed. */
|
||||
TEST_STAGE_TWO_FAILED: 9,
|
||||
|
||||
/**
|
||||
* Event indicating that a buffering proxy is likely between the client and
|
||||
* the server.
|
||||
*/
|
||||
PROXY: 10,
|
||||
|
||||
/**
|
||||
* Event indicating that no buffering proxy is likely between the client and
|
||||
* the server.
|
||||
*/
|
||||
NOPROXY: 11,
|
||||
|
||||
/** Event indicating an unknown SID error. */
|
||||
REQUEST_UNKNOWN_SESSION_ID: 12,
|
||||
|
||||
/** Event indicating a bad status code was received. */
|
||||
REQUEST_BAD_STATUS: 13,
|
||||
|
||||
/** Event indicating incomplete data was received */
|
||||
REQUEST_INCOMPLETE_DATA: 14,
|
||||
|
||||
/** Event indicating bad data was received */
|
||||
REQUEST_BAD_DATA: 15,
|
||||
|
||||
/** Event indicating no data was received when data was expected. */
|
||||
REQUEST_NO_DATA: 16,
|
||||
|
||||
/** Event indicating a request timeout. */
|
||||
REQUEST_TIMEOUT: 17,
|
||||
|
||||
/**
|
||||
* Event indicating that the server never received our hanging GET and so it
|
||||
* is being retried.
|
||||
*/
|
||||
BACKCHANNEL_MISSING: 18,
|
||||
|
||||
/**
|
||||
* Event indicating that we have determined that our hanging GET is not
|
||||
* receiving data when it should be. Thus it is dead dead and will be retried.
|
||||
*/
|
||||
BACKCHANNEL_DEAD: 19,
|
||||
|
||||
/**
|
||||
* The browser declared itself offline during the lifetime of a request, or
|
||||
* was offline when a request was initially made.
|
||||
*/
|
||||
BROWSER_OFFLINE: 20,
|
||||
|
||||
/** ActiveX is blocked by the machine's admin settings. */
|
||||
ACTIVE_X_BLOCKED: 21
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event class for STAT_EVENT.
|
||||
*
|
||||
* @param {goog.events.EventTarget} eventTarget The stat event target for
|
||||
the channel.
|
||||
* @param {requestStats.Stat} stat The stat.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
requestStats.StatEvent = function(eventTarget, stat) {
|
||||
goog.events.Event.call(this, requestStats.Event.STAT_EVENT, eventTarget);
|
||||
|
||||
/**
|
||||
* The stat
|
||||
* @type {requestStats.Stat}
|
||||
*/
|
||||
this.stat = stat;
|
||||
|
||||
};
|
||||
goog.inherits(requestStats.StatEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the singleton event target for stat events.
|
||||
* @return {goog.events.EventTarget} The event target for stat events.
|
||||
*/
|
||||
requestStats.getStatEventTarget = function() {
|
||||
return requestStats.statEventTarget_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to call the stat event callback.
|
||||
* @param {requestStats.Stat} stat The stat.
|
||||
*/
|
||||
requestStats.notifyStatEvent = function(stat) {
|
||||
var target = requestStats.statEventTarget_;
|
||||
target.dispatchEvent(new requestStats.StatEvent(target, stat));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An event that fires when POST requests complete successfully, indicating
|
||||
* the size of the POST and the round trip time.
|
||||
*/
|
||||
requestStats.Event.TIMING_EVENT = 'timingevent';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event class for requestStats.Event.TIMING_EVENT
|
||||
*
|
||||
* @param {goog.events.EventTarget} target The stat event target for
|
||||
the channel.
|
||||
* @param {number} size The number of characters in the POST data.
|
||||
* @param {number} rtt The total round trip time from POST to response in MS.
|
||||
* @param {number} retries The number of times the POST had to be retried.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
requestStats.TimingEvent = function(target, size, rtt, retries) {
|
||||
goog.events.Event.call(this,
|
||||
requestStats.Event.TIMING_EVENT, target);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.size = size;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.rtt = rtt;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.retries = retries;
|
||||
|
||||
};
|
||||
goog.inherits(requestStats.TimingEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to notify listeners about POST request performance.
|
||||
*
|
||||
* @param {number} size Number of characters in the POST data.
|
||||
* @param {number} rtt The amount of time from POST start to response.
|
||||
* @param {number} retries The number of times the POST had to be retried.
|
||||
*/
|
||||
requestStats.notifyTimingEvent = function(size, rtt, retries) {
|
||||
var target = requestStats.statEventTarget_;
|
||||
target.dispatchEvent(
|
||||
new requestStats.TimingEvent(
|
||||
target, size, rtt, retries));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows the application to set an execution hooks for when a channel
|
||||
* starts processing requests. This is useful to track timing or logging
|
||||
* special information. The function takes no parameters and return void.
|
||||
* @param {Function} startHook The function for the start hook.
|
||||
*/
|
||||
requestStats.setStartThreadExecutionHook = function(startHook) {
|
||||
requestStats.startExecutionHook_ = startHook;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows the application to set an execution hooks for when a channel
|
||||
* stops processing requests. This is useful to track timing or logging
|
||||
* special information. The function takes no parameters and return void.
|
||||
* @param {Function} endHook The function for the end hook.
|
||||
*/
|
||||
requestStats.setEndThreadExecutionHook = function(endHook) {
|
||||
requestStats.endExecutionHook_ = endHook;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Application provided execution hook for the start hook.
|
||||
*
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
requestStats.startExecutionHook_ = function() { };
|
||||
|
||||
|
||||
/**
|
||||
* Application provided execution hook for the end hook.
|
||||
*
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
requestStats.endExecutionHook_ = function() { };
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to call the start hook
|
||||
*/
|
||||
requestStats.onStartExecution = function() {
|
||||
requestStats.startExecutionHook_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to call the end hook
|
||||
*/
|
||||
requestStats.onEndExecution = function() {
|
||||
requestStats.endExecutionHook_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper around SafeTimeout which calls the start and end execution hooks
|
||||
* with a try...finally block.
|
||||
* @param {Function} fn The callback function.
|
||||
* @param {number} ms The time in MS for the timer.
|
||||
* @return {number} The ID of the timer.
|
||||
*/
|
||||
requestStats.setTimeout = function(fn, ms) {
|
||||
if (!goog.isFunction(fn)) {
|
||||
throw Error('Fn must not be null and must be a function');
|
||||
}
|
||||
return goog.global.setTimeout(function() {
|
||||
requestStats.onStartExecution();
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
requestStats.onEndExecution();
|
||||
}
|
||||
}, ms);
|
||||
};
|
||||
}); // goog.scope
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Closure Unit Tests - goog.labs.net.webChannel.WebChannelBase</title>
|
||||
<script src="../../../base.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
goog.require('goog.labs.net.webChannel.webChannelBaseTest');
|
||||
</script>
|
||||
<div id="debug" style="font-size: small"></div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
+376
@@ -0,0 +1,376 @@
|
||||
// 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 Implementation of a WebChannel transport using WebChannelBase.
|
||||
*
|
||||
* When WebChannelBase is used as the underlying transport, the capabilities
|
||||
* of the WebChannel are limited to what's supported by the implementation.
|
||||
* Particularly, multiplexing is not possible, and only strings are
|
||||
* supported as message types.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.WebChannelBaseTransport');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelBase');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.net.WebChannel');
|
||||
goog.require('goog.net.WebChannelTransport');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.string.path');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link goog.net.WebChannelTransport} with
|
||||
* {@link goog.labs.net.webChannel.WebChannelBase} as the underlying channel
|
||||
* implementation.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @implements {goog.net.WebChannelTransport}
|
||||
* @final
|
||||
*/
|
||||
goog.labs.net.webChannel.WebChannelBaseTransport = function() {};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var WebChannelBaseTransport = goog.labs.net.webChannel.WebChannelBaseTransport;
|
||||
var WebChannelBase = goog.labs.net.webChannel.WebChannelBase;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.prototype.createWebChannel = function(
|
||||
url, opt_options) {
|
||||
return new WebChannelBaseTransport.Channel(url, opt_options);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of the {@link goog.net.WebChannel} interface.
|
||||
*
|
||||
* @param {string} url The URL path for the new WebChannel instance.
|
||||
* @param {!goog.net.WebChannel.Options=} opt_options Configuration for the
|
||||
* new WebChannel instance.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {goog.net.WebChannel}
|
||||
* @extends {goog.events.EventTarget}
|
||||
* @final
|
||||
*/
|
||||
WebChannelBaseTransport.Channel = function(url, opt_options) {
|
||||
WebChannelBaseTransport.Channel.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* The underlying channel object.
|
||||
*
|
||||
* @private {!WebChannelBase}
|
||||
*/
|
||||
this.channel_ = new WebChannelBase(opt_options);
|
||||
|
||||
/**
|
||||
* The URL of the target server end-point.
|
||||
*
|
||||
* @private {string}
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The test URL of the target server end-point. This value defaults to
|
||||
* this.url_ + '/test'.
|
||||
*
|
||||
* @private {string}
|
||||
*/
|
||||
this.testUrl_ = (opt_options && opt_options.testUrl) ? opt_options.testUrl :
|
||||
goog.string.path.join(this.url_, 'test');
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
* @private {goog.log.Logger}
|
||||
*/
|
||||
this.logger_ = goog.log.getLogger(
|
||||
'goog.labs.net.webChannel.WebChannelBaseTransport');
|
||||
|
||||
/**
|
||||
* @private {Object<string, string>} messageUrlParams_ Extra URL parameters
|
||||
* to be added to each HTTP request.
|
||||
*/
|
||||
this.messageUrlParams_ =
|
||||
(opt_options && opt_options.messageUrlParams) || null;
|
||||
|
||||
var messageHeaders = (opt_options && opt_options.messageHeaders) || null;
|
||||
|
||||
// default is false
|
||||
if (opt_options && opt_options.clientProtocolHeaderRequired) {
|
||||
if (messageHeaders) {
|
||||
goog.object.set(messageHeaders,
|
||||
goog.net.WebChannel.X_CLIENT_PROTOCOL,
|
||||
goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);
|
||||
} else {
|
||||
messageHeaders = goog.object.create(
|
||||
goog.net.WebChannel.X_CLIENT_PROTOCOL,
|
||||
goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
this.channel_.setExtraHeaders(messageHeaders);
|
||||
|
||||
/**
|
||||
* @private {boolean} supportsCrossDomainXhr_ Whether to enable CORS.
|
||||
*/
|
||||
this.supportsCrossDomainXhr_ =
|
||||
(opt_options && opt_options.supportsCrossDomainXhr) || false;
|
||||
};
|
||||
goog.inherits(WebChannelBaseTransport.Channel, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The channel handler.
|
||||
*
|
||||
* @type {WebChannelBase.Handler}
|
||||
* @private
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.channelHandler_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Test path is always set to "/url/test".
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.open = function() {
|
||||
this.channel_.connect(this.testUrl_, this.url_,
|
||||
(this.messageUrlParams_ || undefined));
|
||||
|
||||
this.channelHandler_ = new WebChannelBaseTransport.Channel.Handler_(this);
|
||||
this.channel_.setHandler(this.channelHandler_);
|
||||
if (this.supportsCrossDomainXhr_) {
|
||||
this.channel_.setSupportsCrossDomainXhrs(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.close = function() {
|
||||
this.channel_.disconnect();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The WebChannelBase only supports object types.
|
||||
*
|
||||
* @param {!goog.net.WebChannel.MessageData} message The message to send.
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.send = function(message) {
|
||||
goog.asserts.assert(goog.isObject(message), 'only object type expected');
|
||||
this.channel_.sendMap(message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.disposeInternal = function() {
|
||||
this.channel_.setHandler(null);
|
||||
delete this.channelHandler_;
|
||||
this.channel_.disconnect();
|
||||
delete this.channel_;
|
||||
|
||||
WebChannelBaseTransport.Channel.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The message event.
|
||||
*
|
||||
* @param {!Array<?>} array The data array from the underlying channel.
|
||||
* @constructor
|
||||
* @extends {goog.net.WebChannel.MessageEvent}
|
||||
* @final
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.MessageEvent = function(array) {
|
||||
WebChannelBaseTransport.Channel.MessageEvent.base(this, 'constructor');
|
||||
|
||||
this.data = array;
|
||||
};
|
||||
goog.inherits(WebChannelBaseTransport.Channel.MessageEvent,
|
||||
goog.net.WebChannel.MessageEvent);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The error event.
|
||||
*
|
||||
* @param {WebChannelBase.Error} error The error code.
|
||||
* @constructor
|
||||
* @extends {goog.net.WebChannel.ErrorEvent}
|
||||
* @final
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.ErrorEvent = function(error) {
|
||||
WebChannelBaseTransport.Channel.ErrorEvent.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Transport specific error code is not to be propagated with the event.
|
||||
*/
|
||||
this.status = goog.net.WebChannel.ErrorStatus.NETWORK_ERROR;
|
||||
};
|
||||
goog.inherits(WebChannelBaseTransport.Channel.ErrorEvent,
|
||||
goog.net.WebChannel.ErrorEvent);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link WebChannelBase.Handler} interface.
|
||||
*
|
||||
* @param {!WebChannelBaseTransport.Channel} channel The enclosing WebChannel.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {WebChannelBase.Handler}
|
||||
* @private
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_ = function(channel) {
|
||||
WebChannelBaseTransport.Channel.Handler_.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* @type {!WebChannelBaseTransport.Channel}
|
||||
* @private
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
};
|
||||
goog.inherits(WebChannelBaseTransport.Channel.Handler_, WebChannelBase.Handler);
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_.prototype.channelOpened = function(
|
||||
channel) {
|
||||
goog.log.info(this.channel_.logger_,
|
||||
'WebChannel opened on ' + this.channel_.url_);
|
||||
this.channel_.dispatchEvent(goog.net.WebChannel.EventType.OPEN);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_.prototype.channelHandleArray =
|
||||
function(channel, array) {
|
||||
goog.asserts.assert(array, 'array expected to be defined');
|
||||
this.channel_.dispatchEvent(
|
||||
new WebChannelBaseTransport.Channel.MessageEvent(array));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_.prototype.channelError = function(
|
||||
channel, error) {
|
||||
goog.log.info(this.channel_.logger_,
|
||||
'WebChannel aborted on ' + this.channel_.url_ +
|
||||
' due to channel error: ' + error);
|
||||
this.channel_.dispatchEvent(
|
||||
new WebChannelBaseTransport.Channel.ErrorEvent(error));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_.prototype.channelClosed = function(
|
||||
channel, opt_pendingMaps, opt_undeliveredMaps) {
|
||||
goog.log.info(this.channel_.logger_,
|
||||
'WebChannel closed on ' + this.channel_.url_);
|
||||
this.channel_.dispatchEvent(goog.net.WebChannel.EventType.CLOSE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.getRuntimeProperties = function() {
|
||||
return new WebChannelBaseTransport.ChannelProperties(this.channel_);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of the {@link goog.net.WebChannel.RuntimeProperties}.
|
||||
*
|
||||
* @param {!WebChannelBase} channel The underlying channel object.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {goog.net.WebChannel.RuntimeProperties}
|
||||
* @final
|
||||
*/
|
||||
WebChannelBaseTransport.ChannelProperties = function(channel) {
|
||||
/**
|
||||
* The underlying channel object.
|
||||
*
|
||||
* @private {!WebChannelBase}
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
|
||||
/**
|
||||
* The flag to turn on/off server-side flow control.
|
||||
*
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.serverFlowControlEnabled_ = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.ChannelProperties.prototype.getConcurrentRequestLimit =
|
||||
function() {
|
||||
return this.channel_.getForwardChannelRequestPool().getMaxSize();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.ChannelProperties.prototype.isSpdyEnabled =
|
||||
function() {
|
||||
return this.getConcurrentRequestLimit() > 1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.ChannelProperties.prototype.setServerFlowControl =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.ChannelProperties.prototype.getNonAckedMessageCount =
|
||||
goog.abstractMethod;
|
||||
}); // goog.scope
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2013 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Closure Unit Tests - goog.labs.net.webChannel.WebChannelBaseTransport</title>
|
||||
<script src="../../../base.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
goog.require('goog.labs.net.webChannel.webChannelBaseTransportTest');
|
||||
</script>
|
||||
<div id="debug" style="font-size: small"></div>
|
||||
</body>
|
||||
</html>
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
// 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 Unit tests for goog.labs.net.webChannel.WebChannelBase.
|
||||
* @suppress {accessControls} Private methods are accessed for test purposes.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.webChannelBaseTransportTest');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
|
||||
goog.require('goog.net.WebChannel');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
goog.setTestOnly('goog.labs.net.webChannel.webChannelBaseTransportTest');
|
||||
|
||||
|
||||
var webChannel;
|
||||
var channelUrl = 'http://127.0.0.1:8080/channel';
|
||||
|
||||
|
||||
function setUp() {
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
goog.dispose(webChannel);
|
||||
}
|
||||
|
||||
function testOpenWithUrl() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl);
|
||||
|
||||
var eventFired = false;
|
||||
goog.events.listen(webChannel, goog.net.WebChannel.EventType.OPEN,
|
||||
function(e) {
|
||||
eventFired = true;
|
||||
});
|
||||
|
||||
webChannel.open();
|
||||
assertFalse(eventFired);
|
||||
|
||||
var channel = webChannel.channel_;
|
||||
assertNotNull(channel);
|
||||
|
||||
simulateOpenEvent(channel);
|
||||
assertTrue(eventFired);
|
||||
}
|
||||
|
||||
function testOpenWithTestUrl() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {'testUrl': channelUrl + '/footest'};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
var testPath = webChannel.channel_.connectionTest_.path_;
|
||||
assertNotNullNorUndefined(testPath);
|
||||
}
|
||||
|
||||
function testOpenWithCustomHeaders() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {'messageHeaders': {'foo-key': 'foo-value'}};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
var extraHeaders_ = webChannel.channel_.extraHeaders_;
|
||||
assertNotNullNorUndefined(extraHeaders_);
|
||||
assertEquals('foo-value', extraHeaders_['foo-key']);
|
||||
assertEquals(undefined, extraHeaders_['X-Client-Protocol']);
|
||||
}
|
||||
|
||||
function testClientProtocolHeaderRequired() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {'clientProtocolHeaderRequired': true};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
var extraHeaders_ = webChannel.channel_.extraHeaders_;
|
||||
assertNotNullNorUndefined(extraHeaders_);
|
||||
assertEquals('webchannel', extraHeaders_['X-Client-Protocol']);
|
||||
}
|
||||
|
||||
function testClientProtocolHeaderNotRequiredByDefault() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl);
|
||||
webChannel.open();
|
||||
|
||||
var extraHeaders_ = webChannel.channel_.extraHeaders_;
|
||||
assertNull(extraHeaders_);
|
||||
}
|
||||
|
||||
function testClientProtocolHeaderRequiredWithCustomHeader() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {
|
||||
'clientProtocolHeaderRequired': true,
|
||||
'messageHeaders': {'foo-key': 'foo-value'}
|
||||
};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
var extraHeaders_ = webChannel.channel_.extraHeaders_;
|
||||
assertNotNullNorUndefined(extraHeaders_);
|
||||
assertEquals('foo-value', extraHeaders_['foo-key']);
|
||||
assertEquals('webchannel', extraHeaders_['X-Client-Protocol']);
|
||||
}
|
||||
|
||||
function testOpenWithCustomParams() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {'messageUrlParams': {'foo-key': 'foo-value'}};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
var extraParams = webChannel.channel_.extraParams_;
|
||||
assertNotNullNorUndefined(extraParams);
|
||||
}
|
||||
|
||||
function testOpenWithCorsEnabled() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
var options = {'supportsCrossDomainXhr': true};
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
|
||||
webChannel.open();
|
||||
|
||||
assertTrue(webChannel.channel_.supportsCrossDomainXhrs_);
|
||||
}
|
||||
|
||||
function testOpenThenCloseChannel() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl);
|
||||
|
||||
var eventFired = false;
|
||||
goog.events.listen(webChannel, goog.net.WebChannel.EventType.CLOSE,
|
||||
function(e) {
|
||||
eventFired = true;
|
||||
});
|
||||
|
||||
webChannel.open();
|
||||
assertFalse(eventFired);
|
||||
|
||||
var channel = webChannel.channel_;
|
||||
assertNotNull(channel);
|
||||
|
||||
simulateCloseEvent(channel);
|
||||
assertTrue(eventFired);
|
||||
}
|
||||
|
||||
|
||||
function testChannelError() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl);
|
||||
|
||||
var eventFired = false;
|
||||
goog.events.listen(webChannel, goog.net.WebChannel.EventType.ERROR,
|
||||
function(e) {
|
||||
eventFired = true;
|
||||
assertEquals(goog.net.WebChannel.ErrorStatus.NETWORK_ERROR, e.status);
|
||||
});
|
||||
|
||||
webChannel.open();
|
||||
assertFalse(eventFired);
|
||||
|
||||
var channel = webChannel.channel_;
|
||||
assertNotNull(channel);
|
||||
|
||||
simulateErrorEvent(channel);
|
||||
assertTrue(eventFired);
|
||||
}
|
||||
|
||||
|
||||
function testChannelMessage() {
|
||||
var webChannelTransport =
|
||||
new goog.labs.net.webChannel.WebChannelBaseTransport();
|
||||
webChannel = webChannelTransport.createWebChannel(channelUrl);
|
||||
|
||||
var eventFired = false;
|
||||
var data = 'foo';
|
||||
goog.events.listen(webChannel, goog.net.WebChannel.EventType.MESSAGE,
|
||||
function(e) {
|
||||
eventFired = true;
|
||||
assertEquals(e.data, data);
|
||||
});
|
||||
|
||||
webChannel.open();
|
||||
assertFalse(eventFired);
|
||||
|
||||
var channel = webChannel.channel_;
|
||||
assertNotNull(channel);
|
||||
|
||||
simulateMessageEvent(channel, data);
|
||||
assertTrue(eventFired);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the WebChannelBase firing the open event for the given channel.
|
||||
* @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
|
||||
*/
|
||||
function simulateOpenEvent(channel) {
|
||||
assertNotNull(channel.getHandler());
|
||||
channel.getHandler().channelOpened();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the WebChannelBase firing the close event for the given channel.
|
||||
* @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
|
||||
*/
|
||||
function simulateCloseEvent(channel) {
|
||||
assertNotNull(channel.getHandler());
|
||||
channel.getHandler().channelClosed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the WebChannelBase firing the error event for the given channel.
|
||||
* @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
|
||||
*/
|
||||
function simulateErrorEvent(channel) {
|
||||
assertNotNull(channel.getHandler());
|
||||
channel.getHandler().channelError();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the WebChannelBase firing the message event for the given channel.
|
||||
* @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
|
||||
* @param {String} data The message data.
|
||||
*/
|
||||
function simulateMessageEvent(channel, data) {
|
||||
assertNotNull(channel.getHandler());
|
||||
channel.getHandler().channelHandleArray(channel, data);
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// Copyright 2006 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 Provides a utility for tracing and debugging WebChannel
|
||||
* requests.
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.WebChannelDebug');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Logs and keeps a buffer of debugging info for the Channel.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @final
|
||||
*/
|
||||
goog.labs.net.webChannel.WebChannelDebug = function() {
|
||||
/**
|
||||
* The logger instance.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
this.logger_ = goog.log.getLogger('goog.labs.net.webChannel.WebChannelDebug');
|
||||
};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the logger used by this ChannelDebug.
|
||||
* @return {goog.debug.Logger} The logger used by this WebChannelDebug.
|
||||
*/
|
||||
WebChannelDebug.prototype.getLogger = function() {
|
||||
return this.logger_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs that the browser went offline during the lifetime of a request.
|
||||
* @param {goog.Uri} url The URL being requested.
|
||||
*/
|
||||
WebChannelDebug.prototype.browserOfflineResponse = function(url) {
|
||||
this.info('BROWSER_OFFLINE: ' + url);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs an XmlHttp request..
|
||||
* @param {string} verb The request type (GET/POST).
|
||||
* @param {goog.Uri} uri The request destination.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {number} attempt Which attempt # the request was.
|
||||
* @param {?string} postData The data posted in the request.
|
||||
*/
|
||||
WebChannelDebug.prototype.xmlHttpChannelRequest =
|
||||
function(verb, uri, id, attempt, postData) {
|
||||
this.info(
|
||||
'XMLHTTP REQ (' + id + ') [attempt ' + attempt + ']: ' +
|
||||
verb + '\n' + uri + '\n' +
|
||||
this.maybeRedactPostData_(postData));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs the meta data received from an XmlHttp request.
|
||||
* @param {string} verb The request type (GET/POST).
|
||||
* @param {goog.Uri} uri The request destination.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {number} attempt Which attempt # the request was.
|
||||
* @param {goog.net.XmlHttp.ReadyState} readyState The ready state.
|
||||
* @param {number} statusCode The HTTP status code.
|
||||
*/
|
||||
WebChannelDebug.prototype.xmlHttpChannelResponseMetaData =
|
||||
function(verb, uri, id, attempt, readyState, statusCode) {
|
||||
this.info(
|
||||
'XMLHTTP RESP (' + id + ') [ attempt ' + attempt + ']: ' +
|
||||
verb + '\n' + uri + '\n' + readyState + ' ' + statusCode);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs the response data received from an XmlHttp request.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {?string} responseText The response text.
|
||||
* @param {?string=} opt_desc Optional request description.
|
||||
*/
|
||||
WebChannelDebug.prototype.xmlHttpChannelResponseText =
|
||||
function(id, responseText, opt_desc) {
|
||||
this.info(
|
||||
'XMLHTTP TEXT (' + id + '): ' +
|
||||
this.redactResponse_(responseText) +
|
||||
(opt_desc ? ' ' + opt_desc : ''));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a Trident ActiveX request.
|
||||
* @param {string} verb The request type (GET/POST).
|
||||
* @param {goog.Uri} uri The request destination.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {number} attempt Which attempt # the request was.
|
||||
*/
|
||||
WebChannelDebug.prototype.tridentChannelRequest =
|
||||
function(verb, uri, id, attempt) {
|
||||
this.info(
|
||||
'TRIDENT REQ (' + id + ') [ attempt ' + attempt + ']: ' +
|
||||
verb + '\n' + uri);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs the response text received from a Trident ActiveX request.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {string} responseText The response text.
|
||||
*/
|
||||
WebChannelDebug.prototype.tridentChannelResponseText =
|
||||
function(id, responseText) {
|
||||
this.info(
|
||||
'TRIDENT TEXT (' + id + '): ' +
|
||||
this.redactResponse_(responseText));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs the done response received from a Trident ActiveX request.
|
||||
* @param {string|number|undefined} id The request id.
|
||||
* @param {boolean} successful Whether the request was successful.
|
||||
*/
|
||||
WebChannelDebug.prototype.tridentChannelResponseDone =
|
||||
function(id, successful) {
|
||||
this.info(
|
||||
'TRIDENT TEXT (' + id + '): ' + successful ? 'success' : 'failure');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a request timeout.
|
||||
* @param {goog.Uri} uri The uri that timed out.
|
||||
*/
|
||||
WebChannelDebug.prototype.timeoutResponse = function(uri) {
|
||||
this.info('TIMEOUT: ' + uri);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a debug message.
|
||||
* @param {string} text The message.
|
||||
*/
|
||||
WebChannelDebug.prototype.debug = function(text) {
|
||||
this.info(text);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs an exception
|
||||
* @param {Error} e The error or error event.
|
||||
* @param {string=} opt_msg The optional message, defaults to 'Exception'.
|
||||
*/
|
||||
WebChannelDebug.prototype.dumpException = function(e, opt_msg) {
|
||||
this.severe((opt_msg || 'Exception') + e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
* @param {string} text The message.
|
||||
*/
|
||||
WebChannelDebug.prototype.info = function(text) {
|
||||
goog.log.info(this.logger_, text);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
* @param {string} text The message.
|
||||
*/
|
||||
WebChannelDebug.prototype.warning = function(text) {
|
||||
goog.log.warning(this.logger_, text);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a severe message.
|
||||
* @param {string} text The message.
|
||||
*/
|
||||
WebChannelDebug.prototype.severe = function(text) {
|
||||
goog.log.error(this.logger_, text);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes potentially private data from a response so that we don't
|
||||
* accidentally save private and personal data to the server logs.
|
||||
* @param {?string} responseText A JSON response to clean.
|
||||
* @return {?string} The cleaned response.
|
||||
* @private
|
||||
*/
|
||||
WebChannelDebug.prototype.redactResponse_ = function(responseText) {
|
||||
if (!responseText) {
|
||||
return null;
|
||||
}
|
||||
/** @preserveTry */
|
||||
try {
|
||||
var responseArray = goog.json.unsafeParse(responseText);
|
||||
if (responseArray) {
|
||||
for (var i = 0; i < responseArray.length; i++) {
|
||||
if (goog.isArray(responseArray[i])) {
|
||||
this.maybeRedactArray_(responseArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return goog.json.serialize(responseArray);
|
||||
} catch (e) {
|
||||
this.debug('Exception parsing expected JS array - probably was not JS');
|
||||
return responseText;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes data from a response array that may be sensitive.
|
||||
* @param {!Array<?>} array The array to clean.
|
||||
* @private
|
||||
*/
|
||||
WebChannelDebug.prototype.maybeRedactArray_ = function(array) {
|
||||
if (array.length < 2) {
|
||||
return;
|
||||
}
|
||||
var dataPart = array[1];
|
||||
if (!goog.isArray(dataPart)) {
|
||||
return;
|
||||
}
|
||||
if (dataPart.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = dataPart[0];
|
||||
if (type != 'noop' && type != 'stop') {
|
||||
// redact all fields in the array
|
||||
for (var i = 1; i < dataPart.length; i++) {
|
||||
dataPart[i] = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes potentially private data from a request POST body so that we don't
|
||||
* accidentally save private and personal data to the server logs.
|
||||
* @param {?string} data The data string to clean.
|
||||
* @return {?string} The data string with sensitive data replaced by 'redacted'.
|
||||
* @private
|
||||
*/
|
||||
WebChannelDebug.prototype.maybeRedactPostData_ = function(data) {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
var out = '';
|
||||
var params = data.split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var param = params[i];
|
||||
var keyValue = param.split('=');
|
||||
if (keyValue.length > 1) {
|
||||
var key = keyValue[0];
|
||||
var value = keyValue[1];
|
||||
|
||||
var keyParts = key.split('_');
|
||||
if (keyParts.length >= 2 && keyParts[1] == 'type') {
|
||||
out += key + '=' + value + '&';
|
||||
} else {
|
||||
out += key + '=' + 'redacted' + '&';
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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 Interface and shared data structures for implementing
|
||||
* different wire protocol versions.
|
||||
* @visibility {//closure/goog/bin/sizetests:__pkg__}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.Wire');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The interface class.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
goog.labs.net.webChannel.Wire = function() {};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var Wire = goog.labs.net.webChannel.Wire;
|
||||
|
||||
|
||||
/**
|
||||
* The latest protocol version that this class supports. We request this version
|
||||
* from the server when opening the connection. Should match
|
||||
* LATEST_CHANNEL_VERSION on the server code.
|
||||
* @type {number}
|
||||
*/
|
||||
Wire.LATEST_CHANNEL_VERSION = 8;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple container class for a (mapId, map) pair.
|
||||
* @param {number} mapId The id for this map.
|
||||
* @param {!Object|!goog.structs.Map} map The map itself.
|
||||
* @param {!Object=} opt_context The context associated with the map.
|
||||
* @constructor
|
||||
* @struct
|
||||
*/
|
||||
Wire.QueuedMap = function(mapId, map, opt_context) {
|
||||
/**
|
||||
* The id for this map.
|
||||
* @type {number}
|
||||
*/
|
||||
this.mapId = mapId;
|
||||
|
||||
/**
|
||||
* The map itself.
|
||||
* @type {!Object|!goog.structs.Map}
|
||||
*/
|
||||
this.map = map;
|
||||
|
||||
/**
|
||||
* The context for the map.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.context = opt_context || null;
|
||||
};
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 Codec functions of the v8 wire protocol. Eventually we'd want
|
||||
* to support pluggable wire-format to improve wire efficiency and to enable
|
||||
* binary encoding. Such support will require an interface class, which
|
||||
* will be added later.
|
||||
*
|
||||
* @visibility {:internal}
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.WireV8');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.json.NativeJsonProcessor');
|
||||
goog.require('goog.structs');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The v8 codec class.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
*/
|
||||
goog.labs.net.webChannel.WireV8 = function() {
|
||||
/**
|
||||
* Parser for a response payload. The parser should return an array.
|
||||
* @private {!goog.string.Parser}
|
||||
*/
|
||||
this.parser_ = new goog.json.NativeJsonProcessor();
|
||||
};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var WireV8 = goog.labs.net.webChannel.WireV8;
|
||||
var Wire = goog.labs.net.webChannel.Wire;
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a standalone message into the wire format.
|
||||
*
|
||||
* May throw exception if the message object contains any invalid elements.
|
||||
*
|
||||
* @param {!Object|!goog.structs.Map} message The message data.
|
||||
* V8 only support JS objects (or Map).
|
||||
* @param {!Array<string>} buffer The text buffer to write the message to.
|
||||
* @param {string=} opt_prefix The prefix for each field of the object.
|
||||
*/
|
||||
WireV8.prototype.encodeMessage = function(message, buffer, opt_prefix) {
|
||||
var prefix = opt_prefix || '';
|
||||
try {
|
||||
goog.structs.forEach(message, function(value, key) {
|
||||
var encodedValue = value;
|
||||
if (goog.isObject(value)) {
|
||||
encodedValue = goog.json.serialize(value);
|
||||
} // keep the fast-path for primitive types
|
||||
buffer.push(prefix + key + '=' + encodeURIComponent(encodedValue));
|
||||
});
|
||||
} catch (ex) {
|
||||
// We send a map here because lots of the retry logic relies on map IDs,
|
||||
// so we have to send something (possibly redundant).
|
||||
buffer.push(prefix + 'type' + '=' + encodeURIComponent('_badmap'));
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes all the buffered messages of the forward channel.
|
||||
*
|
||||
* @param {!Array<Wire.QueuedMap>} messageQueue The message data.
|
||||
* V8 only support JS objects.
|
||||
* @param {number} count The number of messages to be encoded.
|
||||
* @param {?function(!Object)} badMapHandler Callback for bad messages.
|
||||
*/
|
||||
WireV8.prototype.encodeMessageQueue = function(messageQueue, count,
|
||||
badMapHandler) {
|
||||
var sb = ['count=' + count];
|
||||
var offset;
|
||||
if (count > 0) {
|
||||
// To save a bit of bandwidth, specify the base mapId and the rest as
|
||||
// offsets from it.
|
||||
offset = messageQueue[0].mapId;
|
||||
sb.push('ofs=' + offset);
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
for (var i = 0; i < count; i++) {
|
||||
var mapId = messageQueue[i].mapId;
|
||||
var map = messageQueue[i].map;
|
||||
mapId -= offset;
|
||||
try {
|
||||
this.encodeMessage(map, sb, 'req' + mapId + '_');
|
||||
} catch (ex) {
|
||||
if (badMapHandler) {
|
||||
badMapHandler(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.join('&');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a standalone message received from the wire. May throw exception
|
||||
* if text is ill-formatted.
|
||||
*
|
||||
* Must be valid JSON as it is insecure to use eval() to decode JS literals;
|
||||
* and eval() is disallowed in Chrome apps too.
|
||||
*
|
||||
* Invalid JS literals include null array elements, quotas etc.
|
||||
*
|
||||
* @param {string} messageText The string content as received from the wire.
|
||||
* @return {*} The decoded message object.
|
||||
*/
|
||||
WireV8.prototype.decodeMessage = function(messageText) {
|
||||
var response = this.parser_.parse(messageText);
|
||||
goog.asserts.assert(goog.isArray(response)); // throw exception
|
||||
return response;
|
||||
};
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2013 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Closure Unit Tests - goog.labs.net.webChannel.WireV8</title>
|
||||
<script src="../../../base.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
goog.require('goog.labs.net.webChannel.WireV8Test');
|
||||
</script>
|
||||
<div id="debug" style="font-size: small"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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 Unit tests for goog.labs.net.webChannel.WireV8.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.webChannel.WireV8Test');
|
||||
|
||||
goog.require('goog.labs.net.webChannel.WireV8');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
goog.setTestOnly('goog.labs.net.webChannel.WireV8Test');
|
||||
|
||||
|
||||
var wireCodec;
|
||||
|
||||
|
||||
function setUp() {
|
||||
wireCodec = new goog.labs.net.webChannel.WireV8();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
}
|
||||
|
||||
|
||||
function testEncodeSimpleMessage() {
|
||||
// scalar types only
|
||||
var message = {
|
||||
a: 'a',
|
||||
b: 'b'
|
||||
};
|
||||
var buff = [];
|
||||
wireCodec.encodeMessage(message, buff, 'prefix_');
|
||||
assertEquals(2, buff.length);
|
||||
assertEquals('prefix_a=a', buff[0]);
|
||||
assertEquals('prefix_b=b', buff[1]);
|
||||
}
|
||||
|
||||
|
||||
function testEncodeComplexMessage() {
|
||||
var message = {
|
||||
a: 'a',
|
||||
b: {
|
||||
x: 1,
|
||||
y: 2
|
||||
}
|
||||
};
|
||||
var buff = [];
|
||||
wireCodec.encodeMessage(message, buff, 'prefix_');
|
||||
assertEquals(2, buff.length);
|
||||
assertEquals('prefix_a=a', buff[0]);
|
||||
// a round-trip URI codec
|
||||
assertEquals('prefix_b={\"x\":1,\"y\":2}', decodeURIComponent(buff[1]));
|
||||
}
|
||||
|
||||
|
||||
function testEncodeMessageQueue() {
|
||||
var message1 = {
|
||||
a: 'a'
|
||||
};
|
||||
var queuedMessage1 = {
|
||||
map: message1,
|
||||
mapId: 3
|
||||
};
|
||||
var message2 = {
|
||||
b: 'b'
|
||||
};
|
||||
var queuedMessage2 = {
|
||||
map: message2,
|
||||
mapId: 4
|
||||
};
|
||||
var queue = [queuedMessage1, queuedMessage2];
|
||||
var result = wireCodec.encodeMessageQueue(queue, 2, null);
|
||||
assertEquals('count=2&ofs=3&req0_a=a&req1_b=b', result);
|
||||
}
|
||||
|
||||
|
||||
function testDecodeMessage() {
|
||||
var message = wireCodec.decodeMessage('[{"a":"a", "x":1}, {"b":"b"}]');
|
||||
assertTrue(goog.isArray(message));
|
||||
assertEquals(2, message.length);
|
||||
assertEquals('a', message[0].a);
|
||||
assertEquals(1, message[0].x);
|
||||
assertEquals('b', message[1].b);
|
||||
}
|
||||
Reference in New Issue
Block a user