kopia lustrzana https://github.com/gaul/s3proxy
Introduce S3Exception
This elides some boilerplate and ensures that we consistently handle errors.pull/47/head
rodzic
7e0c817660
commit
c2724b4399
|
@ -176,6 +176,18 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
public void handle(String target, Request baseRequest,
|
public void handle(String target, Request baseRequest,
|
||||||
HttpServletRequest request, HttpServletResponse response)
|
HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
try {
|
||||||
|
doHandle(target, baseRequest, request, response);
|
||||||
|
} catch (S3Exception se) {
|
||||||
|
sendSimpleErrorResponse(response, se.getError());
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doHandle(String target, Request baseRequest,
|
||||||
|
HttpServletRequest request, HttpServletResponse response)
|
||||||
|
throws IOException, S3Exception {
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
logger.debug("request: {}", request);
|
logger.debug("request: {}", request);
|
||||||
|
@ -220,24 +232,15 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
try {
|
try {
|
||||||
date = request.getDateHeader(HttpHeaders.DATE);
|
date = request.getDateHeader(HttpHeaders.DATE);
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.ACCESS_DENIED, iae);
|
||||||
S3ErrorCode.ACCESS_DENIED);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (date < 0) {
|
if (date < 0) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||||
S3ErrorCode.ACCESS_DENIED);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (now + TimeUnit.DAYS.toMillis(1) < date ||
|
if (now + TimeUnit.DAYS.toMillis(1) < date ||
|
||||||
now - TimeUnit.DAYS.toMillis(1) > date) {
|
now - TimeUnit.DAYS.toMillis(1) > date) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.REQUEST_TIME_TOO_SKEWED);
|
||||||
S3ErrorCode.REQUEST_TIME_TOO_SKEWED);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,47 +256,30 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
String[] values =
|
String[] values =
|
||||||
headerAuthorization.substring(4).split(":", 2);
|
headerAuthorization.substring(4).split(":", 2);
|
||||||
if (values.length != 2) {
|
if (values.length != 2) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
S3ErrorCode.INVALID_ARGUMENT);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
headerIdentity = values[0];
|
headerIdentity = values[0];
|
||||||
headerSignature = values[1];
|
headerSignature = values[1];
|
||||||
} else if (headerAuthorization != null &&
|
} else if (headerAuthorization != null &&
|
||||||
headerAuthorization.startsWith("AWS4-HMAC-SHA256 ")) {
|
headerAuthorization.startsWith("AWS4-HMAC-SHA256 ")) {
|
||||||
// Fail V4 signature requests to allow clients to retry with V2.
|
// Fail V4 signature requests to allow clients to retry with V2.
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
String parameterIdentity = request.getParameter("AWSAccessKeyId");
|
String parameterIdentity = request.getParameter("AWSAccessKeyId");
|
||||||
String parameterSignature = request.getParameter("Signature");
|
String parameterSignature = request.getParameter("Signature");
|
||||||
|
|
||||||
if (headerIdentity != null && headerSignature != null) {
|
if (headerIdentity != null && headerSignature != null) {
|
||||||
if (!identity.equals(headerIdentity)) {
|
if (!identity.equals(headerIdentity)) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
||||||
S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
} else if (!expectedSignature.equals(headerSignature)) {
|
} else if (!expectedSignature.equals(headerSignature)) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
||||||
S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if (parameterIdentity != null &&
|
} else if (parameterIdentity != null &&
|
||||||
parameterSignature != null) {
|
parameterSignature != null) {
|
||||||
if (!identity.equals(parameterIdentity)) {
|
if (!identity.equals(parameterIdentity)) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
||||||
S3ErrorCode.INVALID_ACCESS_KEY_ID);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
} else if (!expectedSignature.equals(parameterSignature)) {
|
} else if (!expectedSignature.equals(parameterSignature)) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
||||||
S3ErrorCode.SIGNATURE_DOES_NOT_MATCH);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String expiresString = request.getParameter("Expires");
|
String expiresString = request.getParameter("Expires");
|
||||||
|
@ -301,16 +287,11 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
long expires = Long.parseLong(expiresString);
|
long expires = Long.parseLong(expiresString);
|
||||||
long nowSeconds = System.currentTimeMillis() / 1000;
|
long nowSeconds = System.currentTimeMillis() / 1000;
|
||||||
if (nowSeconds > expires) {
|
if (nowSeconds > expires) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||||
S3ErrorCode.ACCESS_DENIED);
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.ACCESS_DENIED);
|
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,9 +301,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
if (!SUPPORTED_PARAMETERS.contains(parameter)) {
|
if (!SUPPORTED_PARAMETERS.contains(parameter)) {
|
||||||
logger.error("Unknown parameters {} with URI {}",
|
logger.error("Unknown parameters {} with URI {}",
|
||||||
parameter, request.getRequestURI());
|
parameter, request.getRequestURI());
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,9 +422,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
default:
|
default:
|
||||||
logger.error("Unknown method {} with URI {}",
|
logger.error("Unknown method {} with URI {}",
|
||||||
method, request.getRequestURI());
|
method, request.getRequestURI());
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
baseRequest.setHandled(true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +503,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleSetContainerAcl(HttpServletRequest request,
|
private void handleSetContainerAcl(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, String containerName)
|
||||||
throws IOException {
|
throws IOException, S3Exception {
|
||||||
ContainerAccess access;
|
ContainerAccess access;
|
||||||
|
|
||||||
String cannedAcl = request.getHeader("x-amz-acl");
|
String cannedAcl = request.getHeader("x-amz-acl");
|
||||||
|
@ -535,8 +512,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
} else if ("public-read".equals(cannedAcl)) {
|
} else if ("public-read".equals(cannedAcl)) {
|
||||||
access = ContainerAccess.PUBLIC_READ;
|
access = ContainerAccess.PUBLIC_READ;
|
||||||
} else if (cannedAcl == null || CANNED_ACLS.contains(cannedAcl)) {
|
} else if (cannedAcl == null || CANNED_ACLS.contains(cannedAcl)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
return;
|
return;
|
||||||
|
@ -625,7 +601,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleSetBlobAcl(HttpServletRequest request,
|
private void handleSetBlobAcl(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName) throws IOException {
|
String blobName) throws IOException, S3Exception {
|
||||||
BlobAccess access;
|
BlobAccess access;
|
||||||
|
|
||||||
String cannedAcl = request.getHeader("x-amz-acl");
|
String cannedAcl = request.getHeader("x-amz-acl");
|
||||||
|
@ -634,8 +610,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
} else if ("public-read".equals(cannedAcl)) {
|
} else if ("public-read".equals(cannedAcl)) {
|
||||||
access = BlobAccess.PUBLIC_READ;
|
access = BlobAccess.PUBLIC_READ;
|
||||||
} else if (cannedAcl == null || CANNED_ACLS.contains(cannedAcl)) {
|
} else if (cannedAcl == null || CANNED_ACLS.contains(cannedAcl)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
return;
|
return;
|
||||||
|
@ -705,30 +680,27 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleListMultipartUploads(HttpServletResponse response,
|
private void handleListMultipartUploads(HttpServletResponse response,
|
||||||
String uploadId) throws IOException {
|
String uploadId) throws IOException, S3Exception {
|
||||||
// TODO: list all blobs starting with uploadId
|
// TODO: list all blobs starting with uploadId
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NOT_IMPLEMENTED);
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerExists(HttpServletResponse response,
|
private void handleContainerExists(HttpServletResponse response,
|
||||||
String containerName) throws IOException {
|
String containerName) throws IOException, S3Exception {
|
||||||
if (!blobStore.containerExists(containerName)) {
|
if (!blobStore.containerExists(containerName)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerCreate(HttpServletRequest request,
|
private void handleContainerCreate(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, String containerName)
|
||||||
throws IOException {
|
throws IOException, S3Exception {
|
||||||
if (containerName.isEmpty()) {
|
if (containerName.isEmpty()) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.METHOD_NOT_ALLOWED);
|
throw new S3Exception(S3ErrorCode.METHOD_NOT_ALLOWED);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (containerName.length() < 3 || containerName.length() > 255 ||
|
if (containerName.length() < 3 || containerName.length() > 255 ||
|
||||||
!VALID_BUCKET_PATTERN.matcher(containerName).matches()) {
|
!VALID_BUCKET_PATTERN.matcher(containerName).matches()) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_BUCKET_NAME);
|
throw new S3Exception(S3ErrorCode.INVALID_BUCKET_NAME);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<String> locations;
|
Collection<String> locations;
|
||||||
|
@ -755,9 +727,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.INVALID_LOCATION_CONSTRAINT);
|
||||||
S3ErrorCode.INVALID_LOCATION_CONSTRAINT);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug("Creating bucket with location: {}", location);
|
logger.debug("Creating bucket with location: {}", location);
|
||||||
|
@ -775,12 +745,10 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
try {
|
try {
|
||||||
contentLength = Long.parseLong(contentLengthString);
|
contentLength = Long.parseLong(contentLengthString);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT, nfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (contentLength < 0) {
|
if (contentLength < 0) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,28 +762,24 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
errorCode, errorCode.getMessage(), "BucketName",
|
errorCode, errorCode.getMessage(), "BucketName",
|
||||||
containerName);
|
containerName);
|
||||||
} catch (AuthorizationException ae) {
|
} catch (AuthorizationException ae) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.BUCKET_ALREADY_EXISTS, ae);
|
||||||
S3ErrorCode.BUCKET_ALREADY_EXISTS);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerDelete(HttpServletResponse response,
|
private void handleContainerDelete(HttpServletResponse response,
|
||||||
String containerName) throws IOException {
|
String containerName) throws IOException, S3Exception {
|
||||||
if (!blobStore.containerExists(containerName)) {
|
if (!blobStore.containerExists(containerName)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!blobStore.deleteContainerIfEmpty(containerName)) {
|
if (!blobStore.deleteContainerIfEmpty(containerName)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.BUCKET_NOT_EMPTY);
|
throw new S3Exception(S3ErrorCode.BUCKET_NOT_EMPTY);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobList(HttpServletRequest request,
|
private void handleBlobList(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName)
|
HttpServletResponse response, String containerName)
|
||||||
throws IOException {
|
throws IOException, S3Exception {
|
||||||
ListContainerOptions options = new ListContainerOptions();
|
ListContainerOptions options = new ListContainerOptions();
|
||||||
String delimiter = request.getParameter("delimiter");
|
String delimiter = request.getParameter("delimiter");
|
||||||
if (!(delimiter != null && delimiter.equals("/"))) {
|
if (!(delimiter != null && delimiter.equals("/"))) {
|
||||||
|
@ -835,8 +799,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
try {
|
try {
|
||||||
maxKeys = Integer.parseInt(maxKeysString);
|
maxKeys = Integer.parseInt(maxKeysString);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT, nfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options = options.maxResults(maxKeys);
|
options = options.maxResults(maxKeys);
|
||||||
|
@ -845,8 +808,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
try {
|
try {
|
||||||
set = blobStore.list(containerName, options);
|
set = blobStore.list(containerName, options);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Writer writer = new OutputStreamWriter(response.getOutputStream(),
|
try (Writer writer = new OutputStreamWriter(response.getOutputStream(),
|
||||||
|
@ -980,13 +942,13 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobRemove(HttpServletResponse response,
|
private void handleBlobRemove(HttpServletResponse response,
|
||||||
String containerName, String blobName) throws IOException {
|
String containerName, String blobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
try {
|
try {
|
||||||
blobStore.removeBlob(containerName, blobName);
|
blobStore.removeBlob(containerName, blobName);
|
||||||
response.sendError(HttpServletResponse.SC_NO_CONTENT);
|
response.sendError(HttpServletResponse.SC_NO_CONTENT);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1020,17 +982,16 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBlobMetadata(HttpServletResponse response,
|
private void handleBlobMetadata(HttpServletResponse response,
|
||||||
String containerName, String blobName) throws IOException {
|
String containerName, String blobName)
|
||||||
|
throws IOException, S3Exception {
|
||||||
BlobMetadata metadata;
|
BlobMetadata metadata;
|
||||||
try {
|
try {
|
||||||
metadata = blobStore.blobMetadata(containerName, blobName);
|
metadata = blobStore.blobMetadata(containerName, blobName);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_KEY);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setStatus(HttpServletResponse.SC_OK);
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
@ -1039,7 +1000,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleGetBlob(HttpServletRequest request,
|
private void handleGetBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName) throws IOException {
|
String blobName) throws IOException, S3Exception {
|
||||||
int status = HttpServletResponse.SC_OK;
|
int status = HttpServletResponse.SC_OK;
|
||||||
GetOptions options = new GetOptions();
|
GetOptions options = new GetOptions();
|
||||||
String range = request.getHeader(HttpHeaders.RANGE);
|
String range = request.getHeader(HttpHeaders.RANGE);
|
||||||
|
@ -1063,12 +1024,10 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
try {
|
try {
|
||||||
blob = blobStore.getBlob(containerName, blobName, options);
|
blob = blobStore.getBlob(containerName, blobName, options);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (blob == null) {
|
if (blob == null) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_KEY);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setStatus(status);
|
response.setStatus(status);
|
||||||
|
@ -1082,7 +1041,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleCopyBlob(HttpServletRequest request,
|
private void handleCopyBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String destContainerName,
|
HttpServletResponse response, String destContainerName,
|
||||||
String destBlobName) throws IOException {
|
String destBlobName) throws IOException, S3Exception {
|
||||||
String copySourceHeader = request.getHeader("x-amz-copy-source");
|
String copySourceHeader = request.getHeader("x-amz-copy-source");
|
||||||
if (copySourceHeader.startsWith("/")) {
|
if (copySourceHeader.startsWith("/")) {
|
||||||
// Some clients like boto do not include the leading slash
|
// Some clients like boto do not include the leading slash
|
||||||
|
@ -1100,20 +1059,17 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
if (sourceContainerName.equals(destContainerName) &&
|
if (sourceContainerName.equals(destContainerName) &&
|
||||||
sourceBlobName.equals(destBlobName) &&
|
sourceBlobName.equals(destBlobName) &&
|
||||||
!replaceMetadata) {
|
!replaceMetadata) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_REQUEST);
|
throw new S3Exception(S3ErrorCode.INVALID_REQUEST);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Blob blob;
|
Blob blob;
|
||||||
try {
|
try {
|
||||||
blob = blobStore.getBlob(sourceContainerName, sourceBlobName);
|
blob = blobStore.getBlob(sourceContainerName, sourceBlobName);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (blob == null) {
|
if (blob == null) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_KEY);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream is = blob.getPayload().openStream()) {
|
try (InputStream is = blob.getPayload().openStream()) {
|
||||||
|
@ -1163,7 +1119,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handlePutBlob(HttpServletRequest request,
|
private void handlePutBlob(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName) throws IOException {
|
String blobName) throws IOException, S3Exception {
|
||||||
// Flag headers present since HttpServletResponse.getHeader returns
|
// Flag headers present since HttpServletResponse.getHeader returns
|
||||||
// null for empty headers values.
|
// null for empty headers values.
|
||||||
String contentLengthString = null;
|
String contentLengthString = null;
|
||||||
|
@ -1184,30 +1140,24 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
contentMD5 = HashCode.fromBytes(
|
contentMD5 = HashCode.fromBytes(
|
||||||
BaseEncoding.base64().decode(contentMD5String));
|
BaseEncoding.base64().decode(contentMD5String));
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_DIGEST);
|
throw new S3Exception(S3ErrorCode.INVALID_DIGEST, iae);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (contentMD5.bits() != Hashing.md5().bits()) {
|
if (contentMD5.bits() != Hashing.md5().bits()) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_DIGEST);
|
throw new S3Exception(S3ErrorCode.INVALID_DIGEST);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentLengthString == null) {
|
if (contentLengthString == null) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.MISSING_CONTENT_LENGTH);
|
||||||
S3ErrorCode.MISSING_CONTENT_LENGTH);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
long contentLength;
|
long contentLength;
|
||||||
try {
|
try {
|
||||||
contentLength = Long.parseLong(contentLengthString);
|
contentLength = Long.parseLong(contentLengthString);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT, nfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (contentLength < 0) {
|
if (contentLength < 0) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream is = request.getInputStream()) {
|
try (InputStream is = request.getInputStream()) {
|
||||||
|
@ -1227,8 +1177,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
eTag = blobStore.putBlob(containerName, builder.build(),
|
eTag = blobStore.putBlob(containerName, builder.build(),
|
||||||
options);
|
options);
|
||||||
} catch (ContainerNotFoundException cnfe) {
|
} catch (ContainerNotFoundException cnfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_BUCKET);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET, cnfe);
|
||||||
return;
|
|
||||||
} catch (HttpResponseException hre) {
|
} catch (HttpResponseException hre) {
|
||||||
HttpResponse hr = hre.getResponse();
|
HttpResponse hr = hre.getResponse();
|
||||||
if (hr == null) {
|
if (hr == null) {
|
||||||
|
@ -1238,9 +1187,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case HttpServletResponse.SC_BAD_REQUEST:
|
case HttpServletResponse.SC_BAD_REQUEST:
|
||||||
case 422: // Swift returns 422 Unprocessable Entity
|
case 422: // Swift returns 422 Unprocessable Entity
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.BAD_DIGEST);
|
||||||
S3ErrorCode.BAD_DIGEST);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// TODO: emit hre.getContent() ?
|
// TODO: emit hre.getContent() ?
|
||||||
response.sendError(status);
|
response.sendError(status);
|
||||||
|
@ -1250,9 +1197,7 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
} catch (RuntimeException re) {
|
} catch (RuntimeException re) {
|
||||||
if (Throwables2.getFirstThrowableOfType(re,
|
if (Throwables2.getFirstThrowableOfType(re,
|
||||||
TimeoutException.class) != null) {
|
TimeoutException.class) != null) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.REQUEST_TIMEOUT, re);
|
||||||
S3ErrorCode.REQUEST_TIMEOUT);
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
throw re;
|
throw re;
|
||||||
}
|
}
|
||||||
|
@ -1306,11 +1251,10 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleCompleteMultipartUpload(HttpServletRequest request,
|
private void handleCompleteMultipartUpload(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName, String uploadId) throws IOException {
|
String blobName, String uploadId) throws IOException, S3Exception {
|
||||||
try (InputStream is = request.getInputStream();
|
Collection<String> partNames = new ArrayList<>();
|
||||||
Writer writer = response.getWriter()) {
|
long totalContentLength = 0;
|
||||||
Collection<String> partNames = new ArrayList<>();
|
try (InputStream is = request.getInputStream()) {
|
||||||
long totalContentLength = 0;
|
|
||||||
for (Iterator<String> it = parseSimpleXmlElements(is,
|
for (Iterator<String> it = parseSimpleXmlElements(is,
|
||||||
"PartNumber").iterator(); it.hasNext();) {
|
"PartNumber").iterator(); it.hasNext();) {
|
||||||
String partName = uploadId + "." + it.next();
|
String partName = uploadId + "." + it.next();
|
||||||
|
@ -1321,20 +1265,18 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
metadata.getContentMetadata().getContentLength();
|
metadata.getContentMetadata().getContentLength();
|
||||||
if (contentLength < MINIMUM_MULTIPART_PART_SIZE &&
|
if (contentLength < MINIMUM_MULTIPART_PART_SIZE &&
|
||||||
it.hasNext()) {
|
it.hasNext()) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.ENTITY_TOO_SMALL);
|
||||||
S3ErrorCode.ENTITY_TOO_SMALL);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
totalContentLength += contentLength;
|
totalContentLength += contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partNames.isEmpty()) {
|
if (partNames.isEmpty()) {
|
||||||
// Amazon requires at least one part
|
// Amazon requires at least one part
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.MALFORMED_X_M_L);
|
||||||
S3ErrorCode.MALFORMED_X_M_L);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Writer writer = response.getWriter()) {
|
||||||
BlobMetadata blobMetadata = blobStore.blobMetadata(
|
BlobMetadata blobMetadata = blobStore.blobMetadata(
|
||||||
containerName, uploadId);
|
containerName, uploadId);
|
||||||
ContentMetadata contentMetadata =
|
ContentMetadata contentMetadata =
|
||||||
|
@ -1400,10 +1342,9 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleAbortMultipartUpload(HttpServletRequest request,
|
private void handleAbortMultipartUpload(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName, String uploadId) throws IOException {
|
String blobName, String uploadId) throws IOException, S3Exception {
|
||||||
if (!blobStore.blobExists(containerName, uploadId)) {
|
if (!blobStore.blobExists(containerName, uploadId)) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.NO_SUCH_UPLOAD);
|
throw new S3Exception(S3ErrorCode.NO_SUCH_UPLOAD);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
PageSet<? extends StorageMetadata> pageSet = blobStore.list(
|
PageSet<? extends StorageMetadata> pageSet = blobStore.list(
|
||||||
containerName,
|
containerName,
|
||||||
|
@ -1533,7 +1474,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
|
|
||||||
private void handleUploadPart(HttpServletRequest request,
|
private void handleUploadPart(HttpServletRequest request,
|
||||||
HttpServletResponse response, String containerName,
|
HttpServletResponse response, String containerName,
|
||||||
String blobName, String uploadId) throws IOException {
|
String blobName, String uploadId)
|
||||||
|
throws IOException, S3Exception {
|
||||||
// TODO: duplicated from handlePutBlob
|
// TODO: duplicated from handlePutBlob
|
||||||
String contentLengthString = null;
|
String contentLengthString = null;
|
||||||
String contentMD5String = null;
|
String contentMD5String = null;
|
||||||
|
@ -1553,30 +1495,24 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
contentMD5 = HashCode.fromBytes(
|
contentMD5 = HashCode.fromBytes(
|
||||||
BaseEncoding.base64().decode(contentMD5String));
|
BaseEncoding.base64().decode(contentMD5String));
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_DIGEST);
|
throw new S3Exception(S3ErrorCode.INVALID_DIGEST, iae);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (contentMD5.bits() != Hashing.md5().bits()) {
|
if (contentMD5.bits() != Hashing.md5().bits()) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_DIGEST);
|
throw new S3Exception(S3ErrorCode.INVALID_DIGEST);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentLengthString == null) {
|
if (contentLengthString == null) {
|
||||||
sendSimpleErrorResponse(response,
|
throw new S3Exception(S3ErrorCode.MISSING_CONTENT_LENGTH);
|
||||||
S3ErrorCode.MISSING_CONTENT_LENGTH);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
long contentLength;
|
long contentLength;
|
||||||
try {
|
try {
|
||||||
contentLength = Long.parseLong(contentLengthString);
|
contentLength = Long.parseLong(contentLengthString);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT, nfe);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (contentLength < 0) {
|
if (contentLength < 0) {
|
||||||
sendSimpleErrorResponse(response, S3ErrorCode.INVALID_ARGUMENT);
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String partNumber = request.getParameter("partNumber");
|
String partNumber = request.getParameter("partNumber");
|
||||||
|
@ -1682,6 +1618,23 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class S3Exception extends Exception {
|
||||||
|
private final S3ErrorCode error;
|
||||||
|
|
||||||
|
S3Exception(S3ErrorCode error) {
|
||||||
|
this(error, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
S3Exception(S3ErrorCode error, Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
this.error = checkNotNull(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
S3ErrorCode getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Amazon V2 signature. Reference:
|
* Create Amazon V2 signature. Reference:
|
||||||
* http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
|
* http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
|
||||||
|
|
Ładowanie…
Reference in New Issue