Skip to content

Abort the inflight fetch() when networkTimeoutSeconds is reached #3075

Closed
@pavlexander

Description

@pavlexander

Library Affected:
"workbox-background-sync": "^6.5.3",
"workbox-core": "^6.5.3",
"workbox-precaching": "^6.5.3",
"workbox-window": "^6.5.3"

Browser & Platform:
all browsers

Issue or Feature Request Description:
I have noticed 2 issues with handlerDidError callback.

Base code:

import { Queue } from "workbox-background-sync";
import { Strategy, NetworkOnly } from "workbox-strategies";

const queue = new Queue("myQueueName", {
  onSync: async ({ queue }) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        var response = await fetch(entry.request.clone());
      } catch (error) {
        await queue.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

const statusPlugin = {
  handlerWillRespond: async ({ request, response, event, state }) => {
    console.log("handlerWillRespond");
    return response;
  },
  fetchDidFail: async ({ originalRequest, request, error, event }) => {
    console.log("fetchDidFail");
    //console.log(await originalRequest.clone().json()); // works

    await queue.pushRequest({ request: originalRequest });
  },
  fetchDidSucceed: async ({ response }) => {
    console.log("fetchDidSucceed");
    //console.log(await response.clone().json()); // works

    return response;
  },
  handlerDidError: async ({ error, event, request, state }) => {
    console.log("handlerDidError");
    //console.log(await request.clone().json()); // TypeError: Failed to execute 'clone' on 'Request': Request body is already used
  },
};

registerRoute(
  new RegExp(".*/Exercises/.*"),
  new NetworkOnly({
    networkTimeoutSeconds: 3,
    plugins: [statusPlugin],
  }),
  "POST"
);

Issue 1: Unable to read the request data

Following code:

  handlerDidError: async ({ error, event, request, state }) => {
    console.log("handlerDidError");
    console.log(await request.clone().json()); //  TypeError: Failed to execute 'clone' on 'Request': Request body is already used
  },

will throw:

TypeError: Failed to execute 'clone' on 'Request': Request body is already used

Issue 2: handlerDidError runs before fetchDidFail (subjective)

Current callback flow is:

  1. client makes a request
  2. network is unavailable
  3. handlerDidError is consoled
  4. clients gets the response
  5. fetchDidFail is consoled

I would expect all the pipelines related to errors to be executed before client sees the response.

Temporary solution

I have managed to fix both of the problems by using the custom Network strategy

class NewStrategy extends NetworkOnly {
  async _handle(request, handler) {
    return await handler.fetch(request.clone());
  }
}

See that I have cloned the request before passing it to handler. This will allow us to clone the request later in handlerDidError callback. The second "issue" is also solved. Now the flow is:

  1. client makes a request
  2. network is unavailable
  3. fetchDidFail is consoled
  4. handlerDidError is consoled
  5. clients gets the response

See that the handlerDidError is actually run After the fetchDidFail. This makes sense because the handlerDidError is supposed to be the last logical thing in the pipeline: https://developer.chrome.com/docs/workbox/using-plugins/

handlerDidError: Called if the handler can't provide a valid response from from any source, which is the optimal time to provide some sort of fallback response as an alternative to failing outright.
// Return response, a different Response object as a fallback, or null.

I might be wrong about the pipeline order thingy (it just looks more logical to me) but the "cloning" issue seems to be a valid one regardless.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions