browsertestchannel.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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 BrowserTestChannel class. A
  16. * BrowserTestChannel is used during the first part of channel negotiation
  17. * with the server to create the channel. It helps us determine whether we're
  18. * behind a buffering proxy. It also runs the logic to see if the channel
  19. * has been blocked by a network administrator. This class is part of the
  20. * BrowserChannel implementation and is not for use by normal application code.
  21. *
  22. */
  23. goog.provide('goog.net.BrowserTestChannel');
  24. goog.require('goog.json.NativeJsonProcessor');
  25. goog.require('goog.net.ChannelRequest');
  26. goog.require('goog.net.ChannelRequest.Error');
  27. goog.require('goog.net.tmpnetwork');
  28. goog.require('goog.string.Parser');
  29. /**
  30. * Encapsulates the logic for a single BrowserTestChannel.
  31. *
  32. * @constructor
  33. * @param {goog.net.BrowserChannel} channel The BrowserChannel that owns this
  34. * test channel.
  35. * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for
  36. * logging.
  37. * @final
  38. */
  39. goog.net.BrowserTestChannel = function(channel, channelDebug) {
  40. /**
  41. * The BrowserChannel that owns this test channel
  42. * @type {goog.net.BrowserChannel}
  43. * @private
  44. */
  45. this.channel_ = channel;
  46. /**
  47. * The channel debug to use for logging
  48. * @type {goog.net.ChannelDebug}
  49. * @private
  50. */
  51. this.channelDebug_ = channelDebug;
  52. /**
  53. * Parser for a response payload. The parser should return an array.
  54. * @type {goog.string.Parser}
  55. * @private
  56. */
  57. this.parser_ = new goog.json.NativeJsonProcessor();
  58. };
  59. /**
  60. * Extra HTTP headers to add to all the requests sent to the server.
  61. * @type {Object}
  62. * @private
  63. */
  64. goog.net.BrowserTestChannel.prototype.extraHeaders_ = null;
  65. /**
  66. * The test request.
  67. * @type {goog.net.ChannelRequest}
  68. * @private
  69. */
  70. goog.net.BrowserTestChannel.prototype.request_ = null;
  71. /**
  72. * Whether we have received the first result as an intermediate result. This
  73. * helps us determine whether we're behind a buffering proxy.
  74. * @type {boolean}
  75. * @private
  76. */
  77. goog.net.BrowserTestChannel.prototype.receivedIntermediateResult_ = false;
  78. /**
  79. * The time when the test request was started. We use timing in IE as
  80. * a heuristic for whether we're behind a buffering proxy.
  81. * @type {?number}
  82. * @private
  83. */
  84. goog.net.BrowserTestChannel.prototype.startTime_ = null;
  85. /**
  86. * The time for of the first result part. We use timing in IE as a
  87. * heuristic for whether we're behind a buffering proxy.
  88. * @type {?number}
  89. * @private
  90. */
  91. goog.net.BrowserTestChannel.prototype.firstTime_ = null;
  92. /**
  93. * The time for of the last result part. We use timing in IE as a
  94. * heuristic for whether we're behind a buffering proxy.
  95. * @type {?number}
  96. * @private
  97. */
  98. goog.net.BrowserTestChannel.prototype.lastTime_ = null;
  99. /**
  100. * The relative path for test requests.
  101. * @type {?string}
  102. * @private
  103. */
  104. goog.net.BrowserTestChannel.prototype.path_ = null;
  105. /**
  106. * The state of the state machine for this object.
  107. *
  108. * @type {?number}
  109. * @private
  110. */
  111. goog.net.BrowserTestChannel.prototype.state_ = null;
  112. /**
  113. * The last status code received.
  114. * @type {number}
  115. * @private
  116. */
  117. goog.net.BrowserTestChannel.prototype.lastStatusCode_ = -1;
  118. /**
  119. * A subdomain prefix for using a subdomain in IE for the backchannel
  120. * requests.
  121. * @type {?string}
  122. * @private
  123. */
  124. goog.net.BrowserTestChannel.prototype.hostPrefix_ = null;
  125. /**
  126. * A subdomain prefix for testing whether the channel was disabled by
  127. * a network administrator;
  128. * @type {?string}
  129. * @private
  130. */
  131. goog.net.BrowserTestChannel.prototype.blockedPrefix_ = null;
  132. /**
  133. * Enum type for the browser test channel state machine
  134. * @enum {number}
  135. * @private
  136. */
  137. goog.net.BrowserTestChannel.State_ = {
  138. /**
  139. * The state for the BrowserTestChannel state machine where we making the
  140. * initial call to get the server configured parameters.
  141. */
  142. INIT: 0,
  143. /**
  144. * The state for the BrowserTestChannel state machine where we're checking to
  145. * see if the channel has been blocked.
  146. */
  147. CHECKING_BLOCKED: 1,
  148. /**
  149. * The state for the BrowserTestChannel state machine where we're checking to
  150. * se if we're behind a buffering proxy.
  151. */
  152. CONNECTION_TESTING: 2
  153. };
  154. /**
  155. * Time in MS for waiting for the request to see if the channel is blocked.
  156. * If the response takes longer than this many ms, we assume the request has
  157. * failed.
  158. * @type {number}
  159. * @private
  160. */
  161. goog.net.BrowserTestChannel.BLOCKED_TIMEOUT_ = 5000;
  162. /**
  163. * Number of attempts to try to see if the check to see if we're blocked
  164. * succeeds. Sometimes the request can fail because of flaky network conditions
  165. * and checking multiple times reduces false positives.
  166. * @type {number}
  167. * @private
  168. */
  169. goog.net.BrowserTestChannel.BLOCKED_RETRIES_ = 3;
  170. /**
  171. * Time in ms between retries of the blocked request
  172. * @type {number}
  173. * @private
  174. */
  175. goog.net.BrowserTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_ = 2000;
  176. /**
  177. * Time between chunks in the test connection that indicates that we
  178. * are not behind a buffering proxy. This value should be less than or
  179. * equals to the time between chunks sent from the server.
  180. * @type {number}
  181. * @private
  182. */
  183. goog.net.BrowserTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_ = 500;
  184. /**
  185. * Sets extra HTTP headers to add to all the requests sent to the server.
  186. *
  187. * @param {Object} extraHeaders The HTTP headers.
  188. */
  189. goog.net.BrowserTestChannel.prototype.setExtraHeaders = function(extraHeaders) {
  190. this.extraHeaders_ = extraHeaders;
  191. };
  192. /**
  193. * Sets a new parser for the response payload.
  194. * @param {!goog.string.Parser} parser Parser.
  195. */
  196. goog.net.BrowserTestChannel.prototype.setParser = function(parser) {
  197. this.parser_ = parser;
  198. };
  199. /**
  200. * Starts the test channel. This initiates connections to the server.
  201. *
  202. * @param {string} path The relative uri for the test connection.
  203. */
  204. goog.net.BrowserTestChannel.prototype.connect = function(path) {
  205. this.path_ = path;
  206. var sendDataUri = this.channel_.getForwardChannelUri(this.path_);
  207. goog.net.BrowserChannel.notifyStatEvent(
  208. goog.net.BrowserChannel.Stat.TEST_STAGE_ONE_START);
  209. this.startTime_ = goog.now();
  210. // If the channel already has the result of the first test, then skip it.
  211. var firstTestResults = this.channel_.getFirstTestResults();
  212. if (goog.isDefAndNotNull(firstTestResults)) {
  213. this.hostPrefix_ = this.channel_.correctHostPrefix(firstTestResults[0]);
  214. this.blockedPrefix_ = firstTestResults[1];
  215. if (this.blockedPrefix_) {
  216. this.state_ = goog.net.BrowserTestChannel.State_.CHECKING_BLOCKED;
  217. this.checkBlocked_();
  218. } else {
  219. this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
  220. this.connectStage2_();
  221. }
  222. return;
  223. }
  224. // the first request returns server specific parameters
  225. sendDataUri.setParameterValues('MODE', 'init');
  226. this.request_ =
  227. goog.net.BrowserChannel.createChannelRequest(this, this.channelDebug_);
  228. this.request_.setExtraHeaders(this.extraHeaders_);
  229. this.request_.xmlHttpGet(
  230. sendDataUri, false /* decodeChunks */, null /* hostPrefix */,
  231. true /* opt_noClose */);
  232. this.state_ = goog.net.BrowserTestChannel.State_.INIT;
  233. };
  234. /**
  235. * Checks to see whether the channel is blocked. This is for implementing the
  236. * feature that allows network administrators to block Gmail Chat. The
  237. * strategy to determine if we're blocked is to try to load an image off a
  238. * special subdomain that network administrators will block access to if they
  239. * are trying to block chat. For Gmail Chat, the subdomain is
  240. * chatenabled.mail.google.com.
  241. * @private
  242. */
  243. goog.net.BrowserTestChannel.prototype.checkBlocked_ = function() {
  244. var uri = this.channel_.createDataUri(
  245. this.blockedPrefix_, '/mail/images/cleardot.gif');
  246. uri.makeUnique();
  247. goog.net.tmpnetwork.testLoadImageWithRetries(
  248. uri.toString(), goog.net.BrowserTestChannel.BLOCKED_TIMEOUT_,
  249. goog.bind(this.checkBlockedCallback_, this),
  250. goog.net.BrowserTestChannel.BLOCKED_RETRIES_,
  251. goog.net.BrowserTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_);
  252. this.notifyServerReachabilityEvent(
  253. goog.net.BrowserChannel.ServerReachability.REQUEST_MADE);
  254. };
  255. /**
  256. * Callback for testLoadImageWithRetries to check if browser channel is
  257. * blocked.
  258. * @param {boolean} succeeded Whether the request succeeded.
  259. * @private
  260. */
  261. goog.net.BrowserTestChannel.prototype.checkBlockedCallback_ = function(
  262. succeeded) {
  263. if (succeeded) {
  264. this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
  265. this.connectStage2_();
  266. } else {
  267. goog.net.BrowserChannel.notifyStatEvent(
  268. goog.net.BrowserChannel.Stat.CHANNEL_BLOCKED);
  269. this.channel_.testConnectionBlocked(this);
  270. }
  271. // We don't dispatch a REQUEST_FAILED server reachability event when the
  272. // block request fails, as such a failure is not a good signal that the
  273. // server has actually become unreachable.
  274. if (succeeded) {
  275. this.notifyServerReachabilityEvent(
  276. goog.net.BrowserChannel.ServerReachability.REQUEST_SUCCEEDED);
  277. }
  278. };
  279. /**
  280. * Begins the second stage of the test channel where we test to see if we're
  281. * behind a buffering proxy. The server sends back a multi-chunked response
  282. * with the first chunk containing the content '1' and then two seconds later
  283. * sending the second chunk containing the content '2'. Depending on how we
  284. * receive the content, we can tell if we're behind a buffering proxy.
  285. * @private
  286. * @suppress {missingRequire} goog.net.BrowserChannel
  287. */
  288. goog.net.BrowserTestChannel.prototype.connectStage2_ = function() {
  289. this.channelDebug_.debug('TestConnection: starting stage 2');
  290. // If the second test results are available, skip its execution.
  291. var secondTestResults = this.channel_.getSecondTestResults();
  292. if (goog.isDefAndNotNull(secondTestResults)) {
  293. this.channelDebug_.debug(
  294. 'TestConnection: skipping stage 2, precomputed result is ' +
  295. secondTestResults ?
  296. 'Buffered' :
  297. 'Unbuffered');
  298. goog.net.BrowserChannel.notifyStatEvent(
  299. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_START);
  300. if (secondTestResults) { // Buffered/Proxy connection
  301. goog.net.BrowserChannel.notifyStatEvent(
  302. goog.net.BrowserChannel.Stat.PROXY);
  303. this.channel_.testConnectionFinished(this, false);
  304. } else { // Unbuffered/NoProxy connection
  305. goog.net.BrowserChannel.notifyStatEvent(
  306. goog.net.BrowserChannel.Stat.NOPROXY);
  307. this.channel_.testConnectionFinished(this, true);
  308. }
  309. return; // Skip the test
  310. }
  311. /** @private @suppress {missingRequire} Circular dep. */
  312. this.request_ =
  313. goog.net.BrowserChannel.createChannelRequest(this, this.channelDebug_);
  314. this.request_.setExtraHeaders(this.extraHeaders_);
  315. var recvDataUri = this.channel_.getBackChannelUri(
  316. this.hostPrefix_,
  317. /** @type {string} */ (this.path_));
  318. goog.net.BrowserChannel.notifyStatEvent(
  319. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_START);
  320. if (!goog.net.ChannelRequest.supportsXhrStreaming()) {
  321. recvDataUri.setParameterValues('TYPE', 'html');
  322. this.request_.tridentGet(recvDataUri, Boolean(this.hostPrefix_));
  323. } else {
  324. recvDataUri.setParameterValues('TYPE', 'xmlhttp');
  325. this.request_.xmlHttpGet(
  326. recvDataUri, false /** decodeChunks */, this.hostPrefix_,
  327. false /** opt_noClose */);
  328. }
  329. };
  330. /**
  331. * Factory method for XhrIo objects.
  332. * @param {?string} hostPrefix The host prefix, if we need an XhrIo object
  333. * capable of calling a secondary domain.
  334. * @return {!goog.net.XhrIo} New XhrIo object.
  335. */
  336. goog.net.BrowserTestChannel.prototype.createXhrIo = function(hostPrefix) {
  337. return this.channel_.createXhrIo(hostPrefix);
  338. };
  339. /**
  340. * Aborts the test channel.
  341. */
  342. goog.net.BrowserTestChannel.prototype.abort = function() {
  343. if (this.request_) {
  344. this.request_.cancel();
  345. this.request_ = null;
  346. }
  347. this.lastStatusCode_ = -1;
  348. };
  349. /**
  350. * Returns whether the test channel is closed. The ChannelRequest object expects
  351. * this method to be implemented on its handler.
  352. *
  353. * @return {boolean} Whether the channel is closed.
  354. */
  355. goog.net.BrowserTestChannel.prototype.isClosed = function() {
  356. return false;
  357. };
  358. /**
  359. * Callback from ChannelRequest for when new data is received
  360. *
  361. * @param {goog.net.ChannelRequest} req The request object.
  362. * @param {string} responseText The text of the response.
  363. */
  364. goog.net.BrowserTestChannel.prototype.onRequestData = function(
  365. req, responseText) {
  366. this.lastStatusCode_ = req.getLastStatusCode();
  367. if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
  368. this.channelDebug_.debug('TestConnection: Got data for stage 1');
  369. if (!responseText) {
  370. this.channelDebug_.debug('TestConnection: Null responseText');
  371. // The server should always send text; something is wrong here
  372. this.channel_.testConnectionFailure(
  373. this, goog.net.ChannelRequest.Error.BAD_DATA);
  374. return;
  375. }
  376. try {
  377. var respArray = this.parser_.parse(responseText);
  378. } catch (e) {
  379. this.channelDebug_.dumpException(e);
  380. this.channel_.testConnectionFailure(
  381. this, goog.net.ChannelRequest.Error.BAD_DATA);
  382. return;
  383. }
  384. this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
  385. this.blockedPrefix_ = respArray[1];
  386. } else if (
  387. this.state_ == goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
  388. if (this.receivedIntermediateResult_) {
  389. goog.net.BrowserChannel.notifyStatEvent(
  390. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_TWO);
  391. this.lastTime_ = goog.now();
  392. } else {
  393. // '11111' is used instead of '1' to prevent a small amount of buffering
  394. // by Safari.
  395. if (responseText == '11111') {
  396. goog.net.BrowserChannel.notifyStatEvent(
  397. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_ONE);
  398. this.receivedIntermediateResult_ = true;
  399. this.firstTime_ = goog.now();
  400. if (this.checkForEarlyNonBuffered_()) {
  401. // If early chunk detection is on, and we passed the tests,
  402. // assume HTTP_OK, cancel the test and turn on noproxy mode.
  403. this.lastStatusCode_ = 200;
  404. this.request_.cancel();
  405. this.channelDebug_.debug(
  406. 'Test connection succeeded; using streaming connection');
  407. goog.net.BrowserChannel.notifyStatEvent(
  408. goog.net.BrowserChannel.Stat.NOPROXY);
  409. this.channel_.testConnectionFinished(this, true);
  410. }
  411. } else {
  412. goog.net.BrowserChannel.notifyStatEvent(
  413. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_BOTH);
  414. this.firstTime_ = this.lastTime_ = goog.now();
  415. this.receivedIntermediateResult_ = false;
  416. }
  417. }
  418. }
  419. };
  420. /**
  421. * Callback from ChannelRequest that indicates a request has completed.
  422. *
  423. * @param {goog.net.ChannelRequest} req The request object.
  424. * @suppress {missingRequire} Cannot depend on goog.net.BrowserChannel because
  425. * it creates a circular dependency.
  426. */
  427. goog.net.BrowserTestChannel.prototype.onRequestComplete = function(req) {
  428. this.lastStatusCode_ = this.request_.getLastStatusCode();
  429. if (!this.request_.getSuccess()) {
  430. this.channelDebug_.debug(
  431. 'TestConnection: request failed, in state ' + this.state_);
  432. if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
  433. goog.net.BrowserChannel.notifyStatEvent(
  434. goog.net.BrowserChannel.Stat.TEST_STAGE_ONE_FAILED);
  435. } else if (
  436. this.state_ == goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
  437. goog.net.BrowserChannel.notifyStatEvent(
  438. goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_FAILED);
  439. }
  440. this.channel_.testConnectionFailure(
  441. this,
  442. /** @type {goog.net.ChannelRequest.Error} */
  443. (this.request_.getLastError()));
  444. return;
  445. }
  446. if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
  447. this.channelDebug_.debug(
  448. 'TestConnection: request complete for initial check');
  449. if (this.blockedPrefix_) {
  450. this.state_ = goog.net.BrowserTestChannel.State_.CHECKING_BLOCKED;
  451. this.checkBlocked_();
  452. } else {
  453. this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
  454. this.connectStage2_();
  455. }
  456. } else if (
  457. this.state_ == goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
  458. this.channelDebug_.debug('TestConnection: request complete for stage 2');
  459. var goodConn = false;
  460. if (!goog.net.ChannelRequest.supportsXhrStreaming()) {
  461. // we always get Trident responses in separate calls to
  462. // onRequestData, so we have to check the time they came
  463. var ms = this.lastTime_ - this.firstTime_;
  464. if (ms < 200) {
  465. // TODO: need to empirically verify that this number is OK
  466. // for slow computers
  467. goodConn = false;
  468. } else {
  469. goodConn = true;
  470. }
  471. } else {
  472. goodConn = this.receivedIntermediateResult_;
  473. }
  474. if (goodConn) {
  475. this.channelDebug_.debug(
  476. 'Test connection succeeded; using streaming connection');
  477. goog.net.BrowserChannel.notifyStatEvent(
  478. goog.net.BrowserChannel.Stat.NOPROXY);
  479. this.channel_.testConnectionFinished(this, true);
  480. } else {
  481. this.channelDebug_.debug('Test connection failed; not using streaming');
  482. /** @suppress {missingRequire} Circular dep */
  483. goog.net.BrowserChannel.notifyStatEvent(
  484. goog.net.BrowserChannel.Stat.PROXY);
  485. this.channel_.testConnectionFinished(this, false);
  486. }
  487. }
  488. };
  489. /**
  490. * Returns the last status code received for a request.
  491. * @return {number} The last status code received for a request.
  492. */
  493. goog.net.BrowserTestChannel.prototype.getLastStatusCode = function() {
  494. return this.lastStatusCode_;
  495. };
  496. /**
  497. * @return {boolean} Whether we should be using secondary domains when the
  498. * server instructs us to do so.
  499. */
  500. goog.net.BrowserTestChannel.prototype.shouldUseSecondaryDomains = function() {
  501. return this.channel_.shouldUseSecondaryDomains();
  502. };
  503. /**
  504. * Gets whether this channel is currently active. This is used to determine the
  505. * length of time to wait before retrying.
  506. *
  507. * @param {goog.net.BrowserChannel} browserChannel The browser channel.
  508. * @return {boolean} Whether the channel is currently active.
  509. */
  510. goog.net.BrowserTestChannel.prototype.isActive = function(browserChannel) {
  511. return this.channel_.isActive();
  512. };
  513. /**
  514. * @return {boolean} True if test stage 2 detected a non-buffered
  515. * channel early and early no buffering detection is enabled.
  516. * @private
  517. */
  518. goog.net.BrowserTestChannel.prototype.checkForEarlyNonBuffered_ = function() {
  519. var ms = this.firstTime_ - this.startTime_;
  520. // we always get Trident responses in separate calls to
  521. // onRequestData, so we have to check the time that the first came in
  522. // and verify that the data arrived before the second portion could
  523. // have been sent. For all other browser's we skip the timing test.
  524. return goog.net.ChannelRequest.supportsXhrStreaming() ||
  525. ms < goog.net.BrowserTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_;
  526. };
  527. /**
  528. * Notifies the channel of a fine grained network event.
  529. * @param {goog.net.BrowserChannel.ServerReachability} reachabilityType The
  530. * reachability event type.
  531. */
  532. goog.net.BrowserTestChannel.prototype.notifyServerReachabilityEvent = function(
  533. reachabilityType) {
  534. this.channel_.notifyServerReachabilityEvent(reachabilityType);
  535. };