Add DELETE and HEAD methods for CORS and CORS headers for all responses

pull/570/head
jixinchi 2023-10-18 10:34:50 +08:00 zatwierdzone przez GitHub
rodzic e6955afb43
commit 60f8366d33
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 59 dodań i 49 usunięć

Wyświetl plik

@ -145,7 +145,7 @@ s3proxy.cors-allow-credential=true
``` ```
CORS cannot be configured per bucket. `s3proxy.cors-allow-all=true` will accept any origin and header. CORS cannot be configured per bucket. `s3proxy.cors-allow-all=true` will accept any origin and header.
Actual CORS requests are supported for GET, PUT and POST methods. Actual CORS requests are supported for GET, PUT, POST, HEAD and DELETE methods.
The wiki collects The wiki collects
[compatibility notes](https://github.com/gaul/s3proxy/wiki/Storage-backend-compatibility) [compatibility notes](https://github.com/gaul/s3proxy/wiki/Storage-backend-compatibility)

Wyświetl plik

@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
public final class CrossOriginResourceSharing { public final class CrossOriginResourceSharing {
protected static final Collection<String> SUPPORTED_METHODS = protected static final Collection<String> SUPPORTED_METHODS =
ImmutableList.of("GET", "HEAD", "PUT", "POST"); ImmutableList.of("GET", "HEAD", "PUT", "POST", "DELETE");
private static final String HEADER_VALUE_SEPARATOR = ", "; private static final String HEADER_VALUE_SEPARATOR = ", ";
private static final String ALLOW_ANY_ORIGIN = "*"; private static final String ALLOW_ANY_ORIGIN = "*";

Wyświetl plik

@ -660,26 +660,26 @@ public class S3ProxyHandler {
switch (method) { switch (method) {
case "DELETE": case "DELETE":
if (path.length <= 2 || path[2].isEmpty()) { if (path.length <= 2 || path[2].isEmpty()) {
handleContainerDelete(response, blobStore, path[1]); handleContainerDelete(request, response, blobStore, path[1]);
return; return;
} else if (uploadId != null) { } else if (uploadId != null) {
handleAbortMultipartUpload(request, response, blobStore, handleAbortMultipartUpload(request, response, blobStore,
path[1], path[2], uploadId); path[1], path[2], uploadId);
return; return;
} else { } else {
handleBlobRemove(response, blobStore, path[1], path[2]); handleBlobRemove(request, response, blobStore, path[1], path[2]);
return; return;
} }
case "GET": case "GET":
if (uri.equals("/")) { if (uri.equals("/")) {
handleContainerList(response, blobStore); handleContainerList(request, response, blobStore);
return; return;
} else if (path.length <= 2 || path[2].isEmpty()) { } else if (path.length <= 2 || path[2].isEmpty()) {
if (request.getParameter("acl") != null) { if (request.getParameter("acl") != null) {
handleGetContainerAcl(response, blobStore, path[1]); handleGetContainerAcl(request, response, blobStore, path[1]);
return; return;
} else if (request.getParameter("location") != null) { } else if (request.getParameter("location") != null) {
handleContainerLocation(response); handleContainerLocation(request, response);
return; return;
} else if (request.getParameter("policy") != null) { } else if (request.getParameter("policy") != null) {
handleBucketPolicy(blobStore, path[1]); handleBucketPolicy(blobStore, path[1]);
@ -693,7 +693,7 @@ public class S3ProxyHandler {
return; return;
} else { } else {
if (request.getParameter("acl") != null) { if (request.getParameter("acl") != null) {
handleGetBlobAcl(response, blobStore, path[1], handleGetBlobAcl(request, response, blobStore, path[1],
path[2]); path[2]);
return; return;
} else if (uploadId != null) { } else if (uploadId != null) {
@ -707,7 +707,7 @@ public class S3ProxyHandler {
} }
case "HEAD": case "HEAD":
if (path.length <= 2 || path[2].isEmpty()) { if (path.length <= 2 || path[2].isEmpty()) {
handleContainerExists(blobStore, path[1]); handleContainerExists(request, response, blobStore, path[1]);
return; return;
} else { } else {
handleBlobMetadata(request, response, blobStore, path[1], handleBlobMetadata(request, response, blobStore, path[1],
@ -716,7 +716,7 @@ public class S3ProxyHandler {
} }
case "POST": case "POST":
if (request.getParameter("delete") != null) { if (request.getParameter("delete") != null) {
handleMultiBlobRemove(response, is, blobStore, path[1]); handleMultiBlobRemove(request, response, is, blobStore, path[1]);
return; return;
} else if (request.getParameter("uploads") != null) { } else if (request.getParameter("uploads") != null) {
handleInitiateMultipartUpload(request, response, blobStore, handleInitiateMultipartUpload(request, response, blobStore,
@ -848,15 +848,6 @@ public class S3ProxyHandler {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED); throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
} else { } else {
String containerName = path[1]; String containerName = path[1];
/*
* Only check access on bucket level. The preflight request
* might be for a PUT, so the object is not yet there.
*/
ContainerAccess access = blobStore.getContainerAccess(
containerName);
if (access == ContainerAccess.PRIVATE) {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
}
handleOptionsBlob(request, response, blobStore, containerName); handleOptionsBlob(request, response, blobStore, containerName);
return; return;
} }
@ -868,15 +859,16 @@ public class S3ProxyHandler {
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED); throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
} }
private void handleGetContainerAcl(HttpServletResponse response, private void handleGetContainerAcl(HttpServletRequest request,
BlobStore blobStore, String containerName) HttpServletResponse response, BlobStore blobStore,
throws IOException, S3Exception { String containerName) throws IOException, S3Exception {
if (!blobStore.containerExists(containerName)) { if (!blobStore.containerExists(containerName)) {
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET); throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
} }
ContainerAccess access = blobStore.getContainerAccess(containerName); ContainerAccess access = blobStore.getContainerAccess(containerName);
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -967,14 +959,16 @@ public class S3ProxyHandler {
} }
blobStore.setContainerAccess(containerName, access); blobStore.setContainerAccess(containerName, access);
addCorsResponseHeader(request, response);
} }
private void handleGetBlobAcl(HttpServletResponse response, private void handleGetBlobAcl(HttpServletRequest request,
BlobStore blobStore, String containerName, HttpServletResponse response, BlobStore blobStore,
String blobName) throws IOException { String containerName, String blobName) throws IOException {
BlobAccess access = blobStore.getBlobAccess(containerName, blobName); BlobAccess access = blobStore.getBlobAccess(containerName, blobName);
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -1066,6 +1060,7 @@ public class S3ProxyHandler {
} }
blobStore.setBlobAccess(containerName, blobName, access); blobStore.setBlobAccess(containerName, blobName, access);
addCorsResponseHeader(request, response);
} }
/** Map XML ACLs to a canned policy if an exact tranformation exists. */ /** Map XML ACLs to a canned policy if an exact tranformation exists. */
@ -1105,11 +1100,12 @@ public class S3ProxyHandler {
} }
} }
private void handleContainerList(HttpServletResponse response, private void handleContainerList(HttpServletRequest request,
BlobStore blobStore) throws IOException { HttpServletResponse response, BlobStore blobStore) throws IOException {
PageSet<? extends StorageMetadata> buckets = blobStore.list(); PageSet<? extends StorageMetadata> buckets = blobStore.list();
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -1148,9 +1144,10 @@ public class S3ProxyHandler {
} }
} }
private void handleContainerLocation(HttpServletResponse response) private void handleContainerLocation(HttpServletRequest request,
throws IOException { HttpServletResponse response) throws IOException {
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -1191,6 +1188,7 @@ public class S3ProxyHandler {
container); container);
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -1251,11 +1249,13 @@ public class S3ProxyHandler {
} }
} }
private static void handleContainerExists(BlobStore blobStore, private void handleContainerExists(HttpServletRequest request,
HttpServletResponse response, BlobStore blobStore,
String containerName) throws IOException, S3Exception { String containerName) throws IOException, S3Exception {
if (!blobStore.containerExists(containerName)) { if (!blobStore.containerExists(containerName)) {
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET); throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
} }
addCorsResponseHeader(request, response);
} }
private void handleContainerCreate(HttpServletRequest request, private void handleContainerCreate(HttpServletRequest request,
@ -1331,11 +1331,12 @@ public class S3ProxyHandler {
} }
response.addHeader(HttpHeaders.LOCATION, "/" + containerName); response.addHeader(HttpHeaders.LOCATION, "/" + containerName);
addCorsResponseHeader(request, response);
} }
private static void handleContainerDelete(HttpServletResponse response, private void handleContainerDelete(HttpServletRequest request,
BlobStore blobStore, String containerName) HttpServletResponse response, BlobStore blobStore,
throws IOException, S3Exception { String containerName) throws IOException, S3Exception {
if (!blobStore.containerExists(containerName)) { if (!blobStore.containerExists(containerName)) {
throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET); throw new S3Exception(S3ErrorCode.NO_SUCH_BUCKET);
} }
@ -1354,6 +1355,7 @@ public class S3ProxyHandler {
throw new S3Exception(S3ErrorCode.BUCKET_NOT_EMPTY); throw new S3Exception(S3ErrorCode.BUCKET_NOT_EMPTY);
} }
addCorsResponseHeader(request, response);
response.setStatus(HttpServletResponse.SC_NO_CONTENT); response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} }
@ -1554,15 +1556,18 @@ public class S3ProxyHandler {
} }
} }
private static void handleBlobRemove(HttpServletResponse response, private void handleBlobRemove(HttpServletRequest request,
BlobStore blobStore, String containerName, HttpServletResponse response, BlobStore blobStore,
String blobName) throws IOException, S3Exception { String containerName, String blobName)
throws IOException, S3Exception {
blobStore.removeBlob(containerName, blobName); blobStore.removeBlob(containerName, blobName);
addCorsResponseHeader(request, response);
response.sendError(HttpServletResponse.SC_NO_CONTENT); response.sendError(HttpServletResponse.SC_NO_CONTENT);
} }
private void handleMultiBlobRemove(HttpServletResponse response, private void handleMultiBlobRemove(HttpServletRequest request,
InputStream is, BlobStore blobStore, String containerName) HttpServletResponse response, InputStream is,
BlobStore blobStore, String containerName)
throws IOException, S3Exception { throws IOException, S3Exception {
DeleteMultipleObjectsRequest dmor = mapper.readValue( DeleteMultipleObjectsRequest dmor = mapper.readValue(
is, DeleteMultipleObjectsRequest.class); is, DeleteMultipleObjectsRequest.class);
@ -1579,6 +1584,7 @@ public class S3ProxyHandler {
blobStore.removeBlobs(containerName, blobNames); blobStore.removeBlobs(containerName, blobNames);
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -1605,7 +1611,7 @@ public class S3ProxyHandler {
} }
} }
private static void handleBlobMetadata(HttpServletRequest request, private void handleBlobMetadata(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
BlobStore blobStore, String containerName, BlobStore blobStore, String containerName,
String blobName) throws IOException, S3Exception { String blobName) throws IOException, S3Exception {
@ -1650,6 +1656,7 @@ public class S3ProxyHandler {
response.setStatus(HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK);
addMetadataToResponse(request, response, metadata); addMetadataToResponse(request, response, metadata);
addCorsResponseHeader(request, response);
} }
private void handleOptionsBlob(HttpServletRequest request, private void handleOptionsBlob(HttpServletRequest request,
@ -1871,6 +1878,7 @@ public class S3ProxyHandler {
BlobMetadata blobMetadata = blobStore.blobMetadata(destContainerName, BlobMetadata blobMetadata = blobStore.blobMetadata(destContainerName,
destBlobName); destBlobName);
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -2200,6 +2208,7 @@ public class S3ProxyHandler {
} }
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -2217,8 +2226,6 @@ public class S3ProxyHandler {
} catch (XMLStreamException xse) { } catch (XMLStreamException xse) {
throw new IOException(xse); throw new IOException(xse);
} }
addCorsResponseHeader(request, response);
} }
private void handleCompleteMultipartUpload(HttpServletRequest request, private void handleCompleteMultipartUpload(HttpServletRequest request,
@ -2325,6 +2332,7 @@ public class S3ProxyHandler {
} }
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (PrintWriter writer = response.getWriter()) { try (PrintWriter writer = response.getWriter()) {
response.setStatus(HttpServletResponse.SC_OK); response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
@ -2388,8 +2396,6 @@ public class S3ProxyHandler {
} catch (XMLStreamException xse) { } catch (XMLStreamException xse) {
throw new IOException(xse); throw new IOException(xse);
} }
addCorsResponseHeader(request, response);
} }
private void handleAbortMultipartUpload(HttpServletRequest request, private void handleAbortMultipartUpload(HttpServletRequest request,
@ -2455,6 +2461,7 @@ public class S3ProxyHandler {
String encodingType = request.getParameter("encoding-type"); String encodingType = request.getParameter("encoding-type");
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -2512,8 +2519,6 @@ public class S3ProxyHandler {
} catch (XMLStreamException xse) { } catch (XMLStreamException xse) {
throw new IOException(xse); throw new IOException(xse);
} }
addCorsResponseHeader(request, response);
} }
private void handleCopyPart(HttpServletRequest request, private void handleCopyPart(HttpServletRequest request,
@ -2689,6 +2694,7 @@ public class S3ProxyHandler {
} }
response.setCharacterEncoding(UTF_8); response.setCharacterEncoding(UTF_8);
addCorsResponseHeader(request, response);
try (Writer writer = response.getWriter()) { try (Writer writer = response.getWriter()) {
response.setContentType(XML_CONTENT_TYPE); response.setContentType(XML_CONTENT_TYPE);
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter( XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
@ -2707,8 +2713,6 @@ public class S3ProxyHandler {
} catch (XMLStreamException xse) { } catch (XMLStreamException xse) {
throw new IOException(xse); throw new IOException(xse);
} }
addCorsResponseHeader(request, response);
} }
private void handleUploadPart(HttpServletRequest request, private void handleUploadPart(HttpServletRequest request,

Wyświetl plik

@ -157,7 +157,7 @@ public final class CrossOriginResourceSharingAllowAllResponseTest {
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue(); HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
assertThat(response.getFirstHeader( assertThat(response.getFirstHeader(
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue()) HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue())
.isEqualTo("GET, HEAD, PUT, POST"); .isEqualTo("GET, HEAD, PUT, POST, DELETE");
assertThat(response.containsHeader( assertThat(response.containsHeader(
HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)).isTrue(); HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
assertThat(response.getFirstHeader( assertThat(response.getFirstHeader(
@ -181,7 +181,7 @@ public final class CrossOriginResourceSharingAllowAllResponseTest {
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue(); HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
assertThat(response.getFirstHeader( assertThat(response.getFirstHeader(
HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue()) HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS).getValue())
.isEqualTo("GET, HEAD, PUT, POST"); .isEqualTo("GET, HEAD, PUT, POST, DELETE");
} }
@Test @Test

Wyświetl plik

@ -107,6 +107,12 @@ public final class CrossOriginResourceSharingRuleTest {
probe = "POST"; probe = "POST";
assertThat(corsAll.isMethodAllowed(probe)) assertThat(corsAll.isMethodAllowed(probe))
.as("check '%s' as method", probe).isTrue(); .as("check '%s' as method", probe).isTrue();
probe = "HEAD";
assertThat(corsAll.isMethodAllowed(probe))
.as("check '%s' as method", probe).isTrue();
probe = "DELETE";
assertThat(corsAll.isMethodAllowed(probe))
.as("check '%s' as method", probe).isTrue();
} }
@Test @Test