טיפול בבקשות טווח של קובץ שירות (service worker)

חשוב לוודא ש-service worker יודע מה לעשות כשמתקבלת בקשה לתגובה חלקית.

חלק מבקשות ה-HTTP מכילות כותרת Range:, שמציינת שצריך להחזיר רק חלק מהמשאב המלא. הם משמשים בדרך כלל לסטרימינג של תוכן אודיו או וידאו, כדי לאפשר טעינה של קטעי מדיה קטנים יותר על פי דרישה, במקום לבקש את כל הקובץ המרוחק בבת אחת.

קובץ שירות (service worker) הוא קוד JavaScript שנמצא בין אפליקציית האינטרנט לבין הרשת, ויכול ליירט בקשות יוצאות מהרשת וליצור תשובות עבורן.

בעבר, בקשות לטווחים ועובדי שירות לא שיחקו ביחד. נאלצנו לנקוט פעולות מיוחדות כדי למנוע תוצאות שליליות ב-service worker. למרבה המזל, המצב הזה מתחיל להשתנות. בדפדפנים שמציגים את ההתנהגות הנכונה, בקשות טווח פשוט יפעלו כשהן עוברות דרך service worker.

מה הבעיה?

כדאי להגדיר קובץ שירות (service worker) עם ה-event listener הבא של fetch, שלוקח כל בקשה נכנסת ומעביר אותה לרשת:

self.addEventListener('fetch', (event) => {
  // The Range: header will not pass through in
  // browsers that behave incorrectly.
  event.respondWith(fetch(event.request));
});

בדפדפנים עם ההתנהגות השגויה, אם event.request תכלול כותרת Range:, הכותרת הזו תושמט ללא הודעה. הבקשה שהתקבלה מהשרת המרוחק לא תכלול את Range: בכלל. המצב הזה לא בהכרח 'ישבור' משהו, כי טכנית השרת רשאי להחזיר את גוף התגובה המלא, עם קוד סטטוס 200, גם אם כותרת Range: נמצאת בבקשה המקורית. עם זאת, הדבר יגרום להעברת נתונים רבים יותר ממה שנדרש בקפידה מבחינת הדפדפן.

מפתחים שהיו מודעים להתנהגות הזו יכולים לעקוף אותה על ידי בדיקה מפורשת של קיומה של כותרת Range:, ולא קריאה ל-event.respondWith() אם קיימת כותרת כזו. כך, ה-service worker מסיר את עצמו באופן יעיל מהתמונה של יצירת התגובה, ובמקום זאת נעשה שימוש בלוגיקה שמוגדרת כברירת מחדל ברשת הדפדפן, שמסוגלת לשמור על בקשות טווח.

self.addEventListener('fetch', (event) => {
  // Return without calling event.respondWith()
  // if this is a range request.
  if (event.request.headers.has('range')) {
    return;
  }

  event.respondWith(fetch(event.request));
});

עם זאת, אפשר להניח שרוב המפתחים לא היו מודעים לצורך לעשות זאת. בנוסף, לא היה ברור למה צריך לדרוש זאת. בסופו של דבר, ההגבלה הזו נבעה מהדפדפנים שהיו צריכים להשלים את השינויים במפרט הבסיסי, דבר שהוסיף תמיכה בפונקציונליות הזו.

מה תוקן?

בדפדפנים שמתנהגים בצורה תקינה, הכותרת Range: נשמרת כשהערך event.request מועבר אל fetch(). כלומר, קוד ה-service worker בדוגמה הראשונית שלי יאפשר לשרת המרוחק לראות את הכותרת Range:, אם היא הוגדרה על ידי הדפדפן:

self.addEventListener('fetch', (event) => {
  // The Range: header will pass through in browsers
  // that behave correctly.
  event.respondWith(fetch(event.request));
});

עכשיו יש לשרת הזדמנות לטפל כראוי בבקשה לטווח ולהחזיר תשובה חלקית עם קוד סטטוס 206.

באילו דפדפנים ההתנהגות נכונה?

בגרסאות האחרונות של Safari יש פונקציונליות תקינה. גם Chrome ו-Edge, החל מגרסה 87, פועלים בצורה תקינה.

נכון לאוקטובר 2020, הבעיה הזו עדיין לא תוקנה ב-Firefox, לכן יכול להיות שתצטרכו להביא אותה בחשבון בזמן הפריסה של קוד ה-service worker בסביבת הייצור.

הדרך הטובה ביותר לוודא אם דפדפן מסוים תיקן את ההתנהגות הזו היא לבדוק את השורה 'Include range header in network request' (הכללת כותרת טווח בבקשת רשת) בלוח הבקרה של בדיקות פלטפורמת האינטרנט.

מה לגבי מילוי בקשות לטווח מהמטמון?

ל-Service Workers יש יכולות רבות מעבר להעברת בקשה לרשת. תרחיש לדוגמה נפוץ הוא הוספת משאבים, כמו קובצי אודיו ווידאו, למטמון מקומי. לאחר מכן, Service Worker יכול למלא בקשות מהמטמון וכך לעקוף את הרשת לגמרי.

כל הדפדפנים, כולל Firefox, תומכים בבדיקת בקשה בתוך טיפולן על ידי fetch, בודקים אם הכותרת Range: קיימת ואז ממלאים את הבקשה באופן מקומי באמצעות תשובת 206 שמגיעה מהמטמון. עם זאת, קוד ה-service worker לניתוחים תקינים של הכותרת Range: ולהחזרת החלק המתאים בלבד מהתגובה המלאה שנשמרה במטמון הוא לא טריוויאלי.

למרבה המזל, מפתחים שרוצים לקבל עזרה יכולים לעבור לכלי Workbox – קבוצה של ספריות, שהופכת את לפשוטים יותר של תרחישים לדוגמה של עובדי שירות. ה-workbox-range-request module מטמיע את כל הלוגיקה הנדרשת כדי להציג תשובות חלקיות ישירות מהמטמון. במסמכי התיעוד של Workbox אפשר למצוא הוראות מפורטות לשימוש בתרחיש הזה.

התמונה הראשית (Hero) בפוסט הזה היא של Natalie Rhea Riggs ב-Unsplash.