Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .sdkharness/tests/conformance/LargeResume.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
* <p>Two legs, because the SDK treats the two content shapes differently. Leg "no sha1": the content source does
* not know its sha1, so the large file carries no large_file_sha1 and the rerun is the same request as the first
* run. Leg "sha1": the content source knows its sha1 (the SDK records it as the large_file_sha1 file info); the
* rerun is again the same request as the first run. If the SDK refuses it with a "mismatch" error, the scenario
* FAILS with a FINDING as the reason: the check detects, it does not work around the refusal. The finding is about
* the SDK, not about B2: B2LargeFileUploader removes the key
* {@code X-Bz-Info-large_file_sha1} (B2Headers.LARGE_FILE_SHA1) from the file info before comparing it with the
* request's, but the info map is keyed {@code large_file_sha1} (B2Headers.LARGE_FILE_SHA1_INFO_NAME).
* rerun is again the same request as the first run, and the SDK must accept it. An earlier SDK version refused it
* with a "mismatch" error, because B2LargeFileUploader removed the key {@code X-Bz-Info-large_file_sha1}
* (B2Headers.LARGE_FILE_SHA1) from the stored file info before comparing it with the request's, while the info map
* is keyed {@code large_file_sha1} (B2Headers.LARGE_FILE_SHA1_INFO_NAME). This leg fails with a FINDING as the
* reason if that refusal comes back; the check detects, it does not work around the refusal.
*/
public class LargeResume {
private static final int PARTS = 4;
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
A missing or empty signature header throws `B2SignatureVerificationException`; a null or empty signing secret throws `IllegalArgumentException`.
* Signature comparison is now constant-time.

### Fixed
* `finishUploadingLargeFile` no longer throws a local `mismatch` error when resuming a large file whose content source knows its sha1.

### Added
* Added `B2EventNotification.parseUnverified`, which parses without checking a signature. The one-argument `parse` overloads are deprecated in its favor.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ B2FileVersion finishUploadingLargeFile(B2FileVersion largeFileVersion,
final Map<String,String> infos = new TreeMap<>();
//noinspection CollectionAddAllCanBeReplacedWithConstructor
infos.putAll(largeFileVersion.getFileInfo());
infos.remove(B2Headers.LARGE_FILE_SHA1);
// the stored file info is keyed by the bare name, "large_file_sha1", not by the "X-Bz-Info-" header name.
infos.remove(B2Headers.LARGE_FILE_SHA1_INFO_NAME);
throwIfMismatch("fileInfo", toString(request.getFileInfo()), toString(infos));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,59 @@ public void testThrowIfLargeFileVersionDoesntSeemToMatchRequest_mismatchingSha1(
B2LargeFileUploader.throwIfLargeFileVersionDoesntSeemToMatchRequest(largeFileVersion, contentSource.getContentLength(), request);
}

@Test
public void testThrowIfLargeFileVersionDoesntSeemToMatchRequest_matchingSha1IsNotAFileInfoMismatch() throws IOException, B2Exception {
// the SDK stores the sha1 of a large file as the file info "large_file_sha1" when it starts the file.
// that entry is checked as the sha1, so it must not also count as an unexpected file info entry.
when(contentSource.getSha1OrNull()).thenReturn(makeSha1(1));
final B2UploadFileRequest request = B2UploadFileRequest
.builder(bucketId(1), fileName(2), B2ContentTypes.APPLICATION_OCTET, contentSource)
.build();
final B2FileVersion largeFileVersion = new B2FileVersion(
fileId(1),
fileName(2),
contentSource.getContentLength(),
B2ContentTypes.APPLICATION_OCTET,
null,
null,
B2Collections.mapOf(B2Headers.LARGE_FILE_SHA1_INFO_NAME, makeSha1(1)),
"action",
1234,
null,
null,
null,
null);

// does not throw.
B2LargeFileUploader.throwIfLargeFileVersionDoesntSeemToMatchRequest(largeFileVersion, contentSource.getContentLength(), request);
}

@Test
public void testThrowIfLargeFileVersionDoesntSeemToMatchRequest_otherFileInfoStillMismatches() throws IOException, B2Exception {
when(contentSource.getSha1OrNull()).thenReturn(makeSha1(1));
final B2UploadFileRequest request = B2UploadFileRequest
.builder(bucketId(1), fileName(2), B2ContentTypes.APPLICATION_OCTET, contentSource)
.build();
final B2FileVersion largeFileVersion = new B2FileVersion(
fileId(1),
fileName(2),
contentSource.getContentLength(),
B2ContentTypes.APPLICATION_OCTET,
null,
null,
B2Collections.mapOf(B2Headers.LARGE_FILE_SHA1_INFO_NAME, makeSha1(1), "color", "blue"),
"action",
1234,
null,
null,
null,
null);

thrown.expect(B2LocalException.class);
thrown.expectMessage("contentSource has fileInfo");
B2LargeFileUploader.throwIfLargeFileVersionDoesntSeemToMatchRequest(largeFileVersion, contentSource.getContentLength(), request);
}

@Test
public void testThrowIfLargeFileVersionDoesntSeemToMatchRequest_mismatchingContentType() throws IOException, B2Exception {
final B2UploadFileRequest request = B2UploadFileRequest
Expand Down
Loading