diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/net/channelrequest.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/net/channelrequest.js | 1277 |
1 files changed, 0 insertions, 1277 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/net/channelrequest.js b/contexts/data/lib/closure-library/closure/goog/net/channelrequest.js deleted file mode 100644 index 78701b8..0000000 --- a/contexts/data/lib/closure-library/closure/goog/net/channelrequest.js +++ /dev/null @@ -1,1277 +0,0 @@ -// 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 Definition of the ChannelRequest class. The ChannelRequest - * object encapsulates the logic for making a single request, either for the - * forward channel, back channel, or test channel, to the server. It contains - * the logic for the three types of transports we use in the BrowserChannel: - * XMLHTTP, Trident ActiveX (ie only), and Image request. It provides timeout - * detection. This class is part of the BrowserChannel implementation and is not - * for use by normal application code. - * - */ - - -goog.provide('goog.net.ChannelRequest'); -goog.provide('goog.net.ChannelRequest.Error'); - -goog.require('goog.Timer'); -goog.require('goog.async.Throttle'); -goog.require('goog.events'); -goog.require('goog.events.EventHandler'); -goog.require('goog.net.EventType'); -goog.require('goog.net.XmlHttp.ReadyState'); -goog.require('goog.object'); -goog.require('goog.userAgent'); - - - -/** - * Creates a ChannelRequest object which encapsulates a request to the server. - * A new ChannelRequest is created for each request to the server. - * - * @param {goog.net.BrowserChannel|goog.net.BrowserTestChannel} channel - * The BrowserChannel that owns this request. - * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for - * logging. - * @param {string=} opt_sessionId The session id for the channel. - * @param {string|number=} opt_requestId The request id for this request. - * @param {number=} opt_retryId The retry id for this request. - * @constructor - */ -goog.net.ChannelRequest = function(channel, channelDebug, opt_sessionId, - opt_requestId, opt_retryId) { - /** - * The BrowserChannel object that owns the request. - * @type {goog.net.BrowserChannel|goog.net.BrowserTestChannel} - * @private - */ - this.channel_ = channel; - - /** - * The channel debug to use for logging - * @type {goog.net.ChannelDebug} - * @private - */ - this.channelDebug_ = channelDebug; - - /** - * The Session ID for the channel. - * @type {string|undefined} - * @private - */ - this.sid_ = opt_sessionId; - - /** - * The RID (request ID) for the request. - * @type {string|number|undefined} - * @private - */ - this.rid_ = opt_requestId; - - - /** - * The attempt number of the current request. - * @type {number} - * @private - */ - this.retryId_ = opt_retryId || 1; - - - /** - * The timeout in ms before failing the request. - * @type {number} - * @private - */ - this.timeout_ = goog.net.ChannelRequest.TIMEOUT_MS; - - /** - * An object to keep track of the channel request event listeners. - * @type {!goog.events.EventHandler} - * @private - */ - this.eventHandler_ = new goog.events.EventHandler(this); - - /** - * A timer for polling responseText in browsers that don't fire - * onreadystatechange during incremental loading of responseText. - * @type {goog.Timer} - * @private - */ - this.pollingTimer_ = new goog.Timer(); - - this.pollingTimer_.setInterval(goog.net.ChannelRequest.POLLING_INTERVAL_MS); -}; - - -/** - * Extra HTTP headers to add to all the requests sent to the server. - * @type {Object} - * @private - */ -goog.net.ChannelRequest.prototype.extraHeaders_ = null; - - -/** - * Whether the request was successful. This is only set to true after the - * request successfuly completes. - * @type {boolean} - * @private - */ -goog.net.ChannelRequest.prototype.successful_ = false; - - -/** - * The TimerID of the timer used to detect if the request has timed-out. - * @type {?number} - * @private - */ -goog.net.ChannelRequest.prototype.watchDogTimerId_ = null; - - -/** - * The time in the future when the request will timeout. - * @type {?number} - * @private - */ -goog.net.ChannelRequest.prototype.watchDogTimeoutTime_ = null; - - -/** - * The time the request started. - * @type {?number} - * @private - */ -goog.net.ChannelRequest.prototype.requestStartTime_ = null; - - -/** - * The type of request (XMLHTTP, IMG, Trident) - * @type {?number} - * @private - */ -goog.net.ChannelRequest.prototype.type_ = null; - - -/** - * The base Uri for the request. The includes all the parameters except the - * one that indicates the retry number. - * @type {goog.Uri?} - * @private - */ -goog.net.ChannelRequest.prototype.baseUri_ = null; - - -/** - * The request Uri that was actually used for the most recent request attempt. - * @type {goog.Uri?} - * @private - */ -goog.net.ChannelRequest.prototype.requestUri_ = null; - - -/** - * The post data, if the request is a post. - * @type {?string} - * @private - */ -goog.net.ChannelRequest.prototype.postData_ = null; - - -/** - * The XhrLte request if the request is using XMLHTTP - * @type {goog.net.XhrIo} - * @private - */ -goog.net.ChannelRequest.prototype.xmlHttp_ = null; - - -/** - * The position of where the next unprocessed chunk starts in the response - * text. - * @type {number} - * @private - */ -goog.net.ChannelRequest.prototype.xmlHttpChunkStart_ = 0; - - -/** - * The Trident instance if the request is using Trident. - * @type {ActiveXObject} - * @private - */ -goog.net.ChannelRequest.prototype.trident_ = null; - - -/** - * The verb (Get or Post) for the request. - * @type {?string} - * @private - */ -goog.net.ChannelRequest.prototype.verb_ = null; - - -/** - * The last error if the request failed. - * @type {?goog.net.ChannelRequest.Error} - * @private - */ -goog.net.ChannelRequest.prototype.lastError_ = null; - - -/** - * The last status code received. - * @type {number} - * @private - */ -goog.net.ChannelRequest.prototype.lastStatusCode_ = -1; - - -/** - * Whether to send the Connection:close header as part of the request. - * @type {boolean} - * @private - */ -goog.net.ChannelRequest.prototype.sendClose_ = true; - - -/** - * Whether the request has been cancelled due to a call to cancel. - * @type {boolean} - * @private - */ -goog.net.ChannelRequest.prototype.cancelled_ = false; - - -/** - * A throttle time in ms for readystatechange events for the backchannel. - * Useful for throttling when ready state is INTERACTIVE (partial data). - * If set to zero no throttle is used. - * - * @see goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_ - * - * @type {number} - * @private - */ -goog.net.ChannelRequest.prototype.readyStateChangeThrottleMs_ = 0; - - -/** - * The throttle for readystatechange events for the current request, or null - * if there is none. - * @type {goog.async.Throttle} - * @private - */ -goog.net.ChannelRequest.prototype.readyStateChangeThrottle_ = null; - - -/** - * Default timeout in MS for a request. The server must return data within this - * time limit for the request to not timeout. - * @type {number} - */ -goog.net.ChannelRequest.TIMEOUT_MS = 45 * 1000; - - -/** - * How often to poll (in MS) for changes to responseText in browsers that don't - * fire onreadystatechange during incremental loading of responseText. - * @type {number} - */ -goog.net.ChannelRequest.POLLING_INTERVAL_MS = 250; - - -/** - * Minimum version of Safari that receives a non-null responseText in ready - * state interactive. - * @type {string} - * @private - */ -goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_ = '420+'; - - -/** - * Enum for channel requests type - * @enum {number} - * @private - */ -goog.net.ChannelRequest.Type_ = { - /** - * XMLHTTP requests. - */ - XML_HTTP: 1, - - /** - * IMG requests. - */ - IMG: 2, - - /** - * Requests that use the MSHTML ActiveX control. - */ - TRIDENT: 3 -}; - - -/** - * Enum type for identifying a ChannelRequest error. - * @enum {number} - */ -goog.net.ChannelRequest.Error = { - /** - * Errors due to a non-200 status code. - */ - STATUS: 0, - - /** - * Errors due to no data being returned. - */ - NO_DATA: 1, - - /** - * Errors due to a timeout. - */ - TIMEOUT: 2, - - /** - * Errors due to the server returning an unknown. - */ - UNKNOWN_SESSION_ID: 3, - - /** - * Errors due to bad data being received. - */ - BAD_DATA: 4, - - /** - * Errors due to the handler throwing an exception. - */ - HANDLER_EXCEPTION: 5, - - /** - * The browser declared itself offline during the request. - */ - BROWSER_OFFLINE: 6, - - /** - * IE is blocking ActiveX streaming. - */ - ACTIVE_X_BLOCKED: 7 -}; - - -/** - * Returns a useful error string for debugging based on the specified error - * code. - * @param {goog.net.ChannelRequest.Error} errorCode The error code. - * @param {number} statusCode The HTTP status code. - * @return {string} The error string for the given code combination. - */ -goog.net.ChannelRequest.errorStringFromCode = function(errorCode, statusCode) { - switch (errorCode) { - case goog.net.ChannelRequest.Error.STATUS: - return 'Non-200 return code (' + statusCode + ')'; - case goog.net.ChannelRequest.Error.NO_DATA: - return 'XMLHTTP failure (no data)'; - case goog.net.ChannelRequest.Error.TIMEOUT: - return 'HttpConnection timeout'; - default: - return 'Unknown error'; - } -}; - - -/** - * Sentinel value used to indicate an invalid chunk in a multi-chunk response. - * @type {Object} - * @private - */ -goog.net.ChannelRequest.INVALID_CHUNK_ = {}; - - -/** - * Sentinel value used to indicate an incomplete chunk in a multi-chunk - * response. - * @type {Object} - * @private - */ -goog.net.ChannelRequest.INCOMPLETE_CHUNK_ = {}; - - -/** - * Returns whether XHR streaming is supported on this browser. - * - * If XHR streaming is not supported, we will try to use an ActiveXObject - * to create a Forever IFrame. - * - * @return {boolean} Whether XHR streaming is supported. - * @see http://code.google.com/p/closure-library/issues/detail?id=346 - */ -goog.net.ChannelRequest.supportsXhrStreaming = function() { - return !goog.userAgent.IE || goog.userAgent.isDocumentMode(10); -}; - - -/** - * Sets extra HTTP headers to add to all the requests sent to the server. - * - * @param {Object} extraHeaders The HTTP headers. - */ -goog.net.ChannelRequest.prototype.setExtraHeaders = function(extraHeaders) { - this.extraHeaders_ = extraHeaders; -}; - - -/** - * Sets the timeout for a request - * - * @param {number} timeout The timeout in MS for when we fail the request. - */ -goog.net.ChannelRequest.prototype.setTimeout = function(timeout) { - this.timeout_ = timeout; -}; - - -/** - * Sets the throttle for handling onreadystatechange events for the request. - * - * @param {number} throttle The throttle in ms. A value of zero indicates - * no throttle. - */ -goog.net.ChannelRequest.prototype.setReadyStateChangeThrottle = function( - throttle) { - this.readyStateChangeThrottleMs_ = throttle; -}; - - -/** - * Uses XMLHTTP to send an HTTP POST to the server. - * - * @param {goog.Uri} uri The uri of the request. - * @param {string} postData The data for the post body. - * @param {boolean} decodeChunks Whether to the result is expected to be - * encoded for chunking and thus requires decoding. - */ -goog.net.ChannelRequest.prototype.xmlHttpPost = function(uri, postData, - decodeChunks) { - this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP; - this.baseUri_ = uri.clone().makeUnique(); - this.postData_ = postData; - this.decodeChunks_ = decodeChunks; - this.sendXmlHttp_(null /* hostPrefix */); -}; - - -/** - * Uses XMLHTTP to send an HTTP GET to the server. - * - * @param {goog.Uri} uri The uri of the request. - * @param {boolean} decodeChunks Whether to the result is expected to be - * encoded for chunking and thus requires decoding. - * @param {?string} hostPrefix The host prefix, if we might be using a - * secondary domain. Note that it should also be in the URL, adding this - * won't cause it to be added to the URL. - * @param {boolean=} opt_noClose Whether to request that the tcp/ip connection - * should be closed. - */ -goog.net.ChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks, - hostPrefix, opt_noClose) { - this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP; - this.baseUri_ = uri.clone().makeUnique(); - this.postData_ = null; - this.decodeChunks_ = decodeChunks; - if (opt_noClose) { - this.sendClose_ = false; - } - this.sendXmlHttp_(hostPrefix); -}; - - -/** - * Sends a request via XMLHTTP according to the current state of the - * ChannelRequest object. - * - * @param {?string} hostPrefix The host prefix, if we might be using a secondary - * domain. - * @private - */ -goog.net.ChannelRequest.prototype.sendXmlHttp_ = function(hostPrefix) { - this.requestStartTime_ = goog.now(); - this.ensureWatchDogTimer_(); - - // clone the base URI to create the request URI. The request uri has the - // attempt number as a parameter which helps in debugging. - this.requestUri_ = this.baseUri_.clone(); - this.requestUri_.setParameterValues('t', this.retryId_); - - // send the request either as a POST or GET - this.xmlHttpChunkStart_ = 0; - var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains(); - this.xmlHttp_ = this.channel_.createXhrIo(useSecondaryDomains ? - hostPrefix : null); - - if (this.readyStateChangeThrottleMs_ > 0) { - this.readyStateChangeThrottle_ = new goog.async.Throttle( - goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_), - this.readyStateChangeThrottleMs_); - } - - this.eventHandler_.listen(this.xmlHttp_, - goog.net.EventType.READY_STATE_CHANGE, - this.readyStateChangeHandler_); - - var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {}; - if (this.postData_) { - // todo (jonp) - use POST constant when Dan defines it - this.verb_ = 'POST'; - headers['Content-Type'] = 'application/x-www-form-urlencoded'; - this.xmlHttp_.send(this.requestUri_, this.verb_, this.postData_, headers); - } else { - // todo (jonp) - use GET constant when Dan defines it - this.verb_ = 'GET'; - - // If the user agent is webkit, we cannot send the close header since it is - // disallowed by the browser. If we attempt to set the "Connection: close" - // header in WEBKIT browser, it will actually causes an error message. - if (this.sendClose_ && !goog.userAgent.WEBKIT) { - headers['Connection'] = 'close'; - } - this.xmlHttp_.send(this.requestUri_, this.verb_, null, headers); - } - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.REQUEST_MADE); - this.channelDebug_.xmlHttpChannelRequest(this.verb_, - this.requestUri_, this.rid_, this.retryId_, - this.postData_); -}; - - -/** - * Handles a readystatechange event. - * @param {goog.events.Event} evt The event. - * @private - */ -goog.net.ChannelRequest.prototype.readyStateChangeHandler_ = function(evt) { - var xhr = /** @type {goog.net.XhrIo} */ (evt.target); - var throttle = this.readyStateChangeThrottle_; - if (throttle && - xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) { - // Only throttle in the partial data case. - this.channelDebug_.debug('Throttling readystatechange.'); - throttle.fire(); - } else { - // If we haven't throttled, just handle response directly. - this.xmlHttpHandler_(xhr); - } -}; - - -/** - * XmlHttp handler - * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request. - * @private - */ -goog.net.ChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) { - goog.net.BrowserChannel.onStartExecution(); - /** @preserveTry */ - try { - if (xmlhttp == this.xmlHttp_) { - this.onXmlHttpReadyStateChanged_(); - } else { - this.channelDebug_.warning('Called back with an ' + - 'unexpected xmlhttp'); - } - } catch (ex) { - this.channelDebug_.debug('Failed call to OnXmlHttpReadyStateChanged_'); - if (this.xmlHttp_ && this.xmlHttp_.getResponseText()) { - this.channelDebug_.dumpException(ex, - 'ResponseText: ' + this.xmlHttp_.getResponseText()); - } else { - this.channelDebug_.dumpException(ex, 'No response text'); - } - } finally { - goog.net.BrowserChannel.onEndExecution(); - } -}; - - -/** - * Called by the readystate handler for XMLHTTP requests. - * - * @private - */ -goog.net.ChannelRequest.prototype.onXmlHttpReadyStateChanged_ = function() { - var readyState = this.xmlHttp_.getReadyState(); - var errorCode = this.xmlHttp_.getLastErrorCode(); - var statusCode = this.xmlHttp_.getStatus(); - // If it is Safari less than 420+, there is a bug that causes null to be - // in the responseText on ready state interactive so we must wait for - // ready state complete. - if (!goog.net.ChannelRequest.supportsXhrStreaming() || - (goog.userAgent.WEBKIT && - !goog.userAgent.isVersion( - goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_))) { - if (readyState < goog.net.XmlHttp.ReadyState.COMPLETE) { - // not yet ready - return; - } - } else { - // we get partial results in browsers that support ready state interactive. - // We also make sure that getResponseText is not null in interactive mode - // before we continue. However, we don't do it in Opera because it only - // fire readyState == INTERACTIVE once. We need the following code to poll - if (readyState < goog.net.XmlHttp.ReadyState.INTERACTIVE || - readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE && - !goog.userAgent.OPERA && !this.xmlHttp_.getResponseText()) { - // not yet ready - return; - } - } - - // Dispatch any appropriate network events. - if (!this.cancelled_ && readyState == goog.net.XmlHttp.ReadyState.COMPLETE && - errorCode != goog.net.ErrorCode.ABORT) { - - // Pretty conservative, these are the only known scenarios which we'd - // consider indicative of a truly non-functional network connection. - if (errorCode == goog.net.ErrorCode.TIMEOUT || - statusCode <= 0) { - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED); - } else { - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.REQUEST_SUCCEEDED); - } - } - - // got some data so cancel the watchdog timer - this.cancelWatchDogTimer_(); - - var status = this.xmlHttp_.getStatus(); - this.lastStatusCode_ = status; - var responseText = this.xmlHttp_.getResponseText(); - if (!responseText) { - this.channelDebug_.debug('No response text for uri ' + - this.requestUri_ + ' status ' + status); - } - this.successful_ = (status == 200); - - this.channelDebug_.xmlHttpChannelResponseMetaData( - /** @type {string} */ (this.verb_), - this.requestUri_, this.rid_, this.retryId_, readyState, - status); - - if (!this.successful_) { - if (status == 400 && - responseText.indexOf('Unknown SID') > 0) { - // the server error string will include 'Unknown SID' which indicates the - // server doesn't know about the session (maybe it got restarted, maybe - // the user got moved to another server, etc.,). Handlers can special - // case this error - this.lastError_ = goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_UNKNOWN_SESSION_ID); - } else { - this.lastError_ = goog.net.ChannelRequest.Error.STATUS; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_BAD_STATUS); - } - this.channelDebug_.xmlHttpChannelResponseText(this.rid_, responseText); - this.cleanup_(); - this.dispatchFailure_(); - return; - } - - if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) { - this.cleanup_(); - } - - if (this.decodeChunks_) { - this.decodeNextChunks_(readyState, responseText); - if (goog.userAgent.OPERA && - readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE) { - this.startPolling_(); - } - } else { - this.channelDebug_.xmlHttpChannelResponseText( - this.rid_, responseText, null); - this.safeOnRequestData_(responseText); - } - - if (!this.successful_) { - return; - } - - if (!this.cancelled_) { - if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) { - this.channel_.onRequestComplete(this); - } else { - // The default is false, the result from this callback shouldn't carry - // over to the next callback, otherwise the request looks successful if - // the watchdog timer gets called - this.successful_ = false; - this.ensureWatchDogTimer_(); - } - } -}; - - -/** - * Decodes the next set of available chunks in the response. - * @param {number} readyState The value of readyState. - * @param {string} responseText The value of responseText. - * @private - */ -goog.net.ChannelRequest.prototype.decodeNextChunks_ = function(readyState, - responseText) { - var decodeNextChunksSuccessful = true; - while (!this.cancelled_ && - this.xmlHttpChunkStart_ < responseText.length) { - var chunkText = this.getNextChunk_(responseText); - if (chunkText == goog.net.ChannelRequest.INCOMPLETE_CHUNK_) { - if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) { - // should have consumed entire response when the request is done - this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_INCOMPLETE_DATA); - decodeNextChunksSuccessful = false; - } - this.channelDebug_.xmlHttpChannelResponseText( - this.rid_, null, '[Incomplete Response]'); - break; - } else if (chunkText == goog.net.ChannelRequest.INVALID_CHUNK_) { - this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_BAD_DATA); - this.channelDebug_.xmlHttpChannelResponseText( - this.rid_, responseText, '[Invalid Chunk]'); - decodeNextChunksSuccessful = false; - break; - } else { - this.channelDebug_.xmlHttpChannelResponseText( - this.rid_, /** @type {string} */ (chunkText), null); - this.safeOnRequestData_(/** @type {string} */ (chunkText)); - } - } - if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE && - responseText.length == 0) { - // also an error if we didn't get any response - this.lastError_ = goog.net.ChannelRequest.Error.NO_DATA; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_NO_DATA); - decodeNextChunksSuccessful = false; - } - this.successful_ = this.successful_ && decodeNextChunksSuccessful; - if (!decodeNextChunksSuccessful) { - // malformed response - we make this trigger retry logic - this.channelDebug_.xmlHttpChannelResponseText( - this.rid_, responseText, '[Invalid Chunked Response]'); - this.cleanup_(); - this.dispatchFailure_(); - } -}; - - -/** - * Polls the response for new data. - * @private - */ -goog.net.ChannelRequest.prototype.pollResponse_ = function() { - var readyState = this.xmlHttp_.getReadyState(); - var responseText = this.xmlHttp_.getResponseText(); - if (this.xmlHttpChunkStart_ < responseText.length) { - this.cancelWatchDogTimer_(); - this.decodeNextChunks_(readyState, responseText); - if (this.successful_ && - readyState != goog.net.XmlHttp.ReadyState.COMPLETE) { - this.ensureWatchDogTimer_(); - } - } -}; - - -/** - * Starts a polling interval for changes to responseText of the - * XMLHttpRequest, for browsers that don't fire onreadystatechange - * as data comes in incrementally. This timer is disabled in - * cleanup_(). - * @private - */ -goog.net.ChannelRequest.prototype.startPolling_ = function() { - this.eventHandler_.listen(this.pollingTimer_, goog.Timer.TICK, - this.pollResponse_); - this.pollingTimer_.start(); -}; - - -/** - * Called when the browser declares itself offline at the start of a request or - * during its lifetime. Abandons that request. - * @private - */ -goog.net.ChannelRequest.prototype.cancelRequestAsBrowserIsOffline_ = - function() { - if (this.successful_) { - // Should never happen. - this.channelDebug_.severe( - 'Received browser offline event even though request completed ' + - 'successfully'); - } - - this.channelDebug_.browserOfflineResponse(this.requestUri_); - this.cleanup_(); - - // set error and dispatch failure - this.lastError_ = goog.net.ChannelRequest.Error.BROWSER_OFFLINE; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.BROWSER_OFFLINE); - this.dispatchFailure_(); -}; - - -/** - * Returns the next chunk of a chunk-encoded response. This is not standard - * HTTP chunked encoding because browsers don't expose the chunk boundaries to - * the application through XMLHTTP. So we have an additional chunk encoding at - * the application level that lets us tell where the beginning and end of - * individual responses are so that we can only try to eval a complete JS array. - * - * The encoding is the size of the chunk encoded as a decimal string followed - * by a newline followed by the data. - * - * @param {string} responseText The response text from the XMLHTTP response. - * @return {string|Object} The next chunk string or a sentinel object - * indicating a special condition. - * @private - */ -goog.net.ChannelRequest.prototype.getNextChunk_ = function(responseText) { - var sizeStartIndex = this.xmlHttpChunkStart_; - var sizeEndIndex = responseText.indexOf('\n', sizeStartIndex); - if (sizeEndIndex == -1) { - return goog.net.ChannelRequest.INCOMPLETE_CHUNK_; - } - - var sizeAsString = responseText.substring(sizeStartIndex, sizeEndIndex); - var size = Number(sizeAsString); - if (isNaN(size)) { - return goog.net.ChannelRequest.INVALID_CHUNK_; - } - - var chunkStartIndex = sizeEndIndex + 1; - if (chunkStartIndex + size > responseText.length) { - return goog.net.ChannelRequest.INCOMPLETE_CHUNK_; - } - - var chunkText = responseText.substr(chunkStartIndex, size); - this.xmlHttpChunkStart_ = chunkStartIndex + size; - return chunkText; -}; - - -/** - * Uses the Trident htmlfile ActiveX control to send a GET request in IE. This - * is the innovation discovered that lets us get intermediate results in - * Internet Explorer. Thanks to http://go/kev - * @param {goog.Uri} uri The uri to request from. - * @param {boolean} usingSecondaryDomain Whether to use a secondary domain. - */ -goog.net.ChannelRequest.prototype.tridentGet = function(uri, - usingSecondaryDomain) { - this.type_ = goog.net.ChannelRequest.Type_.TRIDENT; - this.baseUri_ = uri.clone().makeUnique(); - this.tridentGet_(usingSecondaryDomain); -}; - - -/** - * Starts the Trident request. - * @param {boolean} usingSecondaryDomain Whether to use a secondary domain. - * @private - */ -goog.net.ChannelRequest.prototype.tridentGet_ = function(usingSecondaryDomain) { - this.requestStartTime_ = goog.now(); - this.ensureWatchDogTimer_(); - - var hostname = usingSecondaryDomain ? window.location.hostname : ''; - this.requestUri_ = this.baseUri_.clone(); - this.requestUri_.setParameterValue('DOMAIN', hostname); - this.requestUri_.setParameterValue('t', this.retryId_); - - try { - this.trident_ = new ActiveXObject('htmlfile'); - } catch (e) { - this.channelDebug_.severe('ActiveX blocked'); - this.cleanup_(); - - this.lastError_ = goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.ACTIVE_X_BLOCKED); - this.dispatchFailure_(); - return; - } - - var body = '<html><body>'; - if (usingSecondaryDomain) { - body += '<script>document.domain="' + hostname + '"</scr' + 'ipt>'; - } - body += '</body></html>'; - - this.trident_.open(); - this.trident_.write(body); - this.trident_.close(); - - this.trident_.parentWindow['m'] = goog.bind(this.onTridentRpcMessage_, this); - this.trident_.parentWindow['d'] = goog.bind(this.onTridentDone_, this, true); - this.trident_.parentWindow['rpcClose'] = - goog.bind(this.onTridentDone_, this, false); - - var div = this.trident_.createElement('div'); - this.trident_.parentWindow.document.body.appendChild(div); - div.innerHTML = '<iframe src="' + this.requestUri_ + '"></iframe>'; - this.channelDebug_.tridentChannelRequest('GET', - this.requestUri_, this.rid_, this.retryId_); - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.REQUEST_MADE); -}; - - -/** - * Callback from the Trident htmlfile ActiveX control for when a new message - * is received. - * - * @param {string} msg The data payload. - * @private - */ -goog.net.ChannelRequest.prototype.onTridentRpcMessage_ = function(msg) { - // need to do async b/c this gets called off of the context of the ActiveX - goog.net.BrowserChannel.setTimeout( - goog.bind(this.onTridentRpcMessageAsync_, this, msg), 0); -}; - - -/** - * Callback from the Trident htmlfile ActiveX control for when a new message - * is received. - * - * @param {string} msg The data payload. - * @private - */ -goog.net.ChannelRequest.prototype.onTridentRpcMessageAsync_ = function(msg) { - if (this.cancelled_) { - return; - } - this.channelDebug_.tridentChannelResponseText(this.rid_, msg); - this.cancelWatchDogTimer_(); - this.safeOnRequestData_(msg); - this.ensureWatchDogTimer_(); -}; - - -/** - * Callback from the Trident htmlfile ActiveX control for when the request - * is complete - * - * @param {boolean} successful Whether the request successfully completed. - * @private - */ -goog.net.ChannelRequest.prototype.onTridentDone_ = function(successful) { - // need to do async b/c this gets called off of the context of the ActiveX - goog.net.BrowserChannel.setTimeout( - goog.bind(this.onTridentDoneAsync_, this, successful), 0); -}; - - -/** - * Callback from the Trident htmlfile ActiveX control for when the request - * is complete - * - * @param {boolean} successful Whether the request successfully completed. - * @private - */ -goog.net.ChannelRequest.prototype.onTridentDoneAsync_ = function(successful) { - if (this.cancelled_) { - return; - } - this.channelDebug_.tridentChannelResponseDone( - this.rid_, successful); - this.cleanup_(); - this.successful_ = successful; - this.channel_.onRequestComplete(this); - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY); -}; - - -/** - * Uses an IMG tag to send an HTTP get to the server. This is only currently - * used to terminate the connection, as an IMG tag is the most reliable way to - * send something to the server while the page is getting torn down. - * @param {goog.Uri} uri The uri to send a request to. - */ -goog.net.ChannelRequest.prototype.sendUsingImgTag = function(uri) { - this.type_ = goog.net.ChannelRequest.Type_.IMG; - this.baseUri_ = uri.clone().makeUnique(); - this.imgTagGet_(); -}; - - -/** - * Starts the IMG request. - * - * @private - */ -goog.net.ChannelRequest.prototype.imgTagGet_ = function() { - var eltImg = new Image(); - eltImg.src = this.baseUri_; - this.requestStartTime_ = goog.now(); - this.ensureWatchDogTimer_(); -}; - - -/** - * Cancels the request no matter what the underlying transport is. - */ -goog.net.ChannelRequest.prototype.cancel = function() { - this.cancelled_ = true; - this.cleanup_(); -}; - - -/** - * Ensures that there is watchdog timeout which is used to ensure that - * the connection completes in time. - * - * @private - */ -goog.net.ChannelRequest.prototype.ensureWatchDogTimer_ = function() { - this.watchDogTimeoutTime_ = goog.now() + this.timeout_; - this.startWatchDogTimer_(this.timeout_); -}; - - -/** - * Starts the watchdog timer which is used to ensure that the connection - * completes in time. - * @param {number} time The number of milliseconds to wait. - * @private - */ -goog.net.ChannelRequest.prototype.startWatchDogTimer_ = function(time) { - if (this.watchDogTimerId_ != null) { - // assertion - throw Error('WatchDog timer not null'); - } - this.watchDogTimerId_ = goog.net.BrowserChannel.setTimeout( - goog.bind(this.onWatchDogTimeout_, this), time); -}; - - -/** - * Cancels the watchdog timer if it has been started. - * - * @private - */ -goog.net.ChannelRequest.prototype.cancelWatchDogTimer_ = function() { - if (this.watchDogTimerId_) { - goog.global.clearTimeout(this.watchDogTimerId_); - this.watchDogTimerId_ = null; - } -}; - - -/** - * Called when the watchdog timer is triggered. It also handles a case where it - * is called too early which we suspect may be happening sometimes - * (not sure why) - * - * @private - */ -goog.net.ChannelRequest.prototype.onWatchDogTimeout_ = function() { - this.watchDogTimerId_ = null; - var now = goog.now(); - if (now - this.watchDogTimeoutTime_ >= 0) { - this.handleTimeout_(); - } else { - // got called too early for some reason - this.channelDebug_.warning('WatchDog timer called too early'); - this.startWatchDogTimer_(this.watchDogTimeoutTime_ - now); - } -}; - - -/** - * Called when the request has actually timed out. Will cleanup and notify the - * channel of the failure. - * - * @private - */ -goog.net.ChannelRequest.prototype.handleTimeout_ = function() { - if (this.successful_) { - // Should never happen. - this.channelDebug_.severe( - 'Received watchdog timeout even though request loaded successfully'); - } - - this.channelDebug_.timeoutResponse(this.requestUri_); - // IMG requests never notice if they were successful, and always 'time out'. - // This fact says nothing about reachability. - if (this.type_ != goog.net.ChannelRequest.Type_.IMG) { - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED); - } - this.cleanup_(); - - // set error and dispatch failure - this.lastError_ = goog.net.ChannelRequest.Error.TIMEOUT; - goog.net.BrowserChannel.notifyStatEvent( - goog.net.BrowserChannel.Stat.REQUEST_TIMEOUT); - this.dispatchFailure_(); -}; - - -/** - * Notifies the channel that this request failed. - * @private - */ -goog.net.ChannelRequest.prototype.dispatchFailure_ = function() { - if (this.channel_.isClosed() || this.cancelled_) { - return; - } - - this.channel_.onRequestComplete(this); -}; - - -/** - * Cleans up the objects used to make the request. This function is - * idempotent. - * - * @private - */ -goog.net.ChannelRequest.prototype.cleanup_ = function() { - this.cancelWatchDogTimer_(); - - goog.dispose(this.readyStateChangeThrottle_); - this.readyStateChangeThrottle_ = null; - - // Stop the polling timer, if necessary. - this.pollingTimer_.stop(); - - // Unhook all event handlers. - this.eventHandler_.removeAll(); - - if (this.xmlHttp_) { - // clear out this.xmlHttp_ before aborting so we handle getting reentered - // inside abort - var xmlhttp = this.xmlHttp_; - this.xmlHttp_ = null; - xmlhttp.abort(); - xmlhttp.dispose(); - } - - if (this.trident_) { - this.trident_ = null; - } -}; - - -/** - * Indicates whether the request was successful. Only valid after the handler - * is called to indicate completion of the request. - * - * @return {boolean} True if the request succeeded. - */ -goog.net.ChannelRequest.prototype.getSuccess = function() { - return this.successful_; -}; - - -/** - * If the request was not successful, returns the reason. - * - * @return {?goog.net.ChannelRequest.Error} The last error. - */ -goog.net.ChannelRequest.prototype.getLastError = function() { - return this.lastError_; -}; - - -/** - * Returns the status code of the last request. - * @return {number} The status code of the last request. - */ -goog.net.ChannelRequest.prototype.getLastStatusCode = function() { - return this.lastStatusCode_; -}; - - -/** - * Returns the session id for this channel. - * - * @return {string|undefined} The session ID. - */ -goog.net.ChannelRequest.prototype.getSessionId = function() { - return this.sid_; -}; - - -/** - * Returns the request id for this request. Each request has a unique request - * id and the request IDs are a sequential increasing count. - * - * @return {string|number|undefined} The request ID. - */ -goog.net.ChannelRequest.prototype.getRequestId = function() { - return this.rid_; -}; - - -/** - * Returns the data for a post, if this request is a post. - * - * @return {?string} The POST data provided by the request initiator. - */ -goog.net.ChannelRequest.prototype.getPostData = function() { - return this.postData_; -}; - - -/** - * Returns the time that the request started, if it has started. - * - * @return {?number} The time the request started, as returned by goog.now(). - */ -goog.net.ChannelRequest.prototype.getRequestStartTime = function() { - return this.requestStartTime_; -}; - - -/** - * Helper to call the callback's onRequestData, which catches any - * exception and cleans up the request. - * @param {string} data The request data. - * @private - */ -goog.net.ChannelRequest.prototype.safeOnRequestData_ = function(data) { - /** @preserveTry */ - try { - this.channel_.onRequestData(this, data); - this.channel_.notifyServerReachabilityEvent( - goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY); - } catch (e) { - // Dump debug info, but keep going without closing the channel. - this.channelDebug_.dumpException( - e, 'Error in httprequest callback'); - } -}; |