tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_XHR_timeout.js (9841B)


      1 /* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */
      2 
      3 /* Notes:
      4   - All times are expressed in milliseconds in this test suite.
      5   - Test harness code is at the end of this file.
      6   - We generate only one request at a time, to avoid overloading the HTTP
      7   request handlers.
      8 */
      9 
     10 var inWorker = false;
     11 try {
     12  inWorker = !(self instanceof Window);
     13 } catch (e) {
     14  inWorker = true;
     15 }
     16 
     17 function message(data) {
     18  if (inWorker) {
     19    self.postMessage(data);
     20  } else {
     21    self.postMessage(data, "*");
     22  }
     23 }
     24 
     25 function is(got, expected, msg) {
     26  var obj = {};
     27  obj.type = "is";
     28  obj.got = got;
     29  obj.expected = expected;
     30  obj.msg = msg;
     31 
     32  message(obj);
     33 }
     34 
     35 function ok(bool, msg) {
     36  var obj = {};
     37  obj.type = "ok";
     38  obj.bool = bool;
     39  obj.msg = msg;
     40 
     41  message(obj);
     42 }
     43 
     44 /**
     45 * Generate and track results from a XMLHttpRequest with regards to timeouts.
     46 *
     47 * @param {string} id         The test description.
     48 * @param {number} timeLimit  The initial setting for the request timeout.
     49 * @param {number} resetAfter (Optional) The time after sending the request, to
     50 *                            reset the timeout.
     51 * @param {number} resetTo    (Optional) The delay to reset the timeout to.
     52 *
     53 * Note: The actual testing takes place in handleEvent(event).
     54 * The requests are generated in startXHR().
     55 *
     56 * Note: If resetAfter and resetTo are omitted, only the initial timeout setting
     57 * applies.
     58 *
     59 * @class
     60 * @implements DOMEventListener
     61 */
     62 function RequestTracker(async, id, timeLimit /*[, resetAfter, resetTo]*/) {
     63  this.async = async;
     64  this.id = id;
     65  this.timeLimit = timeLimit;
     66 
     67  if (arguments.length > 3) {
     68    this.mustReset = true;
     69    this.resetAfter = arguments[3];
     70    this.resetTo = arguments[4];
     71  }
     72 
     73  this.hasFired = false;
     74 }
     75 RequestTracker.prototype = {
     76  /**
     77   * Start the XMLHttpRequest!
     78   */
     79  startXHR() {
     80    var req = new XMLHttpRequest();
     81    this.request = req;
     82    req.open("GET", "file_XHR_timeout.sjs", this.async);
     83    var me = this;
     84    function handleEvent(e) {
     85      return me.handleEvent(e);
     86    }
     87    req.onerror = handleEvent;
     88    req.onload = handleEvent;
     89    req.onabort = handleEvent;
     90    req.ontimeout = handleEvent;
     91 
     92    req.timeout = this.timeLimit;
     93 
     94    if (this.mustReset) {
     95      var resetTo = this.resetTo;
     96      self.setTimeout(function () {
     97        req.timeout = resetTo;
     98      }, this.resetAfter);
     99    }
    100 
    101    var gotException;
    102    var expectTimeoutException =
    103      !this.async && inWorker && this.timeLimit > 0 && this.timeLimit < 3000;
    104 
    105    try {
    106      req.send(null);
    107    } catch (e) {
    108      gotException = e;
    109      if (expectTimeoutException) {
    110        ok(e.name == "TimeoutError", "Should be a TimeoutError");
    111      }
    112    }
    113 
    114    if (gotException && !expectTimeoutException) {
    115      ok(false, `expected no exception, got ${gotException}`);
    116    } else if (!gotException && expectTimeoutException) {
    117      ok(false, "expected timeout exception");
    118    }
    119  },
    120 
    121  /**
    122   * Get a message describing this test.
    123   *
    124   * @returns {string} The test description.
    125   */
    126  getMessage() {
    127    var rv = this.id + ", ";
    128    if (this.mustReset) {
    129      rv += "original timeout at " + this.timeLimit + ", ";
    130      rv += "reset at " + this.resetAfter + " to " + this.resetTo;
    131    } else {
    132      rv += "timeout scheduled at " + this.timeLimit;
    133    }
    134    return rv;
    135  },
    136 
    137  /**
    138   * Check the event received, and if it's the right (and only) one we get.
    139   *
    140   * @param {DOMProgressEvent} evt An event of type "load" or "timeout".
    141   */
    142  handleEvent(evt) {
    143    if (this.hasFired) {
    144      ok(false, "Only one event should fire: " + this.getMessage());
    145      return;
    146    }
    147    this.hasFired = true;
    148 
    149    var type = evt.type,
    150      expectedType;
    151    // The XHR responds after 3000 milliseconds with a load event.
    152    var timeLimit =
    153      this.mustReset && this.resetAfter < Math.min(3000, this.timeLimit)
    154        ? this.resetTo
    155        : this.timeLimit;
    156    if (timeLimit == 0 || timeLimit >= 3000) {
    157      expectedType = "load";
    158    } else {
    159      expectedType = "timeout";
    160    }
    161    is(type, expectedType, this.getMessage());
    162    TestCounter.testComplete();
    163  },
    164 };
    165 
    166 /**
    167 * Generate and track XMLHttpRequests which will have abort() called on.
    168 *
    169 * @param shouldAbort {Boolean} True if we should call abort at all.
    170 * @param abortDelay  {Number}  The time in ms to wait before calling abort().
    171 */
    172 function AbortedRequest(shouldAbort, abortDelay) {
    173  this.shouldAbort = shouldAbort;
    174  this.abortDelay = abortDelay;
    175  this.hasFired = false;
    176 }
    177 AbortedRequest.prototype = {
    178  /**
    179   * Start the XMLHttpRequest!
    180   */
    181  startXHR() {
    182    var req = new XMLHttpRequest();
    183    this.request = req;
    184    req.open("GET", "file_XHR_timeout.sjs");
    185    var me = this;
    186    function handleEvent(e) {
    187      return me.handleEvent(e);
    188    }
    189    req.onerror = handleEvent;
    190    req.onload = handleEvent;
    191    req.onabort = handleEvent;
    192    req.ontimeout = handleEvent;
    193 
    194    req.timeout = 2000;
    195    var _this = this;
    196 
    197    function abortReq() {
    198      req.abort();
    199    }
    200 
    201    if (!this.shouldAbort) {
    202      self.setTimeout(function () {
    203        try {
    204          _this.noEventsFired();
    205        } catch (e) {
    206          ok(false, "Unexpected error: " + e);
    207          TestCounter.testComplete();
    208        }
    209      }, 5000);
    210    } else {
    211      // Abort events can only be triggered on sent requests.
    212      req.send();
    213      if (this.abortDelay == -1) {
    214        abortReq();
    215      } else {
    216        self.setTimeout(abortReq, this.abortDelay);
    217      }
    218    }
    219  },
    220 
    221  /**
    222   * Ensure that no events fired at all, especially not our timeout event.
    223   */
    224  noEventsFired() {
    225    ok(
    226      !this.hasFired,
    227      "No events should fire for an unsent, unaborted request"
    228    );
    229    // We're done; if timeout hasn't fired by now, it never will.
    230    TestCounter.testComplete();
    231  },
    232 
    233  /**
    234   * Get a message describing this test.
    235   *
    236   * @returns {string} The test description.
    237   */
    238  getMessage() {
    239    return "time to abort is " + this.abortDelay + ", timeout set at 2000";
    240  },
    241 
    242  /**
    243   * Check the event received, and if it's the right (and only) one we get.
    244   *
    245   * @param {DOMProgressEvent} evt An event of type "load" or "timeout".
    246   */
    247  handleEvent(evt) {
    248    if (this.hasFired) {
    249      ok(false, "Only abort event should fire: " + this.getMessage());
    250      return;
    251    }
    252    this.hasFired = true;
    253 
    254    var expectedEvent = this.abortDelay >= 2000 ? "timeout" : "abort";
    255    is(evt.type, expectedEvent, this.getMessage());
    256    TestCounter.testComplete();
    257  },
    258 };
    259 
    260 var SyncRequestSettingTimeoutAfterOpen = {
    261  startXHR() {
    262    var pass = false;
    263    var req = new XMLHttpRequest();
    264    req.open("GET", "file_XHR_timeout.sjs", false);
    265    try {
    266      req.timeout = 1000;
    267    } catch (e) {
    268      pass = true;
    269    }
    270    ok(pass, "Synchronous XHR must not allow a timeout to be set");
    271    TestCounter.testComplete();
    272  },
    273 };
    274 
    275 var SyncRequestSettingTimeoutBeforeOpen = {
    276  startXHR() {
    277    var pass = false;
    278    var req = new XMLHttpRequest();
    279    req.timeout = 1000;
    280    try {
    281      req.open("GET", "file_XHR_timeout.sjs", false);
    282    } catch (e) {
    283      pass = true;
    284    }
    285    ok(pass, "Synchronous XHR must not allow a timeout to be set");
    286    TestCounter.testComplete();
    287  },
    288 };
    289 
    290 var TestRequests = [
    291  // Simple timeouts.
    292  new RequestTracker(true, "no time out scheduled, load fires normally", 0),
    293  new RequestTracker(true, "load fires normally", 5000),
    294  new RequestTracker(true, "timeout hit before load", 2000),
    295 
    296  // Timeouts reset after a certain delay.
    297  new RequestTracker(
    298    true,
    299    "load fires normally with no timeout set, twice",
    300    0,
    301    2000,
    302    0
    303  ),
    304  new RequestTracker(
    305    true,
    306    "load fires normally with same timeout set twice",
    307    5000,
    308    2000,
    309    5000
    310  ),
    311  new RequestTracker(
    312    true,
    313    "timeout fires normally with same timeout set twice",
    314    2000,
    315    1000,
    316    2000
    317  ),
    318 
    319  new RequestTracker(
    320    true,
    321    "timeout disabled after initially set",
    322    5000,
    323    2000,
    324    0
    325  ),
    326  new RequestTracker(
    327    true,
    328    "timeout overrides load after a delay",
    329    5000,
    330    1000,
    331    2000
    332  ),
    333  new RequestTracker(
    334    true,
    335    "timeout enabled after initially disabled",
    336    0,
    337    2000,
    338    5000
    339  ),
    340 
    341  new RequestTracker(
    342    true,
    343    "timeout set to expiring value after load fires",
    344    5000,
    345    4000,
    346    1000
    347  ),
    348  new RequestTracker(
    349    true,
    350    "timeout set to expired value before load fires",
    351    5000,
    352    2000,
    353    1000
    354  ),
    355  new RequestTracker(
    356    true,
    357    "timeout set to non-expiring value after timeout fires",
    358    1000,
    359    2000,
    360    5000
    361  ),
    362 
    363  // Aborted requests.
    364  new AbortedRequest(false),
    365  new AbortedRequest(true, -1),
    366  new AbortedRequest(true, 5000),
    367 ];
    368 
    369 var MainThreadTestRequests = [
    370  new AbortedRequest(true, 0),
    371  new AbortedRequest(true, 1000),
    372 
    373  // Synchronous requests.
    374  SyncRequestSettingTimeoutAfterOpen,
    375  SyncRequestSettingTimeoutBeforeOpen,
    376 ];
    377 
    378 var WorkerThreadTestRequests = [
    379  // Simple timeouts.
    380  new RequestTracker(false, "no time out scheduled, load fires normally", 0),
    381  new RequestTracker(false, "load fires normally", 5000),
    382  new RequestTracker(false, "timeout hit before load", 2000),
    383 
    384  // Reset timeouts don't make much sense with a sync request ...
    385 ];
    386 
    387 if (inWorker) {
    388  TestRequests = TestRequests.concat(WorkerThreadTestRequests);
    389 } else {
    390  TestRequests = TestRequests.concat(MainThreadTestRequests);
    391 }
    392 
    393 // This code controls moving from one test to another.
    394 var TestCounter = {
    395  testComplete() {
    396    // Allow for the possibility there are other events coming.
    397    self.setTimeout(function () {
    398      TestCounter.next();
    399    }, 5000);
    400  },
    401 
    402  next() {
    403    var test = TestRequests.shift();
    404 
    405    if (test) {
    406      test.startXHR();
    407    } else {
    408      message("done");
    409    }
    410  },
    411 };
    412 
    413 self.addEventListener("message", function (event) {
    414  if (event.data == "start") {
    415    TestCounter.next();
    416  }
    417 });