Skip to content

Commit

Permalink
Fix iOS auth for resumable uploads in Storage Emulator (#4184)
Browse files Browse the repository at this point in the history
* Fix iOS auth for resumable uploads in Storage Emulator

* Store auth header in ResumableUpload instead of app.locals

* Rename upload resumable integration test

* Add additional expect for upload request 200
  • Loading branch information
tohhsinpei committed Feb 18, 2022
1 parent a799118 commit aa178b2
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
32 changes: 32 additions & 0 deletions scripts/storage-emulator-integration/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,38 @@ describe("Storage emulator", () => {
expect(md.downloadTokens.split(",")).to.not.deep.equal([token]);
});
});

it("#uploadResumableDoesNotRequireMultipleAuthHeaders", async () => {
const uploadURL = await supertest(STORAGE_EMULATOR_HOST)
.post(
`/v0/b/${storageBucket}/o/test_upload.jpg?uploadType=resumable&name=test_upload.jpg`
)
.set({
Authorization: "Bearer owner",
"X-Goog-Upload-Protocol": "resumable",
"X-Goog-Upload-Command": "start",
})
.expect(200)
.then((res) => new URL(res.header["x-goog-upload-url"]));

await supertest(STORAGE_EMULATOR_HOST)
.put(uploadURL.pathname + uploadURL.search)
.set({
// No Authorization required in upload
"X-Goog-Upload-Protocol": "resumable",
"X-Goog-Upload-Command": "upload",
})
.expect(200);

await supertest(STORAGE_EMULATOR_HOST)
.put(uploadURL.pathname + uploadURL.search)
.set({
// No Authorization required in finalize
"X-Goog-Upload-Protocol": "resumable",
"X-Goog-Upload-Command": "upload, finalize",
})
.expect(200);
});
});

after(async function (this) {
Expand Down
6 changes: 4 additions & 2 deletions src/emulator/storage/apis/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,9 @@ export function createFirebaseEndpoints(emulator: StorageEmulator): Router {
req.params.bucketId,
name,
objectContentType,
req.body
req.body,
// Store auth header for use in the finalize request
req.header("authorization")
);

storageLayer.uploadBytes(upload.uploadId, Buffer.alloc(0));
Expand Down Expand Up @@ -544,7 +546,7 @@ export function createFirebaseEndpoints(emulator: StorageEmulator): Router {
// TODO This will be either create or update
method: RulesetOperationMethod.CREATE,
path: operationPath,
authorization: req.header("authorization"),
authorization: upload.authorization,
file: {
after: storageLayer.getMetadata(req.params.bucketId, name)?.asRulesResource(),
},
Expand Down
20 changes: 17 additions & 3 deletions src/emulator/storage/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class ResumableUpload {
private _bucketId: string;
private _objectId: string;
private _contentType: string;
private _authorization: string | undefined;
private _currentBytesUploaded = 0;
private _status: UploadStatus = UploadStatus.ACTIVE;
private _fileLocation: string;
Expand All @@ -62,13 +63,15 @@ export class ResumableUpload {
objectId: string,
uploadId: string,
contentType: string,
metadata: IncomingMetadata
metadata: IncomingMetadata,
authorization?: string
) {
this._bucketId = bucketId;
this._objectId = objectId;
this._uploadId = uploadId;
this._contentType = contentType;
this._metadata = metadata;
this._authorization = authorization;
this._fileLocation = encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
this._currentBytesUploaded = 0;
}
Expand All @@ -91,6 +94,9 @@ export class ResumableUpload {
public set contentType(contentType: string) {
this._contentType = contentType;
}
public get authorization(): string | undefined {
return this._authorization;
}
public get currentBytesUploaded(): number {
return this._currentBytesUploaded;
}
Expand Down Expand Up @@ -190,10 +196,18 @@ export class StorageLayer {
bucket: string,
object: string,
contentType: string,
metadata: IncomingMetadata
metadata: IncomingMetadata,
authorization?: string
): ResumableUpload {
const uploadId = v4();
const upload = new ResumableUpload(bucket, object, uploadId, contentType, metadata);
const upload = new ResumableUpload(
bucket,
object,
uploadId,
contentType,
metadata,
authorization
);
this._uploads.set(uploadId, upload);
return upload;
}
Expand Down

0 comments on commit aa178b2

Please sign in to comment.