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

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

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

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

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

מה הבעיה?

נניח שיש לכם שירות עבודה עם מאזין האירועים 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 יש יכולות רבות מעבר להעברת בקשה לרשת. תרחיש לדוגמה נפוץ הוא הוספת משאבים, כמו קובצי אודיו ווידאו, למטמון מקומי. לאחר מכן, עובד שירות יכול למלא בקשות מהמטמון הזה, תוך עקיפה מוחלטת של הרשת.

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

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

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