channelrequest.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Definition of the ChannelRequest class. The ChannelRequest
  16. * object encapsulates the logic for making a single request, either for the
  17. * forward channel, back channel, or test channel, to the server. It contains
  18. * the logic for the three types of transports we use in the BrowserChannel:
  19. * XMLHTTP, Trident ActiveX (ie only), and Image request. It provides timeout
  20. * detection. This class is part of the BrowserChannel implementation and is not
  21. * for use by normal application code.
  22. *
  23. */
  24. goog.provide('goog.net.ChannelRequest');
  25. goog.provide('goog.net.ChannelRequest.Error');
  26. goog.require('goog.Timer');
  27. goog.require('goog.async.Throttle');
  28. goog.require('goog.dom.TagName');
  29. goog.require('goog.dom.safe');
  30. goog.require('goog.events.EventHandler');
  31. goog.require('goog.html.SafeUrl');
  32. goog.require('goog.html.uncheckedconversions');
  33. goog.require('goog.net.ErrorCode');
  34. goog.require('goog.net.EventType');
  35. goog.require('goog.net.XmlHttp');
  36. goog.require('goog.object');
  37. goog.require('goog.string');
  38. goog.require('goog.string.Const');
  39. goog.require('goog.userAgent');
  40. // TODO(nnaze): This file depends on goog.net.BrowserChannel and vice versa (a
  41. // circular dependency). Usages of BrowserChannel are marked as
  42. // "missingRequire" below for now. This should be fixed through refactoring.
  43. /**
  44. * Creates a ChannelRequest object which encapsulates a request to the server.
  45. * A new ChannelRequest is created for each request to the server.
  46. *
  47. * @param {goog.net.BrowserChannel|goog.net.BrowserTestChannel} channel
  48. * The BrowserChannel that owns this request.
  49. * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for
  50. * logging.
  51. * @param {string=} opt_sessionId The session id for the channel.
  52. * @param {string|number=} opt_requestId The request id for this request.
  53. * @param {number=} opt_retryId The retry id for this request.
  54. * @constructor
  55. */
  56. goog.net.ChannelRequest = function(
  57. channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId) {
  58. /**
  59. * The BrowserChannel object that owns the request.
  60. * @type {goog.net.BrowserChannel|goog.net.BrowserTestChannel}
  61. * @private
  62. */
  63. this.channel_ = channel;
  64. /**
  65. * The channel debug to use for logging
  66. * @type {goog.net.ChannelDebug}
  67. * @private
  68. */
  69. this.channelDebug_ = channelDebug;
  70. /**
  71. * The Session ID for the channel.
  72. * @type {string|undefined}
  73. * @private
  74. */
  75. this.sid_ = opt_sessionId;
  76. /**
  77. * The RID (request ID) for the request.
  78. * @type {string|number|undefined}
  79. * @private
  80. */
  81. this.rid_ = opt_requestId;
  82. /**
  83. * The attempt number of the current request.
  84. * @type {number}
  85. * @private
  86. */
  87. this.retryId_ = opt_retryId || 1;
  88. /**
  89. * The timeout in ms before failing the request.
  90. * @type {number}
  91. * @private
  92. */
  93. this.timeout_ = goog.net.ChannelRequest.TIMEOUT_MS;
  94. /**
  95. * An object to keep track of the channel request event listeners.
  96. * @type {!goog.events.EventHandler<!goog.net.ChannelRequest>}
  97. * @private
  98. */
  99. this.eventHandler_ = new goog.events.EventHandler(this);
  100. /**
  101. * A timer for polling responseText in browsers that don't fire
  102. * onreadystatechange during incremental loading of responseText.
  103. * @type {goog.Timer}
  104. * @private
  105. */
  106. this.pollingTimer_ = new goog.Timer();
  107. this.pollingTimer_.setInterval(goog.net.ChannelRequest.POLLING_INTERVAL_MS);
  108. };
  109. /**
  110. * Extra HTTP headers to add to all the requests sent to the server.
  111. * @type {Object}
  112. * @private
  113. */
  114. goog.net.ChannelRequest.prototype.extraHeaders_ = null;
  115. /**
  116. * Whether the request was successful. This is only set to true after the
  117. * request successfuly completes.
  118. * @type {boolean}
  119. * @private
  120. */
  121. goog.net.ChannelRequest.prototype.successful_ = false;
  122. /**
  123. * The TimerID of the timer used to detect if the request has timed-out.
  124. * @type {?number}
  125. * @private
  126. */
  127. goog.net.ChannelRequest.prototype.watchDogTimerId_ = null;
  128. /**
  129. * The time in the future when the request will timeout.
  130. * @type {?number}
  131. * @private
  132. */
  133. goog.net.ChannelRequest.prototype.watchDogTimeoutTime_ = null;
  134. /**
  135. * The time the request started.
  136. * @type {?number}
  137. * @private
  138. */
  139. goog.net.ChannelRequest.prototype.requestStartTime_ = null;
  140. /**
  141. * The type of request (XMLHTTP, IMG, Trident)
  142. * @type {?number}
  143. * @private
  144. */
  145. goog.net.ChannelRequest.prototype.type_ = null;
  146. /**
  147. * The base Uri for the request. The includes all the parameters except the
  148. * one that indicates the retry number.
  149. * @type {goog.Uri?}
  150. * @private
  151. */
  152. goog.net.ChannelRequest.prototype.baseUri_ = null;
  153. /**
  154. * The request Uri that was actually used for the most recent request attempt.
  155. * @type {goog.Uri?}
  156. * @private
  157. */
  158. goog.net.ChannelRequest.prototype.requestUri_ = null;
  159. /**
  160. * The post data, if the request is a post.
  161. * @type {?string}
  162. * @private
  163. */
  164. goog.net.ChannelRequest.prototype.postData_ = null;
  165. /**
  166. * The XhrLte request if the request is using XMLHTTP
  167. * @type {goog.net.XhrIo}
  168. * @private
  169. */
  170. goog.net.ChannelRequest.prototype.xmlHttp_ = null;
  171. /**
  172. * The position of where the next unprocessed chunk starts in the response
  173. * text.
  174. * @type {number}
  175. * @private
  176. */
  177. goog.net.ChannelRequest.prototype.xmlHttpChunkStart_ = 0;
  178. /**
  179. * The Trident instance if the request is using Trident.
  180. * @type {Object}
  181. * @private
  182. */
  183. goog.net.ChannelRequest.prototype.trident_ = null;
  184. /**
  185. * The verb (Get or Post) for the request.
  186. * @type {?string}
  187. * @private
  188. */
  189. goog.net.ChannelRequest.prototype.verb_ = null;
  190. /**
  191. * The last error if the request failed.
  192. * @type {?goog.net.ChannelRequest.Error}
  193. * @private
  194. */
  195. goog.net.ChannelRequest.prototype.lastError_ = null;
  196. /**
  197. * The last status code received.
  198. * @type {number}
  199. * @private
  200. */
  201. goog.net.ChannelRequest.prototype.lastStatusCode_ = -1;
  202. /**
  203. * Whether to send the Connection:close header as part of the request.
  204. * @type {boolean}
  205. * @private
  206. */
  207. goog.net.ChannelRequest.prototype.sendClose_ = true;
  208. /**
  209. * Whether the request has been cancelled due to a call to cancel.
  210. * @type {boolean}
  211. * @private
  212. */
  213. goog.net.ChannelRequest.prototype.cancelled_ = false;
  214. /**
  215. * A throttle time in ms for readystatechange events for the backchannel.
  216. * Useful for throttling when ready state is INTERACTIVE (partial data).
  217. * If set to zero no throttle is used.
  218. *
  219. * @see goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_
  220. *
  221. * @type {number}
  222. * @private
  223. */
  224. goog.net.ChannelRequest.prototype.readyStateChangeThrottleMs_ = 0;
  225. /**
  226. * The throttle for readystatechange events for the current request, or null
  227. * if there is none.
  228. * @type {goog.async.Throttle}
  229. * @private
  230. */
  231. goog.net.ChannelRequest.prototype.readyStateChangeThrottle_ = null;
  232. /**
  233. * Default timeout in MS for a request. The server must return data within this
  234. * time limit for the request to not timeout.
  235. * @type {number}
  236. */
  237. goog.net.ChannelRequest.TIMEOUT_MS = 45 * 1000;
  238. /**
  239. * How often to poll (in MS) for changes to responseText in browsers that don't
  240. * fire onreadystatechange during incremental loading of responseText.
  241. * @type {number}
  242. */
  243. goog.net.ChannelRequest.POLLING_INTERVAL_MS = 250;
  244. /**
  245. * Minimum version of Safari that receives a non-null responseText in ready
  246. * state interactive.
  247. * @type {string}
  248. * @private
  249. */
  250. goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_ = '420+';
  251. /**
  252. * Enum for channel requests type
  253. * @enum {number}
  254. * @private
  255. */
  256. goog.net.ChannelRequest.Type_ = {
  257. /**
  258. * XMLHTTP requests.
  259. */
  260. XML_HTTP: 1,
  261. /**
  262. * IMG requests.
  263. */
  264. IMG: 2,
  265. /**
  266. * Requests that use the MSHTML ActiveX control.
  267. */
  268. TRIDENT: 3
  269. };
  270. /**
  271. * Enum type for identifying a ChannelRequest error.
  272. * @enum {number}
  273. */
  274. goog.net.ChannelRequest.Error = {
  275. /**
  276. * Errors due to a non-200 status code.
  277. */
  278. STATUS: 0,
  279. /**
  280. * Errors due to no data being returned.
  281. */
  282. NO_DATA: 1,
  283. /**
  284. * Errors due to a timeout.
  285. */
  286. TIMEOUT: 2,
  287. /**
  288. * Errors due to the server returning an unknown.
  289. */
  290. UNKNOWN_SESSION_ID: 3,
  291. /**
  292. * Errors due to bad data being received.
  293. */
  294. BAD_DATA: 4,
  295. /**
  296. * Errors due to the handler throwing an exception.
  297. */
  298. HANDLER_EXCEPTION: 5,
  299. /**
  300. * The browser declared itself offline during the request.
  301. */
  302. BROWSER_OFFLINE: 6,
  303. /**
  304. * IE is blocking ActiveX streaming.
  305. */
  306. ACTIVE_X_BLOCKED: 7
  307. };
  308. /**
  309. * Returns a useful error string for debugging based on the specified error
  310. * code.
  311. * @param {goog.net.ChannelRequest.Error} errorCode The error code.
  312. * @param {number} statusCode The HTTP status code.
  313. * @return {string} The error string for the given code combination.
  314. */
  315. goog.net.ChannelRequest.errorStringFromCode = function(errorCode, statusCode) {
  316. switch (errorCode) {
  317. case goog.net.ChannelRequest.Error.STATUS:
  318. return 'Non-200 return code (' + statusCode + ')';
  319. case goog.net.ChannelRequest.Error.NO_DATA:
  320. return 'XMLHTTP failure (no data)';
  321. case goog.net.ChannelRequest.Error.TIMEOUT:
  322. return 'HttpConnection timeout';
  323. default:
  324. return 'Unknown error';
  325. }
  326. };
  327. /**
  328. * Sentinel value used to indicate an invalid chunk in a multi-chunk response.
  329. * @type {Object}
  330. * @private
  331. */
  332. goog.net.ChannelRequest.INVALID_CHUNK_ = {};
  333. /**
  334. * Sentinel value used to indicate an incomplete chunk in a multi-chunk
  335. * response.
  336. * @type {Object}
  337. * @private
  338. */
  339. goog.net.ChannelRequest.INCOMPLETE_CHUNK_ = {};
  340. /**
  341. * Returns whether XHR streaming is supported on this browser.
  342. *
  343. * If XHR streaming is not supported, we will try to use an ActiveXObject
  344. * to create a Forever IFrame.
  345. *
  346. * @return {boolean} Whether XHR streaming is supported.
  347. * @see http://code.google.com/p/closure-library/issues/detail?id=346
  348. */
  349. goog.net.ChannelRequest.supportsXhrStreaming = function() {
  350. return !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(10);
  351. };
  352. /**
  353. * Sets extra HTTP headers to add to all the requests sent to the server.
  354. *
  355. * @param {Object} extraHeaders The HTTP headers.
  356. */
  357. goog.net.ChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {
  358. this.extraHeaders_ = extraHeaders;
  359. };
  360. /**
  361. * Sets the timeout for a request
  362. *
  363. * @param {number} timeout The timeout in MS for when we fail the request.
  364. */
  365. goog.net.ChannelRequest.prototype.setTimeout = function(timeout) {
  366. this.timeout_ = timeout;
  367. };
  368. /**
  369. * Sets the throttle for handling onreadystatechange events for the request.
  370. *
  371. * @param {number} throttle The throttle in ms. A value of zero indicates
  372. * no throttle.
  373. */
  374. goog.net.ChannelRequest.prototype.setReadyStateChangeThrottle = function(
  375. throttle) {
  376. this.readyStateChangeThrottleMs_ = throttle;
  377. };
  378. /**
  379. * Uses XMLHTTP to send an HTTP POST to the server.
  380. *
  381. * @param {goog.Uri} uri The uri of the request.
  382. * @param {string} postData The data for the post body.
  383. * @param {boolean} decodeChunks Whether to the result is expected to be
  384. * encoded for chunking and thus requires decoding.
  385. */
  386. goog.net.ChannelRequest.prototype.xmlHttpPost = function(
  387. uri, postData, decodeChunks) {
  388. this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP;
  389. this.baseUri_ = uri.clone().makeUnique();
  390. this.postData_ = postData;
  391. this.decodeChunks_ = decodeChunks;
  392. this.sendXmlHttp_(null /* hostPrefix */);
  393. };
  394. /**
  395. * Uses XMLHTTP to send an HTTP GET to the server.
  396. *
  397. * @param {goog.Uri} uri The uri of the request.
  398. * @param {boolean} decodeChunks Whether to the result is expected to be
  399. * encoded for chunking and thus requires decoding.
  400. * @param {?string} hostPrefix The host prefix, if we might be using a
  401. * secondary domain. Note that it should also be in the URL, adding this
  402. * won't cause it to be added to the URL.
  403. * @param {boolean=} opt_noClose Whether to request that the tcp/ip connection
  404. * should be closed.
  405. */
  406. goog.net.ChannelRequest.prototype.xmlHttpGet = function(
  407. uri, decodeChunks, hostPrefix, opt_noClose) {
  408. this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP;
  409. this.baseUri_ = uri.clone().makeUnique();
  410. this.postData_ = null;
  411. this.decodeChunks_ = decodeChunks;
  412. if (opt_noClose) {
  413. this.sendClose_ = false;
  414. }
  415. this.sendXmlHttp_(hostPrefix);
  416. };
  417. /**
  418. * Sends a request via XMLHTTP according to the current state of the
  419. * ChannelRequest object.
  420. *
  421. * @param {?string} hostPrefix The host prefix, if we might be using a secondary
  422. * domain.
  423. * @private
  424. */
  425. goog.net.ChannelRequest.prototype.sendXmlHttp_ = function(hostPrefix) {
  426. this.requestStartTime_ = goog.now();
  427. this.ensureWatchDogTimer_();
  428. // clone the base URI to create the request URI. The request uri has the
  429. // attempt number as a parameter which helps in debugging.
  430. this.requestUri_ = this.baseUri_.clone();
  431. this.requestUri_.setParameterValues('t', this.retryId_);
  432. // send the request either as a POST or GET
  433. this.xmlHttpChunkStart_ = 0;
  434. var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains();
  435. this.xmlHttp_ =
  436. this.channel_.createXhrIo(useSecondaryDomains ? hostPrefix : null);
  437. if (this.readyStateChangeThrottleMs_ > 0) {
  438. this.readyStateChangeThrottle_ = new goog.async.Throttle(
  439. goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_),
  440. this.readyStateChangeThrottleMs_);
  441. }
  442. this.eventHandler_.listen(
  443. this.xmlHttp_, goog.net.EventType.READY_STATE_CHANGE,
  444. this.readyStateChangeHandler_);
  445. var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {};
  446. if (this.postData_) {
  447. // todo (jonp) - use POST constant when Dan defines it
  448. this.verb_ = 'POST';
  449. headers['Content-Type'] = 'application/x-www-form-urlencoded';
  450. this.xmlHttp_.send(this.requestUri_, this.verb_, this.postData_, headers);
  451. } else {
  452. // todo (jonp) - use GET constant when Dan defines it
  453. this.verb_ = 'GET';
  454. // If the user agent is webkit, we cannot send the close header since it is
  455. // disallowed by the browser. If we attempt to set the "Connection: close"
  456. // header in WEBKIT browser, it will actually causes an error message.
  457. if (this.sendClose_ && !goog.userAgent.WEBKIT) {
  458. headers['Connection'] = 'close';
  459. }
  460. this.xmlHttp_.send(this.requestUri_, this.verb_, null, headers);
  461. }
  462. this.channel_.notifyServerReachabilityEvent(
  463. /** @suppress {missingRequire} */ (
  464. goog.net.BrowserChannel.ServerReachability.REQUEST_MADE));
  465. this.channelDebug_.xmlHttpChannelRequest(
  466. this.verb_, this.requestUri_, this.rid_, this.retryId_, this.postData_);
  467. };
  468. /**
  469. * Handles a readystatechange event.
  470. * @param {goog.events.Event} evt The event.
  471. * @private
  472. */
  473. goog.net.ChannelRequest.prototype.readyStateChangeHandler_ = function(evt) {
  474. var xhr = /** @type {goog.net.XhrIo} */ (evt.target);
  475. var throttle = this.readyStateChangeThrottle_;
  476. if (throttle &&
  477. xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
  478. // Only throttle in the partial data case.
  479. this.channelDebug_.debug('Throttling readystatechange.');
  480. throttle.fire();
  481. } else {
  482. // If we haven't throttled, just handle response directly.
  483. this.xmlHttpHandler_(xhr);
  484. }
  485. };
  486. /**
  487. * XmlHttp handler
  488. * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request.
  489. * @private
  490. */
  491. goog.net.ChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) {
  492. /** @suppress {missingRequire} */
  493. goog.net.BrowserChannel.onStartExecution();
  494. try {
  495. if (xmlhttp == this.xmlHttp_) {
  496. this.onXmlHttpReadyStateChanged_();
  497. } else {
  498. this.channelDebug_.warning(
  499. 'Called back with an ' +
  500. 'unexpected xmlhttp');
  501. }
  502. } catch (ex) {
  503. this.channelDebug_.debug('Failed call to OnXmlHttpReadyStateChanged_');
  504. if (this.xmlHttp_ && this.xmlHttp_.getResponseText()) {
  505. this.channelDebug_.dumpException(
  506. ex, 'ResponseText: ' + this.xmlHttp_.getResponseText());
  507. } else {
  508. this.channelDebug_.dumpException(ex, 'No response text');
  509. }
  510. } finally {
  511. /** @suppress {missingRequire} */
  512. goog.net.BrowserChannel.onEndExecution();
  513. }
  514. };
  515. /**
  516. * Called by the readystate handler for XMLHTTP requests.
  517. *
  518. * @private
  519. */
  520. goog.net.ChannelRequest.prototype.onXmlHttpReadyStateChanged_ = function() {
  521. var readyState = this.xmlHttp_.getReadyState();
  522. var errorCode = this.xmlHttp_.getLastErrorCode();
  523. var statusCode = this.xmlHttp_.getStatus();
  524. // If it is Safari less than 420+, there is a bug that causes null to be
  525. // in the responseText on ready state interactive so we must wait for
  526. // ready state complete.
  527. if (!goog.net.ChannelRequest.supportsXhrStreaming() ||
  528. (goog.userAgent.WEBKIT &&
  529. !goog.userAgent.isVersionOrHigher(
  530. goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_))) {
  531. if (readyState < goog.net.XmlHttp.ReadyState.COMPLETE) {
  532. // not yet ready
  533. return;
  534. }
  535. } else {
  536. // we get partial results in browsers that support ready state interactive.
  537. // We also make sure that getResponseText is not null in interactive mode
  538. // before we continue. However, we don't do it in Opera because it only
  539. // fire readyState == INTERACTIVE once. We need the following code to poll
  540. if (readyState < goog.net.XmlHttp.ReadyState.INTERACTIVE ||
  541. readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
  542. !goog.userAgent.OPERA && !this.xmlHttp_.getResponseText()) {
  543. // not yet ready
  544. return;
  545. }
  546. }
  547. // Dispatch any appropriate network events.
  548. if (!this.cancelled_ && readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
  549. errorCode != goog.net.ErrorCode.ABORT) {
  550. // Pretty conservative, these are the only known scenarios which we'd
  551. // consider indicative of a truly non-functional network connection.
  552. if (errorCode == goog.net.ErrorCode.TIMEOUT || statusCode <= 0) {
  553. this.channel_.notifyServerReachabilityEvent(
  554. /** @suppress {missingRequire} */
  555. goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED);
  556. } else {
  557. this.channel_.notifyServerReachabilityEvent(
  558. /** @suppress {missingRequire} */
  559. goog.net.BrowserChannel.ServerReachability.REQUEST_SUCCEEDED);
  560. }
  561. }
  562. // got some data so cancel the watchdog timer
  563. this.cancelWatchDogTimer_();
  564. var status = this.xmlHttp_.getStatus();
  565. this.lastStatusCode_ = status;
  566. var responseText = this.xmlHttp_.getResponseText();
  567. if (!responseText) {
  568. this.channelDebug_.debug(
  569. 'No response text for uri ' + this.requestUri_ + ' status ' + status);
  570. }
  571. this.successful_ = (status == 200);
  572. this.channelDebug_.xmlHttpChannelResponseMetaData(
  573. /** @type {string} */ (this.verb_), this.requestUri_, this.rid_,
  574. this.retryId_, readyState, status);
  575. if (!this.successful_) {
  576. if (status == 400 && responseText.indexOf('Unknown SID') > 0) {
  577. // the server error string will include 'Unknown SID' which indicates the
  578. // server doesn't know about the session (maybe it got restarted, maybe
  579. // the user got moved to another server, etc.,). Handlers can special
  580. // case this error
  581. this.lastError_ = goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID;
  582. /** @suppress {missingRequire} */
  583. goog.net.BrowserChannel.notifyStatEvent(
  584. /** @suppress {missingRequire} */
  585. goog.net.BrowserChannel.Stat.REQUEST_UNKNOWN_SESSION_ID);
  586. this.channelDebug_.warning('XMLHTTP Unknown SID (' + this.rid_ + ')');
  587. } else {
  588. this.lastError_ = goog.net.ChannelRequest.Error.STATUS;
  589. /** @suppress {missingRequire} */
  590. goog.net.BrowserChannel.notifyStatEvent(
  591. /** @suppress {missingRequire} */
  592. goog.net.BrowserChannel.Stat.REQUEST_BAD_STATUS);
  593. this.channelDebug_.warning(
  594. 'XMLHTTP Bad status ' + status + ' (' + this.rid_ + ')');
  595. }
  596. this.cleanup_();
  597. this.dispatchFailure_();
  598. return;
  599. }
  600. if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
  601. this.cleanup_();
  602. }
  603. if (this.decodeChunks_) {
  604. this.decodeNextChunks_(readyState, responseText);
  605. if (goog.userAgent.OPERA && this.successful_ &&
  606. readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
  607. this.startPolling_();
  608. }
  609. } else {
  610. this.channelDebug_.xmlHttpChannelResponseText(
  611. this.rid_, responseText, null);
  612. this.safeOnRequestData_(responseText);
  613. }
  614. if (!this.successful_) {
  615. return;
  616. }
  617. if (!this.cancelled_) {
  618. if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
  619. this.channel_.onRequestComplete(this);
  620. } else {
  621. // The default is false, the result from this callback shouldn't carry
  622. // over to the next callback, otherwise the request looks successful if
  623. // the watchdog timer gets called
  624. this.successful_ = false;
  625. this.ensureWatchDogTimer_();
  626. }
  627. }
  628. };
  629. /**
  630. * Decodes the next set of available chunks in the response.
  631. * @param {number} readyState The value of readyState.
  632. * @param {string} responseText The value of responseText.
  633. * @private
  634. */
  635. goog.net.ChannelRequest.prototype.decodeNextChunks_ = function(
  636. readyState, responseText) {
  637. var decodeNextChunksSuccessful = true;
  638. while (!this.cancelled_ && this.xmlHttpChunkStart_ < responseText.length) {
  639. var chunkText = this.getNextChunk_(responseText);
  640. if (chunkText == goog.net.ChannelRequest.INCOMPLETE_CHUNK_) {
  641. if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
  642. // should have consumed entire response when the request is done
  643. this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA;
  644. /** @suppress {missingRequire} */
  645. goog.net.BrowserChannel.notifyStatEvent(
  646. /** @suppress {missingRequire} */
  647. goog.net.BrowserChannel.Stat.REQUEST_INCOMPLETE_DATA);
  648. decodeNextChunksSuccessful = false;
  649. }
  650. this.channelDebug_.xmlHttpChannelResponseText(
  651. this.rid_, null, '[Incomplete Response]');
  652. break;
  653. } else if (chunkText == goog.net.ChannelRequest.INVALID_CHUNK_) {
  654. this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA;
  655. /** @suppress {missingRequire} */
  656. goog.net.BrowserChannel.notifyStatEvent(
  657. /** @suppress {missingRequire} */
  658. goog.net.BrowserChannel.Stat.REQUEST_BAD_DATA);
  659. this.channelDebug_.xmlHttpChannelResponseText(
  660. this.rid_, responseText, '[Invalid Chunk]');
  661. decodeNextChunksSuccessful = false;
  662. break;
  663. } else {
  664. this.channelDebug_.xmlHttpChannelResponseText(
  665. this.rid_, /** @type {string} */ (chunkText), null);
  666. this.safeOnRequestData_(/** @type {string} */ (chunkText));
  667. }
  668. }
  669. if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
  670. responseText.length == 0) {
  671. // also an error if we didn't get any response
  672. this.lastError_ = goog.net.ChannelRequest.Error.NO_DATA;
  673. /** @suppress {missingRequire} */
  674. goog.net.BrowserChannel.notifyStatEvent(
  675. /** @suppress {missingRequire} */
  676. goog.net.BrowserChannel.Stat.REQUEST_NO_DATA);
  677. decodeNextChunksSuccessful = false;
  678. }
  679. this.successful_ = this.successful_ && decodeNextChunksSuccessful;
  680. if (!decodeNextChunksSuccessful) {
  681. // malformed response - we make this trigger retry logic
  682. this.channelDebug_.xmlHttpChannelResponseText(
  683. this.rid_, responseText, '[Invalid Chunked Response]');
  684. this.cleanup_();
  685. this.dispatchFailure_();
  686. }
  687. };
  688. /**
  689. * Polls the response for new data.
  690. * @private
  691. */
  692. goog.net.ChannelRequest.prototype.pollResponse_ = function() {
  693. var readyState = this.xmlHttp_.getReadyState();
  694. var responseText = this.xmlHttp_.getResponseText();
  695. if (this.xmlHttpChunkStart_ < responseText.length) {
  696. this.cancelWatchDogTimer_();
  697. this.decodeNextChunks_(readyState, responseText);
  698. if (this.successful_ &&
  699. readyState != goog.net.XmlHttp.ReadyState.COMPLETE) {
  700. this.ensureWatchDogTimer_();
  701. }
  702. }
  703. };
  704. /**
  705. * Starts a polling interval for changes to responseText of the
  706. * XMLHttpRequest, for browsers that don't fire onreadystatechange
  707. * as data comes in incrementally. This timer is disabled in
  708. * cleanup_().
  709. * @private
  710. */
  711. goog.net.ChannelRequest.prototype.startPolling_ = function() {
  712. this.eventHandler_.listen(
  713. this.pollingTimer_, goog.Timer.TICK, this.pollResponse_);
  714. this.pollingTimer_.start();
  715. };
  716. /**
  717. * Returns the next chunk of a chunk-encoded response. This is not standard
  718. * HTTP chunked encoding because browsers don't expose the chunk boundaries to
  719. * the application through XMLHTTP. So we have an additional chunk encoding at
  720. * the application level that lets us tell where the beginning and end of
  721. * individual responses are so that we can only try to eval a complete JS array.
  722. *
  723. * The encoding is the size of the chunk encoded as a decimal string followed
  724. * by a newline followed by the data.
  725. *
  726. * @param {string} responseText The response text from the XMLHTTP response.
  727. * @return {string|Object} The next chunk string or a sentinel object
  728. * indicating a special condition.
  729. * @private
  730. */
  731. goog.net.ChannelRequest.prototype.getNextChunk_ = function(responseText) {
  732. var sizeStartIndex = this.xmlHttpChunkStart_;
  733. var sizeEndIndex = responseText.indexOf('\n', sizeStartIndex);
  734. if (sizeEndIndex == -1) {
  735. return goog.net.ChannelRequest.INCOMPLETE_CHUNK_;
  736. }
  737. var sizeAsString = responseText.substring(sizeStartIndex, sizeEndIndex);
  738. var size = Number(sizeAsString);
  739. if (isNaN(size)) {
  740. return goog.net.ChannelRequest.INVALID_CHUNK_;
  741. }
  742. var chunkStartIndex = sizeEndIndex + 1;
  743. if (chunkStartIndex + size > responseText.length) {
  744. return goog.net.ChannelRequest.INCOMPLETE_CHUNK_;
  745. }
  746. var chunkText = responseText.substr(chunkStartIndex, size);
  747. this.xmlHttpChunkStart_ = chunkStartIndex + size;
  748. return chunkText;
  749. };
  750. /**
  751. * Uses the Trident htmlfile ActiveX control to send a GET request in IE. This
  752. * is the innovation discovered that lets us get intermediate results in
  753. * Internet Explorer. Thanks to http://go/kev
  754. * @param {goog.Uri} uri The uri to request from.
  755. * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
  756. */
  757. goog.net.ChannelRequest.prototype.tridentGet = function(
  758. uri, usingSecondaryDomain) {
  759. this.type_ = goog.net.ChannelRequest.Type_.TRIDENT;
  760. this.baseUri_ = uri.clone().makeUnique();
  761. this.tridentGet_(usingSecondaryDomain);
  762. };
  763. /**
  764. * Starts the Trident request.
  765. * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
  766. * @private
  767. */
  768. goog.net.ChannelRequest.prototype.tridentGet_ = function(usingSecondaryDomain) {
  769. this.requestStartTime_ = goog.now();
  770. this.ensureWatchDogTimer_();
  771. var hostname = usingSecondaryDomain ? window.location.hostname : '';
  772. this.requestUri_ = this.baseUri_.clone();
  773. this.requestUri_.setParameterValue('DOMAIN', hostname);
  774. this.requestUri_.setParameterValue('t', this.retryId_);
  775. try {
  776. this.trident_ = new ActiveXObject('htmlfile');
  777. } catch (e) {
  778. this.channelDebug_.severe('ActiveX blocked');
  779. this.cleanup_();
  780. this.lastError_ = goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED;
  781. /** @suppress {missingRequire} */
  782. goog.net.BrowserChannel.notifyStatEvent(
  783. /** @suppress {missingRequire} */
  784. goog.net.BrowserChannel.Stat.ACTIVE_X_BLOCKED);
  785. this.dispatchFailure_();
  786. return;
  787. }
  788. // Using goog.html.SafeHtml.create() might be viable here but since
  789. // this code is now superseded by
  790. // closure/labs/net/webchannel/channelrequest.js it's not worth risking
  791. // the performance regressions and bugs that might result. Instead we
  792. // do an unchecked conversion. Please be extra careful if modifying
  793. // the HTML construction in this code, it's brittle and so it's easy to make
  794. // mistakes.
  795. var body = '<html><body>';
  796. if (usingSecondaryDomain) {
  797. var escapedHostname =
  798. goog.net.ChannelRequest.escapeForStringInScript_(hostname);
  799. body += '<script>document.domain="' + escapedHostname + '"</scr' +
  800. 'ipt>';
  801. }
  802. body += '</body></html>';
  803. var bodyHtml = goog.html.uncheckedconversions
  804. .safeHtmlFromStringKnownToSatisfyTypeContract(
  805. goog.string.Const.from('b/12014412'), body);
  806. this.trident_.open();
  807. goog.dom.safe.documentWrite(
  808. /** @type {!Document} */ (this.trident_), bodyHtml);
  809. this.trident_.close();
  810. this.trident_.parentWindow['m'] = goog.bind(this.onTridentRpcMessage_, this);
  811. this.trident_.parentWindow['d'] = goog.bind(this.onTridentDone_, this, true);
  812. this.trident_.parentWindow['rpcClose'] =
  813. goog.bind(this.onTridentDone_, this, false);
  814. var div = this.trident_.createElement(String(goog.dom.TagName.DIV));
  815. this.trident_.parentWindow.document.body.appendChild(div);
  816. var safeUrl = goog.html.SafeUrl.sanitize(this.requestUri_.toString());
  817. var sanitizedEscapedUrl =
  818. goog.string.htmlEscape(goog.html.SafeUrl.unwrap(safeUrl));
  819. var iframeHtml =
  820. goog.html.uncheckedconversions
  821. .safeHtmlFromStringKnownToSatisfyTypeContract(
  822. goog.string.Const.from('b/12014412'),
  823. '<iframe src="' + sanitizedEscapedUrl + '"></iframe>');
  824. goog.dom.safe.setInnerHtml(div, iframeHtml);
  825. this.channelDebug_.tridentChannelRequest(
  826. 'GET', this.requestUri_, this.rid_, this.retryId_);
  827. this.channel_.notifyServerReachabilityEvent(
  828. /** @suppress {missingRequire} */
  829. goog.net.BrowserChannel.ServerReachability.REQUEST_MADE);
  830. };
  831. /**
  832. * JavaScript-escapes a string so that it can be included inside a JS string.
  833. * Since the JS string is expected to be inside a <script>, HTML-escaping
  834. * cannot be used and thus '<' and '>' are also JS-escaped.
  835. * @param {string} string
  836. * @return {string}
  837. * @private
  838. */
  839. goog.net.ChannelRequest.escapeForStringInScript_ = function(string) {
  840. var escaped = '';
  841. for (var i = 0; i < string.length; i++) {
  842. var c = string.charAt(i);
  843. if (c == '<') {
  844. escaped += '\\x3c';
  845. } else if (c == '>') {
  846. escaped += '\\x3e';
  847. } else {
  848. // This will escape both " and '.
  849. escaped += goog.string.escapeChar(c);
  850. }
  851. }
  852. return escaped;
  853. };
  854. /**
  855. * Callback from the Trident htmlfile ActiveX control for when a new message
  856. * is received.
  857. *
  858. * @param {string} msg The data payload.
  859. * @private
  860. */
  861. goog.net.ChannelRequest.prototype.onTridentRpcMessage_ = function(msg) {
  862. // need to do async b/c this gets called off of the context of the ActiveX
  863. /** @suppress {missingRequire} */
  864. goog.net.BrowserChannel.setTimeout(
  865. goog.bind(this.onTridentRpcMessageAsync_, this, msg), 0);
  866. };
  867. /**
  868. * Callback from the Trident htmlfile ActiveX control for when a new message
  869. * is received.
  870. *
  871. * @param {string} msg The data payload.
  872. * @private
  873. */
  874. goog.net.ChannelRequest.prototype.onTridentRpcMessageAsync_ = function(msg) {
  875. if (this.cancelled_) {
  876. return;
  877. }
  878. this.channelDebug_.tridentChannelResponseText(this.rid_, msg);
  879. this.cancelWatchDogTimer_();
  880. this.safeOnRequestData_(msg);
  881. this.ensureWatchDogTimer_();
  882. };
  883. /**
  884. * Callback from the Trident htmlfile ActiveX control for when the request
  885. * is complete
  886. *
  887. * @param {boolean} successful Whether the request successfully completed.
  888. * @private
  889. */
  890. goog.net.ChannelRequest.prototype.onTridentDone_ = function(successful) {
  891. // need to do async b/c this gets called off of the context of the ActiveX
  892. /** @suppress {missingRequire} */
  893. goog.net.BrowserChannel.setTimeout(
  894. goog.bind(this.onTridentDoneAsync_, this, successful), 0);
  895. };
  896. /**
  897. * Callback from the Trident htmlfile ActiveX control for when the request
  898. * is complete
  899. *
  900. * @param {boolean} successful Whether the request successfully completed.
  901. * @private
  902. */
  903. goog.net.ChannelRequest.prototype.onTridentDoneAsync_ = function(successful) {
  904. if (this.cancelled_) {
  905. return;
  906. }
  907. this.channelDebug_.tridentChannelResponseDone(this.rid_, successful);
  908. this.cleanup_();
  909. this.successful_ = successful;
  910. this.channel_.onRequestComplete(this);
  911. this.channel_.notifyServerReachabilityEvent(
  912. /** @suppress {missingRequire} */
  913. goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY);
  914. };
  915. /**
  916. * Uses an IMG tag to send an HTTP get to the server. This is only currently
  917. * used to terminate the connection, as an IMG tag is the most reliable way to
  918. * send something to the server while the page is getting torn down.
  919. * @param {goog.Uri} uri The uri to send a request to.
  920. */
  921. goog.net.ChannelRequest.prototype.sendUsingImgTag = function(uri) {
  922. this.type_ = goog.net.ChannelRequest.Type_.IMG;
  923. this.baseUri_ = uri.clone().makeUnique();
  924. this.imgTagGet_();
  925. };
  926. /**
  927. * Starts the IMG request.
  928. *
  929. * @private
  930. */
  931. goog.net.ChannelRequest.prototype.imgTagGet_ = function() {
  932. var eltImg = new Image();
  933. eltImg.src = this.baseUri_;
  934. this.requestStartTime_ = goog.now();
  935. this.ensureWatchDogTimer_();
  936. };
  937. /**
  938. * Cancels the request no matter what the underlying transport is.
  939. */
  940. goog.net.ChannelRequest.prototype.cancel = function() {
  941. this.cancelled_ = true;
  942. this.cleanup_();
  943. };
  944. /**
  945. * Ensures that there is watchdog timeout which is used to ensure that
  946. * the connection completes in time.
  947. *
  948. * @private
  949. */
  950. goog.net.ChannelRequest.prototype.ensureWatchDogTimer_ = function() {
  951. this.watchDogTimeoutTime_ = goog.now() + this.timeout_;
  952. this.startWatchDogTimer_(this.timeout_);
  953. };
  954. /**
  955. * Starts the watchdog timer which is used to ensure that the connection
  956. * completes in time.
  957. * @param {number} time The number of milliseconds to wait.
  958. * @private
  959. * @suppress {missingRequire} goog.net.BrowserChannel
  960. */
  961. goog.net.ChannelRequest.prototype.startWatchDogTimer_ = function(time) {
  962. if (this.watchDogTimerId_ != null) {
  963. // assertion
  964. throw Error('WatchDog timer not null');
  965. }
  966. /** @private @suppress {missingRequire} Circular dep. */
  967. this.watchDogTimerId_ = goog.net.BrowserChannel.setTimeout(
  968. goog.bind(this.onWatchDogTimeout_, this), time);
  969. };
  970. /**
  971. * Cancels the watchdog timer if it has been started.
  972. *
  973. * @private
  974. */
  975. goog.net.ChannelRequest.prototype.cancelWatchDogTimer_ = function() {
  976. if (this.watchDogTimerId_) {
  977. goog.global.clearTimeout(this.watchDogTimerId_);
  978. this.watchDogTimerId_ = null;
  979. }
  980. };
  981. /**
  982. * Called when the watchdog timer is triggered. It also handles a case where it
  983. * is called too early which we suspect may be happening sometimes
  984. * (not sure why)
  985. *
  986. * @private
  987. */
  988. goog.net.ChannelRequest.prototype.onWatchDogTimeout_ = function() {
  989. this.watchDogTimerId_ = null;
  990. var now = goog.now();
  991. if (now - this.watchDogTimeoutTime_ >= 0) {
  992. this.handleTimeout_();
  993. } else {
  994. // got called too early for some reason
  995. this.channelDebug_.warning('WatchDog timer called too early');
  996. this.startWatchDogTimer_(this.watchDogTimeoutTime_ - now);
  997. }
  998. };
  999. /**
  1000. * Called when the request has actually timed out. Will cleanup and notify the
  1001. * channel of the failure.
  1002. *
  1003. * @private
  1004. */
  1005. goog.net.ChannelRequest.prototype.handleTimeout_ = function() {
  1006. if (this.successful_) {
  1007. // Should never happen.
  1008. this.channelDebug_.severe(
  1009. 'Received watchdog timeout even though request loaded successfully');
  1010. }
  1011. this.channelDebug_.timeoutResponse(this.requestUri_);
  1012. // IMG requests never notice if they were successful, and always 'time out'.
  1013. // This fact says nothing about reachability.
  1014. if (this.type_ != goog.net.ChannelRequest.Type_.IMG) {
  1015. this.channel_.notifyServerReachabilityEvent(
  1016. /** @suppress {missingRequire} */
  1017. goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED);
  1018. }
  1019. this.cleanup_();
  1020. // set error and dispatch failure
  1021. this.lastError_ = goog.net.ChannelRequest.Error.TIMEOUT;
  1022. /** @suppress {missingRequire} */
  1023. goog.net.BrowserChannel.notifyStatEvent(
  1024. /** @suppress {missingRequire} */
  1025. goog.net.BrowserChannel.Stat.REQUEST_TIMEOUT);
  1026. this.dispatchFailure_();
  1027. };
  1028. /**
  1029. * Notifies the channel that this request failed.
  1030. * @private
  1031. */
  1032. goog.net.ChannelRequest.prototype.dispatchFailure_ = function() {
  1033. if (this.channel_.isClosed() || this.cancelled_) {
  1034. return;
  1035. }
  1036. this.channel_.onRequestComplete(this);
  1037. };
  1038. /**
  1039. * Cleans up the objects used to make the request. This function is
  1040. * idempotent.
  1041. *
  1042. * @private
  1043. */
  1044. goog.net.ChannelRequest.prototype.cleanup_ = function() {
  1045. this.cancelWatchDogTimer_();
  1046. goog.dispose(this.readyStateChangeThrottle_);
  1047. this.readyStateChangeThrottle_ = null;
  1048. // Stop the polling timer, if necessary.
  1049. this.pollingTimer_.stop();
  1050. // Unhook all event handlers.
  1051. this.eventHandler_.removeAll();
  1052. if (this.xmlHttp_) {
  1053. // clear out this.xmlHttp_ before aborting so we handle getting reentered
  1054. // inside abort
  1055. var xmlhttp = this.xmlHttp_;
  1056. this.xmlHttp_ = null;
  1057. xmlhttp.abort();
  1058. xmlhttp.dispose();
  1059. }
  1060. if (this.trident_) {
  1061. this.trident_ = null;
  1062. }
  1063. };
  1064. /**
  1065. * Indicates whether the request was successful. Only valid after the handler
  1066. * is called to indicate completion of the request.
  1067. *
  1068. * @return {boolean} True if the request succeeded.
  1069. */
  1070. goog.net.ChannelRequest.prototype.getSuccess = function() {
  1071. return this.successful_;
  1072. };
  1073. /**
  1074. * If the request was not successful, returns the reason.
  1075. *
  1076. * @return {?goog.net.ChannelRequest.Error} The last error.
  1077. */
  1078. goog.net.ChannelRequest.prototype.getLastError = function() {
  1079. return this.lastError_;
  1080. };
  1081. /**
  1082. * Returns the status code of the last request.
  1083. * @return {number} The status code of the last request.
  1084. */
  1085. goog.net.ChannelRequest.prototype.getLastStatusCode = function() {
  1086. return this.lastStatusCode_;
  1087. };
  1088. /**
  1089. * Returns the session id for this channel.
  1090. *
  1091. * @return {string|undefined} The session ID.
  1092. */
  1093. goog.net.ChannelRequest.prototype.getSessionId = function() {
  1094. return this.sid_;
  1095. };
  1096. /**
  1097. * Returns the request id for this request. Each request has a unique request
  1098. * id and the request IDs are a sequential increasing count.
  1099. *
  1100. * @return {string|number|undefined} The request ID.
  1101. */
  1102. goog.net.ChannelRequest.prototype.getRequestId = function() {
  1103. return this.rid_;
  1104. };
  1105. /**
  1106. * Returns the data for a post, if this request is a post.
  1107. *
  1108. * @return {?string} The POST data provided by the request initiator.
  1109. */
  1110. goog.net.ChannelRequest.prototype.getPostData = function() {
  1111. return this.postData_;
  1112. };
  1113. /**
  1114. * Returns the time that the request started, if it has started.
  1115. *
  1116. * @return {?number} The time the request started, as returned by goog.now().
  1117. */
  1118. goog.net.ChannelRequest.prototype.getRequestStartTime = function() {
  1119. return this.requestStartTime_;
  1120. };
  1121. /**
  1122. * Helper to call the callback's onRequestData, which catches any
  1123. * exception and cleans up the request.
  1124. * @param {string} data The request data.
  1125. * @private
  1126. */
  1127. goog.net.ChannelRequest.prototype.safeOnRequestData_ = function(data) {
  1128. try {
  1129. this.channel_.onRequestData(this, data);
  1130. /** @suppress {missingRequire} goog.net.BrowserChannel */
  1131. this.channel_.notifyServerReachabilityEvent(
  1132. goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY);
  1133. } catch (e) {
  1134. // Dump debug info, but keep going without closing the channel.
  1135. this.channelDebug_.dumpException(e, 'Error in httprequest callback');
  1136. }
  1137. };