טיפול בבקשות טווח ב-Service Worker

תאריך פרסום: 6 באוקטובר 2020

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

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

בעבר, בקשות טווח וקובצי שירות (service workers) לא הסתדרו טוב ביחד. היה צורך לבצע פעולות מיוחדות כדי למנוע תוצאות לא רצויות ב-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' (הכללת כותרת טווח בבקשת רשת) בלוח הבקרה של Web Platform Tests.

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

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

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

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