Service Worker

Service Workers

1. Zastanawialiście się kiedyś, jak to się dzieje, że utraciliście dostęp do Internetu, a aplikacja na stronie internetowej nadal działa?

Poniższy artykuł ma na celu przybliżenie tematu Service Workerów, czyli mechanizmu który z powodzeniem zadomowił się w najnowszych przeglądarkach internetowych, definiując sposób w jaki powinna zachować się aplikacja w przypadku właśnie utraty połączenia z Internetem. Możecie kojarzyć poprzednika tego typu rozwiązania problemu z dostępem offline do aplikacji internetowych był nip AppCache, który pozwalał programistom zdefiniować listę plików pamięci podręcznej (cache). Jak się z czasem okazało, AppCache nie jest dostosowany do swobodnej implementacji przez developerów w aplikacjach webowych. Doprowadziło to do poszukiwania innego (prostszego) rozwiązania z poziomu środowiska JavaScript. Service Workers to plik napisany w JS, który daje potężną możliwość pełnej kontroli pobierania wszystkich dostępnych zasobów z domeny. Największe (domyślne) wsparcie dla tego mechanizmu od samego początku proponuje przeglądarka koncernu Google – Chrome. W telegraficznym skrócie ujmując, mechanizm ten uruchamiany jest oddzielnie od głównego wątku przeglądarki internetowej, tak aby umożliwić takie funkcje jak: obsługę powiadomień w trybie „push”, natychmiastową synchronizację danych w tle, zachowanie pomięci podręcznej oraz buforowanie jej oraz odbieranie scentralizowanych aktualizacji.

 

Skrypt wykorzystywany jest do zapewnienia progresywnym aplikacjom webowym wysokiej wydajności i dobrej współpracy z natywnymi aplikacjami mobilnymi przy niewielkim zużyciu miejsca na dysku, aktualizacji w czasie rzeczywistym oraz przy zdecydowanie lepszej widoczności tradycyjnych aplikacji sieciowych w wyszukiwarkach internetowych. Service Workery udostępniając ogromne możliwości ingerencji w aplikację, jednocześnie naznaczone ogromnym ryzykiem ataku man-in-the-middle w przypadkach, gdy udostępnianie ich następuje poprzez mniej bezpieczny niż protokół HTTPS – HTTP. Istnieje jednak wyjątek: w przypadku umiejscowienia w localhost nie występuje podobne utrudnienie, co daje nam możliwość przeprowadzania bezpiecznych testów.

2. Cykl życia i zdarzenia skryptu Service Worker

Cykl życia Service Workers jest właściwie jego najbardziej skomplikowaną częścią. Jeśli developer dobrze zaplanuje działania, może dostarczać użytkownikom niezauważalne aktualizacje, które pozwolą na przyjemne korzystanie ze strony internetowej.

Intencją cyklu życia jest:

 

  • Sprawianie, by założenie offline-first było możliwe do wdrożenia.
  • Pozwolenie nowemu mechanizmowi Service Workers na wdrażanie się bez zakłócania pracy obecnego skryptu.
  • Upewnienie się, że strona jest kontrolowana przez ten sam Service Worker (lub jej brak) przez cały czas.
  • Sprawdzanie, że istnieje tylko jedna wersja strony działająca jednocześnie.

 

Ostatni podpunkt jest szczególnie istotny, ponieważ bez sprawnego Service Workera, użytkownicy strony mogą otworzyć w dwóch zakładkach Twoją stronę, a jeśli w tym czasie była ona aktualizowana, to dwie jej wersje mogą działać jednoczenie. Nie jest to przypadek, w którym za każdym razem jest to uciążliwy problem, lecz jeśli przechowywane są dane, może pojawić się konflikt pomiędzy zarządzaniem tymi stronami. Może to nie tylko prowadzić do niewygodnych dla użytkownika błędów, lecz także do utraty cennych danych. To na pewno może zniechęcić odwiedzającego do ponownej wizyty na stronie.

 

W skrócie:

  • Zdarzenie instalacji jest pierwszym eventem, które otrzymuje Service Workers i zdarza się tylko raz, a polecenie przekazane do installEvent.waitUntil() definiuje czas trwania i powodzenie lub niepowodzenie instalacji.
  • Skrypt nie odbierze zdarzeń takich jak pobranie i powiadamianie w trybie „push”, dopóki nie zakończy pomyślnie instalacji i nie stanie się aktywny.
  • Domyślnie strona nie przejdzie przez Service Worskery, chyba że samo jej żądanie przeszło przez ten proces. Koniecznym jest więc odświeżenie strony, aby zobaczyć efekty działania tego mechanizmu.
  • clients.claim() może nadpisać to domyślne ustawienie i przejąć kontrolę nad niekontrolowanymi stronami.

Jeśli zdefiniujemu ścieżkę na /sw.js, to wszystkie akcje Service Workera wykonywane są w ramach zdarzeń (eventów): install, activate, fetch. tzn. w pliku /sw.js).

 

Oto przykładowy kod:

  // Zdarzenie wywoływane jest po zarejestrowaniu Service Workera

});

self.addEventListener(’activate’, function(ev) {

  // Zdarzenie, które wywołamy po aktualizacji pliku Service Workera

});

self.addEventListener(’fetch’, function(ev) {

  // Zdarzenie wywoływane podczas próby pobrania zasobu

});

 

Zazwyczaj w zdarzeniu „install” wypełniamy pamięć podręczną jakimiś podstawowymi i początkowymi danymi. Kolejno w evencie „active”, najważniejsze są wszelkie możliwe zmiany w pamięci podręcznej (przykładowo zostaje z niego wyrzucony jeden z zasobów), a zdarzenie „fetch” wywołane jest za każdym razem, gdy pobierane są jakieś zasoby. Ostatnie zdarzenie wywołane jest przy każdej próbie pobrania zasobu, więc to nie ma znaczenia, czy aplikacja jest obecnie offline czy on-line. 

 

3. Pierwsza rejestracja użytkownika

Service Workers wpływają na opinię użytkownika, który korzysta ze strony. To ten mechanizm pomaga przyspieszyć oraz zwielokrotnić wizyty osób, a jego nieprawidłowe działanie może wpływać na negatywną ocenę strony przez odwiedzającego, a tym samym doprowadzić do sytuacji, że nie powróci już nigdy na stronę. Ważnym jest, aby rejestracja Service Workers była wstrzymana do momentu załadowania się pierwszej odwiedzanej strony. To pozwoli na lepsze wrażenie dla użytkownika serwisu, szczególnie takiego, który korzysta z powolnego łącza internetowego (np. na urządzeniach mobilnych).

 

W przypadku pierwszej wizyty użytkownika w aplikacji internetowej, przeglądarka nie ma możliwości „przewidzenia”, czy zostanie zainstalowany mechanizm. Zadaniem programisty jest zadbanie o to, aby taka informacja priorytetowo trafiła do przeglądarki użytkownika oraz zawierała minimalny zestaw informacji potrzebnych do wyświetlania interaktywnej strony. Wszelkie zasoby strony, które powodują spowolnienia w pobieraniu tych odpowiedzi, są traktowane jako niesprzyjające szybkiej implementacji interaktywnej strony, a tym samym mogą powodować negatywne doświadczenie użytkownika.

 

Pozytywne rozwiązanie tego problemu, polega na kontrolowaniu startu Service Workera poprzez wybranie momentu wywołania pliku navigator.serviceWorker.register(). Prostą zasadą jest opóźnienie rejestracji do momentu, gdy w oknie pojawi się zdarzenie obciążenia. Można zapisać to za pomocą poniższej formuły:

if(’serviceWorker’ in navigator) {
  window.addEventListener(’load’, function() {
    navigator.serviceWorker.register(’/service-worker.js’);
  });
}

 

Ważnym aspektem podczas budowy strony jest umiejętne wskazanie, w którym momencie taka rejestracja mechanizmu powinna nastąpić. Zależne jest to od tego, co następuje zaraz po jej załadowaniu. Przykładem jest aplikacja internetowa Google I/O 2016, która przed przejściem do ekranu głównego zawiera krótką animację.

 

Rozpoczęcie rejestracji Service Worker podczas animacji mogłoby doprowadzić do poważnych utrudnień w ładowaniu np. na urządzeniach mobilnych. Na stronie pojawiłby się konflikt, który nie tylko opóźni ładowanie strony, ale może ją nawet „wykrzaczyć”. Żadne z tych zachować aplikacji nie wpływa na pozytywny odbiór przez użytkownika. Zapobiegając niewłaściwemu zachowaniu strony, rejestracja została opóźniona do momentu zakończenia animacji. Zaraz po niej można było „wyłapać” kilka bezczynnych sekund na wprowadzenie Service Workera. 

 

Jeśli dowolna aplikacja korzysta z platformy programistycznej, która wykonuje dodatkowe czynności po załadowaniu strony, warto znaleźć zdarzenie specyficznego dla tego frameworka, która sygnalizuje, kiedy te czynności zostaną wykonane.

 

4. Kolejne wizyty

Wiemy już, jak przebiega rejestracja podczas pierwszej wizyty, ale jaki wpływ na taka opóźniona rejestracja mechanizmu na ponowne odwiedziny strony przez użytkownika? 

 

Zaskakujące jest to, że nie powinno to mieć wpływu. Zarejestrowany Service Worker przechodzi przez instalację i aktywuje zdarzenia cyklu życia. Po aktywacji może on obsługiwać kolejne wizyty w aplikacji internetowej oraz wydarzenia z nim związane. Istniejący Service Worker uruchamiany jest jeszcze przed zgłoszeniem zapotrzebowania na jakiekolwiek strony wchodzące w jego zakres, co ma znaczący wpływ na pozytywną realizację żądań dotyczących nawigacji. 

 

Nie ma więc znaczenia, kiedy wywołany zostanie navigator.serviceWorker.register() oraz czy w ogóle to nastąpi. Jeśli nie zmienisz adresu URL skryptu mechanizmu, navigator.serviceWorker.register() nie będzie działać podczas kolejnych wizyt – ponieważ został już zarejestrowany wcześniej.

5. Testowanie procesu

Developerzy i testerzy, podczas budowy i udoskonalania, najczęściej ładują swoją aplikację kilkadziesiąt razy dziennie. Wracając w ten sposób na stronę, mamy już zarejestrowanego Service Worker-a, a pamięć podręczna jest w pełni zapełniona. Jeśli jest jakiś problem z aplikacją lub stroną, trudno w tej sposób doświadczyć tego samego, co użytkownik podczas pierwszej wizyty. Przez to można nie być świadomym potencjalnego problemu ze stroną lub aplikacją. Symulując pierwszą wizytę, warto otworzyć aplikację internetową w trybie Incognito. Pomoże to przyjrzenie się ruchowi sieciowemu dzięki narzędziu DevTools Chrome.

6. Czy da się wyłączyć ten skrypt?

To, jak można wyłączyć (wyrejestrować) Service Workers najprościej przedstawić na przykładzie przeglądarki Chrom. Wchodząc na adres: chrome://inspect/#service-workers przeglądarka zwróci nam listę działających obecnie Service Workerów dla wszystkich domen (rys.1). 

 

Kliknięcie przycisku „Terminate” nie sprawi, że Service Worker zostanie zatrzymany. 

 

Intuicyjnie może wydawać się, że po kliknięciu na przycisk „Terminate”, Service Worker zostanie zatrzymany. Okazuje się, że nic bardziej mylnego, ponieważ zostaje on na nowo uruchamiany przy każdej próbie dostępu do zasobów strony. Istnieje jednak adres pod którym w przeglądarce Chrome, znajdziemy możliwość wyrejestrowania po kliknięciu „Unregister”. W skrócie, po tym zabiegu, zdefiniowane wcześniej adresy przestaną działać. Można to sprawdzić pod adresem: chrome://serviceworker-internals/

 

7. Kilka słów o bezpieczeństwie Service Workers

Zdefiniowany wcześniej Service Worker może być trudny do usunięcia, ale można go też nadpisać, podstawiając własną odpowiedź do zasobów, których adres kończy się na „.worker”. To oznacza, że jeśli na danej domenie pojawi się błąd XSS ((Cross Site Scripting), który pozwala na zdefiniowanie własnego Service Workera, potencjalnym zagrożeniem jest podmiana każdej podstrony przez atakującego, a nawet jeśli użytkownik strony zorientuje się, że doszło do ataku, może mieć problem z usunięciem złośliwego skryptu Service Workera. Nie jest to jednak takie proste, ponieważ wymagane jest, aby skrypt zwracał nagłówek Content-Type: application/javascript. Aplikacje webowe w większości nie zezwalają na jakiekolwiek modyfikacje ich zasobów. Istnieją oczywiście wyjątki, jakim jest udostępnianie przez aplikację interfejsu JSONP. 

 

Twórcy przeglądarek cały czas starają się chronić użytkowników przed złośliwym użyciem tego mechanizmu. 

 

8. Kilka słów podsumowania

Proces Service Workera pozwala developerom aplikacji internetowych na określenie sposobu pobierania zasobów (w tym definiuje zachowywanie aplikacji w trybie off-line). Ogromne możliwości niosą za sobą zawsze duże ryzyko, dlatego błąd typu XSS może spowodować stałą podmianę zawartości wszystkich podstron przez atak zewnętrzny. Zabezpieczając stronę przed atakiem z wykorzystaniem Service Workers, należy do minimum ograniczyć możliwość umieszczania przez użytkownika treści w skryptach JavaScript. Można też przedsięwziąć dodatkowe działania, jak np. modyfikacja konfiguracji serwera, która polega na odrzucaniu wszelkich żądań, w których nagłówek to „Service-Worker: script”.

internet

Service-Workers

skrypt

strona internetowa

Czytaj również