Skip to content

Commit

Permalink
feat: add useMultipart flag
Browse files Browse the repository at this point in the history
  • Loading branch information
friendlymatthew committed Mar 4, 2024
1 parent 9bf2ee5 commit 3dfe48f
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 28 deletions.
1 change: 1 addition & 0 deletions examples/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ <h2>Results</h2>
Appendable.init(
"green_tripdata_2023-01.jsonl",
"green_tripdata_2023-01.index",
{ useMultipartByteRanges: false },
).then(async (db) => {
// populate fields
db.fields().then((fields) => {
Expand Down
7 changes: 4 additions & 3 deletions src/data-file.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Config } from ".";
import { requestRanges } from "./range-request";
import { LengthIntegrityError, RangeResolver } from "./resolver";
import { RangeResolver } from "./resolver";

export class DataFile {
private originalResolver?: RangeResolver;

private constructor(private resolver: RangeResolver) {}

static forUrl(url: string) {
static forUrl(url: string, config: Config) {
return DataFile.forResolver(
async (ranges) => await requestRanges(url, ranges),
async (ranges) => await requestRanges(url, ranges, config),
);
}

Expand Down
5 changes: 3 additions & 2 deletions src/index-file/index-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import {
} from "./meta";
import { FieldType } from "../db/database";
import { requestRanges } from "../range-request";
import { Config } from "..";

export class IndexFile {
static async forUrl<T = any>(url: string) {
static async forUrl<T = any>(url: string, config: Config) {
return await IndexFile.forResolver<T>(
async (ranges) => await requestRanges(url, ranges),
async (ranges) => await requestRanges(url, ranges, config),
);
}

Expand Down
9 changes: 7 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ import { Database, FieldType, fieldTypeToString } from "./db/database";
import { IndexFile } from "./index-file/index-file";
import { RangeResolver } from "./resolver";

export type Config = {
useMultipartByteRanges?: boolean;
};

export async function init(
dataUrl: string | RangeResolver,
indexUrl: string | RangeResolver,
config: Config,
) {
return Database.forDataFileAndIndexFile(
typeof dataUrl === "string"
? DataFile.forUrl(dataUrl)
? DataFile.forUrl(dataUrl, config)
: DataFile.forResolver(dataUrl),
typeof indexUrl === "string"
? await IndexFile.forUrl(indexUrl)
? await IndexFile.forUrl(indexUrl, config)
: await IndexFile.forResolver(indexUrl),
);
}
Expand Down
62 changes: 41 additions & 21 deletions src/range-request.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
import { Config } from ".";
import parseMultipartBody from "./multipart";
import { LengthIntegrityError } from "./resolver";

async function resolveIndividualPromises(
url: string,
ranges: { start: number; end: number; expectedLength?: number }[],
) {
console.log("resolving ranges individually");
// fallback to resolving ranges individually
const individualRangePromises = ranges.map(
async ({ start, end, expectedLength }) => {
const rangeHeader = `${start}-${end}`;
const res = await fetch(url, {
headers: { Range: `bytes=${rangeHeader}` },
});

const totalLength = Number(
res.headers.get("Content-Range")!.split("/")[1],
);
if (expectedLength && totalLength !== expectedLength) {
throw new LengthIntegrityError();
}
return {
data: await res.arrayBuffer(),
totalLength: totalLength,
};
},
);
return await Promise.all(individualRangePromises);
}

export async function requestRanges(
url: string,
ranges: { start: number; end: number; expectedLength?: number }[],
config: Config,
): Promise<{ data: ArrayBuffer; totalLength: number }[]> {
const { useMultipartByteRanges } = config;
if (
useMultipartByteRanges === false ||
useMultipartByteRanges === undefined
) {
return await resolveIndividualPromises(url, ranges);
}

const rangesHeader = ranges
.map(({ start, end }) => `${start}-${end}`)
.join(",");
Expand All @@ -18,28 +56,10 @@ export async function requestRanges(

switch (response.status) {
case 200:
// fallback to resolving ranges individually
const individualRangePromises = ranges.map(
async ({ start, end, expectedLength }) => {
const rangeHeader = `${start}-${end}`;
const res = await fetch(url, {
headers: { Range: `bytes=${rangeHeader}` },
});

const totalLength = Number(
res.headers.get("Content-Range")!.split("/")[1],
);
if (expectedLength && totalLength !== expectedLength) {
throw new LengthIntegrityError();
}
return {
data: await res.arrayBuffer(),
totalLength: totalLength,
};
},
console.warn(
`useMultipartByteRanges has not been set to false. The server can not handle multipart byte ranges.`,
);
return await Promise.all(individualRangePromises);

return await resolveIndividualPromises(url, ranges);
case 206:
const contentType = response.headers.get("Content-Type");
if (!contentType) {
Expand Down

0 comments on commit 3dfe48f

Please sign in to comment.