Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/labs/net/webchannel/basetestchannel.js
1865 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 Base TestChannel implementation.
17
*
18
*/
19
20
21
goog.provide('goog.labs.net.webChannel.BaseTestChannel');
22
23
goog.require('goog.labs.net.webChannel.Channel');
24
goog.require('goog.labs.net.webChannel.ChannelRequest');
25
goog.require('goog.labs.net.webChannel.WebChannelDebug');
26
goog.require('goog.labs.net.webChannel.requestStats');
27
goog.require('goog.labs.net.webChannel.requestStats.Stat');
28
goog.require('goog.net.WebChannel');
29
30
31
32
/**
33
* A TestChannel is used during the first part of channel negotiation
34
* with the server to create the channel. It helps us determine whether we're
35
* behind a buffering proxy.
36
*
37
* @constructor
38
* @struct
39
* @param {!goog.labs.net.webChannel.Channel} channel The channel
40
* that owns this test channel.
41
* @param {!goog.labs.net.webChannel.WebChannelDebug} channelDebug A
42
* WebChannelDebug instance to use for logging.
43
* @implements {goog.labs.net.webChannel.Channel}
44
*/
45
goog.labs.net.webChannel.BaseTestChannel = function(channel, channelDebug) {
46
/**
47
* The channel that owns this test channel
48
* @private {!goog.labs.net.webChannel.Channel}
49
*/
50
this.channel_ = channel;
51
52
/**
53
* The channel debug to use for logging
54
* @private {!goog.labs.net.webChannel.WebChannelDebug}
55
*/
56
this.channelDebug_ = channelDebug;
57
58
/**
59
* Extra HTTP headers to add to all the requests sent to the server.
60
* @private {Object}
61
*/
62
this.extraHeaders_ = null;
63
64
/**
65
* The test request.
66
* @private {goog.labs.net.webChannel.ChannelRequest}
67
*/
68
this.request_ = null;
69
70
/**
71
* Whether we have received the first result as an intermediate result. This
72
* helps us determine whether we're behind a buffering proxy.
73
* @private {boolean}
74
*/
75
this.receivedIntermediateResult_ = false;
76
77
/**
78
* The relative path for test requests.
79
* @private {?string}
80
*/
81
this.path_ = null;
82
83
/**
84
* The last status code received.
85
* @private {number}
86
*/
87
this.lastStatusCode_ = -1;
88
89
/**
90
* A subdomain prefix for using a subdomain in IE for the backchannel
91
* requests.
92
* @private {?string}
93
*/
94
this.hostPrefix_ = null;
95
96
/**
97
* The effective client protocol as indicated by the initial handshake
98
* response via the x-client-wire-protocol header.
99
*
100
* @private {?string}
101
*/
102
this.clientProtocol_ = null;
103
};
104
105
106
goog.scope(function() {
107
var WebChannel = goog.net.WebChannel;
108
var BaseTestChannel = goog.labs.net.webChannel.BaseTestChannel;
109
var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
110
var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
111
var requestStats = goog.labs.net.webChannel.requestStats;
112
var Channel = goog.labs.net.webChannel.Channel;
113
114
115
/**
116
* Enum type for the test channel state machine
117
* @enum {number}
118
* @private
119
*/
120
BaseTestChannel.State_ = {
121
/**
122
* The state for the TestChannel state machine where we making the
123
* initial call to get the server configured parameters.
124
*/
125
INIT: 0,
126
127
/**
128
* The state for the TestChannel state machine where we're checking to
129
* se if we're behind a buffering proxy.
130
*/
131
CONNECTION_TESTING: 1
132
};
133
134
135
/**
136
* The state of the state machine for this object.
137
*
138
* @private {?BaseTestChannel.State_}
139
*/
140
BaseTestChannel.prototype.state_ = null;
141
142
143
/**
144
* Sets extra HTTP headers to add to all the requests sent to the server.
145
*
146
* @param {Object} extraHeaders The HTTP headers.
147
*/
148
BaseTestChannel.prototype.setExtraHeaders = function(extraHeaders) {
149
this.extraHeaders_ = extraHeaders;
150
};
151
152
153
/**
154
* Starts the test channel. This initiates connections to the server.
155
*
156
* @param {string} path The relative uri for the test connection.
157
*/
158
BaseTestChannel.prototype.connect = function(path) {
159
this.path_ = path;
160
var sendDataUri = this.channel_.getForwardChannelUri(this.path_);
161
162
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_START);
163
164
// If the channel already has the result of the handshake, then skip it.
165
var handshakeResult = this.channel_.getConnectionState().handshakeResult;
166
if (goog.isDefAndNotNull(handshakeResult)) {
167
this.hostPrefix_ = this.channel_.correctHostPrefix(handshakeResult[0]);
168
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
169
this.checkBufferingProxy_();
170
return;
171
}
172
173
// the first request returns server specific parameters
174
sendDataUri.setParameterValues('MODE', 'init');
175
176
// http-session-id to be generated as the response
177
if (!this.channel_.getBackgroundChannelTest() &&
178
this.channel_.getHttpSessionIdParam()) {
179
sendDataUri.setParameterValues(WebChannel.X_HTTP_SESSION_ID,
180
this.channel_.getHttpSessionIdParam());
181
}
182
183
this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
184
185
this.request_.setExtraHeaders(this.extraHeaders_);
186
187
this.request_.xmlHttpGet(
188
sendDataUri, false /* decodeChunks */, null /* hostPrefix */,
189
true /* opt_noClose */);
190
this.state_ = BaseTestChannel.State_.INIT;
191
};
192
193
194
/**
195
* Begins the second stage of the test channel where we test to see if we're
196
* behind a buffering proxy. The server sends back a multi-chunked response
197
* with the first chunk containing the content '1' and then two seconds later
198
* sending the second chunk containing the content '2'. Depending on how we
199
* receive the content, we can tell if we're behind a buffering proxy.
200
* @private
201
*/
202
BaseTestChannel.prototype.checkBufferingProxy_ = function() {
203
this.channelDebug_.debug('TestConnection: starting stage 2');
204
205
// If the test result is already available, skip its execution.
206
var bufferingProxyResult =
207
this.channel_.getConnectionState().bufferingProxyResult;
208
if (goog.isDefAndNotNull(bufferingProxyResult)) {
209
this.channelDebug_.debug(
210
'TestConnection: skipping stage 2, precomputed result is ' +
211
bufferingProxyResult ?
212
'Buffered' :
213
'Unbuffered');
214
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
215
if (bufferingProxyResult) { // Buffered/Proxy connection
216
requestStats.notifyStatEvent(requestStats.Stat.PROXY);
217
this.channel_.testConnectionFinished(this, false);
218
} else { // Unbuffered/NoProxy connection
219
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
220
this.channel_.testConnectionFinished(this, true);
221
}
222
return; // Skip the test
223
}
224
this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
225
this.request_.setExtraHeaders(this.extraHeaders_);
226
var recvDataUri = this.channel_.getBackChannelUri(
227
this.hostPrefix_,
228
/** @type {string} */ (this.path_));
229
230
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
231
recvDataUri.setParameterValues('TYPE', 'xmlhttp');
232
233
var param = this.channel_.getHttpSessionIdParam();
234
var value = this.channel_.getHttpSessionId();
235
if (param && value) {
236
recvDataUri.setParameterValue(param, value);
237
}
238
239
this.request_.xmlHttpGet(
240
recvDataUri, false /** decodeChunks */, this.hostPrefix_,
241
false /** opt_noClose */);
242
};
243
244
245
/**
246
* @override
247
*/
248
BaseTestChannel.prototype.createXhrIo = function(hostPrefix) {
249
return this.channel_.createXhrIo(hostPrefix);
250
};
251
252
253
/**
254
* Aborts the test channel.
255
*/
256
BaseTestChannel.prototype.abort = function() {
257
if (this.request_) {
258
this.request_.cancel();
259
this.request_ = null;
260
}
261
this.lastStatusCode_ = -1;
262
};
263
264
265
/**
266
* Returns whether the test channel is closed. The ChannelRequest object expects
267
* this method to be implemented on its handler.
268
*
269
* @return {boolean} Whether the channel is closed.
270
* @override
271
*/
272
BaseTestChannel.prototype.isClosed = function() {
273
return false;
274
};
275
276
277
/**
278
* Callback from ChannelRequest for when new data is received
279
*
280
* @param {ChannelRequest} req The request object.
281
* @param {string} responseText The text of the response.
282
* @override
283
*/
284
BaseTestChannel.prototype.onRequestData = function(req, responseText) {
285
this.lastStatusCode_ = req.getLastStatusCode();
286
if (this.state_ == BaseTestChannel.State_.INIT) {
287
this.channelDebug_.debug('TestConnection: Got data for stage 1');
288
289
this.applyControlHeaders_(req);
290
291
if (!responseText) {
292
this.channelDebug_.debug('TestConnection: Null responseText');
293
// The server should always send text; something is wrong here
294
this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
295
return;
296
}
297
298
299
try {
300
var channel = /** @type {!goog.labs.net.webChannel.WebChannelBase} */ (
301
this.channel_);
302
var respArray = channel.getWireCodec().decodeMessage(responseText);
303
} catch (e) {
304
this.channelDebug_.dumpException(e);
305
this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
306
return;
307
}
308
this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
309
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
310
if (this.receivedIntermediateResult_) {
311
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_TWO);
312
} else {
313
// '11111' is used instead of '1' to prevent a small amount of buffering
314
// by Safari.
315
if (responseText == '11111') {
316
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_ONE);
317
this.receivedIntermediateResult_ = true;
318
if (this.checkForEarlyNonBuffered_()) {
319
// If early chunk detection is on, and we passed the tests,
320
// assume HTTP_OK, cancel the test and turn on noproxy mode.
321
this.lastStatusCode_ = 200;
322
this.request_.cancel();
323
this.channelDebug_.debug(
324
'Test connection succeeded; using streaming connection');
325
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
326
this.channel_.testConnectionFinished(this, true);
327
}
328
} else {
329
requestStats.notifyStatEvent(
330
requestStats.Stat.TEST_STAGE_TWO_DATA_BOTH);
331
this.receivedIntermediateResult_ = false;
332
}
333
}
334
}
335
};
336
337
338
/**
339
* Callback from ChannelRequest that indicates a request has completed.
340
*
341
* @param {!ChannelRequest} req The request object.
342
* @override
343
*/
344
BaseTestChannel.prototype.onRequestComplete = function(req) {
345
this.lastStatusCode_ = this.request_.getLastStatusCode();
346
if (!this.request_.getSuccess()) {
347
this.channelDebug_.debug(
348
'TestConnection: request failed, in state ' + this.state_);
349
if (this.state_ == BaseTestChannel.State_.INIT) {
350
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_FAILED);
351
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
352
requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_FAILED);
353
}
354
this.channel_.testConnectionFailure(
355
this,
356
/** @type {ChannelRequest.Error} */
357
(this.request_.getLastError()));
358
return;
359
}
360
361
if (this.state_ == BaseTestChannel.State_.INIT) {
362
this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
363
364
this.channelDebug_.debug(
365
'TestConnection: request complete for initial check');
366
367
this.checkBufferingProxy_();
368
} else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
369
this.channelDebug_.debug('TestConnection: request complete for stage 2');
370
371
var goodConn = this.receivedIntermediateResult_;
372
if (goodConn) {
373
this.channelDebug_.debug(
374
'Test connection succeeded; using streaming connection');
375
requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
376
this.channel_.testConnectionFinished(this, true);
377
} else {
378
this.channelDebug_.debug('Test connection failed; not using streaming');
379
requestStats.notifyStatEvent(requestStats.Stat.PROXY);
380
this.channel_.testConnectionFinished(this, false);
381
}
382
}
383
};
384
385
386
/**
387
* Apply any control headers from the initial handshake response.
388
*
389
* @param {!ChannelRequest} req The request object.
390
* @private
391
*/
392
BaseTestChannel.prototype.applyControlHeaders_ = function(req) {
393
if (this.channel_.getBackgroundChannelTest()) {
394
return;
395
}
396
397
var xhr = req.getXhr();
398
if (xhr) {
399
var protocolHeader = xhr.getStreamingResponseHeader(
400
WebChannel.X_CLIENT_WIRE_PROTOCOL);
401
this.clientProtocol_ = protocolHeader ? protocolHeader : null;
402
403
if (this.channel_.getHttpSessionIdParam()) {
404
var httpSessionIdHeader = xhr.getStreamingResponseHeader(
405
WebChannel.X_HTTP_SESSION_ID);
406
if (httpSessionIdHeader) {
407
this.channel_.setHttpSessionId(httpSessionIdHeader);
408
} else {
409
this.channelDebug_.warning(
410
'Missing X_HTTP_SESSION_ID in the handshake response');
411
}
412
}
413
}
414
};
415
416
417
/**
418
* @return {?string} The client protocol as recorded with the init handshake
419
* request.
420
*/
421
BaseTestChannel.prototype.getClientProtocol = function() {
422
return this.clientProtocol_;
423
};
424
425
426
/**
427
* Returns the last status code received for a request.
428
* @return {number} The last status code received for a request.
429
*/
430
BaseTestChannel.prototype.getLastStatusCode = function() {
431
return this.lastStatusCode_;
432
};
433
434
435
/**
436
* @return {boolean} Whether we should be using secondary domains when the
437
* server instructs us to do so.
438
* @override
439
*/
440
BaseTestChannel.prototype.shouldUseSecondaryDomains = function() {
441
return this.channel_.shouldUseSecondaryDomains();
442
};
443
444
445
/**
446
* @override
447
*/
448
BaseTestChannel.prototype.isActive = function() {
449
return this.channel_.isActive();
450
};
451
452
453
/**
454
* @return {boolean} True if test stage 2 detected a non-buffered
455
* channel early and early no buffering detection is enabled.
456
* @private
457
*/
458
BaseTestChannel.prototype.checkForEarlyNonBuffered_ = function() {
459
return ChannelRequest.supportsXhrStreaming();
460
};
461
462
463
/**
464
* @override
465
*/
466
BaseTestChannel.prototype.getForwardChannelUri = goog.abstractMethod;
467
468
469
/**
470
* @override
471
*/
472
BaseTestChannel.prototype.getBackChannelUri = goog.abstractMethod;
473
474
475
/**
476
* @override
477
*/
478
BaseTestChannel.prototype.correctHostPrefix = goog.abstractMethod;
479
480
481
/**
482
* @override
483
*/
484
BaseTestChannel.prototype.createDataUri = goog.abstractMethod;
485
486
487
/**
488
* @override
489
*/
490
BaseTestChannel.prototype.testConnectionFinished = goog.abstractMethod;
491
492
493
/**
494
* @override
495
*/
496
BaseTestChannel.prototype.testConnectionFailure = goog.abstractMethod;
497
498
499
/**
500
* @override
501
*/
502
BaseTestChannel.prototype.getConnectionState = goog.abstractMethod;
503
504
505
/**
506
* @override
507
*/
508
BaseTestChannel.prototype.setHttpSessionIdParam = goog.abstractMethod;
509
510
511
/**
512
* @override
513
*/
514
BaseTestChannel.prototype.getHttpSessionIdParam = goog.abstractMethod;
515
516
517
/**
518
* @override
519
*/
520
BaseTestChannel.prototype.setHttpSessionId = goog.abstractMethod;
521
522
523
/**
524
* @override
525
*/
526
BaseTestChannel.prototype.getHttpSessionId = goog.abstractMethod;
527
528
529
/**
530
* @override
531
*/
532
BaseTestChannel.prototype.getBackgroundChannelTest = goog.abstractMethod;
533
}); // goog.scope
534
535