in hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java [416:584]
public Response get(
@PathParam("bucket") String bucketName,
@PathParam("path") String keyPath,
@QueryParam("partNumber") int partNumber,
@QueryParam("uploadId") String uploadId,
@QueryParam("max-parts") @DefaultValue("1000") int maxParts,
@QueryParam("part-number-marker") String partNumberMarker,
@QueryParam("tagging") String taggingMarker)
throws IOException, OS3Exception {
long startNanos = Time.monotonicNowNanos();
S3GAction s3GAction = S3GAction.GET_KEY;
PerformanceStringBuilder perf = new PerformanceStringBuilder();
try {
if (taggingMarker != null) {
s3GAction = S3GAction.GET_OBJECT_TAGGING;
return getObjectTagging(bucketName, keyPath);
}
if (uploadId != null) {
// When we have uploadId, this is the request for list Parts.
s3GAction = S3GAction.LIST_PARTS;
int partMarker = parsePartNumberMarker(partNumberMarker);
Response response = listParts(bucketName, keyPath, uploadId,
partMarker, maxParts, perf);
AUDIT.logReadSuccess(buildAuditMessageForSuccess(s3GAction,
getAuditParameters(), perf));
return response;
}
OzoneKeyDetails keyDetails = (partNumber != 0) ?
getClientProtocol().getS3KeyDetails(bucketName, keyPath, partNumber) :
getClientProtocol().getS3KeyDetails(bucketName, keyPath);
isFile(keyPath, keyDetails);
long length = keyDetails.getDataSize();
LOG.debug("Data length of the key {} is {}", keyPath, length);
String rangeHeaderVal = headers.getHeaderString(RANGE_HEADER);
RangeHeader rangeHeader = null;
LOG.debug("range Header provided value: {}", rangeHeaderVal);
if (rangeHeaderVal != null) {
rangeHeader = RangeHeaderParserUtil.parseRangeHeader(rangeHeaderVal,
length);
LOG.debug("range Header provided: {}", rangeHeader);
if (rangeHeader.isInValidRange()) {
throw newError(S3ErrorTable.INVALID_RANGE, rangeHeaderVal);
}
}
ResponseBuilder responseBuilder;
if (rangeHeaderVal == null || rangeHeader.isReadFull()) {
StreamingOutput output = dest -> {
try (OzoneInputStream key = keyDetails.getContent()) {
long readLength = IOUtils.copy(key, dest, getIOBufferSize(keyDetails.getDataSize()));
getMetrics().incGetKeySuccessLength(readLength);
perf.appendSizeBytes(readLength);
}
long opLatencyNs = getMetrics().updateGetKeySuccessStats(startNanos);
perf.appendOpLatencyNanos(opLatencyNs);
AUDIT.logReadSuccess(buildAuditMessageForSuccess(S3GAction.GET_KEY,
getAuditParameters(), perf));
};
responseBuilder = Response
.ok(output)
.header(CONTENT_LENGTH, keyDetails.getDataSize());
} else {
long startOffset = rangeHeader.getStartOffset();
long endOffset = rangeHeader.getEndOffset();
// eg. if range header is given as bytes=0-0, then we should return 1
// byte from start offset
long copyLength = endOffset - startOffset + 1;
StreamingOutput output = dest -> {
try (OzoneInputStream ozoneInputStream = keyDetails.getContent()) {
ozoneInputStream.seek(startOffset);
long readLength = IOUtils.copyLarge(ozoneInputStream, dest, 0,
copyLength, new byte[getIOBufferSize(copyLength)]);
getMetrics().incGetKeySuccessLength(readLength);
perf.appendSizeBytes(readLength);
}
long opLatencyNs = getMetrics().updateGetKeySuccessStats(startNanos);
perf.appendOpLatencyNanos(opLatencyNs);
AUDIT.logReadSuccess(buildAuditMessageForSuccess(S3GAction.GET_KEY,
getAuditParameters(), perf));
};
responseBuilder = Response
.status(Status.PARTIAL_CONTENT)
.entity(output)
.header(CONTENT_LENGTH, copyLength);
String contentRangeVal = RANGE_HEADER_SUPPORTED_UNIT + " " +
rangeHeader.getStartOffset() + "-" + rangeHeader.getEndOffset() +
"/" + length;
responseBuilder.header(CONTENT_RANGE_HEADER, contentRangeVal);
}
responseBuilder
.header(ACCEPT_RANGE_HEADER, RANGE_HEADER_SUPPORTED_UNIT);
String eTag = keyDetails.getMetadata().get(ETAG);
if (eTag != null) {
responseBuilder.header(ETAG, wrapInQuotes(eTag));
String partsCount = extractPartsCount(eTag);
if (partsCount != null) {
responseBuilder.header(MP_PARTS_COUNT, partsCount);
}
}
// if multiple query parameters having same name,
// Only the first parameters will be recognized
// eg:
// http://localhost:9878/bucket/key?response-expires=1&response-expires=2
// only response-expires=1 is valid
MultivaluedMap<String, String> queryParams = context
.getUriInfo().getQueryParameters();
for (Map.Entry<String, String> entry :
overrideQueryParameter.entrySet()) {
String headerValue = headers.getHeaderString(entry.getKey());
/* "Overriding Response Header" by query parameter, See:
https://docs.aws.amazon.com/de_de/AmazonS3/latest/API/API_GetObject.html
*/
String queryValue = queryParams.getFirst(entry.getValue());
if (queryValue != null) {
headerValue = queryValue;
}
if (headerValue != null) {
responseBuilder.header(entry.getKey(), headerValue);
}
}
addLastModifiedDate(responseBuilder, keyDetails);
addTagCountIfAny(responseBuilder, keyDetails);
long metadataLatencyNs =
getMetrics().updateGetKeyMetadataStats(startNanos);
perf.appendMetaLatencyNanos(metadataLatencyNs);
return responseBuilder.build();
} catch (OMException ex) {
AUDIT.logReadFailure(
buildAuditMessageForFailure(s3GAction, getAuditParameters(), ex)
);
if (taggingMarker != null) {
getMetrics().updateGetObjectTaggingFailureStats(startNanos);
} else if (uploadId != null) {
getMetrics().updateListPartsFailureStats(startNanos);
} else {
getMetrics().updateGetKeyFailureStats(startNanos);
}
if (ex.getResult() == ResultCodes.KEY_NOT_FOUND) {
throw newError(S3ErrorTable.NO_SUCH_KEY, keyPath, ex);
} else if (isAccessDenied(ex)) {
throw newError(S3ErrorTable.ACCESS_DENIED, keyPath, ex);
} else if (ex.getResult() == ResultCodes.BUCKET_NOT_FOUND) {
throw newError(S3ErrorTable.NO_SUCH_BUCKET, bucketName, ex);
} else {
throw ex;
}
} catch (Exception ex) {
AUDIT.logReadFailure(
buildAuditMessageForFailure(s3GAction, getAuditParameters(), ex)
);
throw ex;
}
}