Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miniflare3: Add Content-Length and FixedLengthStream to R2 get response #712

Merged
merged 2 commits into from Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/miniflare/src/workers/r2/bucket.worker.ts
Expand Up @@ -184,6 +184,7 @@ function encodeResult(
headers: {
[R2Headers.METADATA_SIZE]: `${encoded.metadataSize}`,
"Content-Type": "application/json",
"Content-Length": `${encoded.size}`,
},
});
}
Expand Down
9 changes: 6 additions & 3 deletions packages/miniflare/src/workers/r2/r2Object.worker.ts
Expand Up @@ -9,6 +9,7 @@ import {
export interface EncodedMetadata {
metadataSize: number;
value: ReadableStream<Uint8Array>;
size: number;
}

export class InternalR2Object {
Expand Down Expand Up @@ -68,7 +69,7 @@ export class InternalR2Object {
encode(): EncodedMetadata {
const json = JSON.stringify(this.#rawProperties());
const blob = new Blob([json]);
return { metadataSize: blob.size, value: blob.stream() };
return { metadataSize: blob.size, value: blob.stream(), size: blob.size };
}

static encodeMultiple(objects: InternalR2Objects): EncodedMetadata {
Expand All @@ -77,7 +78,7 @@ export class InternalR2Object {
objects: objects.objects.map((o) => o.#rawProperties()),
});
const blob = new Blob([json]);
return { metadataSize: blob.size, value: blob.stream() };
return { metadataSize: blob.size, value: blob.stream(), size: blob.size };
}
}

Expand All @@ -92,13 +93,15 @@ export class InternalR2ObjectBody extends InternalR2Object {

encode(): EncodedMetadata {
const { metadataSize, value: metadata } = super.encode();
const identity = new IdentityTransformStream();
const size = this.range?.length ?? this.size;
const identity = new FixedLengthStream(size + metadataSize);
void metadata
.pipeTo(identity.writable, { preventClose: true })
.then(() => this.body.pipeTo(identity.writable));
return {
metadataSize: metadataSize,
value: identity.readable,
size,
};
}
}
Expand Down
44 changes: 44 additions & 0 deletions packages/miniflare/test/plugins/r2/index.spec.ts
Expand Up @@ -549,6 +549,50 @@ test("put: validates metadata size", async (t) => {
expectations
);
});
test("put: can copy values", async (t) => {
const mf = new Miniflare({
r2Buckets: ["BUCKET"],
modules: true,
script: `export default {
async fetch(request, env, ctx) {
await env.BUCKET.put("key", "0123456789");

let object = await env.BUCKET.get("key");
await env.BUCKET.put("key-copy", object.body);
const copy = await (await env.BUCKET.get("key-copy"))?.text();

object = await env.BUCKET.get("key", { range: { offset: 1, length: 4 } });
await env.BUCKET.put("key-copy-range-1", object.body);
const copyRange1 = await (await env.BUCKET.get("key-copy-range-1"))?.text();

object = await env.BUCKET.get("key", { range: { length: 3 } });
await env.BUCKET.put("key-copy-range-2", object.body);
const copyRange2 = await (await env.BUCKET.get("key-copy-range-2"))?.text();

object = await env.BUCKET.get("key", { range: { suffix: 5 } });
await env.BUCKET.put("key-copy-range-3", object.body);
const copyRange3 = await (await env.BUCKET.get("key-copy-range-3"))?.text();

const range = new Headers();
range.set("Range", "bytes=0-5");
object = await env.BUCKET.get("key", { range });
await env.BUCKET.put("key-copy-range-4", object.body);
const copyRange4 = await (await env.BUCKET.get("key-copy-range-4"))?.text();

return Response.json({ copy, copyRange1, copyRange2, copyRange3, copyRange4 });
}
}`,
});
t.teardown(() => mf.dispose());
const res = await mf.dispatchFetch("http://localhost");
t.deepEqual(await res.json(), {
copy: "0123456789",
copyRange1: "1234",
copyRange2: "012",
copyRange3: "56789",
copyRange4: "012345",
});
});

test("delete: deletes existing keys", async (t) => {
const { r2, ns, object } = t.context;
Expand Down