Why we chose Service Worker API

D4e1d473a995ef37b3e03e9e6006c3e3?s=47 majek04
November 16, 2017

Why we chose Service Worker API

D4e1d473a995ef37b3e03e9e6006c3e3?s=128

majek04

November 16, 2017
Tweet

Transcript

  1. Programming on the Edge: Why we chose Service Worker API?

    Marek Majkowski @majek04
  2. Who we are? 2

  3. Reverse proxy 3 Eyeball Reverse proxy Origin server • Optimizations

    • Caching • DDoS protection • Security
  4. The context 4

  5. 5

  6. Two problems • A/ Configuration gets more complex over time

    • Wants to be a programming language • B/ Configuration is tied to server • Specific nomenclature phases/hooks 6
  7. The configuration gets messy 7

  8. 8

  9. Messy configuration • General problem • Apache, Nginx, Lighttpd, all

    started with clean configs • Varnish has VCL • Syntax resembles programming language 9
  10. 10

  11. Two problems • A/ Configuration gets more complex over time

    • Wants to be a programming language • B/ Configuration is tied to server • Specific nomenclature phases/hooks 11
  12. 12

  13. 13

  14. 14

  15. JS Service Workers API 15

  16. Offline web apps • Google Gears • ApplicationCache • Service

    Worker API • Buzzword: PWA Progressive Web Apps 16
  17. 17

  18. 18 your PC

  19. ServiceWorkers Service workers essentially act as proxy servers that sit

    between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs. 19
  20. 20 • install + waitUntil (seamless worker upgrade) • note:

    skipWaiting • activate • message • fetch • push • sync Events
  21. 21

  22. Hint • > P.S.: Chrome (normally) won't stop a service

    worker while you have its DevTools interface open, but this is only to ease debugging and is not behavior you should rely on for a real application. 22
  23. 23 navigator.serviceWorker.controller

  24. Debugging • Opera browser://serviceworker-internals • FF about:serviceworkers • Chrome: Application

    panel in devtools • chrome://inspect/#service-workers • chrome://serviceworker-internals 24
  25. 25

  26. Service Workers • Chrome 40 (Jan 2015) • Firefox 44

    (Jan 2016) • https://jakearchibald.github.io/isserviceworkerready/ 26
  27. 27

  28. Promises all the way! • All API's take and return

    promises • ServiceWorker must be over HTTPS • no localstorage (sync) • no DOM access • IndexedDB is there • Isn’t tied to a particular page • https://github.com/w3c/ServiceWorker/blob/master/ 28
  29. register 29 navigator.serviceWorker.register('sw.js').then( () => console.log('Registered'), err => console.error(err.stack) )

  30. unregister 30 navigator.serviceWorker.getRegistrations().then( registrations => { for(let registration of registrations)

    { registration.unregister() } })
  31. register scope 31 navigator.serviceWorker.register('/service-worker.js', { scope: '/app/' });

  32. 32 addEventListener('fetch', event => { console.log(event.request); event.respondWith(fetch(event.request)) }); fetch

  33. fetch • method, body, integrity • headers: {} • mode:

    'cors', 'no-cors', 'same-origin', 'navigate' • cache: 'default', 'no-store', 'reload', 'force-cache'.... • redirect: 'follow', 'error', 'manual' • credentials: 'include', 'same-origin', 'omit' • referrer: 'no-referrer', 'client' 33
  34. 34 addEventListener('fetch', event => { var r = new Response('Bye

    bye world!') event.respondWith(r) }) Response
  35. What can possibly go wrong • Service worker goes through

    service worker..... • > If you force-reload the page (shift-reload) it bypasses the service worker entirely. It'll be uncontrolled. This feature is in the spec, so it works in other service- worker-supporting browsers. 35
  36. 36

  37. 37 addEventListener('fetch', event => { console.log(event.request); if (event.request.url == 'http://localhost:8080/other.html')

    { event.respondWith(new Response('Bye bye world!')) } else { event.respondWith(fetch(event.request)) } }); fetch
  38. CacheStorage vs Cache 38 caches.match(event.request).then( response => { if (response)

    return response; return fetch(event.request).then( r => { caches.open('v1').then( cache => {cache.put(event.request, r);} ); return r.clone(); }); }).catch(function() { return caches.match('/sw-test/gallery/myLittleVader.jpg'); });
  39. sync 39 // main navigator.serviceWorker.ready.then(function(swRegistration) { return swRegistration.sync.register('leaderboard'); }); //

    sw.js self.addEventListener('sync', function(event) { if (event.id == 'leaderboard') { event.waitUntil( caches.open('mygame-dynamic').then(function(cache) { return cache.add('/leaderboard.json'); }) ); } });
  40. Service workers • Service worker • MITM - TLS only

    • Fetch • Cache • Sync • Push 40
  41. The solution 41

  42. Service worker API on edge? 42 Origin Visitor Service Worker

    API?
  43. Two problems • A/ Configuration gets more complex over time

    • Wants to be a programming language • B/ Configuration is tied to server • Specific nomenclature phases/hooks 43
  44. 44

  45. 45

  46. 46 addEventListener('fetch', event => { var url = new URL(event.request.url)

    url.scheme = 'https'; url.hostname = 'www.google.com'; event.respondWith(fetch(url.toString())); })
  47. 47 // A Service Worker which replaces the word "Worker"

    with // "Minion" in all site content. addEventListener("fetch", event => { event.respondWith(fetchAndReplace(event.request)) }) async function fetchAndReplace(request) { // Fetch from origin server. let response = await fetch(request) // Make sure we only modify text, not images. let type = response.headers.get("Content-Type") || "" if (!type.startsWith("text/html")) { // Not text. Don't modify. return response } // Read response body. let text = await response.text() // Modify it. let modified = text.replace(/(Java|Cloud)/g, "Butt") // Return modified response. return new Response(modified, { status: response.status, statusText: response.statusText, headers: response.headers }) }
  48. ReadableStream 48 const stream = new ReadableStream({ start(controller) { interval

    = setInterval(() => { let string = randomChars(); // Add the string to the stream controller.enqueue(string); // show it on the screen let listItem = document.createElement('li'); listItem.textContent = string; list1.appendChild(listItem); }, 1000); button.addEventListener('click', function() { clearInterval(interval); fetchStream(); controller.close(); }) }, pull(controller) { // We don't really need a pull in this example }, cancel() { // This is called if the reader cancels, // so we should stop generating strings clearInterval(interval); } });
  49. Conclusion 49

  50. Opportunity to practice • Promises (integration with browser events) •

    ES6 arrow functions • Async/await • Streams 50