Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/net/xhrio.js
2868 views
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
/**
16
* @fileoverview Wrapper class for handling XmlHttpRequests.
17
*
18
* One off requests can be sent through goog.net.XhrIo.send() or an
19
* instance can be created to send multiple requests. Each request uses its
20
* own XmlHttpRequest object and handles clearing of the event callback to
21
* ensure no leaks.
22
*
23
* XhrIo is event based, it dispatches events on success, failure, finishing,
24
* ready-state change, or progress (download and upload).
25
*
26
* The ready-state or timeout event fires first, followed by
27
* a generic completed event. Then the abort, error, or success event
28
* is fired as appropriate. Progress events are fired as they are
29
* received. Lastly, the ready event will fire to indicate that the
30
* object may be used to make another request.
31
*
32
* The error event may also be called before completed and
33
* ready-state-change if the XmlHttpRequest.open() or .send() methods throw.
34
*
35
* This class does not support multiple requests, queuing, or prioritization.
36
*
37
* When progress events are supported by the browser, and progress is
38
* enabled via .setProgressEventsEnabled(true), the
39
* goog.net.EventType.PROGRESS event will be the re-dispatched browser
40
* progress event. Additionally, a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event
41
* will be fired for download and upload progress respectively.
42
*
43
*/
44
45
46
goog.provide('goog.net.XhrIo');
47
goog.provide('goog.net.XhrIo.ResponseType');
48
49
goog.require('goog.Timer');
50
goog.require('goog.array');
51
goog.require('goog.asserts');
52
goog.require('goog.debug.entryPointRegistry');
53
goog.require('goog.events.EventTarget');
54
goog.require('goog.json');
55
goog.require('goog.log');
56
goog.require('goog.net.ErrorCode');
57
goog.require('goog.net.EventType');
58
goog.require('goog.net.HttpStatus');
59
goog.require('goog.net.XmlHttp');
60
goog.require('goog.string');
61
goog.require('goog.structs');
62
goog.require('goog.structs.Map');
63
goog.require('goog.uri.utils');
64
goog.require('goog.userAgent');
65
66
goog.forwardDeclare('goog.Uri');
67
68
69
70
/**
71
* Basic class for handling XMLHttpRequests.
72
* @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when
73
* creating XMLHttpRequest objects.
74
* @constructor
75
* @extends {goog.events.EventTarget}
76
*/
77
goog.net.XhrIo = function(opt_xmlHttpFactory) {
78
goog.net.XhrIo.base(this, 'constructor');
79
80
/**
81
* Map of default headers to add to every request, use:
82
* XhrIo.headers.set(name, value)
83
* @type {!goog.structs.Map}
84
*/
85
this.headers = new goog.structs.Map();
86
87
/**
88
* Optional XmlHttpFactory
89
* @private {goog.net.XmlHttpFactory}
90
*/
91
this.xmlHttpFactory_ = opt_xmlHttpFactory || null;
92
93
/**
94
* Whether XMLHttpRequest is active. A request is active from the time send()
95
* is called until onReadyStateChange() is complete, or error() or abort()
96
* is called.
97
* @private {boolean}
98
*/
99
this.active_ = false;
100
101
/**
102
* The XMLHttpRequest object that is being used for the transfer.
103
* @private {?goog.net.XhrLike.OrNative}
104
*/
105
this.xhr_ = null;
106
107
/**
108
* The options to use with the current XMLHttpRequest object.
109
* @private {Object}
110
*/
111
this.xhrOptions_ = null;
112
113
/**
114
* Last URL that was requested.
115
* @private {string|goog.Uri}
116
*/
117
this.lastUri_ = '';
118
119
/**
120
* Method for the last request.
121
* @private {string}
122
*/
123
this.lastMethod_ = '';
124
125
/**
126
* Last error code.
127
* @private {!goog.net.ErrorCode}
128
*/
129
this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
130
131
/**
132
* Last error message.
133
* @private {Error|string}
134
*/
135
this.lastError_ = '';
136
137
/**
138
* Used to ensure that we don't dispatch an multiple ERROR events. This can
139
* happen in IE when it does a synchronous load and one error is handled in
140
* the ready statte change and one is handled due to send() throwing an
141
* exception.
142
* @private {boolean}
143
*/
144
this.errorDispatched_ = false;
145
146
/**
147
* Used to make sure we don't fire the complete event from inside a send call.
148
* @private {boolean}
149
*/
150
this.inSend_ = false;
151
152
/**
153
* Used in determining if a call to {@link #onReadyStateChange_} is from
154
* within a call to this.xhr_.open.
155
* @private {boolean}
156
*/
157
this.inOpen_ = false;
158
159
/**
160
* Used in determining if a call to {@link #onReadyStateChange_} is from
161
* within a call to this.xhr_.abort.
162
* @private {boolean}
163
*/
164
this.inAbort_ = false;
165
166
/**
167
* Number of milliseconds after which an incomplete request will be aborted
168
* and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout
169
* is set.
170
* @private {number}
171
*/
172
this.timeoutInterval_ = 0;
173
174
/**
175
* Timer to track request timeout.
176
* @private {?number}
177
*/
178
this.timeoutId_ = null;
179
180
/**
181
* The requested type for the response. The empty string means use the default
182
* XHR behavior.
183
* @private {goog.net.XhrIo.ResponseType}
184
*/
185
this.responseType_ = goog.net.XhrIo.ResponseType.DEFAULT;
186
187
/**
188
* Whether a "credentialed" request is to be sent (one that is aware of
189
* cookies and authentication). This is applicable only for cross-domain
190
* requests and more recent browsers that support this part of the HTTP Access
191
* Control standard.
192
*
193
* @see http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute
194
*
195
* @private {boolean}
196
*/
197
this.withCredentials_ = false;
198
199
/**
200
* Whether progress events are enabled for this request. This is
201
* disabled by default because setting a progress event handler
202
* causes pre-flight OPTIONS requests to be sent for CORS requests,
203
* even in cases where a pre-flight request would not otherwise be
204
* sent.
205
*
206
* @see http://xhr.spec.whatwg.org/#security-considerations
207
*
208
* Note that this can cause problems for Firefox 22 and below, as an
209
* older "LSProgressEvent" will be dispatched by the browser. That
210
* progress event is no longer supported, and can lead to failures,
211
* including throwing exceptions.
212
*
213
* @see http://bugzilla.mozilla.org/show_bug.cgi?id=845631
214
* @see b/23469793
215
*
216
* @private {boolean}
217
*/
218
this.progressEventsEnabled_ = false;
219
220
/**
221
* True if we can use XMLHttpRequest's timeout directly.
222
* @private {boolean}
223
*/
224
this.useXhr2Timeout_ = false;
225
};
226
goog.inherits(goog.net.XhrIo, goog.events.EventTarget);
227
228
229
/**
230
* Response types that may be requested for XMLHttpRequests.
231
* @enum {string}
232
* @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetype-attribute
233
*/
234
goog.net.XhrIo.ResponseType = {
235
DEFAULT: '',
236
TEXT: 'text',
237
DOCUMENT: 'document',
238
// Not supported as of Chrome 10.0.612.1 dev
239
BLOB: 'blob',
240
ARRAY_BUFFER: 'arraybuffer'
241
};
242
243
244
/**
245
* A reference to the XhrIo logger
246
* @private {?goog.log.Logger}
247
* @const
248
*/
249
goog.net.XhrIo.prototype.logger_ = goog.log.getLogger('goog.net.XhrIo');
250
251
252
/**
253
* The Content-Type HTTP header name
254
* @type {string}
255
*/
256
goog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type';
257
258
259
/**
260
* The Content-Transfer-Encoding HTTP header name
261
* @type {string}
262
*/
263
goog.net.XhrIo.CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding';
264
265
266
/**
267
* The pattern matching the 'http' and 'https' URI schemes
268
* @type {!RegExp}
269
*/
270
goog.net.XhrIo.HTTP_SCHEME_PATTERN = /^https?$/i;
271
272
273
/**
274
* The methods that typically come along with form data. We set different
275
* headers depending on whether the HTTP action is one of these.
276
*/
277
goog.net.XhrIo.METHODS_WITH_FORM_DATA = ['POST', 'PUT'];
278
279
280
/**
281
* The Content-Type HTTP header value for a url-encoded form
282
* @type {string}
283
*/
284
goog.net.XhrIo.FORM_CONTENT_TYPE =
285
'application/x-www-form-urlencoded;charset=utf-8';
286
287
288
/**
289
* The XMLHttpRequest Level two timeout delay ms property name.
290
*
291
* @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
292
*
293
* @private {string}
294
* @const
295
*/
296
goog.net.XhrIo.XHR2_TIMEOUT_ = 'timeout';
297
298
299
/**
300
* The XMLHttpRequest Level two ontimeout handler property name.
301
*
302
* @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
303
*
304
* @private {string}
305
* @const
306
*/
307
goog.net.XhrIo.XHR2_ON_TIMEOUT_ = 'ontimeout';
308
309
310
/**
311
* All non-disposed instances of goog.net.XhrIo created
312
* by {@link goog.net.XhrIo.send} are in this Array.
313
* @see goog.net.XhrIo.cleanup
314
* @private {!Array<!goog.net.XhrIo>}
315
*/
316
goog.net.XhrIo.sendInstances_ = [];
317
318
319
/**
320
* Static send that creates a short lived instance of XhrIo to send the
321
* request.
322
* @see goog.net.XhrIo.cleanup
323
* @param {string|goog.Uri} url Uri to make request to.
324
* @param {?function(this:goog.net.XhrIo, ?)=} opt_callback Callback function
325
* for when request is complete.
326
* @param {string=} opt_method Send method, default: GET.
327
* @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
328
* opt_content Body data.
329
* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
330
* request.
331
* @param {number=} opt_timeoutInterval Number of milliseconds after which an
332
* incomplete request will be aborted; 0 means no timeout is set.
333
* @param {boolean=} opt_withCredentials Whether to send credentials with the
334
* request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
335
* @return {!goog.net.XhrIo} The sent XhrIo.
336
*/
337
goog.net.XhrIo.send = function(
338
url, opt_callback, opt_method, opt_content, opt_headers,
339
opt_timeoutInterval, opt_withCredentials) {
340
var x = new goog.net.XhrIo();
341
goog.net.XhrIo.sendInstances_.push(x);
342
if (opt_callback) {
343
x.listen(goog.net.EventType.COMPLETE, opt_callback);
344
}
345
x.listenOnce(goog.net.EventType.READY, x.cleanupSend_);
346
if (opt_timeoutInterval) {
347
x.setTimeoutInterval(opt_timeoutInterval);
348
}
349
if (opt_withCredentials) {
350
x.setWithCredentials(opt_withCredentials);
351
}
352
x.send(url, opt_method, opt_content, opt_headers);
353
return x;
354
};
355
356
357
/**
358
* Disposes all non-disposed instances of goog.net.XhrIo created by
359
* {@link goog.net.XhrIo.send}.
360
* {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance
361
* it creates when the request completes or fails. However, if
362
* the request never completes, then the goog.net.XhrIo is not disposed.
363
* This can occur if the window is unloaded before the request completes.
364
* We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo
365
* it creates and make the client of {@link goog.net.XhrIo.send} be
366
* responsible for disposing it in this case. However, this makes things
367
* significantly more complicated for the client, and the whole point
368
* of {@link goog.net.XhrIo.send} is that it's simple and easy to use.
369
* Clients of {@link goog.net.XhrIo.send} should call
370
* {@link goog.net.XhrIo.cleanup} when doing final
371
* cleanup on window unload.
372
*/
373
goog.net.XhrIo.cleanup = function() {
374
var instances = goog.net.XhrIo.sendInstances_;
375
while (instances.length) {
376
instances.pop().dispose();
377
}
378
};
379
380
381
/**
382
* Installs exception protection for all entry point introduced by
383
* goog.net.XhrIo instances which are not protected by
384
* {@link goog.debug.ErrorHandler#protectWindowSetTimeout},
385
* {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or
386
* {@link goog.events.protectBrowserEventEntryPoint}.
387
*
388
* @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
389
* protect the entry point(s).
390
*/
391
goog.net.XhrIo.protectEntryPoints = function(errorHandler) {
392
goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
393
errorHandler.protectEntryPoint(
394
goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
395
};
396
397
398
/**
399
* Disposes of the specified goog.net.XhrIo created by
400
* {@link goog.net.XhrIo.send} and removes it from
401
* {@link goog.net.XhrIo.pendingStaticSendInstances_}.
402
* @private
403
*/
404
goog.net.XhrIo.prototype.cleanupSend_ = function() {
405
this.dispose();
406
goog.array.remove(goog.net.XhrIo.sendInstances_, this);
407
};
408
409
410
/**
411
* Returns the number of milliseconds after which an incomplete request will be
412
* aborted, or 0 if no timeout is set.
413
* @return {number} Timeout interval in milliseconds.
414
*/
415
goog.net.XhrIo.prototype.getTimeoutInterval = function() {
416
return this.timeoutInterval_;
417
};
418
419
420
/**
421
* Sets the number of milliseconds after which an incomplete request will be
422
* aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
423
* timeout is set.
424
* @param {number} ms Timeout interval in milliseconds; 0 means none.
425
*/
426
goog.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
427
this.timeoutInterval_ = Math.max(0, ms);
428
};
429
430
431
/**
432
* Sets the desired type for the response. At time of writing, this is only
433
* supported in very recent versions of WebKit (10.0.612.1 dev and later).
434
*
435
* If this is used, the response may only be accessed via {@link #getResponse}.
436
*
437
* @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
438
*/
439
goog.net.XhrIo.prototype.setResponseType = function(type) {
440
this.responseType_ = type;
441
};
442
443
444
/**
445
* Gets the desired type for the response.
446
* @return {goog.net.XhrIo.ResponseType} The desired type for the response.
447
*/
448
goog.net.XhrIo.prototype.getResponseType = function() {
449
return this.responseType_;
450
};
451
452
453
/**
454
* Sets whether a "credentialed" request that is aware of cookie and
455
* authentication information should be made. This option is only supported by
456
* browsers that support HTTP Access Control. As of this writing, this option
457
* is not supported in IE.
458
*
459
* @param {boolean} withCredentials Whether this should be a "credentialed"
460
* request.
461
*/
462
goog.net.XhrIo.prototype.setWithCredentials = function(withCredentials) {
463
this.withCredentials_ = withCredentials;
464
};
465
466
467
/**
468
* Gets whether a "credentialed" request is to be sent.
469
* @return {boolean} The desired type for the response.
470
*/
471
goog.net.XhrIo.prototype.getWithCredentials = function() {
472
return this.withCredentials_;
473
};
474
475
476
/**
477
* Sets whether progress events are enabled for this request. Note
478
* that progress events require pre-flight OPTIONS request handling
479
* for CORS requests, and may cause trouble with older browsers. See
480
* progressEventsEnabled_ for details.
481
* @param {boolean} enabled Whether progress events should be enabled.
482
*/
483
goog.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) {
484
this.progressEventsEnabled_ = enabled;
485
};
486
487
488
/**
489
* Gets whether progress events are enabled.
490
* @return {boolean} Whether progress events are enabled for this request.
491
*/
492
goog.net.XhrIo.prototype.getProgressEventsEnabled = function() {
493
return this.progressEventsEnabled_;
494
};
495
496
497
/**
498
* Instance send that actually uses XMLHttpRequest to make a server call.
499
* @param {string|goog.Uri} url Uri to make request to.
500
* @param {string=} opt_method Send method, default: GET.
501
* @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
502
* opt_content Body data.
503
* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
504
* request.
505
* @suppress {deprecated} Use deprecated goog.structs.forEach to allow different
506
* types of parameters for opt_headers.
507
*/
508
goog.net.XhrIo.prototype.send = function(
509
url, opt_method, opt_content, opt_headers) {
510
if (this.xhr_) {
511
throw Error(
512
'[goog.net.XhrIo] Object is active with another request=' +
513
this.lastUri_ + '; newUri=' + url);
514
}
515
516
var method = opt_method ? opt_method.toUpperCase() : 'GET';
517
518
this.lastUri_ = url;
519
this.lastError_ = '';
520
this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
521
this.lastMethod_ = method;
522
this.errorDispatched_ = false;
523
this.active_ = true;
524
525
// Use the factory to create the XHR object and options
526
this.xhr_ = this.createXhr();
527
this.xhrOptions_ = this.xmlHttpFactory_ ? this.xmlHttpFactory_.getOptions() :
528
goog.net.XmlHttp.getOptions();
529
530
// Set up the onreadystatechange callback
531
this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this);
532
533
// Set up upload/download progress events, if progress events are supported.
534
if (this.getProgressEventsEnabled() && 'onprogress' in this.xhr_) {
535
this.xhr_.onprogress =
536
goog.bind(function(e) { this.onProgressHandler_(e, true); }, this);
537
if (this.xhr_.upload) {
538
this.xhr_.upload.onprogress = goog.bind(this.onProgressHandler_, this);
539
}
540
}
541
542
/**
543
* Try to open the XMLHttpRequest (always async), if an error occurs here it
544
* is generally permission denied
545
*/
546
try {
547
goog.log.fine(this.logger_, this.formatMsg_('Opening Xhr'));
548
this.inOpen_ = true;
549
this.xhr_.open(method, String(url), true); // Always async!
550
this.inOpen_ = false;
551
} catch (err) {
552
goog.log.fine(
553
this.logger_, this.formatMsg_('Error opening Xhr: ' + err.message));
554
this.error_(goog.net.ErrorCode.EXCEPTION, err);
555
return;
556
}
557
558
// We can't use null since this won't allow requests with form data to have a
559
// content length specified which will cause some proxies to return a 411
560
// error.
561
var content = opt_content || '';
562
563
var headers = this.headers.clone();
564
565
// Add headers specific to this request
566
if (opt_headers) {
567
goog.structs.forEach(
568
opt_headers, function(value, key) { headers.set(key, value); });
569
}
570
571
// Find whether a content type header is set, ignoring case.
572
// HTTP header names are case-insensitive. See:
573
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
574
var contentTypeKey =
575
goog.array.find(headers.getKeys(), goog.net.XhrIo.isContentTypeHeader_);
576
577
var contentIsFormData =
578
(goog.global['FormData'] && (content instanceof goog.global['FormData']));
579
if (goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA, method) &&
580
!contentTypeKey && !contentIsFormData) {
581
// For requests typically with form data, default to the url-encoded form
582
// content type unless this is a FormData request. For FormData,
583
// the browser will automatically add a multipart/form-data content type
584
// with an appropriate multipart boundary.
585
headers.set(
586
goog.net.XhrIo.CONTENT_TYPE_HEADER, goog.net.XhrIo.FORM_CONTENT_TYPE);
587
}
588
589
// Add the headers to the Xhr object
590
headers.forEach(function(value, key) {
591
this.xhr_.setRequestHeader(key, value);
592
}, this);
593
594
if (this.responseType_) {
595
this.xhr_.responseType = this.responseType_;
596
}
597
// Set xhr_.withCredentials only when the value is different, or else in
598
// synchronous XMLHtppRequest.open Firefox will throw an exception.
599
// https://bugzilla.mozilla.org/show_bug.cgi?id=736340
600
if ('withCredentials' in this.xhr_ &&
601
this.xhr_.withCredentials !== this.withCredentials_) {
602
this.xhr_.withCredentials = this.withCredentials_;
603
}
604
605
/**
606
* Try to send the request, or other wise report an error (404 not found).
607
*/
608
try {
609
this.cleanUpTimeoutTimer_(); // Paranoid, should never be running.
610
if (this.timeoutInterval_ > 0) {
611
this.useXhr2Timeout_ = goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_);
612
goog.log.fine(
613
this.logger_, this.formatMsg_(
614
'Will abort after ' + this.timeoutInterval_ +
615
'ms if incomplete, xhr2 ' + this.useXhr2Timeout_));
616
if (this.useXhr2Timeout_) {
617
this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_] = this.timeoutInterval_;
618
this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] =
619
goog.bind(this.timeout_, this);
620
} else {
621
this.timeoutId_ =
622
goog.Timer.callOnce(this.timeout_, this.timeoutInterval_, this);
623
}
624
}
625
goog.log.fine(this.logger_, this.formatMsg_('Sending request'));
626
this.inSend_ = true;
627
this.xhr_.send(content);
628
this.inSend_ = false;
629
630
} catch (err) {
631
goog.log.fine(this.logger_, this.formatMsg_('Send error: ' + err.message));
632
this.error_(goog.net.ErrorCode.EXCEPTION, err);
633
}
634
};
635
636
637
/**
638
* Determines if the argument is an XMLHttpRequest that supports the level 2
639
* timeout value and event.
640
*
641
* Currently, FF 21.0 OS X has the fields but won't actually call the timeout
642
* handler. Perhaps the confusion in the bug referenced below hasn't
643
* entirely been resolved.
644
*
645
* @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
646
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=525816
647
*
648
* @param {!goog.net.XhrLike.OrNative} xhr The request.
649
* @return {boolean} True if the request supports level 2 timeout.
650
* @private
651
*/
652
goog.net.XhrIo.shouldUseXhr2Timeout_ = function(xhr) {
653
return goog.userAgent.IE && goog.userAgent.isVersionOrHigher(9) &&
654
goog.isNumber(xhr[goog.net.XhrIo.XHR2_TIMEOUT_]) &&
655
goog.isDef(xhr[goog.net.XhrIo.XHR2_ON_TIMEOUT_]);
656
};
657
658
659
/**
660
* @param {string} header An HTTP header key.
661
* @return {boolean} Whether the key is a content type header (ignoring
662
* case.
663
* @private
664
*/
665
goog.net.XhrIo.isContentTypeHeader_ = function(header) {
666
return goog.string.caseInsensitiveEquals(
667
goog.net.XhrIo.CONTENT_TYPE_HEADER, header);
668
};
669
670
671
/**
672
* Creates a new XHR object.
673
* @return {!goog.net.XhrLike.OrNative} The newly created XHR object.
674
* @protected
675
*/
676
goog.net.XhrIo.prototype.createXhr = function() {
677
return this.xmlHttpFactory_ ? this.xmlHttpFactory_.createInstance() :
678
goog.net.XmlHttp();
679
};
680
681
682
/**
683
* The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_}
684
* milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts
685
* the request.
686
* @private
687
*/
688
goog.net.XhrIo.prototype.timeout_ = function() {
689
if (typeof goog == 'undefined') {
690
// If goog is undefined then the callback has occurred as the application
691
// is unloading and will error. Thus we let it silently fail.
692
} else if (this.xhr_) {
693
this.lastError_ =
694
'Timed out after ' + this.timeoutInterval_ + 'ms, aborting';
695
this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
696
goog.log.fine(this.logger_, this.formatMsg_(this.lastError_));
697
this.dispatchEvent(goog.net.EventType.TIMEOUT);
698
this.abort(goog.net.ErrorCode.TIMEOUT);
699
}
700
};
701
702
703
/**
704
* Something errorred, so inactivate, fire error callback and clean up
705
* @param {goog.net.ErrorCode} errorCode The error code.
706
* @param {Error} err The error object.
707
* @private
708
*/
709
goog.net.XhrIo.prototype.error_ = function(errorCode, err) {
710
this.active_ = false;
711
if (this.xhr_) {
712
this.inAbort_ = true;
713
this.xhr_.abort(); // Ensures XHR isn't hung (FF)
714
this.inAbort_ = false;
715
}
716
this.lastError_ = err;
717
this.lastErrorCode_ = errorCode;
718
this.dispatchErrors_();
719
this.cleanUpXhr_();
720
};
721
722
723
/**
724
* Dispatches COMPLETE and ERROR in case of an error. This ensures that we do
725
* not dispatch multiple error events.
726
* @private
727
*/
728
goog.net.XhrIo.prototype.dispatchErrors_ = function() {
729
if (!this.errorDispatched_) {
730
this.errorDispatched_ = true;
731
this.dispatchEvent(goog.net.EventType.COMPLETE);
732
this.dispatchEvent(goog.net.EventType.ERROR);
733
}
734
};
735
736
737
/**
738
* Abort the current XMLHttpRequest
739
* @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
740
* defaults to ABORT.
741
*/
742
goog.net.XhrIo.prototype.abort = function(opt_failureCode) {
743
if (this.xhr_ && this.active_) {
744
goog.log.fine(this.logger_, this.formatMsg_('Aborting'));
745
this.active_ = false;
746
this.inAbort_ = true;
747
this.xhr_.abort();
748
this.inAbort_ = false;
749
this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
750
this.dispatchEvent(goog.net.EventType.COMPLETE);
751
this.dispatchEvent(goog.net.EventType.ABORT);
752
this.cleanUpXhr_();
753
}
754
};
755
756
757
/**
758
* Nullifies all callbacks to reduce risks of leaks.
759
* @override
760
* @protected
761
*/
762
goog.net.XhrIo.prototype.disposeInternal = function() {
763
if (this.xhr_) {
764
// We explicitly do not call xhr_.abort() unless active_ is still true.
765
// This is to avoid unnecessarily aborting a successful request when
766
// dispose() is called in a callback triggered by a complete response, but
767
// in which browser cleanup has not yet finished.
768
// (See http://b/issue?id=1684217.)
769
if (this.active_) {
770
this.active_ = false;
771
this.inAbort_ = true;
772
this.xhr_.abort();
773
this.inAbort_ = false;
774
}
775
this.cleanUpXhr_(true);
776
}
777
778
goog.net.XhrIo.base(this, 'disposeInternal');
779
};
780
781
782
/**
783
* Internal handler for the XHR object's readystatechange event. This method
784
* checks the status and the readystate and fires the correct callbacks.
785
* If the request has ended, the handlers are cleaned up and the XHR object is
786
* nullified.
787
* @private
788
*/
789
goog.net.XhrIo.prototype.onReadyStateChange_ = function() {
790
if (this.isDisposed()) {
791
// This method is the target of an untracked goog.Timer.callOnce().
792
return;
793
}
794
if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) {
795
// Were not being called from within a call to this.xhr_.send
796
// this.xhr_.abort, or this.xhr_.open, so this is an entry point
797
this.onReadyStateChangeEntryPoint_();
798
} else {
799
this.onReadyStateChangeHelper_();
800
}
801
};
802
803
804
/**
805
* Used to protect the onreadystatechange handler entry point. Necessary
806
* as {#onReadyStateChange_} maybe called from within send or abort, this
807
* method is only called when {#onReadyStateChange_} is called as an
808
* entry point.
809
* {@see #protectEntryPoints}
810
* @private
811
*/
812
goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {
813
this.onReadyStateChangeHelper_();
814
};
815
816
817
/**
818
* Helper for {@link #onReadyStateChange_}. This is used so that
819
* entry point calls to {@link #onReadyStateChange_} can be routed through
820
* {@link #onReadyStateChangeEntryPoint_}.
821
* @private
822
*/
823
goog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() {
824
if (!this.active_) {
825
// can get called inside abort call
826
return;
827
}
828
829
if (typeof goog == 'undefined') {
830
// NOTE(user): If goog is undefined then the callback has occurred as the
831
// application is unloading and will error. Thus we let it silently fail.
832
833
} else if (
834
this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] &&
835
this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE &&
836
this.getStatus() == 2) {
837
// NOTE(user): In IE if send() errors on a *local* request the readystate
838
// is still changed to COMPLETE. We need to ignore it and allow the
839
// try/catch around send() to pick up the error.
840
goog.log.fine(
841
this.logger_,
842
this.formatMsg_('Local request error detected and ignored'));
843
844
} else {
845
// In IE when the response has been cached we sometimes get the callback
846
// from inside the send call and this usually breaks code that assumes that
847
// XhrIo is asynchronous. If that is the case we delay the callback
848
// using a timer.
849
if (this.inSend_ &&
850
this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) {
851
goog.Timer.callOnce(this.onReadyStateChange_, 0, this);
852
return;
853
}
854
855
this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
856
857
// readyState indicates the transfer has finished
858
if (this.isComplete()) {
859
goog.log.fine(this.logger_, this.formatMsg_('Request complete'));
860
861
this.active_ = false;
862
863
try {
864
// Call the specific callbacks for success or failure. Only call the
865
// success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED)
866
if (this.isSuccess()) {
867
this.dispatchEvent(goog.net.EventType.COMPLETE);
868
this.dispatchEvent(goog.net.EventType.SUCCESS);
869
} else {
870
this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
871
this.lastError_ =
872
this.getStatusText() + ' [' + this.getStatus() + ']';
873
this.dispatchErrors_();
874
}
875
} finally {
876
this.cleanUpXhr_();
877
}
878
}
879
}
880
};
881
882
883
/**
884
* Internal handler for the XHR object's onprogress event. Fires both a generic
885
* PROGRESS event and either a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event to
886
* allow specific binding for each XHR progress event.
887
* @param {!ProgressEvent} e XHR progress event.
888
* @param {boolean=} opt_isDownload Whether the current progress event is from a
889
* download. Used to determine whether DOWNLOAD_PROGRESS or UPLOAD_PROGRESS
890
* event should be dispatched.
891
* @private
892
*/
893
goog.net.XhrIo.prototype.onProgressHandler_ = function(e, opt_isDownload) {
894
goog.asserts.assert(
895
e.type === goog.net.EventType.PROGRESS,
896
'goog.net.EventType.PROGRESS is of the same type as raw XHR progress.');
897
this.dispatchEvent(
898
goog.net.XhrIo.buildProgressEvent_(e, goog.net.EventType.PROGRESS));
899
this.dispatchEvent(
900
goog.net.XhrIo.buildProgressEvent_(
901
e, opt_isDownload ? goog.net.EventType.DOWNLOAD_PROGRESS :
902
goog.net.EventType.UPLOAD_PROGRESS));
903
};
904
905
906
/**
907
* Creates a representation of the native ProgressEvent. IE doesn't support
908
* constructing ProgressEvent via "new", and the alternatives (e.g.,
909
* ProgressEvent.initProgressEvent) are non-standard or deprecated.
910
* @param {!ProgressEvent} e XHR progress event.
911
* @param {!goog.net.EventType} eventType The type of the event.
912
* @return {!ProgressEvent} The progress event.
913
* @private
914
*/
915
goog.net.XhrIo.buildProgressEvent_ = function(e, eventType) {
916
return /** @type {!ProgressEvent} */ ({
917
type: eventType,
918
lengthComputable: e.lengthComputable,
919
loaded: e.loaded,
920
total: e.total
921
});
922
};
923
924
925
/**
926
* Remove the listener to protect against leaks, and nullify the XMLHttpRequest
927
* object.
928
* @param {boolean=} opt_fromDispose If this is from the dispose (don't want to
929
* fire any events).
930
* @private
931
*/
932
goog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) {
933
if (this.xhr_) {
934
// Cancel any pending timeout event handler.
935
this.cleanUpTimeoutTimer_();
936
937
// Save reference so we can mark it as closed after the READY event. The
938
// READY event may trigger another request, thus we must nullify this.xhr_
939
var xhr = this.xhr_;
940
var clearedOnReadyStateChange =
941
this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ?
942
goog.nullFunction :
943
null;
944
this.xhr_ = null;
945
this.xhrOptions_ = null;
946
947
if (!opt_fromDispose) {
948
this.dispatchEvent(goog.net.EventType.READY);
949
}
950
951
try {
952
// NOTE(user): Not nullifying in FireFox can still leak if the callbacks
953
// are defined in the same scope as the instance of XhrIo. But, IE doesn't
954
// allow you to set the onreadystatechange to NULL so nullFunction is
955
// used.
956
xhr.onreadystatechange = clearedOnReadyStateChange;
957
} catch (e) {
958
// This seems to occur with a Gears HTTP request. Delayed the setting of
959
// this onreadystatechange until after READY is sent out and catching the
960
// error to see if we can track down the problem.
961
goog.log.error(
962
this.logger_,
963
'Problem encountered resetting onreadystatechange: ' + e.message);
964
}
965
}
966
};
967
968
969
/**
970
* Make sure the timeout timer isn't running.
971
* @private
972
*/
973
goog.net.XhrIo.prototype.cleanUpTimeoutTimer_ = function() {
974
if (this.xhr_ && this.useXhr2Timeout_) {
975
this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] = null;
976
}
977
if (goog.isNumber(this.timeoutId_)) {
978
goog.Timer.clear(this.timeoutId_);
979
this.timeoutId_ = null;
980
}
981
};
982
983
984
/**
985
* @return {boolean} Whether there is an active request.
986
*/
987
goog.net.XhrIo.prototype.isActive = function() {
988
return !!this.xhr_;
989
};
990
991
992
/**
993
* @return {boolean} Whether the request has completed.
994
*/
995
goog.net.XhrIo.prototype.isComplete = function() {
996
return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE;
997
};
998
999
1000
/**
1001
* @return {boolean} Whether the request completed with a success.
1002
*/
1003
goog.net.XhrIo.prototype.isSuccess = function() {
1004
var status = this.getStatus();
1005
// A zero status code is considered successful for local files.
1006
return goog.net.HttpStatus.isSuccess(status) ||
1007
status === 0 && !this.isLastUriEffectiveSchemeHttp_();
1008
};
1009
1010
1011
/**
1012
* @return {boolean} whether the effective scheme of the last URI that was
1013
* fetched was 'http' or 'https'.
1014
* @private
1015
*/
1016
goog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {
1017
var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));
1018
return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(scheme);
1019
};
1020
1021
1022
/**
1023
* Get the readystate from the Xhr object
1024
* Will only return correct result when called from the context of a callback
1025
* @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*.
1026
*/
1027
goog.net.XhrIo.prototype.getReadyState = function() {
1028
return this.xhr_ ?
1029
/** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) :
1030
goog.net.XmlHttp.ReadyState
1031
.UNINITIALIZED;
1032
};
1033
1034
1035
/**
1036
* Get the status from the Xhr object
1037
* Will only return correct result when called from the context of a callback
1038
* @return {number} Http status.
1039
*/
1040
goog.net.XhrIo.prototype.getStatus = function() {
1041
/**
1042
* IE doesn't like you checking status until the readystate is greater than 2
1043
* (i.e. it is receiving or complete). The try/catch is used for when the
1044
* page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
1045
*/
1046
try {
1047
return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
1048
this.xhr_.status :
1049
-1;
1050
} catch (e) {
1051
return -1;
1052
}
1053
};
1054
1055
1056
/**
1057
* Get the status text from the Xhr object
1058
* Will only return correct result when called from the context of a callback
1059
* @return {string} Status text.
1060
*/
1061
goog.net.XhrIo.prototype.getStatusText = function() {
1062
/**
1063
* IE doesn't like you checking status until the readystate is greater than 2
1064
* (i.e. it is receiving or complete). The try/catch is used for when the
1065
* page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
1066
*/
1067
try {
1068
return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
1069
this.xhr_.statusText :
1070
'';
1071
} catch (e) {
1072
goog.log.fine(this.logger_, 'Can not get status: ' + e.message);
1073
return '';
1074
}
1075
};
1076
1077
1078
/**
1079
* Get the last Uri that was requested
1080
* @return {string} Last Uri.
1081
*/
1082
goog.net.XhrIo.prototype.getLastUri = function() {
1083
return String(this.lastUri_);
1084
};
1085
1086
1087
/**
1088
* Get the response text from the Xhr object
1089
* Will only return correct result when called from the context of a callback.
1090
* @return {string} Result from the server, or '' if no result available.
1091
*/
1092
goog.net.XhrIo.prototype.getResponseText = function() {
1093
try {
1094
return this.xhr_ ? this.xhr_.responseText : '';
1095
} catch (e) {
1096
// http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
1097
// states that responseText should return '' (and responseXML null)
1098
// when the state is not LOADING or DONE. Instead, IE can
1099
// throw unexpected exceptions, for example when a request is aborted
1100
// or no data is available yet.
1101
goog.log.fine(this.logger_, 'Can not get responseText: ' + e.message);
1102
return '';
1103
}
1104
};
1105
1106
1107
/**
1108
* Get the response body from the Xhr object. This property is only available
1109
* in IE since version 7 according to MSDN:
1110
* http://msdn.microsoft.com/en-us/library/ie/ms534368(v=vs.85).aspx
1111
* Will only return correct result when called from the context of a callback.
1112
*
1113
* One option is to construct a VBArray from the returned object and convert
1114
* it to a JavaScript array using the toArray method:
1115
* {@code (new window['VBArray'](xhrIo.getResponseBody())).toArray()}
1116
* This will result in an array of numbers in the range of [0..255]
1117
*
1118
* Another option is to use the VBScript CStr method to convert it into a
1119
* string as outlined in http://stackoverflow.com/questions/1919972
1120
*
1121
* @return {Object} Binary result from the server or null if not available.
1122
*/
1123
goog.net.XhrIo.prototype.getResponseBody = function() {
1124
1125
try {
1126
if (this.xhr_ && 'responseBody' in this.xhr_) {
1127
return this.xhr_['responseBody'];
1128
}
1129
} catch (e) {
1130
// IE can throw unexpected exceptions, for example when a request is aborted
1131
// or no data is yet available.
1132
goog.log.fine(this.logger_, 'Can not get responseBody: ' + e.message);
1133
}
1134
return null;
1135
};
1136
1137
1138
/**
1139
* Get the response XML from the Xhr object
1140
* Will only return correct result when called from the context of a callback.
1141
* @return {Document} The DOM Document representing the XML file, or null
1142
* if no result available.
1143
*/
1144
goog.net.XhrIo.prototype.getResponseXml = function() {
1145
1146
try {
1147
return this.xhr_ ? this.xhr_.responseXML : null;
1148
} catch (e) {
1149
goog.log.fine(this.logger_, 'Can not get responseXML: ' + e.message);
1150
return null;
1151
}
1152
};
1153
1154
1155
/**
1156
* Get the response and evaluates it as JSON from the Xhr object
1157
* Will only return correct result when called from the context of a callback
1158
* @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
1159
* stripping of the response before parsing. This needs to be set only if
1160
* your backend server prepends the same prefix string to the JSON response.
1161
* @throws Error if the response text is invalid JSON.
1162
* @return {Object|undefined} JavaScript object.
1163
*/
1164
goog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
1165
if (!this.xhr_) {
1166
return undefined;
1167
}
1168
1169
var responseText = this.xhr_.responseText;
1170
if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
1171
responseText = responseText.substring(opt_xssiPrefix.length);
1172
}
1173
1174
return goog.json.parse(responseText);
1175
};
1176
1177
1178
/**
1179
* Get the response as the type specificed by {@link #setResponseType}. At time
1180
* of writing, this is only directly supported in very recent versions of WebKit
1181
* (10.0.612.1 dev and later). If the field is not supported directly, we will
1182
* try to emulate it.
1183
*
1184
* Emulating the response means following the rules laid out at
1185
* http://www.w3.org/TR/XMLHttpRequest/#the-response-attribute
1186
*
1187
* On browsers with no support for this (Chrome < 10, Firefox < 4, etc), only
1188
* response types of DEFAULT or TEXT may be used, and the response returned will
1189
* be the text response.
1190
*
1191
* On browsers with Mozilla's draft support for array buffers (Firefox 4, 5),
1192
* only response types of DEFAULT, TEXT, and ARRAY_BUFFER may be used, and the
1193
* response returned will be either the text response or the Mozilla
1194
* implementation of the array buffer response.
1195
*
1196
* On browsers will full support, any valid response type supported by the
1197
* browser may be used, and the response provided by the browser will be
1198
* returned.
1199
*
1200
* @return {*} The response.
1201
*/
1202
goog.net.XhrIo.prototype.getResponse = function() {
1203
1204
try {
1205
if (!this.xhr_) {
1206
return null;
1207
}
1208
if ('response' in this.xhr_) {
1209
return this.xhr_.response;
1210
}
1211
switch (this.responseType_) {
1212
case goog.net.XhrIo.ResponseType.DEFAULT:
1213
case goog.net.XhrIo.ResponseType.TEXT:
1214
return this.xhr_.responseText;
1215
// DOCUMENT and BLOB don't need to be handled here because they are
1216
// introduced in the same spec that adds the .response field, and would
1217
// have been caught above.
1218
// ARRAY_BUFFER needs an implementation for Firefox 4, where it was
1219
// implemented using a draft spec rather than the final spec.
1220
case goog.net.XhrIo.ResponseType.ARRAY_BUFFER:
1221
if ('mozResponseArrayBuffer' in this.xhr_) {
1222
return this.xhr_.mozResponseArrayBuffer;
1223
}
1224
}
1225
// Fell through to a response type that is not supported on this browser.
1226
goog.log.error(
1227
this.logger_, 'Response type ' + this.responseType_ + ' is not ' +
1228
'supported on this browser');
1229
return null;
1230
} catch (e) {
1231
goog.log.fine(this.logger_, 'Can not get response: ' + e.message);
1232
return null;
1233
}
1234
};
1235
1236
1237
/**
1238
* Get the value of the response-header with the given name from the Xhr object
1239
* Will only return correct result when called from the context of a callback
1240
* and the request has completed
1241
* @param {string} key The name of the response-header to retrieve.
1242
* @return {string|undefined} The value of the response-header named key.
1243
*/
1244
goog.net.XhrIo.prototype.getResponseHeader = function(key) {
1245
if (!this.xhr_ || !this.isComplete()) {
1246
return undefined;
1247
}
1248
1249
var value = this.xhr_.getResponseHeader(key);
1250
return goog.isNull(value) ? undefined : value;
1251
};
1252
1253
1254
/**
1255
* Gets the text of all the headers in the response.
1256
* Will only return correct result when called from the context of a callback
1257
* and the request has completed.
1258
* @return {string} The value of the response headers or empty string.
1259
*/
1260
goog.net.XhrIo.prototype.getAllResponseHeaders = function() {
1261
return this.xhr_ && this.isComplete() ? this.xhr_.getAllResponseHeaders() :
1262
'';
1263
};
1264
1265
1266
/**
1267
* Returns all response headers as a key-value map.
1268
* Multiple values for the same header key can be combined into one,
1269
* separated by a comma and a space.
1270
* Note that the native getResponseHeader method for retrieving a single header
1271
* does a case insensitive match on the header name. This method does not
1272
* include any case normalization logic, it will just return a key-value
1273
* representation of the headers.
1274
* See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
1275
* @return {!Object<string, string>} An object with the header keys as keys
1276
* and header values as values.
1277
*/
1278
goog.net.XhrIo.prototype.getResponseHeaders = function() {
1279
var headersObject = {};
1280
var headersArray = this.getAllResponseHeaders().split('\r\n');
1281
for (var i = 0; i < headersArray.length; i++) {
1282
if (goog.string.isEmptyOrWhitespace(headersArray[i])) {
1283
continue;
1284
}
1285
var keyValue = goog.string.splitLimit(headersArray[i], ': ', 2);
1286
if (headersObject[keyValue[0]]) {
1287
headersObject[keyValue[0]] += ', ' + keyValue[1];
1288
} else {
1289
headersObject[keyValue[0]] = keyValue[1];
1290
}
1291
}
1292
return headersObject;
1293
};
1294
1295
1296
/**
1297
* Get the value of the response-header with the given name from the Xhr object.
1298
* As opposed to {@link #getResponseHeader}, this method does not require that
1299
* the request has completed.
1300
* @param {string} key The name of the response-header to retrieve.
1301
* @return {?string} The value of the response-header, or null if it is
1302
* unavailable.
1303
*/
1304
goog.net.XhrIo.prototype.getStreamingResponseHeader = function(key) {
1305
return this.xhr_ ? this.xhr_.getResponseHeader(key) : null;
1306
};
1307
1308
1309
/**
1310
* Gets the text of all the headers in the response. As opposed to
1311
* {@link #getAllResponseHeaders}, this method does not require that the request
1312
* has completed.
1313
* @return {string} The value of the response headers or empty string.
1314
*/
1315
goog.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() {
1316
return this.xhr_ ? this.xhr_.getAllResponseHeaders() : '';
1317
};
1318
1319
1320
/**
1321
* Get the last error message
1322
* @return {goog.net.ErrorCode} Last error code.
1323
*/
1324
goog.net.XhrIo.prototype.getLastErrorCode = function() {
1325
return this.lastErrorCode_;
1326
};
1327
1328
1329
/**
1330
* Get the last error message
1331
* @return {string} Last error message.
1332
*/
1333
goog.net.XhrIo.prototype.getLastError = function() {
1334
return goog.isString(this.lastError_) ? this.lastError_ :
1335
String(this.lastError_);
1336
};
1337
1338
1339
/**
1340
* Adds the last method, status and URI to the message. This is used to add
1341
* this information to the logging calls.
1342
* @param {string} msg The message text that we want to add the extra text to.
1343
* @return {string} The message with the extra text appended.
1344
* @private
1345
*/
1346
goog.net.XhrIo.prototype.formatMsg_ = function(msg) {
1347
return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' +
1348
this.getStatus() + ']';
1349
};
1350
1351
1352
// Register the xhr handler as an entry point, so that
1353
// it can be monitored for exception handling, etc.
1354
goog.debug.entryPointRegistry.register(
1355
/**
1356
* @param {function(!Function): !Function} transformer The transforming
1357
* function.
1358
*/
1359
function(transformer) {
1360
goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
1361
transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
1362
});
1363
1364