FetchSource handles servers that return 416 for requests beyond end of file.

* Refine the detection of servers that don't support byte serving.
* Improve console errors related to failed Fetch.
This commit is contained in:
Brandon Liu
2022-12-04 22:57:07 +08:00
parent ca4c8094a8
commit cffdb681cf

View File

@@ -283,24 +283,41 @@ export class FetchSource implements Source {
signal = controller.signal; signal = controller.signal;
} }
const resp = await fetch(this.url, { let resp = await fetch(this.url, {
signal: signal, signal: signal,
headers: { Range: "bytes=" + offset + "-" + (offset + length - 1) }, headers: { Range: "bytes=" + offset + "-" + (offset + length - 1) },
}); });
// can return 416, which will have a blank etag on S3. // TODO: can return 416 with offset > 0 if content changed, which will have a blank etag.
// See https://github.com/protomaps/PMTiles/issues/90 // See https://github.com/protomaps/PMTiles/issues/90
if (resp.status >= 300) { if (resp.status === 416 && offset === 0) {
throw Error("404"); // some HTTP servers don't accept ranges beyond the end of the resource.
controller.abort(); // Retry with the exact length
const content_range = resp.headers.get("Content-Range");
if (!content_range || !content_range.startsWith("bytes */")) {
throw Error("Missing content-length on 416 response");
}
const actual_length = +content_range.substr(8);
resp = await fetch(this.url, {
signal: signal,
headers: { Range: "bytes=0-" + (actual_length - 1) },
});
} }
const contentLength = resp.headers.get("Content-Length");
if (!contentLength || +contentLength !== length) { if (resp.status >= 300) {
console.error( throw Error("Bad response code: " + resp.status);
"Content-Length mismatch indicates byte serving not supported; aborting." }
);
const content_length = resp.headers.get("Content-Length");
// some well-behaved backends, e.g. DigitalOcean CDN, respond with 200 instead of 206
// but we also need to detect no support for Byte Serving which is returning the whole file
if (resp.status === 200 && (!content_length || +content_length > length)) {
if (controller) controller.abort(); if (controller) controller.abort();
throw Error(
"Server returned no content-length header or content-length exceeding request. Check that your storage backend supports HTTP Byte Serving."
);
} }
const a = await resp.arrayBuffer(); const a = await resp.arrayBuffer();