Re: [slightlyoff/ServiceWorker] Eliminating SW startup latency for common case (#920)

_(My kind-of a proposal for this case, a bit rethinked/reworked)_

## The `requests`

This API is supposed to solve this issue and the race issue for `link=preload` https://github.com/slightlyoff/ServiceWorker/issues/959

### Preflight

First, here are some scetches of the _"pre-flight opt-in"_ which are based on what was writting here and decided on F2F meeting:

##### Registration, preflight is opt-in

Each preflight request will have `Service-Worker-Preflight: something` header. 

```js
navigator.serviceWorker.register('/sw.js', {
  preflight: {
    // Preflight interval. Is it really needed?
    // There was some mentions that HTTP cached would solve this.
    // Will `Vary: Service-Worker-Preflight` on response work fine here? CDN?
    ttl: 1000 * 60
  }
});
```

##### Pre-flight data

```js
// Naming not final of course
self.registration.setPreflightProperties([
  headers: {
    'X-Status': 'Y'
  }
]).then(() => {
  console.log('Done');
});
```

This is what is related to preflight, following API is for general use cases which may solve Facebook/Google Docs issue with ServiceWorker.

### API

(_in TypeScript format, not spec format_)

```ts
interface RequestsStorage {
  match(Request | string): Promise<Response>;
  putUntil(Request | string, Promise<Response>): void;
}

interface ServiceWorkerGlobalScope {
 readonly requests: RequestsStorage;
}
```

* `RequestsStorage#putUntil`: Puts a request to the storage until `Promise<Response>` settled.  
_For the case of preflight, UA need to hold the request in the store until `fetch` event ends_
* `RequestsStorage#match`: Macthes passed request agains stored ones. Requests should be differentiated by `Service-Worker-Preflight` header too.


### Example

The website. It wants to preflight requests and wants to handle them differently based on the requested page. It also sends `Link` preload headers for some pages.

```js

self.addEventListener('fetch', event => {
  const request = event.request;
  const url = new URL(request.url);

  // Detect if it's a preflight request
  if (request.mode === 'navigate' && request.header.get('Service-Worker-Preflight')) {
    let result;

    // Note that self.request.match() should take into account
    // existence of Service-Worker-Preflight header

    // Case A: Construct the page on the fly (FB case)
    if (url.pathname === '/newsfeed') {
      result = self.requests.match(request).then(res => {
        // `res` returns dynamic data, the shell is cached.
        // Streaming rendering could be used here too
        return constructThePage(res);
      });
    }

    // Case B: Simple race with response for simple page
    else if (url.pathname === '/about') {
      const fetching = fetchRequest(request);

      const result = Promise.race([
        fetching,
        self.requests.match(request).catch(() => fetching)
      ]);
    }

    // Case C: Canceling huge **response**
    else if (url.pathname.endsWith('.mp4')) {
      self.requests.match(request).then(res => {
        res.body.cancel();
      });
    }

    event.respondWith(result);
    return;
  }

  // Handle normal and preload requests. They are going here too
  // and if page requests same preload assets which are now _ingligh_
  // then the same request is reused.
  const result = self.requests.match(request).then(inflight => {
    return inflight || self.caches.match(request);
  }).then(res => {
    if (res) return res;

    return fetch(request).then(res => {
      if (res && res.ok) {
        putCache(request, res);
      }

      return res;
    });
  });

  event.respondWith(result);
});

function fetchRequest(request) {
  return caches.match(request, {
    cacheName: 'cache'
  }).then(res => {
    if (res) return res;

    return fetch(request).then(res => {
      if (res && res.ok) {
        putCache(request, res);
      }

      return res;
    });
  });
}

function putCache(req, res) {
  return caches.open('cache').then(cache => {
    return cache.put(req, res);
  });
}

```

_____________________________

This is how I think it may work. I hope it isn't very stupud :-)

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/slightlyoff/ServiceWorker/issues/920#issuecomment-241153009

Received on Friday, 19 August 2016 22:42:07 UTC