Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Who we are? 2

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

The context 4

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

The configuration gets messy 7

Slide 8

Slide 8 text

8

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

12

Slide 13

Slide 13 text

13

Slide 14

Slide 14 text

14

Slide 15

Slide 15 text

JS Service Workers API 15

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

18 your PC

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

21

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

23 navigator.serviceWorker.controller

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

25

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

27

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

The solution 41

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

44

Slide 45

Slide 45 text

45

Slide 46

Slide 46 text

46 addEventListener('fetch', event => { var url = new URL(event.request.url) url.scheme = 'https'; url.hostname = 'www.google.com'; event.respondWith(fetch(url.toString())); })

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Conclusion 49

Slide 50

Slide 50 text

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