$30 off During Our Annual Pro Sale. View Details »

Why we chose Service Worker API

majek04
November 16, 2017

Why we chose Service Worker API

majek04

November 16, 2017
Tweet

More Decks by majek04

Other Decks in Programming

Transcript

  1. Programming on the Edge:
    Why we chose Service Worker API?
    Marek Majkowski @majek04

    View Slide

  2. Who we are?
    2

    View Slide

  3. Reverse proxy
    3
    Eyeball Reverse proxy Origin server
    • Optimizations
    • Caching
    • DDoS protection
    • Security

    View Slide

  4. The context
    4

    View Slide

  5. 5

    View Slide

  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

    View Slide

  7. The configuration gets messy
    7

    View Slide

  8. 8

    View Slide

  9. Messy configuration
    • General problem
    • Apache, Nginx, Lighttpd, all started with clean configs
    • Varnish has VCL
    • Syntax resembles programming language
    9

    View Slide

  10. 10

    View Slide

  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

    View Slide

  12. 12

    View Slide

  13. 13

    View Slide

  14. 14

    View Slide

  15. JS Service Workers API
    15

    View Slide

  16. Offline web apps
    • Google Gears
    • ApplicationCache
    • Service Worker API
    • Buzzword: PWA Progressive Web Apps
    16

    View Slide

  17. 17

    View Slide

  18. 18
    your PC

    View Slide

  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

    View Slide

  20. 20
    • install + waitUntil (seamless worker upgrade)
    • note: skipWaiting
    • activate
    • message
    • fetch
    • push
    • sync
    Events

    View Slide

  21. 21

    View Slide

  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

    View Slide

  23. 23
    navigator.serviceWorker.controller

    View Slide

  24. Debugging
    • Opera browser://serviceworker-internals
    • FF about:serviceworkers
    • Chrome: Application panel in devtools
    • chrome://inspect/#service-workers
    • chrome://serviceworker-internals
    24

    View Slide

  25. 25

    View Slide

  26. Service Workers
    • Chrome 40 (Jan 2015)
    • Firefox 44 (Jan 2016)
    • https://jakearchibald.github.io/isserviceworkerready/
    26

    View Slide

  27. 27

    View Slide

  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

    View Slide

  29. register
    29
    navigator.serviceWorker.register('sw.js').then(
    () => console.log('Registered'),
    err => console.error(err.stack)
    )

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  34. 34
    addEventListener('fetch', event => {
    var r = new Response('Bye bye world!')
    event.respondWith(r)
    })
    Response

    View Slide

  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

    View Slide

  36. 36

    View Slide

  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

    View Slide

  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');
    });

    View Slide

  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');
    })
    );
    }
    });

    View Slide

  40. Service workers
    • Service worker
    • MITM - TLS only
    • Fetch
    • Cache
    • Sync
    • Push
    40

    View Slide

  41. The solution
    41

    View Slide

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

    View Slide

  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

    View Slide

  44. 44

    View Slide

  45. 45

    View Slide

  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()));
    })

    View Slide

  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
    })
    }

    View Slide

  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);
    }
    });

    View Slide

  49. Conclusion
    49

    View Slide

  50. Opportunity to practice
    • Promises (integration with browser events)
    • ES6 arrow functions
    • Async/await
    • Streams
    50

    View Slide