Xử lý yêu cầu về phạm vi trong trình chạy dịch vụ

Đảm bảo trình chạy dịch vụ biết việc cần làm khi phản hồi một phần được yêu cầu.

Một số yêu cầu HTTP chứa tiêu đề Range:, cho biết rằng chỉ một phần của tài nguyên đầy đủ sẽ được trả về. Các tệp này thường được dùng để truyền trực tuyến nội dung âm thanh hoặc video nhằm cho phép tải các phần nội dung nghe nhìn nhỏ hơn theo yêu cầu, thay vì yêu cầu toàn bộ tệp từ xa cùng một lúc.

service worker là mã JavaScript nằm giữa ứng dụng web và mạng, có khả năng chặn các yêu cầu mạng gửi đi và tạo ra phản hồi cho những yêu cầu đó.

Trước đây, yêu cầu phạm vi và trình chạy dịch vụ chưa kết hợp hài hoà với nhau. Cần phải thực hiện các bước đặc biệt để tránh kết quả xấu trong trình chạy dịch vụ của bạn. Thật may là tình hình này đang bắt đầu thay đổi. Trong các trình duyệt thể hiện hành vi chính xác, các yêu cầu phạm vi sẽ "chỉ hoạt động" khi chuyển qua một trình chạy dịch vụ.

Bạn đang gặp vấn đề gì?

Hãy xem xét một trình chạy dịch vụ với trình nghe sự kiện fetch sau đây. Trình nghe này sẽ nhận mọi yêu cầu đến và chuyển đến mạng:

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

Trong các trình duyệt có hành vi không chính xác, nếu event.request có tiêu đề Range:, thì tiêu đề đó sẽ tự động bị loại bỏ. Yêu cầu mà máy chủ từ xa nhận được sẽ không bao gồm Range:. Điều này không nhất thiết sẽ "làm hỏng" bất kỳ điều gì vì máy chủ được phép về mặt kỹ thuật để trả về toàn bộ nội dung phản hồi, với mã trạng thái 200, ngay cả khi tiêu đề Range: có trong yêu cầu ban đầu. Nhưng điều đó sẽ dẫn đến việc chuyển nhiều dữ liệu hơn mức cần thiết từ quan điểm của trình duyệt.

Các nhà phát triển nhận biết được hành vi này có thể giải quyết bằng cách kiểm tra rõ ràng xem có tiêu đề Range: hay không và không gọi event.respondWith() nếu có tiêu đề. Bằng cách này, trình chạy dịch vụ sẽ tự xoá một cách hiệu quả khỏi chế độ tạo phản hồi và logic nối mạng của trình duyệt mặc định (biết cách duy trì các yêu cầu phạm vi) sẽ được sử dụng.

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));
});

Tuy nhiên, có thể nói rằng hầu hết các nhà phát triển đều không nhận thức được sự cần thiết của việc này. Chưa rõ lý do cần phải có hành động này. Cuối cùng, hạn chế này là do các trình duyệt cần phải bắt kịp những thay đổi trong thông số kỹ thuật cơ bản, giúp hỗ trợ thêm chức năng này.

Những vấn đề nào đã được khắc phục?

Các trình duyệt hoạt động chính xác sẽ giữ nguyên tiêu đề Range: khi event.request được chuyển đến fetch(). Điều này có nghĩa là mã service worker trong ví dụ ban đầu của tôi sẽ cho phép máy chủ từ xa xem tiêu đề Range:, nếu trình duyệt đã đặt tiêu đề này:

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

Máy chủ giờ đây sẽ có cơ hội xử lý đúng cách yêu cầu phạm vi và trả về phản hồi một phần bằng mã trạng thái 206.

Trình duyệt nào hoạt động chính xác?

Các phiên bản Safari gần đây có chức năng chính xác. Chrome và Edge (bắt đầu từ phiên bản 87) cũng hoạt động đúng cách.

Kể từ tháng 10 năm 2020, Firefox vẫn chưa khắc phục hành vi này, vì vậy có thể bạn vẫn cần phải tính đến nó trong khi triển khai mã của trình chạy dịch vụ trong quá trình sản xuất.

Kiểm tra hàng "Bao gồm tiêu đề phạm vi trong yêu cầu mạng" của trang tổng quan Kiểm thử nền tảng web là cách tốt nhất để xác nhận xem một trình duyệt cụ thể đã khắc phục hành vi này hay chưa.

Còn các yêu cầu phạm vi phân phát từ bộ nhớ đệm thì sao?

Trình chạy dịch vụ có thể làm được nhiều việc hơn là chỉ truyền yêu cầu tới mạng. Một trường hợp sử dụng phổ biến là thêm tài nguyên, chẳng hạn như tệp âm thanh và video, vào bộ nhớ đệm cục bộ. Sau đó, một trình chạy dịch vụ có thể thực hiện các yêu cầu từ bộ nhớ đệm đó, bỏ qua mạng hoàn toàn.

Tất cả trình duyệt, bao gồm cả Firefox, đều hỗ trợ kiểm tra yêu cầu bên trong trình xử lý fetch, kiểm tra sự hiện diện của tiêu đề Range:, sau đó thực hiện yêu cầu trên máy bằng một phản hồi 206 lấy từ bộ nhớ đệm. Tuy nhiên, mã trình chạy dịch vụ để phân tích cú pháp tiêu đề Range: một cách chính xác và chỉ trả về phân đoạn phù hợp của phản hồi hoàn chỉnh đã lưu vào bộ nhớ đệm không hề nhỏ.

May mắn là các nhà phát triển cần được trợ giúp có thể sử dụng Workbox. Đây là một tập hợp thư viện giúp đơn giản hoá các trường hợp sử dụng phổ biến của trình chạy dịch vụ. workbox-range-request module triển khai tất cả logic cần thiết để phân phát phản hồi một phần ngay từ bộ nhớ đệm. Bạn có thể xem công thức đầy đủ cho trường hợp sử dụng này trong tài liệu về Workbox.

Hình ảnh chính trong bài đăng này là của Natalie Ryan Riggs trên Unsplash.