Skip to content

Commit 9cc696a

Browse files
committed
Fix range handling for zero-length blobs
1 parent 61d92d6 commit 9cc696a

File tree

2 files changed

+39
-22
lines changed

2 files changed

+39
-22
lines changed

test/fixtures/gcs-fixture/src/main/java/fixture/gcs/GoogleCloudStorageHttpHandler.java

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,35 +129,25 @@ public void handle(final HttpExchange exchange) throws IOException {
129129
final MockGcsBlobStore.BlobVersion blob = mockGcsBlobStore.getBlob(path, ifGenerationMatch);
130130
if (blob != null) {
131131
final String rangeHeader = exchange.getRequestHeaders().getFirst("Range");
132-
final long offset;
133-
final long end;
132+
final BytesReference response;
134133
if (rangeHeader == null) {
135-
offset = 0L;
136-
end = blob.contents().length() - 1;
134+
response = blob.contents();
137135
} else {
138136
final HttpHeaderParser.Range range = HttpHeaderParser.parseRangeHeader(rangeHeader);
139137
if (range == null) {
140138
throw new AssertionError("Range bytes header does not match expected format: " + rangeHeader);
141139
}
142-
offset = range.start();
143-
end = range.end();
144-
}
145140

146-
if (offset >= blob.contents().length()) {
147-
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
148-
exchange.sendResponseHeaders(RestStatus.REQUESTED_RANGE_NOT_SATISFIED.getStatus(), -1);
149-
return;
150-
}
141+
if (range.start() >= blob.contents().length()) {
142+
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
143+
exchange.sendResponseHeaders(RestStatus.REQUESTED_RANGE_NOT_SATISFIED.getStatus(), -1);
144+
return;
145+
}
151146

152-
BytesReference response = blob.contents();
153-
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
154-
final int bufferedLength = response.length();
155-
if (offset > 0 || bufferedLength > end) {
156-
response = response.slice(
157-
Math.toIntExact(offset),
158-
Math.toIntExact(Math.min(end + 1 - offset, bufferedLength - offset))
159-
);
147+
final long lastIndex = Math.min(range.end(), blob.contents().length() - 1);
148+
response = blob.contents().slice(Math.toIntExact(range.start()), Math.toIntExact(lastIndex - range.start() + 1));
160149
}
150+
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
161151
exchange.sendResponseHeaders(RestStatus.OK.getStatus(), response.length());
162152
response.writeTo(exchange.getResponseBody());
163153
} else {

test/fixtures/gcs-fixture/src/test/java/fixture/gcs/GoogleCloudStorageHttpHandlerTests.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,33 @@ public void testGetWithBytesRange() {
186186
);
187187
}
188188

189+
public void testZeroLengthObjectGets() {
190+
final var bucket = randomIdentifier();
191+
final var handler = new GoogleCloudStorageHttpHandler(bucket);
192+
final var blobName = "blob_name_" + randomIdentifier();
193+
final var blobBytes = BytesArray.EMPTY;
194+
195+
assertEquals(RestStatus.OK, executeMultipartUpload(handler, bucket, blobName, blobBytes, 0L).restStatus());
196+
197+
assertEquals(
198+
"No Range",
199+
new TestHttpResponse(RestStatus.OK, blobBytes, TestHttpExchange.EMPTY_HEADERS),
200+
getBlobContents(handler, bucket, blobName, null, null)
201+
);
202+
203+
assertEquals(
204+
"Range 0-0",
205+
new TestHttpResponse(RestStatus.REQUESTED_RANGE_NOT_SATISFIED, BytesArray.EMPTY, TestHttpExchange.EMPTY_HEADERS),
206+
getBlobContents(handler, bucket, blobName, null, new HttpHeaderParser.Range(0, 0))
207+
);
208+
209+
assertEquals(
210+
"Random range x-y",
211+
new TestHttpResponse(RestStatus.REQUESTED_RANGE_NOT_SATISFIED, BytesArray.EMPTY, TestHttpExchange.EMPTY_HEADERS),
212+
getBlobContents(handler, bucket, blobName, null, new HttpHeaderParser.Range(randomIntBetween(0, 30), randomIntBetween(31, 100)))
213+
);
214+
}
215+
189216
public void testResumableUpload() {
190217
final var bucket = randomIdentifier();
191218
final var handler = new GoogleCloudStorageHttpHandler(bucket);
@@ -545,7 +572,6 @@ private static TestHttpResponse executeUpload(
545572
BytesReference bytes,
546573
Long ifGenerationMatch
547574
) {
548-
assert bytes.length() > 20;
549575
if (randomBoolean()) {
550576
return executeResumableUpload(handler, bucket, blobName, bytes, ifGenerationMatch);
551577
} else {
@@ -560,6 +586,7 @@ private static TestHttpResponse executeResumableUpload(
560586
BytesReference bytes,
561587
Long ifGenerationMatch
562588
) {
589+
assert bytes.length() >= 2 : "We can't split anything smaller than two";
563590
final var createUploadResponse = handleRequest(
564591
handler,
565592
"POST",
@@ -572,7 +599,7 @@ private static TestHttpResponse executeResumableUpload(
572599
final var sessionURI = locationHeader.substring(locationHeader.indexOf(HOST) + HOST.length());
573600
assertEquals(RestStatus.OK, createUploadResponse.restStatus());
574601

575-
final int partBoundary = randomIntBetween(10, bytes.length() - 1);
602+
final int partBoundary = randomIntBetween(1, bytes.length() - 1);
576603
final var part1 = bytes.slice(0, partBoundary);
577604
final var uploadPart1Response = handleRequest(handler, "PUT", sessionURI, part1, contentRangeHeader(0, partBoundary - 1, null));
578605
assertEquals(RESUME_INCOMPLETE, uploadPart1Response.status());

0 commit comments

Comments
 (0)