From 7db3bac3babe9001051b124751920a3807ed2aae Mon Sep 17 00:00:00 2001 From: Dmitry Litvintsev Date: Fri, 24 Apr 2026 09:40:12 -0500 Subject: [PATCH] bulk: report 413 Entity Too Large is limits on bulk requests are exceeded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: ----------- Issue https://github.com/dCache/dcache/issues/7997 reports that when the number of targets in a bulk request exceeds the configured limit, dCache replies with HTTP 403 – Forbidden which is confusing. Suggested response: 413 Content Too Large Modification: ------------- Instead of BulkPermissionDeniedException throw newly added BulkContentTooLargeException when limits are exceeded and throwing ClientErrorException(REQUEST_ENTITY_TOO_LARGE, error) in the sunbsequent handling resulting in 413 code returned to the client. Result: -------- dCache replies w/ 413 Entity Too Large code when various bulk request limits are exceeded. python dcache_tape_api.py -d uqbar -s /pnfs/fs/usr/fermilab/users/litvinse/1.data /pnfs/fs/usr/fermilab/users/litvinse/10.data 413 Client Error: Request Entity Too Large for url: https://uqbar:3880/api/v1/tape/stage Ticket: https://github.com/dCache/dcache/issues/7997 Acked-by: Anastasiia Chub Patch: https://rb.dcache.org/r/14683/ Target: trunk Request: 11.2, 11.1, 11.0, 10.2 Require-book: no Require-notes: yes --- .../org/dcache/services/bulk/BulkService.java | 16 ++-- .../resources/tape/StageResources.java | 2 + .../util/bulk/BulkServiceCommunicator.java | 4 + .../bulk/BulkContentTooLargeException.java | 73 +++++++++++++++++++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 modules/dcache-vehicles/src/main/java/org/dcache/services/bulk/BulkContentTooLargeException.java diff --git a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/BulkService.java b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/BulkService.java index 822debac7b0..5dd6411c779 100644 --- a/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/BulkService.java +++ b/modules/dcache-bulk/src/main/java/org/dcache/services/bulk/BulkService.java @@ -497,7 +497,7 @@ private void relayToLeader(M message, MessageReply reply) } private synchronized void checkDepthConstraints(BulkRequest request) - throws BulkPermissionDeniedException { + throws BulkContentTooLargeException { switch (request.getExpandDirectories()) { case ALL: switch (allowedDepth) { @@ -505,7 +505,7 @@ private synchronized void checkDepthConstraints(BulkRequest request) checkTargetCount(request); return; default: - throw new BulkPermissionDeniedException( + throw new BulkContentTooLargeException( "full directory recursion not permitted."); } case TARGETS: @@ -515,7 +515,7 @@ private synchronized void checkDepthConstraints(BulkRequest request) checkTargetCount(request); return; default: - throw new BulkPermissionDeniedException( + throw new BulkContentTooLargeException( "processing children of a directory not permitted."); } default: @@ -548,27 +548,27 @@ private void checkRestrictions(Restriction restriction, String uuid) } private synchronized void checkTargetCount(BulkRequest request) - throws BulkPermissionDeniedException { + throws BulkContentTooLargeException { List targets = request.getTarget(); int listSize = targets == null ? 0 : targets.size(); switch (request.getExpandDirectories()) { case NONE: if (listSize > maxFlatTargets) { - throw new BulkPermissionDeniedException( + throw new BulkContentTooLargeException( String.format(TARGET_COUNT_ERROR_FORMAT, listSize, maxFlatTargets, Depth.NONE.name())); } break; case TARGETS: if (listSize > maxShallowTargets) { - throw new BulkPermissionDeniedException( + throw new BulkContentTooLargeException( String.format(TARGET_COUNT_ERROR_FORMAT, listSize, maxShallowTargets, Depth.TARGETS.name())); } break; case ALL: if (listSize > maxRecursiveTargets) { - throw new BulkPermissionDeniedException( + throw new BulkContentTooLargeException( String.format(TARGET_COUNT_ERROR_FORMAT, listSize, maxRecursiveTargets, Depth.ALL.name())); } @@ -700,4 +700,4 @@ private void validateTargets(String uuid, Subject subject, List paths) } } } -} \ No newline at end of file +} diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java index 8ac882b6144..ad2b4832b17 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/resources/tape/StageResources.java @@ -210,6 +210,7 @@ public StageRequestInfo getStageInfo(@ApiParam("The unique id of the request.") @ApiResponse(code = 401, message = "Unauthorized"), @ApiResponse(code = 403, message = "Forbidden"), @ApiResponse(code = 404, message = "Not Found"), + @ApiResponse(code = 413, message = "Content Too Large"), @ApiResponse(code = 429, message = "Too many requests"), @ApiResponse(code = 500, message = "Internal Server Error") }) @@ -275,6 +276,7 @@ public Response cancel( @ApiResponse(code = 400, message = "Bad request"), @ApiResponse(code = 401, message = "Unauthorized"), @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 413, message = "Content Too Large"), @ApiResponse(code = 429, message = "Too many requests"), @ApiResponse(code = 500, message = "Internal Server Error") }) diff --git a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/bulk/BulkServiceCommunicator.java b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/bulk/BulkServiceCommunicator.java index 033799f98fe..8487bcf13e9 100644 --- a/modules/dcache-frontend/src/main/java/org/dcache/restful/util/bulk/BulkServiceCommunicator.java +++ b/modules/dcache-frontend/src/main/java/org/dcache/restful/util/bulk/BulkServiceCommunicator.java @@ -60,6 +60,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING package org.dcache.restful.util.bulk; import static javax.ws.rs.core.Response.Status.TOO_MANY_REQUESTS; +import static javax.ws.rs.core.Response.Status.REQUEST_ENTITY_TOO_LARGE; import com.google.common.base.Throwables; import dmg.util.Exceptions; @@ -71,6 +72,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.NotFoundException; import org.dcache.cells.CellStub; +import org.dcache.services.bulk.BulkContentTooLargeException; import org.dcache.services.bulk.BulkPermissionDeniedException; import org.dcache.services.bulk.BulkQuotaExceededException; import org.dcache.services.bulk.BulkRequestNotFoundException; @@ -124,6 +126,8 @@ private void checkError(BulkServiceMessage message) { if (error instanceof BulkPermissionDeniedException) { throw new ForbiddenException(error); + } else if (error instanceof BulkContentTooLargeException) { + throw new ClientErrorException(REQUEST_ENTITY_TOO_LARGE, error); } else if (error instanceof BulkQuotaExceededException) { throw new ClientErrorException(TOO_MANY_REQUESTS, error); } else if (error instanceof BulkRequestNotFoundException) { diff --git a/modules/dcache-vehicles/src/main/java/org/dcache/services/bulk/BulkContentTooLargeException.java b/modules/dcache-vehicles/src/main/java/org/dcache/services/bulk/BulkContentTooLargeException.java new file mode 100644 index 00000000000..9567d87d49b --- /dev/null +++ b/modules/dcache-vehicles/src/main/java/org/dcache/services/bulk/BulkContentTooLargeException.java @@ -0,0 +1,73 @@ +/* +COPYRIGHT STATUS: +Dec 1st 2001, Fermi National Accelerator Laboratory (FNAL) documents and +software are sponsored by the U.S. Department of Energy under Contract No. +DE-AC02-76CH03000. Therefore, the U.S. Government retains a world-wide +non-exclusive, royalty-free license to publish or reproduce these documents +and software for U.S. Government purposes. All documents and software +available from this server are protected under the U.S. and Foreign +Copyright Laws, and FNAL reserves all rights. + +Distribution of the software available from this server is free of +charge subject to the user following the terms of the Fermitools +Software Legal Information. + +Redistribution and/or modification of the software shall be accompanied +by the Fermitools Software Legal Information (including the copyright +notice). + +The user is asked to feed back problems, benefits, and/or suggestions +about the software to the Fermilab Software Providers. + +Neither the name of Fermilab, the URA, nor the names of the contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +DISCLAIMER OF LIABILITY (BSD): + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FERMILAB, +OR THE URA, OR THE U.S. DEPARTMENT of ENERGY, OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Liabilities of the Government: + +This software is provided by URA, independent from its Prime Contract +with the U.S. Department of Energy. URA is acting independently from +the Government and in its own private capacity and is not acting on +behalf of the U.S. Government, nor as its contractor nor its agent. +Correspondingly, it is understood and agreed that the U.S. Government +has no connection to this software and in no manner whatsoever shall +be liable for nor assume any responsibility or obligation for any claim, +cost, or damages arising out of or resulting from the use of the software +available from this server. + +Export Control: + +All documents and software available from this server are subject to U.S. +export control laws. Anyone downloading information from this server is +obligated to secure any necessary Government licenses before exporting +documents or software obtained from this server. + */ +package org.dcache.services.bulk; + +public class BulkContentTooLargeException extends BulkServiceException { + + private static final long serialVersionUID = 1L; + + public BulkContentTooLargeException(String message) { + super(message); + } + + public BulkContentTooLargeException(String message, Throwable cause) { + super(message, cause); + } +}