Skip to content

Commit 997afee

Browse files
authored
fix(datasource/zarr) HEAD to get content length (#611)
Use a HEAD request as fallback for http 206 status with no content-length.
1 parent 23218f9 commit 997afee

File tree

1 file changed

+38
-26
lines changed

1 file changed

+38
-26
lines changed

src/kvstore/special/index.ts

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@ class SpecialProtocolKvStore implements ReadableKvStore {
5151
public credentialsProvider: SpecialProtocolCredentialsProvider,
5252
public baseUrl: string,
5353
) {}
54+
55+
async getObjectLength(url: string, options: ReadOptions) {
56+
// Use a HEAD request to get the length of an object
57+
const { cancellationToken = uncancelableToken } = options;
58+
const headResponse = await cancellableFetchSpecialOk(
59+
this.credentialsProvider,
60+
url,
61+
{ method: "HEAD" },
62+
async (response) => response,
63+
cancellationToken,
64+
);
65+
66+
if (headResponse.status !== 200) {
67+
throw new Error(
68+
"Failed to determine total size in order to fetch suffix",
69+
);
70+
}
71+
const contentLength = headResponse.headers.get("content-length");
72+
if (contentLength === undefined) {
73+
throw new Error(
74+
"Failed to determine total size in order to fetch suffix",
75+
);
76+
}
77+
const contentLengthNumber = Number(contentLength);
78+
return contentLengthNumber;
79+
}
80+
5481
async read(
5582
key: string,
5683
options: ReadOptions,
@@ -83,14 +110,17 @@ class SpecialProtocolKvStore implements ReadableKvStore {
83110
if (contentRange === null) {
84111
if (byteRangeRequest !== undefined) {
85112
if ("suffixLength" in byteRangeRequest) {
86-
throw new Error(
87-
"Content-range header not provided with HTTP 206 response. Check server CORS configuration.",
88-
);
113+
const objectSize = await this.getObjectLength(url, options);
114+
byteRange = {
115+
offset: objectSize - byteRangeRequest.suffixLength,
116+
length: Number(response.headers.get("content-length")),
117+
};
118+
} else {
119+
byteRange = {
120+
offset: byteRangeRequest.offset,
121+
length: data.byteLength,
122+
};
89123
}
90-
byteRange = {
91-
offset: byteRangeRequest.offset,
92-
length: data.byteLength,
93-
};
94124
} else {
95125
throw new Error(
96126
"Unexpected HTTP 206 response when no byte range specified.",
@@ -134,25 +164,7 @@ class SpecialProtocolKvStore implements ReadableKvStore {
134164
) {
135165
// Some servers, such as the npm http-server package, do not support suffixLength
136166
// byte-range requests.
137-
const headResponse = await cancellableFetchSpecialOk(
138-
this.credentialsProvider,
139-
url,
140-
{ method: "HEAD" },
141-
async (response) => response,
142-
cancellationToken,
143-
);
144-
if (headResponse.status !== 200) {
145-
throw new Error(
146-
"Failed to determine total size in order to fetch suffix",
147-
);
148-
}
149-
const contentLength = headResponse.headers.get("content-length");
150-
if (contentLength === undefined) {
151-
throw new Error(
152-
"Failed to determine total size in order to fetch suffix",
153-
);
154-
}
155-
const contentLengthNumber = Number(contentLength);
167+
const contentLengthNumber = await this.getObjectLength(url, options);
156168
byteRangeRequest = composeByteRangeRequest(
157169
{ offset: 0, length: contentLengthNumber },
158170
byteRangeRequest,

0 commit comments

Comments
 (0)