Exploring network timeouts

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Exploring network timeouts

Ethan Glasser-Camp
Hi,

I'm currently removing a network timeout behavior in a JS library that is used in Firefox and I'm trying to ensure that I won't consume network resources forever if the user is on a slow connection.

Consider this xpcshell test:

```
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://services-common/async.js");
Cu.importGlobalProperties(['fetch']);

function sleep(aMs) {
  return new Promise((resolve, error) => {
    let timer = Cc["@mozilla.org/timer;1"]
        .createInstance(Ci.nsITimer);

    timer.initWithCallback({
      notify: function () {
        resolve();
      },
    }, aMs, timer.TYPE_ONE_SHOT);
  });
}

add_task(function* test_something() {
  dump(`-------------- PREFS ----------------\n`);
  dump(`response timeout: ${Services.prefs.getIntPref("network.http.response.timeout")}\n`);
  dump(`keepalive timeout: ${Services.prefs.getIntPref("network.http.keep-alive.timeout")}\n`);

  Services.prefs.setIntPref("network.http.response.timeout", 3);
  Services.prefs.setIntPref("network.http.keep-alive.timeout", 115);

  const server = new HttpServer();
  server.start();

  server.registerPathHandler("/test-resource", () => {
    Async.promiseSpinningly(sleep(150000));
    response.setStatusLine(null, 200, "OK");
    response.write("Test resource response");
  });

  try {
    const result = yield fetch(`<a href="http://localhost:$">http://localhost:${server.identity.primaryPort}/test-resource`);
    dump(`${result.status} ${result.statusText}\n`);
    dump(`${result.text()}\n`);
  } finally {
    server.stop(() => {});
  }
});
```

Sometimes this test fails with an error message like:

 0:23.14 LOG: Thread-1 ERROR Unexpected exception TypeError: NetworkError when attempting to fetch resource. at resource://services-common/async.js:98

But it seems to fail at strange times -- for example, this time it failed at the 23 second mark, which doesn't seem related to either the 115-second keepalive timeout or the 3-second response timeout. And even worse, sometimes the test succeeds after 150 seconds. What's going on? Is this expected? More generally, are there any best practices I should use to ensure responsible use of network resources when writing JS code in Firefox?

Thanks,

Ethan
_______________________________________________
dev-tech-network mailing list
[hidden email]
https://lists.mozilla.org/listinfo/dev-tech-network
Reply | Threaded
Open this post in threaded view
|

Re: Exploring network timeouts

patrick.ducksong
any prefs are global to firefox and aren't guarantees at the xhr or fetch layer.. I would be shocked to see our chrome code setting them. you should use js timeouts and cancel things as needed as the only reliable way to deal with this stuff at the js layer for xhr. (I forget if fetch has an attribute to deal with it - but that would basically be implemented the same way)

On Thursday, March 2, 2017 at 12:36:46 PM UTC-5, Ethan Glasser-Camp wrote:

> Hi,
>
> I'm currently removing a network timeout behavior in a JS library that is used in Firefox and I'm trying to ensure that I won't consume network resources forever if the user is on a slow connection.
>
> Consider this xpcshell test:
>
> ```
> Cu.import("resource://testing-common/httpd.js");
> Cu.import("resource://services-common/async.js");
> Cu.importGlobalProperties(['fetch']);
>
> function sleep(aMs) {
>   return new Promise((resolve, error) => {
>     let timer = Cc["@mozilla.org/timer;1"]
>         .createInstance(Ci.nsITimer);
>
>     timer.initWithCallback({
>       notify: function () {
>         resolve();
>       },
>     }, aMs, timer.TYPE_ONE_SHOT);
>   });
> }
>
> add_task(function* test_something() {
>   dump(`-------------- PREFS ----------------\n`);
>   dump(`response timeout: ${Services.prefs.getIntPref("network.http.response.timeout")}\n`);
>   dump(`keepalive timeout: ${Services.prefs.getIntPref("network.http.keep-alive.timeout")}\n`);
>
>   Services.prefs.setIntPref("network.http.response.timeout", 3);
>   Services.prefs.setIntPref("network.http.keep-alive.timeout", 115);
>
>   const server = new HttpServer();
>   server.start();
>
>   server.registerPathHandler("/test-resource", () => {
>     Async.promiseSpinningly(sleep(150000));
>     response.setStatusLine(null, 200, "OK");
>     response.write("Test resource response");
>   });
>
>   try {
>     const result = yield fetch(`<a href="http://localhost:$">http://localhost:${server.identity.primaryPort}/test-resource`);
>     dump(`${result.status} ${result.statusText}\n`);
>     dump(`${result.text()}\n`);
>   } finally {
>     server.stop(() => {});
>   }
> });
> ```
>
> Sometimes this test fails with an error message like:
>
>  0:23.14 LOG: Thread-1 ERROR Unexpected exception TypeError: NetworkError when attempting to fetch resource. at resource://services-common/async.js:98
>
> But it seems to fail at strange times -- for example, this time it failed at the 23 second mark, which doesn't seem related to either the 115-second keepalive timeout or the 3-second response timeout. And even worse, sometimes the test succeeds after 150 seconds. What's going on? Is this expected? More generally, are there any best practices I should use to ensure responsible use of network resources when writing JS code in Firefox?
>
> Thanks,
>
> Ethan

_______________________________________________
dev-tech-network mailing list
[hidden email]
https://lists.mozilla.org/listinfo/dev-tech-network
Reply | Threaded
Open this post in threaded view
|

Re: Exploring network timeouts

Ethan Glasser-Camp
I spoke to :mcmanus on IRC to clarify. I'm not trying to use the prefs to enforce a timeout, and doing so would be wrong. You should definitely institute your own timeout at the JS layer if you need to, e.g. for a UX that requires some kind of update.

Necko itself doesn't commit to any kind of network timeout happening. Under certain circumstances, Necko will report an "error" on a certain channel, but exactly what those circumstances are is best viewed as an implementation detail, subject to change, and not part of the API contract in any way.

That said, we use TCP keepalives to ensure that connections are still active. If those keepalives stop getting responses, the OS will tell us that the connection was lost, and we'll detect a network error. So eventually all the connections will get cleaned up, and I don't need to worry about it at the JS layer.

Thanks :mcmanus for the patient explanations.

Ethan

On Monday, March 6, 2017 at 11:54:33 AM UTC-5, [hidden email] wrote:

> any prefs are global to firefox and aren't guarantees at the xhr or fetch layer.. I would be shocked to see our chrome code setting them. you should use js timeouts and cancel things as needed as the only reliable way to deal with this stuff at the js layer for xhr. (I forget if fetch has an attribute to deal with it - but that would basically be implemented the same way)
>
> On Thursday, March 2, 2017 at 12:36:46 PM UTC-5, Ethan Glasser-Camp wrote:
> > Hi,
> >
> > I'm currently removing a network timeout behavior in a JS library that is used in Firefox and I'm trying to ensure that I won't consume network resources forever if the user is on a slow connection.
> >
> > Consider this xpcshell test:
> >
> > ```
> > Cu.import("resource://testing-common/httpd.js");
> > Cu.import("resource://services-common/async.js");
> > Cu.importGlobalProperties(['fetch']);
> >
> > function sleep(aMs) {
> >   return new Promise((resolve, error) => {
> >     let timer = Cc["@mozilla.org/timer;1"]
> >         .createInstance(Ci.nsITimer);
> >
> >     timer.initWithCallback({
> >       notify: function () {
> >         resolve();
> >       },
> >     }, aMs, timer.TYPE_ONE_SHOT);
> >   });
> > }
> >
> > add_task(function* test_something() {
> >   dump(`-------------- PREFS ----------------\n`);
> >   dump(`response timeout: ${Services.prefs.getIntPref("network.http.response.timeout")}\n`);
> >   dump(`keepalive timeout: ${Services.prefs.getIntPref("network.http.keep-alive.timeout")}\n`);
> >
> >   Services.prefs.setIntPref("network.http.response.timeout", 3);
> >   Services.prefs.setIntPref("network.http.keep-alive.timeout", 115);
> >
> >   const server = new HttpServer();
> >   server.start();
> >
> >   server.registerPathHandler("/test-resource", () => {
> >     Async.promiseSpinningly(sleep(150000));
> >     response.setStatusLine(null, 200, "OK");
> >     response.write("Test resource response");
> >   });
> >
> >   try {
> >     const result = yield fetch(`<a href="http://localhost:$">http://localhost:${server.identity.primaryPort}/test-resource`);
> >     dump(`${result.status} ${result.statusText}\n`);
> >     dump(`${result.text()}\n`);
> >   } finally {
> >     server.stop(() => {});
> >   }
> > });
> > ```
> >
> > Sometimes this test fails with an error message like:
> >
> >  0:23.14 LOG: Thread-1 ERROR Unexpected exception TypeError: NetworkError when attempting to fetch resource. at resource://services-common/async.js:98
> >
> > But it seems to fail at strange times -- for example, this time it failed at the 23 second mark, which doesn't seem related to either the 115-second keepalive timeout or the 3-second response timeout. And even worse, sometimes the test succeeds after 150 seconds. What's going on? Is this expected? More generally, are there any best practices I should use to ensure responsible use of network resources when writing JS code in Firefox?
> >
> > Thanks,
> >
> > Ethan

_______________________________________________
dev-tech-network mailing list
[hidden email]
https://lists.mozilla.org/listinfo/dev-tech-network