Update wmts-hidpi, add nicer-api-docs
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
// Copyright 2012 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 Simple image loader, used for preloading.
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.net.image');
|
||||
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.net.EventType');
|
||||
goog.require('goog.result.SimpleResult');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Loads a single image. Useful for preloading images. May be combined with
|
||||
* goog.result.combine to preload many images.
|
||||
*
|
||||
* @param {string} uri URI of the image.
|
||||
* @param {(Image|function(): !Image)=} opt_image If present, instead of
|
||||
* creating a new Image instance the function will use the passed Image
|
||||
* instance or the result of calling the Image factory respectively. This
|
||||
* can be used to control exactly how Image instances are created, for
|
||||
* example if they should be created in a particular document element, or
|
||||
* have fields that will trigger CORS image fetches.
|
||||
* @return {!goog.result.Result} An asyncronous result that will succeed
|
||||
* if the image successfully loads or error if the image load fails.
|
||||
*/
|
||||
goog.labs.net.image.load = function(uri, opt_image) {
|
||||
var image;
|
||||
if (!goog.isDef(opt_image)) {
|
||||
image = new Image();
|
||||
} else if (goog.isFunction(opt_image)) {
|
||||
image = opt_image();
|
||||
} else {
|
||||
image = opt_image;
|
||||
}
|
||||
|
||||
// IE's load event on images can be buggy. Instead, we wait for
|
||||
// readystatechange events and check if readyState is 'complete'.
|
||||
// See:
|
||||
// http://msdn.microsoft.com/en-us/library/ie/ms536957(v=vs.85).aspx
|
||||
// http://msdn.microsoft.com/en-us/library/ie/ms534359(v=vs.85).aspx
|
||||
var loadEvent = goog.userAgent.IE ? goog.net.EventType.READY_STATE_CHANGE :
|
||||
goog.events.EventType.LOAD;
|
||||
|
||||
var result = new goog.result.SimpleResult();
|
||||
|
||||
var handler = new goog.events.EventHandler();
|
||||
handler.listen(
|
||||
image,
|
||||
[loadEvent, goog.net.EventType.ABORT, goog.net.EventType.ERROR],
|
||||
function(e) {
|
||||
|
||||
// We only registered listeners for READY_STATE_CHANGE for IE.
|
||||
// If readyState is now COMPLETE, the image has loaded.
|
||||
// See related comment above.
|
||||
if (e.type == goog.net.EventType.READY_STATE_CHANGE &&
|
||||
image.readyState != goog.net.EventType.COMPLETE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, we know whether the image load was successful
|
||||
// and no longer care about image events.
|
||||
goog.dispose(handler);
|
||||
|
||||
// Whether the image successfully loaded.
|
||||
if (e.type == loadEvent) {
|
||||
result.setValue(image);
|
||||
} else {
|
||||
result.setError();
|
||||
}
|
||||
});
|
||||
|
||||
// Initiate the image request.
|
||||
image.src = uri;
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright 2012 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.Image.
|
||||
*
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
|
||||
/** @suppress {extraProvide} */
|
||||
goog.provide('goog.labs.net.imageTest');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.labs.net.image');
|
||||
goog.require('goog.result');
|
||||
goog.require('goog.result.Result');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.testing.AsyncTestCase');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.testing.recordFunction');
|
||||
|
||||
goog.setTestOnly('goog.labs.net.ImageTest');
|
||||
|
||||
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
|
||||
|
||||
function testValidImage() {
|
||||
var url = 'testdata/cleardot.gif';
|
||||
|
||||
asyncTestCase.waitForAsync('image load');
|
||||
|
||||
assertEquals(0, goog.events.getTotalListenerCount());
|
||||
|
||||
var result = goog.labs.net.image.load(url);
|
||||
|
||||
goog.result.waitOnSuccess(result, function(value) {
|
||||
|
||||
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
|
||||
|
||||
assertEquals('IMG', value.tagName);
|
||||
assertTrue(goog.string.endsWith(value.src, url));
|
||||
assertUndefined(result.getError());
|
||||
|
||||
assertEquals('Listeners should have been cleaned up.',
|
||||
0, goog.events.getTotalListenerCount());
|
||||
|
||||
asyncTestCase.continueTesting();
|
||||
});
|
||||
}
|
||||
|
||||
function testInvalidImage() {
|
||||
|
||||
var url = 'testdata/invalid.gif'; // This file does not exist.
|
||||
|
||||
asyncTestCase.waitForAsync('image load');
|
||||
|
||||
assertEquals(0, goog.events.getTotalListenerCount());
|
||||
|
||||
var result = goog.labs.net.image.load(url);
|
||||
|
||||
goog.result.wait(result, function(result) {
|
||||
|
||||
assertEquals(goog.result.Result.State.ERROR, result.getState());
|
||||
assertUndefined(result.getValue());
|
||||
assertUndefined(result.getError());
|
||||
|
||||
assertEquals('Listeners should have been cleaned up.',
|
||||
0, goog.events.getTotalListenerCount());
|
||||
|
||||
asyncTestCase.continueTesting();
|
||||
});
|
||||
}
|
||||
|
||||
function testImageFactory() {
|
||||
var returnedImage = new Image();
|
||||
var factory = function() {
|
||||
return returnedImage;
|
||||
};
|
||||
var countedFactory = goog.testing.recordFunction(factory);
|
||||
|
||||
var url = 'testdata/cleardot.gif';
|
||||
|
||||
asyncTestCase.waitForAsync('image load');
|
||||
assertEquals(0, goog.events.getTotalListenerCount());
|
||||
var result = goog.labs.net.image.load(url, countedFactory);
|
||||
|
||||
goog.result.waitOnSuccess(result, function(value) {
|
||||
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
|
||||
assertEquals(returnedImage, value);
|
||||
assertEquals(1, countedFactory.getCallCount());
|
||||
assertUndefined(result.getError());
|
||||
|
||||
assertEquals('Listeners should have been cleaned up.',
|
||||
0, goog.events.getTotalListenerCount());
|
||||
asyncTestCase.continueTesting();
|
||||
});
|
||||
}
|
||||
|
||||
function testExistingImage() {
|
||||
var image = new Image();
|
||||
|
||||
var url = 'testdata/cleardot.gif';
|
||||
|
||||
asyncTestCase.waitForAsync('image load');
|
||||
assertEquals(0, goog.events.getTotalListenerCount());
|
||||
var result = goog.labs.net.image.load(url, image);
|
||||
|
||||
goog.result.waitOnSuccess(result, function(value) {
|
||||
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
|
||||
assertEquals(image, value);
|
||||
assertUndefined(result.getError());
|
||||
|
||||
assertEquals('Listeners should have been cleaned up.',
|
||||
0, goog.events.getTotalListenerCount());
|
||||
asyncTestCase.continueTesting();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// 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 The API spec for the WebChannel messaging library.
|
||||
*
|
||||
* Similar to HTML5 WebSocket and Closure BrowserChannel, WebChannel
|
||||
* offers an abstraction for point-to-point socket-like communication between
|
||||
* a browser client and a remote origin.
|
||||
*
|
||||
* WebChannels are created via <code>WebChannel</code>. Multiple WebChannels
|
||||
* may be multiplexed over the same WebChannelTransport, which represents
|
||||
* the underlying physical connectivity over standard wire protocols
|
||||
* such as HTTP and SPDY.
|
||||
*
|
||||
* A WebChannels in turn represents a logical communication channel between
|
||||
* the client and server end point. A WebChannel remains open for
|
||||
* as long as the client or server end-point allows.
|
||||
*
|
||||
* Messages may be delivered in-order or out-of-order, reliably or unreliably
|
||||
* over the same WebChannel. Message delivery guarantees of a WebChannel is
|
||||
* to be specified by the application code; and the choice of the
|
||||
* underlying wire protocols is completely transparent to the API users.
|
||||
*
|
||||
* Client-to-client messaging via WebRTC based transport may also be support
|
||||
* via the same WebChannel API in future.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.net.WebChannel');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.Event');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A WebChannel represents a logical bi-directional channel over which the
|
||||
* client communicates with a remote server that holds the other endpoint
|
||||
* of the channel. A WebChannel is always created in the context of a shared
|
||||
* {@link WebChannelTransport} instance. It is up to the underlying client-side
|
||||
* and server-side implementations to decide how or when multiplexing is
|
||||
* to be enabled.
|
||||
*
|
||||
* @interface
|
||||
* @extends {EventTarget}
|
||||
*/
|
||||
goog.net.WebChannel = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Configuration spec of a WebChannel (TODO(user): to complete):
|
||||
* 1) delivery: ordered, reliable, timeout
|
||||
* 2) HTTP: special headers, URI prefix, cross domains
|
||||
* 3) debugging: stats, logging
|
||||
* 4) pattern: full-duplex, server-client or client-server messaging
|
||||
* 5) QoS: priority, throttling,
|
||||
* 6) buffer management: batch size, delivery interval
|
||||
*
|
||||
* WebChannels are configured in the context of the containing
|
||||
* {@link WebChannelTransport}. The configuration parameters are specified
|
||||
* when a new instance of WebChannel is created via {@link WebChannelTransport}.
|
||||
*
|
||||
* @typedef {{
|
||||
* ordered: (boolean|undefined),
|
||||
* reliable: (boolean|undefined),
|
||||
* timeoutMs: (number|undefined),
|
||||
* priority: (number|undefined)
|
||||
* }}
|
||||
*/
|
||||
goog.net.WebChannel.Options;
|
||||
|
||||
|
||||
/**
|
||||
* Types that are allowed as message data.
|
||||
*
|
||||
* @typedef {(ArrayBuffer|Blob|Object.<String>|Array)}
|
||||
*/
|
||||
goog.net.WebChannel.MessageData;
|
||||
|
||||
|
||||
/**
|
||||
* Open the WebChannel against the URI specified in the constructor.
|
||||
*/
|
||||
goog.net.WebChannel.prototype.open = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Close the WebChannel.
|
||||
*/
|
||||
goog.net.WebChannel.prototype.close = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message to the server that maintains the other end point of
|
||||
* the WebChannel.
|
||||
*
|
||||
* @param {!goog.net.WebChannel.MessageData} message The message to send.
|
||||
*/
|
||||
goog.net.WebChannel.prototype.send = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Common events fired by WebChannels.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.net.WebChannel.EventType = {
|
||||
/** Dispatched when the channel is opened. */
|
||||
OPEN: goog.events.getUniqueId('open'),
|
||||
|
||||
/** Dispatched when the channel is closed. */
|
||||
CLOSE: goog.events.getUniqueId('close'),
|
||||
|
||||
/** Dispatched when the channel is aborted due to errors. */
|
||||
ERROR: goog.events.getUniqueId('error'),
|
||||
|
||||
/** Dispatched when the channel has received a new message. */
|
||||
MESSAGE: goog.events.getUniqueId('message')
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The event interface for the MESSAGE event.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.net.WebChannel.MessageEvent = function() {
|
||||
goog.base(this, goog.net.WebChannel.EventType.MESSAGE);
|
||||
};
|
||||
goog.inherits(goog.net.WebChannel.MessageEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* The content of the message received from the server.
|
||||
*
|
||||
* @type {!goog.net.WebChannel.MessageData}
|
||||
*/
|
||||
goog.net.WebChannel.MessageEvent.prototype.data;
|
||||
|
||||
|
||||
/**
|
||||
* WebChannel level error conditions.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.net.WebChannel.ErrorStatus = {
|
||||
/** No error has occurred. */
|
||||
OK: 0,
|
||||
|
||||
/** Communication to the server has failed. */
|
||||
NETWORK_ERROR: 1,
|
||||
|
||||
/** The server fails to accept the WebChannel. */
|
||||
SERVER_ERROR: 2
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The event interface for the ERROR event.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.net.WebChannel.ErrorEvent = function() {
|
||||
goog.base(this, goog.net.WebChannel.EventType.ERROR);
|
||||
};
|
||||
goog.inherits(goog.net.WebChannel.ErrorEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* The error status.
|
||||
*
|
||||
* @type {!goog.net.WebChannel.ErrorStatus}
|
||||
*/
|
||||
goog.net.WebChannel.ErrorEvent.prototype.status;
|
||||
@@ -0,0 +1,639 @@
|
||||
// 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.json.EvalJsonProcessor');
|
||||
goog.require('goog.labs.net.webChannel.Channel');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelRequest');
|
||||
goog.require('goog.labs.net.webChannel.requestStats');
|
||||
goog.require('goog.labs.net.webChannel.requestStats.ServerReachability');
|
||||
goog.require('goog.labs.net.webChannel.requestStats.Stat');
|
||||
goog.require('goog.net.tmpnetwork');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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. It also runs the logic to see if the channel
|
||||
* has been blocked by a network administrator.
|
||||
*
|
||||
* @constructor
|
||||
* @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
|
||||
* @type {!goog.labs.net.webChannel.Channel}
|
||||
* @private
|
||||
*/
|
||||
this.channel_ = channel;
|
||||
|
||||
/**
|
||||
* The channel debug to use for logging
|
||||
* @type {!goog.labs.net.webChannel.WebChannelDebug}
|
||||
* @private
|
||||
*/
|
||||
this.channelDebug_ = channelDebug;
|
||||
|
||||
/**
|
||||
* Parser for a response payload. Defaults to use
|
||||
* {@code goog.json.unsafeParse}. The parser should return an array.
|
||||
* @type {goog.string.Parser}
|
||||
* @private
|
||||
*/
|
||||
this.parser_ = new goog.json.EvalJsonProcessor(null, true);
|
||||
};
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var BaseTestChannel = goog.labs.net.webChannel.BaseTestChannel;
|
||||
var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
|
||||
var WebChannelRequest = goog.labs.net.webChannel.WebChannelRequest;
|
||||
var requestStats = goog.labs.net.webChannel.requestStats;
|
||||
var Channel = goog.labs.net.webChannel.Channel;
|
||||
|
||||
|
||||
/**
|
||||
* Extra HTTP headers to add to all the requests sent to the server.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.extraHeaders_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The test request.
|
||||
* @type {WebChannelRequest}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.request_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether we have received the first result as an intermediate result. This
|
||||
* helps us determine whether we're behind a buffering proxy.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.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.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.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.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.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.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.lastTime_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The relative path for test requests.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.path_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The state of the state machine for this object.
|
||||
*
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.state_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The last status code received.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.lastStatusCode_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* A subdomain prefix for using a subdomain in IE for the backchannel
|
||||
* requests.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.hostPrefix_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* A subdomain prefix for testing whether the channel was disabled by
|
||||
* a network administrator;
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.blockedPrefix_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* see if the channel has been blocked.
|
||||
*/
|
||||
CHECKING_BLOCKED: 1,
|
||||
|
||||
/**
|
||||
* The state for the TestChannel state machine where we're checking to
|
||||
* se if we're behind a buffering proxy.
|
||||
*/
|
||||
CONNECTION_TESTING: 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Time in MS for waiting for the request to see if the channel is blocked.
|
||||
* If the response takes longer than this many ms, we assume the request has
|
||||
* failed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.BLOCKED_TIMEOUT_ = 5000;
|
||||
|
||||
|
||||
/**
|
||||
* Number of attempts to try to see if the check to see if we're blocked
|
||||
* succeeds. Sometimes the request can fail because of flaky network conditions
|
||||
* and checking multiple times reduces false positives.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.BLOCKED_RETRIES_ = 3;
|
||||
|
||||
|
||||
/**
|
||||
* Time in ms between retries of the blocked request
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_ = 2000;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a new parser for the response payload. A custom parser may be set to
|
||||
* avoid using eval(), for example.
|
||||
* By default, the parser uses {@code goog.json.unsafeParse}.
|
||||
* @param {!goog.string.Parser} parser Parser.
|
||||
*/
|
||||
BaseTestChannel.prototype.setParser = function(parser) {
|
||||
this.parser_ = parser;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 first test, then skip it.
|
||||
var firstTestResults = this.channel_.getFirstTestResults();
|
||||
if (goog.isDefAndNotNull(firstTestResults)) {
|
||||
this.hostPrefix_ = this.channel_.correctHostPrefix(firstTestResults[0]);
|
||||
this.blockedPrefix_ = firstTestResults[1];
|
||||
if (this.blockedPrefix_) {
|
||||
this.state_ = BaseTestChannel.State_.CHECKING_BLOCKED;
|
||||
this.checkBlocked_();
|
||||
} else {
|
||||
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
|
||||
this.connectStage2_();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// the first request returns server specific parameters
|
||||
sendDataUri.setParameterValues('MODE', 'init');
|
||||
this.request_ = WebChannelRequest.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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see whether the channel is blocked. This is for implementing the
|
||||
* feature that allows network administrators to block Gmail Chat. The
|
||||
* strategy to determine if we're blocked is to try to load an image off a
|
||||
* special subdomain that network administrators will block access to if they
|
||||
* are trying to block chat. For Gmail Chat, the subdomain is
|
||||
* chatenabled.mail.google.com.
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.checkBlocked_ = function() {
|
||||
var uri = this.channel_.createDataUri(this.blockedPrefix_,
|
||||
'/mail/images/cleardot.gif');
|
||||
uri.makeUnique();
|
||||
goog.net.tmpnetwork.testLoadImageWithRetries(uri.toString(),
|
||||
BaseTestChannel.BLOCKED_TIMEOUT_,
|
||||
goog.bind(this.checkBlockedCallback_, this),
|
||||
BaseTestChannel.BLOCKED_RETRIES_,
|
||||
BaseTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_);
|
||||
requestStats.notifyServerReachabilityEvent(
|
||||
requestStats.ServerReachability.REQUEST_MADE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback for testLoadImageWithRetries to check if a channel is blocked.
|
||||
* @param {boolean} succeeded Whether the request succeeded.
|
||||
* @private
|
||||
*/
|
||||
BaseTestChannel.prototype.checkBlockedCallback_ = function(
|
||||
succeeded) {
|
||||
if (succeeded) {
|
||||
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
|
||||
this.connectStage2_();
|
||||
} else {
|
||||
requestStats.notifyStatEvent(requestStats.Stat.CHANNEL_BLOCKED);
|
||||
this.channel_.testConnectionBlocked(this);
|
||||
}
|
||||
|
||||
// We don't dispatch a REQUEST_FAILED server reachability event when the
|
||||
// block request fails, as such a failure is not a good signal that the
|
||||
// server has actually become unreachable.
|
||||
if (succeeded) {
|
||||
requestStats.notifyServerReachabilityEvent(
|
||||
requestStats.ServerReachability.REQUEST_SUCCEEDED);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.connectStage2_ = function() {
|
||||
this.channelDebug_.debug('TestConnection: starting stage 2');
|
||||
|
||||
// If the second test results are available, skip its execution.
|
||||
var secondTestResults = this.channel_.getSecondTestResults();
|
||||
if (goog.isDefAndNotNull(secondTestResults)) {
|
||||
this.channelDebug_.debug(
|
||||
'TestConnection: skipping stage 2, precomputed result is ' +
|
||||
secondTestResults ? 'Buffered' : 'Unbuffered');
|
||||
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
|
||||
if (secondTestResults) { // 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_ = WebChannelRequest.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 (!WebChannelRequest.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 {WebChannelRequest} 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,
|
||||
WebChannelRequest.Error.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
/** @preserveTry */
|
||||
try {
|
||||
var respArray = this.parser_.parse(responseText);
|
||||
} catch (e) {
|
||||
this.channelDebug_.dumpException(e);
|
||||
this.channel_.testConnectionFailure(this,
|
||||
WebChannelRequest.Error.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
|
||||
this.blockedPrefix_ = respArray[1];
|
||||
} 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 {WebChannelRequest} 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 {WebChannelRequest.Error} */
|
||||
(this.request_.getLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state_ == BaseTestChannel.State_.INIT) {
|
||||
this.channelDebug_.debug(
|
||||
'TestConnection: request complete for initial check');
|
||||
if (this.blockedPrefix_) {
|
||||
this.state_ = BaseTestChannel.State_.CHECKING_BLOCKED;
|
||||
this.checkBlocked_();
|
||||
} else {
|
||||
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
|
||||
this.connectStage2_();
|
||||
}
|
||||
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
|
||||
this.channelDebug_.debug('TestConnection: request complete for stage 2');
|
||||
var goodConn = false;
|
||||
|
||||
if (!WebChannelRequest.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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 WebChannelRequest.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.testConnectionBlocked = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.testConnectionFinished = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.testConnectionFailure = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
BaseTestChannel.prototype.getFirstTestResults = goog.abstractMethod;
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,195 @@
|
||||
// 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 {//visibility:private}
|
||||
*/
|
||||
|
||||
|
||||
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.WebChannelRequest} 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.WebChannelRequest} 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 blocked.
|
||||
* @param {goog.labs.net.webChannel.BaseTestChannel} testChannel
|
||||
* The TestChannel.
|
||||
*/
|
||||
Channel.prototype.testConnectionBlocked = 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.WebChannelRequest.Error} errorCode
|
||||
* The error code of the failure.
|
||||
*/
|
||||
Channel.prototype.testConnectionFailure = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Not needed for testchannel.
|
||||
* Gets the results for the first channel test
|
||||
* @return {Array.<string>} The results.
|
||||
*/
|
||||
Channel.prototype.getFirstTestResults = goog.abstractMethod;
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,391 @@
|
||||
// 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
|
||||
* WebChannelRequest.
|
||||
*
|
||||
* @visibility {//visibility:private}
|
||||
*/
|
||||
|
||||
|
||||
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.
|
||||
* @type {Object}
|
||||
*/
|
||||
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.
|
||||
* TODO(user) - Change name not to use Event or use EventTarget
|
||||
* @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 channel is blocked by a network administrator. */
|
||||
CHANNEL_BLOCKED: 4,
|
||||
|
||||
/** Event indicating the start of test stage two. */
|
||||
TEST_STAGE_TWO_START: 5,
|
||||
|
||||
/** Event indicating the first piece of test data was received. */
|
||||
TEST_STAGE_TWO_DATA_ONE: 6,
|
||||
|
||||
/**
|
||||
* Event indicating that the second piece of test data was received and it was
|
||||
* recieved separately from the first.
|
||||
*/
|
||||
TEST_STAGE_TWO_DATA_TWO: 7,
|
||||
|
||||
/** Event indicating both pieces of test data were received simultaneously. */
|
||||
TEST_STAGE_TWO_DATA_BOTH: 8,
|
||||
|
||||
/** Event indicating stage one of the test request failed. */
|
||||
TEST_STAGE_ONE_FAILED: 9,
|
||||
|
||||
/** Event indicating stage two of the test request failed. */
|
||||
TEST_STAGE_TWO_FAILED: 10,
|
||||
|
||||
/**
|
||||
* Event indicating that a buffering proxy is likely between the client and
|
||||
* the server.
|
||||
*/
|
||||
PROXY: 11,
|
||||
|
||||
/**
|
||||
* Event indicating that no buffering proxy is likely between the client and
|
||||
* the server.
|
||||
*/
|
||||
NOPROXY: 12,
|
||||
|
||||
/** Event indicating an unknown SID error. */
|
||||
REQUEST_UNKNOWN_SESSION_ID: 13,
|
||||
|
||||
/** Event indicating a bad status code was received. */
|
||||
REQUEST_BAD_STATUS: 14,
|
||||
|
||||
/** Event indicating incomplete data was received */
|
||||
REQUEST_INCOMPLETE_DATA: 15,
|
||||
|
||||
/** Event indicating bad data was received */
|
||||
REQUEST_BAD_DATA: 16,
|
||||
|
||||
/** Event indicating no data was received when data was expected. */
|
||||
REQUEST_NO_DATA: 17,
|
||||
|
||||
/** Event indicating a request timeout. */
|
||||
REQUEST_TIMEOUT: 18,
|
||||
|
||||
/**
|
||||
* Event indicating that the server never received our hanging GET and so it
|
||||
* is being retried.
|
||||
*/
|
||||
BACKCHANNEL_MISSING: 19,
|
||||
|
||||
/**
|
||||
* 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: 20,
|
||||
|
||||
/**
|
||||
* The browser declared itself offline during the lifetime of a request, or
|
||||
* was offline when a request was initially made.
|
||||
*/
|
||||
BROWSER_OFFLINE: 21,
|
||||
|
||||
/** ActiveX is blocked by the machine's admin settings. */
|
||||
ACTIVE_X_BLOCKED: 22
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,274 @@
|
||||
// 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.string.path');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link goog.net.WebChannelTransport} with
|
||||
* {@link goog.labs.net.webChannel.WebChannelBase} as the underlying channel
|
||||
* implementation.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {goog.net.WebChannelTransport}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
WebChannelBaseTransport.Channel = function(url, opt_options) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The underlying channel object.
|
||||
*
|
||||
* @type {!WebChannelBase}
|
||||
* @private
|
||||
*/
|
||||
this.channel_ = new WebChannelBase();
|
||||
|
||||
/**
|
||||
* The URL of the target server end-point.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The channel options.
|
||||
*
|
||||
* @type {?goog.net.WebChannel.Options}
|
||||
* @private
|
||||
*/
|
||||
this.options_ = opt_options || null;
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
this.logger_ = goog.log.getLogger(
|
||||
'goog.labs.net.webChannel.WebChannelBaseTransport');
|
||||
|
||||
};
|
||||
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".
|
||||
*
|
||||
* TODO(user): The test path may be made configurable via the options.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.prototype.open = function() {
|
||||
var testUrl = goog.string.path.join(this.url_, 'test');
|
||||
this.channel_.connect(testUrl, this.url_);
|
||||
|
||||
this.channelHandler_ = new WebChannelBaseTransport.Channel.Handler_(this);
|
||||
this.channel_.setHandler(this.channelHandler_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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_;
|
||||
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The message event.
|
||||
*
|
||||
* @param {!Array} array The data array from the underlying channel.
|
||||
* @constructor
|
||||
* @extends {goog.net.WebChannel.MessageEvent}
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.MessageEvent = function(array) {
|
||||
goog.base(this);
|
||||
|
||||
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}
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.ErrorEvent = function(error) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* 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 the {@link WebChannelBase.Handler} interface.
|
||||
*
|
||||
* @param {!WebChannelBaseTransport.Channel} channel The enclosing WebChannel.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {WebChannelBase.Handler}
|
||||
* @private
|
||||
*/
|
||||
WebChannelBaseTransport.Channel.Handler_ = function(channel) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}); // goog.scope
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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 {//visibility:private}
|
||||
*/
|
||||
|
||||
|
||||
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
|
||||
*/
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
// 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 Transport support for WebChannel.
|
||||
*
|
||||
* The <code>WebChannelTransport</code> implementation serves as the factory
|
||||
* for <code>WebChannel</code>, which offers an abstraction for
|
||||
* point-to-point socket-like communication similar to what BrowserChannel
|
||||
* or HTML5 WebSocket offers.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.net.WebChannelTransport');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A WebChannelTransport instance represents a shared context of logical
|
||||
* connectivity between a browser client and a remote origin.
|
||||
*
|
||||
* Over a single WebChannelTransport instance, multiple WebChannels may be
|
||||
* created against different URLs, which may all share the same
|
||||
* underlying connectivity (i.e. TCP connection) whenever possible.
|
||||
*
|
||||
* When multi-domains are supported, such as CORS, multiple origins may be
|
||||
* supported over a single WebChannelTransport instance at the same time.
|
||||
*
|
||||
* Sharing between different window contexts such as tabs is not addressed
|
||||
* by WebChannelTransport. Applications may choose HTML5 shared workers
|
||||
* or other techniques to access the same transport instance
|
||||
* across different window contexts.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
goog.net.WebChannelTransport = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* The latest protocol version. The protocol version is requested
|
||||
* from the server which is responsible for terminating the underlying
|
||||
* wire protocols.
|
||||
*
|
||||
* @const
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.net.WebChannelTransport.LATEST_VERSION_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WebChannel instance.
|
||||
*
|
||||
* The new WebChannel is to be opened against the server-side resource
|
||||
* as specified by the given URL. See {@link goog.net.WebChannel} for detailed
|
||||
* semantics.
|
||||
*
|
||||
* @param {string} url The URL path for the new WebChannel instance.
|
||||
* @param {!goog.net.WebChannel.Options=} opt_options Configuration for the
|
||||
* new WebChannel instance.
|
||||
* @return {!goog.net.WebChannel} the newly created WebChannel instance.
|
||||
*/
|
||||
goog.net.WebChannelTransport.prototype.createWebChannel = goog.abstractMethod;
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 Default factory for <code>WebChannelTransport</code> to
|
||||
* avoid exposing concrete classes to clients.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.net.createWebChannelTransport');
|
||||
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WebChannelTransport instance using the default implementation.
|
||||
*
|
||||
* @return {!goog.net.WebChannelTransport} the newly created transport instance.
|
||||
*/
|
||||
goog.net.createWebChannelTransport =
|
||||
/** @type {function(): !goog.net.WebChannelTransport} */ (
|
||||
goog.partial(goog.functions.create,
|
||||
goog.labs.net.webChannel.WebChannelBaseTransport));
|
||||
449
nicer-api-docs/closure-library/closure/goog/labs/net/xhr.js
Normal file
449
nicer-api-docs/closure-library/closure/goog/labs/net/xhr.js
Normal file
@@ -0,0 +1,449 @@
|
||||
// Copyright 2011 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 Offered as an alternative to XhrIo as a way for making requests
|
||||
* via XMLHttpRequest. Instead of mirroring the XHR interface and exposing
|
||||
* events, results are used as a way to pass a "promise" of the response to
|
||||
* interested parties.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.net.xhr');
|
||||
goog.provide('goog.labs.net.xhr.Error');
|
||||
goog.provide('goog.labs.net.xhr.HttpError');
|
||||
goog.provide('goog.labs.net.xhr.TimeoutError');
|
||||
|
||||
goog.require('goog.debug.Error');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.net.HttpStatus');
|
||||
goog.require('goog.net.XmlHttp');
|
||||
goog.require('goog.result');
|
||||
goog.require('goog.result.SimpleResult');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.uri.utils');
|
||||
|
||||
|
||||
|
||||
goog.scope(function() {
|
||||
var _ = goog.labs.net.xhr;
|
||||
var Result = goog.result.Result;
|
||||
var SimpleResult = goog.result.SimpleResult;
|
||||
var Wait = goog.result.wait;
|
||||
var HttpStatus = goog.net.HttpStatus;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration options for an XMLHttpRequest.
|
||||
* - headers: map of header key/value pairs.
|
||||
* - timeoutMs: number of milliseconds after which the request will be timed
|
||||
* out by the client. Default is to allow the browser to handle timeouts.
|
||||
* - withCredentials: whether user credentials are to be included in a
|
||||
* cross-origin request. See:
|
||||
* http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
|
||||
* - mimeType: allows the caller to override the content-type and charset for
|
||||
* the request, which is useful when requesting binary data. See:
|
||||
* http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
|
||||
* - xssiPrefix: Prefix used for protecting against XSSI attacks, which should
|
||||
* be removed before parsing the response as JSON.
|
||||
*
|
||||
* @typedef {{
|
||||
* headers: (Object.<string>|undefined),
|
||||
* timeoutMs: (number|undefined),
|
||||
* withCredentials: (boolean|undefined),
|
||||
* mimeType: (string|undefined),
|
||||
* xssiPrefix: (string|undefined)
|
||||
* }}
|
||||
*/
|
||||
_.Options;
|
||||
|
||||
|
||||
/**
|
||||
* Defines the types that are allowed as post data.
|
||||
* @typedef {(ArrayBuffer|Blob|Document|FormData|null|string|undefined)}
|
||||
*/
|
||||
_.PostData;
|
||||
|
||||
|
||||
/**
|
||||
* The Content-Type HTTP header name.
|
||||
* @type {string}
|
||||
*/
|
||||
_.CONTENT_TYPE_HEADER = 'Content-Type';
|
||||
|
||||
|
||||
/**
|
||||
* The Content-Type HTTP header value for a url-encoded form.
|
||||
* @type {string}
|
||||
*/
|
||||
_.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8';
|
||||
|
||||
|
||||
/**
|
||||
* Sends a get request, returning a transformed result which will be resolved
|
||||
* with the response text once the request completes.
|
||||
*
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @return {!Result} A result object that will be resolved
|
||||
* with the response text once the request finishes.
|
||||
*/
|
||||
_.get = function(url, opt_options) {
|
||||
var result = _.send('GET', url, null, opt_options);
|
||||
var transformedResult = goog.result.transform(result,
|
||||
_.getResponseText_);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a post request, returning a transformed result which will be resolved
|
||||
* with the response text once the request completes.
|
||||
*
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.PostData} data The body of the post request.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @return {!Result} A result object that will be resolved
|
||||
* with the response text once the request finishes.
|
||||
*/
|
||||
_.post = function(url, data, opt_options) {
|
||||
var result = _.send('POST', url, data, opt_options);
|
||||
var transformedResult = goog.result.transform(result,
|
||||
_.getResponseText_);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a get request, returning a result which will be resolved with
|
||||
* the parsed response text once the request completes.
|
||||
*
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @return {!Result} A result object that will be resolved
|
||||
* with the response JSON once the request finishes.
|
||||
*/
|
||||
_.getJson = function(url, opt_options) {
|
||||
var result = _.send('GET', url, null, opt_options);
|
||||
var transformedResult = _.addJsonParsingCallbacks_(result, opt_options);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a post request, returning a result which will be resolved with
|
||||
* the parsed response text once the request completes.
|
||||
*
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.PostData} data The body of the post request.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @return {!Result} A result object that will be resolved
|
||||
* with the response JSON once the request finishes.
|
||||
*/
|
||||
_.postJson = function(url, data, opt_options) {
|
||||
var result = _.send('POST', url, data, opt_options);
|
||||
var transformedResult = _.addJsonParsingCallbacks_(result, opt_options);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a request using XMLHttpRequest and returns a result.
|
||||
*
|
||||
* @param {string} method The HTTP method for the request.
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.PostData} data The body of the post request.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @return {!Result} A result object that will be resolved
|
||||
* with the XHR object as it's value when the request finishes.
|
||||
*/
|
||||
_.send = function(method, url, data, opt_options) {
|
||||
|
||||
var result = new SimpleResult();
|
||||
|
||||
// When the deferred is cancelled, we abort the XHR. We want to make sure
|
||||
// the readystatechange event still fires, so it can do the timeout
|
||||
// cleanup, however we don't want the callback or errback to be called
|
||||
// again. Thus the slight ugliness here. If results were pushed into
|
||||
// makeRequest, this could become a lot cleaner but we want an option for
|
||||
// people not to include goog.result.Result.
|
||||
goog.result.waitOnError(result, function(error, result) {
|
||||
if (result.isCanceled()) {
|
||||
xhr.abort();
|
||||
xhr.onreadystatechange = goog.nullFunction;
|
||||
}
|
||||
});
|
||||
|
||||
function callback(data) {
|
||||
result.setValue(data);
|
||||
}
|
||||
|
||||
function errback(err) {
|
||||
result.setError(err);
|
||||
}
|
||||
|
||||
var xhr = _.makeRequest(method, url, data, opt_options, callback, errback);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new XMLHttpRequest and initiates a request.
|
||||
*
|
||||
* @param {string} method The HTTP method for the request.
|
||||
* @param {string} url The URL to request.
|
||||
* @param {_.PostData} data The body of the post request, unless the content
|
||||
* type is explicitly set in the Options, then it will default to form
|
||||
* urlencoded.
|
||||
* @param {_.Options=} opt_options Configuration options for the request.
|
||||
* @param {function(XMLHttpRequest)=} opt_callback Optional callback to call
|
||||
* when the request completes.
|
||||
* @param {function(Error)=} opt_errback Optional callback to call
|
||||
* when there is an error.
|
||||
* @return {!XMLHttpRequest} The new XMLHttpRequest.
|
||||
*/
|
||||
_.makeRequest = function(
|
||||
method, url, data, opt_options, opt_callback, opt_errback) {
|
||||
var options = opt_options || {};
|
||||
var callback = opt_callback || goog.nullFunction;
|
||||
var errback = opt_errback || goog.nullFunction;
|
||||
var timer;
|
||||
|
||||
var xhr = /** @type {!XMLHttpRequest} */ (goog.net.XmlHttp());
|
||||
try {
|
||||
xhr.open(method, url, true);
|
||||
} catch (e) {
|
||||
// XMLHttpRequest.open may throw when 'open' is called, for example, IE7
|
||||
// throws "Access Denied" for cross-origin requests.
|
||||
errback(new _.Error('Error opening XHR: ' + e.message, url, xhr));
|
||||
return xhr;
|
||||
}
|
||||
|
||||
// So sad that IE doesn't support onload and onerror.
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
|
||||
window.clearTimeout(timer);
|
||||
// Note: When developing locally, XHRs to file:// schemes return a status
|
||||
// code of 0. We mark that case as a success too.
|
||||
if (HttpStatus.isSuccess(xhr.status) ||
|
||||
xhr.status === 0 && !_.isEffectiveSchemeHttp_(url)) {
|
||||
callback(xhr);
|
||||
} else {
|
||||
errback(new _.HttpError(xhr.status, url, xhr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set the headers.
|
||||
var contentTypeIsSet = false;
|
||||
if (options.headers) {
|
||||
for (var key in options.headers) {
|
||||
xhr.setRequestHeader(key, options.headers[key]);
|
||||
}
|
||||
contentTypeIsSet = _.CONTENT_TYPE_HEADER in options.headers;
|
||||
}
|
||||
|
||||
// If a content type hasn't been set, default to form-urlencoded/UTF8 for
|
||||
// POSTs. This is because some proxies have been known to reject posts
|
||||
// without a content-type.
|
||||
if (method == 'POST' && !contentTypeIsSet) {
|
||||
xhr.setRequestHeader(_.CONTENT_TYPE_HEADER, _.FORM_CONTENT_TYPE);
|
||||
}
|
||||
|
||||
// Set whether to pass cookies on cross-domain requests (if applicable).
|
||||
// @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
|
||||
if (options.withCredentials) {
|
||||
xhr.withCredentials = options.withCredentials;
|
||||
}
|
||||
|
||||
// Allow the request to override the mime type, useful for getting binary
|
||||
// data from the server. e.g. 'text/plain; charset=x-user-defined'.
|
||||
// @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
|
||||
if (options.mimeType) {
|
||||
xhr.overrideMimeType(options.mimeType);
|
||||
}
|
||||
|
||||
// Handle timeouts, if requested.
|
||||
if (options.timeoutMs > 0) {
|
||||
timer = window.setTimeout(function() {
|
||||
// Clear event listener before aborting so the errback will not be
|
||||
// called twice.
|
||||
xhr.onreadystatechange = goog.nullFunction;
|
||||
xhr.abort();
|
||||
errback(new _.TimeoutError(url, xhr));
|
||||
}, options.timeoutMs);
|
||||
}
|
||||
|
||||
// Trigger the send.
|
||||
try {
|
||||
xhr.send(data);
|
||||
} catch (e) {
|
||||
// XMLHttpRequest.send is known to throw on some versions of FF, for example
|
||||
// if a cross-origin request is disallowed.
|
||||
xhr.onreadystatechange = goog.nullFunction;
|
||||
window.clearTimeout(timer);
|
||||
errback(new _.Error('Error sending XHR: ' + e.message, url, xhr));
|
||||
}
|
||||
|
||||
return xhr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} url The URL to test.
|
||||
* @return {boolean} Whether the effective scheme is HTTP or HTTPs.
|
||||
* @private
|
||||
*/
|
||||
_.isEffectiveSchemeHttp_ = function(url) {
|
||||
var scheme = goog.uri.utils.getEffectiveScheme(url);
|
||||
// NOTE(user): Empty-string is for the case under FF3.5 when the location
|
||||
// is not defined inside a web worker.
|
||||
return scheme == 'http' || scheme == 'https' || scheme == '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the response text of an XHR object. Intended to be called when
|
||||
* the result resolves.
|
||||
*
|
||||
* @param {!XMLHttpRequest} xhr The XHR object.
|
||||
* @return {string} The response text.
|
||||
* @private
|
||||
*/
|
||||
_.getResponseText_ = function(xhr) {
|
||||
return xhr.responseText;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Transforms a result, parsing the JSON in the original result value's
|
||||
* responseText. The transformed result's value is a javascript object.
|
||||
* Parse errors resolve the transformed result in an error.
|
||||
*
|
||||
* @param {!Result} result The result to wait on.
|
||||
* @param {_.Options|undefined} options The options object.
|
||||
*
|
||||
* @return {!Result} The transformed result.
|
||||
* @private
|
||||
*/
|
||||
_.addJsonParsingCallbacks_ = function(result, options) {
|
||||
var resultWithResponseText = goog.result.transform(result,
|
||||
_.getResponseText_);
|
||||
var prefixStrippedResult = resultWithResponseText;
|
||||
if (options && options.xssiPrefix) {
|
||||
prefixStrippedResult = goog.result.transform(resultWithResponseText,
|
||||
goog.partial(_.stripXssiPrefix_, options.xssiPrefix));
|
||||
}
|
||||
|
||||
var jsonParsedResult = goog.result.transform(prefixStrippedResult,
|
||||
goog.json.parse);
|
||||
return jsonParsedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Strips the XSSI prefix from the input string.
|
||||
*
|
||||
* @param {string} prefix The XSSI prefix.
|
||||
* @param {string} string The string to strip the prefix from.
|
||||
* @return {string} The input string without the prefix.
|
||||
* @private
|
||||
*/
|
||||
_.stripXssiPrefix_ = function(prefix, string) {
|
||||
if (goog.string.startsWith(string, prefix)) {
|
||||
string = string.substring(prefix.length);
|
||||
}
|
||||
return string;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generic error that may occur during a request.
|
||||
*
|
||||
* @param {string} message The error message.
|
||||
* @param {string} url The URL that was being requested.
|
||||
* @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
|
||||
* @extends {goog.debug.Error}
|
||||
* @constructor
|
||||
*/
|
||||
_.Error = function(message, url, xhr) {
|
||||
goog.base(this, message + ', url=' + url);
|
||||
|
||||
/**
|
||||
* The URL that was requested.
|
||||
* @type {string}
|
||||
*/
|
||||
this.url = url;
|
||||
|
||||
/**
|
||||
* The XMLHttpRequest corresponding with the failed request.
|
||||
* @type {!XMLHttpRequest}
|
||||
*/
|
||||
this.xhr = xhr;
|
||||
};
|
||||
goog.inherits(_.Error, goog.debug.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
_.Error.prototype.name = 'XhrError';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for HTTP errors.
|
||||
*
|
||||
* @param {number} status The HTTP status code of the response.
|
||||
* @param {string} url The URL that was being requested.
|
||||
* @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
|
||||
* @extends {_.Error}
|
||||
* @constructor
|
||||
*/
|
||||
_.HttpError = function(status, url, xhr) {
|
||||
goog.base(this, 'Request Failed, status=' + status, url, xhr);
|
||||
|
||||
/**
|
||||
* The HTTP status code for the error.
|
||||
* @type {number}
|
||||
*/
|
||||
this.status = status;
|
||||
};
|
||||
goog.inherits(_.HttpError, _.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
_.HttpError.prototype.name = 'XhrHttpError';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for Timeout errors.
|
||||
*
|
||||
* @param {string} url The URL that timed out.
|
||||
* @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
|
||||
* @extends {_.Error}
|
||||
* @constructor
|
||||
*/
|
||||
_.TimeoutError = function(url, xhr) {
|
||||
goog.base(this, 'Request timed out', url, xhr);
|
||||
};
|
||||
goog.inherits(_.TimeoutError, _.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
_.TimeoutError.prototype.name = 'XhrTimeoutError';
|
||||
|
||||
}); // goog.scope
|
||||
Reference in New Issue
Block a user