-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Make GCS HttpHandler more compliant #126007
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
Changes from all commits
9cc696a
309ff68
f94da75
d3dd8ac
2d4be15
b1f192b
76dca02
0cf2ef0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -129,36 +129,33 @@ public void handle(final HttpExchange exchange) throws IOException { | |
| final MockGcsBlobStore.BlobVersion blob = mockGcsBlobStore.getBlob(path, ifGenerationMatch); | ||
| if (blob != null) { | ||
| final String rangeHeader = exchange.getRequestHeaders().getFirst("Range"); | ||
| final long offset; | ||
| final long end; | ||
| final BytesReference response; | ||
| final int statusCode; | ||
| if (rangeHeader == null) { | ||
| offset = 0L; | ||
| end = blob.contents().length() - 1; | ||
| response = blob.contents(); | ||
| statusCode = RestStatus.OK.getStatus(); | ||
| } else { | ||
| final HttpHeaderParser.Range range = HttpHeaderParser.parseRangeHeader(rangeHeader); | ||
| if (range == null) { | ||
| throw new AssertionError("Range bytes header does not match expected format: " + rangeHeader); | ||
| } | ||
| offset = range.start(); | ||
| end = range.end(); | ||
| } | ||
|
|
||
| if (offset >= blob.contents().length()) { | ||
| exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); | ||
| exchange.sendResponseHeaders(RestStatus.REQUESTED_RANGE_NOT_SATISFIED.getStatus(), -1); | ||
| return; | ||
| } | ||
| if (range.start() >= blob.contents().length()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder, should we be checking the range end here? If you ask for bytes 0-10 of an 8-byte blob, we cannot satisfy that range.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, probably. I'll have a look at what the spec says, currently we just send as much of it as we have.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also adding ETags, because it looks like that's the mechanism that the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it looks like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I confirmed this testing against the actual API
|
||
| exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); | ||
| exchange.sendResponseHeaders(RestStatus.REQUESTED_RANGE_NOT_SATISFIED.getStatus(), -1); | ||
| return; | ||
| } | ||
|
|
||
| BytesReference response = blob.contents(); | ||
| exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); | ||
| final int bufferedLength = response.length(); | ||
| if (offset > 0 || bufferedLength > end) { | ||
| response = response.slice( | ||
| Math.toIntExact(offset), | ||
| Math.toIntExact(Math.min(end + 1 - offset, bufferedLength - offset)) | ||
| ); | ||
| final long lastIndex = Math.min(range.end(), blob.contents().length() - 1); | ||
| response = blob.contents().slice(Math.toIntExact(range.start()), Math.toIntExact(lastIndex - range.start() + 1)); | ||
| statusCode = RestStatus.PARTIAL_CONTENT.getStatus(); | ||
| } | ||
| exchange.sendResponseHeaders(RestStatus.OK.getStatus(), response.length()); | ||
| // I think it's enough to use the generation here, at least until | ||
| // we implement "metageneration", at that point we must incorporate both | ||
| // See: https://cloud.google.com/storage/docs/metadata#etags | ||
| exchange.getResponseHeaders().add("ETag", String.valueOf(blob.generation())); | ||
| exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); | ||
| exchange.sendResponseHeaders(statusCode, response.length()); | ||
| response.writeTo(exchange.getResponseBody()); | ||
| } else { | ||
| exchange.sendResponseHeaders(RestStatus.NOT_FOUND.getStatus(), -1); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a bug when we wrote a multiple of the chunk size in bytes, it sends
POST metadata
PUT chunk1
PUT chunk2
...
PUT no data with content-range header to indicate we're done
We had assumed the finished header was sent with the final chunk.