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

Hãy đảm bảo trình chạy dịch vụ của bạn 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 tài nguyên đầy đủ được trả về. Các giao thức này thường được dùng để truyền trực tuyến nội dung âm thanh hoặc video để cho phép tải các đoạn nội dung đa phương tiệ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.

Trình chạy dịch vụ là mã JavaScript nằm giữa ứng dụng web và mạng, có thể chặn các yêu cầu mạng đi và tạo phản hồi cho các yêu cầu đó.

Trước đây, các yêu cầu phạm vi và trình chạy dịch vụ không hoạt động tốt với nhau. Bạn cần phải thực hiện các bước đặc biệt để tránh kết quả không tốt trong worker dịch vụ. Rất may, điều 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 truyền qua trình chạy dịch vụ.

Vấn đề của bạn là gì?

Hãy xem xét một worker dịch vụ có 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 yêu cầu đó đế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 phải "phá vỡ" bất kỳ điều gì, vì máy chủ về mặt kỹ thuật được phép 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. Tuy nhiên, điều này 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.

Những nhà phát triển nhận thấy hành vi này có thể khắc phục bằng cách kiểm tra rõ ràng sự hiện diện của tiêu đề Range: và không gọi event.respondWith() nếu có. Bằng cách này, worker dịch vụ sẽ tự xoá khỏi hình ảnh tạo phản hồi một cách hiệu quả và logic kết nối mạng mặc định của trình duyệt (biết cách lưu giữ 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ấy sự cần thiết phải làm việc này. Và không rõ lý do cần phải có yêu cầu đó. Cuối cùng, hạn chế này là do các trình duyệt cần bắt kịp những thay đổi trong thông số kỹ thuật cơ bản, nhờ đó hỗ trợ thêm chức năng này.

Những vấn đề đã đượ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 truyền đến fetch(). Điều này có nghĩa là mã 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));
});

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

Những trình duyệt nào hoạt động đúng cách?

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

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, bạn vẫn có thể cần tính đến hành vi này trong khi triển khai mã của worker dịch vụ cho môi trường phát hành công khai.

Cách tốt nhất để xác nhận xem một trình duyệt nhất định có khắc phục hành vi này hay không là kiểm tra hàng "Thêm tiêu đề phạm vi vào yêu cầu mạng" của trang tổng quan về Kiểm thử nền tảng web.

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

Worker dịch vụ có thể làm được nhiều việc hơn là chỉ chuyển yêu cầu đến 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 đó, trình chạy dịch vụ có thể thực hiện các yêu cầu từ bộ nhớ đệm đó, hoàn toàn bỏ qua mạng.

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

May mắn thay, những nhà phát triển muốn được trợ giúp có thể chuyển sang Workbox. Đây là một bộ 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 một phần các phản hồi trực tiếp 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 trên bài đăng này là của Natalie Rhea Riggs trên Unsplash.