Kaynaklar Arası Hizmet Çalışanları - Yabancı Getirme ile Deneme Yapma

Cem Posnick
Jeff Posnick

Arka plan

Service Worker, web geliştiricilerinin web uygulamaları tarafından yapılan ağ isteklerine yanıt vermelerine, çevrimdışıyken bile çalışmaya devam etmelerine, lie-fi ile mücadele etmesine ve eski-tekrar doğrulama gibi karmaşık önbellek etkileşimleri uygulamasına olanak tanır. Ancak Service Worker'lar geçmişte belirli bir kaynağa bağlıdır. Bir web uygulamasının sahibi olarak, web uygulamanızın gönderdiği tüm ağ isteklerine müdahale etmek için bir hizmet çalışanı yazıp dağıtmak sizin sorumluluğunuzdadır. Bu modelde her hizmet çalışanı, üçüncü taraf API'leri veya web yazı tipleri gibi kaynaklar arası istekleri bile ele almaktan sorumludur.

Bir API'nin, web yazı tiplerinin ya da yaygın olarak kullanılan başka bir hizmetin üçüncü taraf sağlayıcısı, diğer kaynaklardan gelen istekleri kendi kaynağına gönderme fırsatı bulan kendi servis çalışanlarını dağıtma gücüne sahip olsaydı ne olacak? Sağlayıcılar kendi özel ağ iletişimi mantığını uygulayabilir ve yanıtlarını depolamak için tek, yetkili bir önbellek örneğinden yararlanabilir. Artık yabancı getirme sayesinde bu tür bir üçüncü taraf hizmet çalışanı dağıtımı artık mümkün.

Yabancı getirme uygulayan bir hizmet çalışanının dağıtılması, tarayıcılardan HTTPS istekleri aracılığıyla erişilen herhangi bir hizmetin sağlayıcısı için anlamlıdır. Hizmetinizin ağdan bağımsız bir sürümünü sağlayabileceğiniz ve tarayıcıların ortak kaynak önbelleğinden yararlanabileceği senaryoları düşünün. Bu durumdan yararlanabilecek hizmetler aşağıda belirtilmiştir, ancak bunlarla sınırlı değildir:

  • RESTful arayüze sahip API sağlayıcılar
  • Web yazı tipi sağlayıcıları
  • Analiz sağlayıcıları
  • Resim barındırma sağlayıcıları
  • Genel içerik yayınlama ağları

Örneğin, bir analiz sağlayıcısı olduğunuzu düşünün. Yabancı bir getirme hizmeti çalışanı dağıtarak, kullanıcı çevrimdışıyken hizmetinize yönelik başarısız olan tüm isteklerin kuyruğa alınmasını ve bağlantı geri geldiğinde tekrar oynatılmasını sağlayabilirsiniz. Bir hizmetin istemcilerinin birinci taraf hizmet çalışanları aracılığıyla benzer bir davranış uygulaması mümkün olsa da her istemcinin hizmetiniz için özel mantık yazmasını zorunlu kılmak, dağıttığınız paylaşılan bir yabancı getirme hizmeti çalışanına güvenmek kadar ölçeklenebilir değildir.

Ön koşullar

Kaynak Deneme jetonu

Yabancı dil getirme işlemi hâlâ deneysel olarak kabul edilir. Bu tasarımın, tarayıcı tedarikçileri tarafından tam olarak belirtilmeden ve kabul edilmeden erkenden işlenmesini önlemek için Chrome 54'te Kaynak Denemesi olarak uygulanmıştır. Yabancı getirme deneysel olduğu sürece, bu yeni özelliği barındırdığınız hizmetle kullanmak için hizmetinizin belirli kaynağına ayarlanmış bir jeton istemeniz gerekir. Bu jeton, yabancı getirme aracılığıyla işlemek istediğiniz kaynakların tüm çapraz kaynak isteklerine ve hizmet çalışanı JavaScript kaynağınıza verilen yanıta HTTP yanıt başlığı olarak eklenmelidir:

Origin-Trial: token_obtained_from_signup

Deneme süresi Mart 2017'de sona erecektir. Bu noktaya kadar, özelliği kararlı hale getirmek için gerekli değişiklikleri tespit etmiş ve (umuyoruz) özelliği varsayılan olarak etkinleştirmiş olacağız. Bu tarihe kadar yabancı getirme varsayılan olarak etkinleştirilmemişse mevcut Kaynak Deneme jetonlarına bağlı işlevler çalışmayı durdurur.

Resmi Kaynak Deneme jetonuna kaydolmadan önce yabancı getirme denemesi yapmayı kolaylaştırmak için chrome://flags/#enable-experimental-web-platform-features adresine gidip "Deneysel Web Platformu özellikleri" işaretini etkinleştirerek yerel bilgisayarınız için Chrome şartını atlayabilirsiniz. Bu işlemin, yerel denemelerinizde kullanmak istediğiniz her Chrome örneğinde yapılması gerektiğini, Kaynak Deneme jetonuyla ise özelliğin tüm Chrome kullanıcılarınız tarafından kullanılabileceğini lütfen unutmayın.

HTTPS

Tüm hizmet çalışanı dağıtımlarında olduğu gibi, hem kaynaklarınızı hem de hizmet çalışanı komut dosyanızı sunmak için kullandığınız web sunucusuna HTTPS üzerinden erişilmelidir. Ayrıca, yabancı getirme müdahalesi yalnızca güvenli kaynaklarda barındırılan sayfalardan gelen istekler için geçerlidir. Bu nedenle, hizmetinizin istemcilerinin yabancı getirme uygulamanızdan yararlanmak için HTTPS kullanması gerekir.

Yabancı Getirme'yi Kullanma

Önkoşullar ortadan kalktıktan sonra, bir yabancı getirme hizmeti çalışanının hazır ve çalışır duruma gelmesi için gerekli teknik ayrıntılara girelim.

Hizmet çalışanınız kaydediliyor

Karşılaşabileceğiniz ilk zorluk, hizmet çalışanınızı nasıl kaydedeceğinizdir. Daha önce Service Worker'larla çalıştıysanız aşağıdaki konuları muhtemelen biliyorsunuzdur:

// You can't do this!
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('service-worker.js');
}

Birinci taraf hizmet çalışanı kaydı için oluşturulan bu JavaScript kodu, kullanıcının kontrol ettiğiniz bir URL'ye gitmesiyle tetiklenen bir web uygulaması bağlamında anlamlıdır. Ancak, sunucunuzla tek etkileşim tarayıcısı tam gezinme değil, belirli bir alt kaynak isteğinde bulunduğunda bu, üçüncü taraf hizmet çalışanı kaydettirmeye uygun bir yaklaşım değildir. Tarayıcı, örneğin yönettiğiniz bir CDN sunucusundan resim isterse bu JavaScript snippet'ini yanıtınızın başına ekleyemez ve çalıştırılmasını bekleyemezsiniz. Normal JavaScript yürütme bağlamı dışında farklı bir hizmet çalışanı kayıt yöntemi gereklidir.

Çözüm, sunucunuzun herhangi bir yanıta ekleyebileceği bir HTTP üstbilgisi biçiminde gelir:

Link: </service-worker.js>; rel="serviceworker"; scope="/"

Bu örnek başlığı, her biri bir ; karakteriyle ayrılan bileşenlerine ayıralım.

  • </service-worker.js> gereklidir ve hizmet çalışanı dosyanızın yolunu belirtmek için kullanılır (/service-worker.js ifadesinin yerine komut dosyanızın uygun yolunu girin). Normalde navigator.serviceWorker.register() öğesine ilk parametre olarak iletilecek olan scriptURL dizesine doğrudan karşılık gelir. Değerin <> karakterleri içine alınması gerekir (Link başlık spesifikasyonunun gerektirdiği gibi) ve mutlak URL yerine göreli bir URL sağlanırsa yanıtın konumuna göre yorumlanır.
  • rel="serviceworker" de gereklidir ve özelleştirmeye gerek kalmadan dahil edilmelidir.
  • scope=/, navigator.serviceWorker.register() öğesine ikinci parametre olarak iletebileceğiniz options.scope dizesine eşdeğer olan, isteğe bağlı bir kapsam bildirimidir. Çoğu kullanım alanında varsayılan kapsamı kullanmanız sorun yaratmaz. Dolayısıyla ihtiyacınız olduğunu bilmiyorsanız bu alanı dışarıda bırakabilirsiniz. İzin verilen maksimum kapsamla ilgili kısıtlamalar ve Service-Worker-Allowed başlığı aracılığıyla bu kısıtlamaları gevşetme seçeneğinin yanı sıra Link başlık kayıtları için de geçerlidir.

"Geleneksel" hizmet çalışanı kaydında olduğu gibi, Link başlığı kullanıldığında da kayıtlı kapsamda yapılan sonraki istek için kullanılacak bir hizmet çalışanı yüklenir. Özel başlığı içeren yanıtın gövdesi olduğu gibi kullanılır ve yabancı hizmet çalışanının kurulumu tamamlaması beklenmeden sayfaya hemen erişilebilir.

Yabancı getirmenin şu anda bir Kaynak Denemesi olarak uygulandığını unutmayın. Bu nedenle, Bağlantı yanıtı başlığınızla birlikte geçerli bir Origin-Trial üstbilgisi de eklemeniz gerekir. Yabancı dil getirme hizmeti çalışanınızı kaydetmek için eklenecek minimum yanıt başlığı kümesi:

Link: </service-worker.js>; rel="serviceworker"
Origin-Trial: token_obtained_from_signup

Hata ayıklama kaydı

Geliştirme sırasında, muhtemelen yabancı getirme hizmeti çalışanınızın düzgün şekilde yüklendiğini ve istekleri işlediğini onaylamak isteyebilirsiniz. İşlerin beklendiği gibi çalıştığını doğrulamak için Chrome'un Geliştirici Araçları'nda kontrol edebileceğiniz birkaç şey vardır.

Doğru yanıt üstbilgileri gönderiliyor mu?

Yabancı getirme hizmeti çalışanını kaydetmek için alan adınızda barındırılan bir kaynağa verilen yanıtta, bu yayının önceki bölümlerinde açıklandığı gibi bir Bağlantı üstbilgisi ayarlamanız gerekir. Kaynak Deneme süresi boyunca ve ayarlanmış chrome://flags/#enable-experimental-web-platform-features olmadığını varsayarsak bir Origin-Trial yanıt başlığı da ayarlamanız gerekir. DevTools'un Ağ paneli'ndeki girişe bakarak web sunucunuzun bu üstbilgileri ayarladığını doğrulayabilirsiniz:

Başlıklar, Ağ panelinde görüntülenir.

Yabancı Getirme hizmeti çalışanı düzgün şekilde kaydedildi mi?

Geliştirici Araçları'nın Uygulama panelinde, hizmet çalışanlarının tam listesine bakarak kapsamı da dahil olmak üzere temel hizmet çalışanı kaydını onaylayabilirsiniz. Varsayılan olarak yalnızca geçerli kaynağa ait hizmet çalışanlarını göreceğiniz için "Tümünü göster" seçeneğini işaretlediğinizden emin olun.

Uygulamalar panelindeki yabancı getirme hizmeti çalışanı.

Yükleme etkinlik işleyicisi

Üçüncü taraf hizmet çalışanınızı kaydettirdiğinize göre bu çalışan, diğer hizmet çalışanı gibi install ve activate etkinliklerine yanıt verme fırsatına sahip olacak. Örneğin, install etkinliği sırasında önbellekleri gerekli kaynaklarla doldurmak veya activate etkinliğindeki güncel olmayan önbellekleri ayıklamak için bu etkinliklerden yararlanabilir.

Normal install etkinliklerini önbelleğe alma etkinliklerinin ötesinde, üçüncü taraf hizmet çalışanınızın install etkinlik işleyicisinde zorunlu ek bir adım vardır. Kodunuzun aşağıdaki örnekte olduğu gibi registerForeignFetch() yöntemini çağırması gerekir:

self.addEventListener('install', event => {
    event.registerForeignFetch({
    scopes: [self.registration.scope], // or some sub-scope
    origins: ['*'] // or ['https://example.com']
    });
});

İki yapılandırma seçeneği vardır ve her ikisi de gereklidir:

  • scopes, bir veya daha fazla dizeden oluşan bir dizi alır. Bu dizelerin her biri, foreignfetch etkinliğini tetikleyecek isteklerin kapsamını temsil eder. Ama bir dakika, Hizmet çalışanı kaydı sırasında zaten bir kapsam tanımladım şeklinde düşünebilirsiniz. Genel kapsam da geçerliliğini korur. Burada belirttiğiniz her kapsam, hizmet çalışanının genel kapsamına eşit veya onun bir alt kapsamı olmalıdır. Buradaki ek kapsam kısıtlamaları, hem birinci taraf fetch etkinliklerini (kendi sitenizden yapılan istekler için) hem de üçüncü taraf foreignfetch etkinliklerini (diğer alanlardan yapılan istekler için) işleyebilen çok amaçlı bir hizmet çalışanı dağıtmanıza ve daha geniş kapsamınızın yalnızca bir alt kümesinin foreignfetch etkinliğini tetikleyeceğini açıkça belirtmenize olanak tanır. Pratikte yalnızca üçüncü taraf foreignfetch etkinliklerini işlemeye özel bir hizmet çalışanı dağıtıyorsanız yalnızca hizmet çalışanınızın genel kapsamına eşit olan tek ve açık bir kapsam kullanmanız gerekir. Yukarıdaki örnek, self.registration.scope değerini kullanarak bu işlemi gerçekleştirecektir.
  • origins ayrıca bir veya daha fazla dizeden oluşan bir dizi alır ve foreignfetch işleyicinizi yalnızca belirli alan adlarından gelen isteklere yanıt verecek şekilde kısıtlamanıza olanak tanır. Örneğin, "https://example.com"a açıkça izin verirseniz yabancı getirme kapsamınızdan sunulan bir kaynak için https://example.com/path/to/page.html adresinde barındırılan bir sayfadan yapılan istek, yabancı getirme işleyicinizi tetikler ancak https://random-domain.com/path/to/page.html uygulamasından yapılan istekler işleyicinizi tetiklemez. Uzak kaynakların yalnızca bir alt kümesi için yabancı getirme mantığınızı tetiklemek üzere belirli bir nedeniniz yoksa dizideki tek değer olarak '*' değerini belirtebilirsiniz. Bu durumda tüm kaynaklara izin verilir.

yabancı Getirme etkinlik işleyicisi

Üçüncü taraf hizmet çalışanınızı yüklediğinize ve registerForeignFetch() aracılığıyla yapılandırıldığına göre artık sunucunuza yapılan, yabancı getirme kapsamına giren kaynaklar arası alt kaynak isteklerine müdahale etme fırsatı elde edeceksiniz.

Geleneksel birinci taraf Service Worker'da her istek, hizmet çalışanınızın yanıt verebileceği bir fetch etkinliğini tetikler. Üçüncü taraf hizmet çalışanımıza foreignfetch adlı biraz farklı bir etkinliği gerçekleştirme şansı verildi. Kavramsal olarak bu iki etkinlik oldukça benzerdir ve size gelen isteği inceleme ve isteğe bağlı olarak respondWith() aracılığıyla yanıt verme fırsatı sunar:

self.addEventListener('foreignfetch', event => {
    // Assume that requestLogic() is a custom function that takes
    // a Request and returns a Promise which resolves with a Response.
    event.respondWith(
    requestLogic(event.request).then(response => {
        return {
        response: response,
        // Omit to origin to return an opaque response.
        // With this set, the client will receive a CORS response.
        origin: event.origin,
        // Omit headers unless you need additional header filtering.
        // With this set, only Content-Type will be exposed.
        headers: ['Content-Type']
        };
    })
    );
});

Kavramsal benzerliklere rağmen, ForeignFetchEvent üzerinde respondWith() çağrılırken uygulama açısından birkaç farklılık vardır. respondWith() için yalnızca bir Response (veya Response ile çözümlenen Promise) sağlamak yerine, FetchEvent ile çözülür gibi, ForeignFetchEvent öğesinin respondWith() öğesine belirli özelliklere sahip bir nesneyle çözümlenen Promise değerini iletmeniz gerekir:

  • response gerekli ve isteği gönderen istemciye döndürülecek Response nesnesi olarak ayarlanmalıdır. Geçerli bir Response dışında bir bilgi sağlarsanız istemcinin isteği ağ hatasıyla sonlandırılır. Bir fetch etkinlik işleyicinin içinde respondWith() çağırmanın aksine, burada bir Response sağlamanız zorunludur, Response ile çözümlenen Promise değeri yerine! Yanıtınızı bir taahhüt zinciri aracılığıyla oluşturabilir ve bu zinciri, foreignfetch respondWith() nesnesine parametre olarak aktarabilirsiniz. Ancak zincirin, Response nesnesine ayarlanmış response özelliğini içeren bir Nesne ile çözümlenmesi gerekir. Bunun bir örneğini yukarıdaki kod örneğinde görebilirsiniz.
  • origin isteğe bağlıdır ve döndürülen yanıtın opak olup olmadığını belirlemek için kullanılır. Bunu dışarıda bırakırsanız yanıt opak olur ve istemci, yanıtın gövdesine ve başlıklarına sınırlı şekilde erişebilir. İstek mode: 'cors' ile yapıldıysa opak yanıt döndürülmesi hata olarak değerlendirilir. Ancak, uzak istemcinin kaynağına (event.origin aracılığıyla edinilebilir) eşit bir dize değeri belirtirseniz, istemciye CORS özellikli bir yanıt sağlama özelliğini açıkça etkinleştirirsiniz.
  • headers de isteğe bağlıdır ve yalnızca origin özelliğini belirtiyor ve bir CORS yanıtı döndürüyorsanız kullanışlıdır. Varsayılan olarak, yanıtınıza yalnızca CORS güvenli listedeki yanıt başlığı listesindeki başlıklar dahil edilir. Döndürülen içeriğe daha fazla filtre uygulamanız gerekirse üstbilgiler, bir veya daha fazla üstbilgi adının yer aldığı bir listeyi alır. Bu liste, yanıtta gösterilecek üstbilgilerin izin verilenler listesi olarak kullanılır. Bu sayede, CORS'u etkinleştirirken hassas olabilecek yanıt başlıklarının doğrudan uzak istemciye gösterilmesini önlersiniz.

foreignfetch işleyici çalıştırıldığında, hizmet çalışanını barındıran kaynağın tüm kimlik bilgilerine ve ortam yetkililerine erişimi olduğunu aklınızda bulundurun. Yabancı bir getirme özelliğinin etkin olduğu hizmet çalışanı dağıtan bir geliştirici olarak, söz konusu kimlik bilgileri aracılığıyla başka şekilde erişilemeyecek ayrıcalıklı yanıt verilerini sızdırmadığınızdan emin olmak sizin sorumluluğunuzdadır. CORS yanıtlarının etkinleştirilmesini zorunlu kılmak, yanlışlıkla maruz kalmayı sınırlamanın bir adımıdır ancak bir geliştirici olarak foreignfetch işleyicinizin içinde, aşağıdakiler aracılığıyla ima edilen kimlik bilgilerini kullanmayan açıkça fetch() isteklerinde bulunabilirsiniz:

self.addEventListener('foreignfetch', event => {
    // The new Request will have credentials omitted by default.
    const noCredentialsRequest = new Request(event.request.url);
    event.respondWith(
    // Replace with your own request logic as appropriate.
    fetch(noCredentialsRequest)
        .catch(() => caches.match(noCredentialsRequest))
        .then(response => ({response}))
    );
});

Müşteriler için dikkat edilmesi gereken noktalar

Yabancı getirme hizmeti çalışanınızın, hizmetinizin istemcilerinden gelen istekleri işleme şeklini etkileyen bazı ek hususlar vardır.

Kendi birinci taraf hizmet çalışanı olan müşteriler

Hizmetinizdeki bazı istemcilerin, web uygulamalarından gelen istekleri ele alan kendilerine ait birinci taraf hizmet çalışanları zaten olabilir. Bu, üçüncü taraf, yabancı getirme hizmeti çalışanınız için ne anlama geliyor?

Birinci taraf hizmet çalışanının fetch işleyicileri, foreignfetch özelliğine sahip ve isteği kapsayan bir kapsama sahip üçüncü taraf hizmet çalışanı olsa bile web uygulaması tarafından yapılan tüm isteklere yanıt vermek için ilk fırsatı elde eder. Ancak birinci taraf hizmet çalışanları olan müşteriler, yabancı getirme hizmeti çalışanınızdan yararlanmaya devam edebilir.

Birinci taraf hizmet çalışanı içinde, kaynaklar arası kaynakları almak için fetch() kullanmak uygun yabancı getirme hizmeti çalışanını tetikler. Bu durumda, aşağıdaki gibi bir kod foreignfetch işleyicinizden yararlanabilir:

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    // If event.request is under your foreign fetch service worker's
    // scope, this will trigger your foreignfetch handler.
    event.respondWith(fetch(event.request));
});

Benzer şekilde, birinci taraf getirme işleyicileri varsa ancak bu işleyiciler çapraz kaynak kaynağınızla ilgili istekleri işlerken event.respondWith() yöntemini çağırmıyorsa istek otomatik olarak foreignfetch işleyicinize "yedek" olarak geçer:

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    if (event.request.mode === 'same-origin') {
    event.respondWith(localRequestLogic(event.request));
    }

    // Since event.respondWith() isn't called for cross-origin requests,
    // any foreignfetch handlers scoped to the request will get a chance
    // to provide a response.
});

Birinci taraf fetch işleyici event.respondWith() çağırır ancak yabancı getirme kapsamınız kapsamında kaynak istemek için fetch() hizmetini kullanmazsa yabancı getirme hizmeti çalışanınızın isteği işleme fırsatı olmaz.

Kendi hizmet çalışanı olmayan istemciler

Üçüncü taraf hizmetlerine istek gönderen tüm istemciler, hizmet bir yabancı getirme hizmeti çalışanı dağıttığında kendi hizmet çalışanını kullanmıyor olsalar bile bundan yararlanabilir. Destekleyen bir tarayıcı kullandıkları sürece, yabancı getirme hizmeti çalışanlarını kullanmaya başlamak için istemcilerin yapmaları gereken özel bir işlem yoktur. Bu, yabancı bir getirme hizmeti çalışanının dağıtımının, özel istek mantığınızın ve paylaşılan önbelleğinizin, hizmetinizin birçok istemcisine herhangi bir işlem gerçekleştirmeden, anında fayda sağlayacağı anlamına gelir.

Her şeyi bir araya getirmek: Müşterilerin yanıt aradığı yer

Yukarıdaki bilgileri göz önünde bulundurarak, bir istemcinin çapraz kaynak isteklerine yanıt bulmak için kullanacağı kaynak hiyerarşisini bir araya getirebiliriz.

  1. Birinci taraf hizmet çalışanının fetch işleyicisi (varsa)
  2. Üçüncü taraf hizmet çalışanının foreignfetch işleyicisi (varsa ve yalnızca kaynaklar arası istekler için)
  3. Tarayıcının HTTP önbelleği (yeni bir yanıt varsa)

Tarayıcı tepeden başlar ve hizmet çalışanının uygulamasına bağlı olarak yanıtın kaynağını bulana kadar listede ilerlemeye devam eder.

Daha fazla bilgi

En son gelişmeleri kaçırmayın

Geliştiricilerden gelen geri bildirimler doğrultusunda Chrome'un yabancı getirme Kaynak Denemesi uygulaması değişebilir. Satır içi değişiklikler yoluyla bu yayını güncel tutacağız ve aşağıdaki belirli değişiklikleri yapıldıkça not edeceğiz. Ayrıca @chromiumdev Twitter hesabı üzerinden önemli değişikliklerle ilgili bilgileri de paylaşacağız.