Skip to content

Commit

Permalink
webdav: return 507 Insufficient Storage when dCache is full
Browse files Browse the repository at this point in the history
Motivation:

The WebDAV protocol includes a special return code to indicate that a
PUT request was denied because the filesystem is full: 507 Insufficient
Storage.  This has the following semantics (from RFC 4918)

   The 507 (Insufficient Storage) status code means the method could not
   be performed on the resource because the server is unable to store
   the representation needed to successfully complete the request.  This
   condition is considered to be temporary.  If the request that
   received this status code was the result of a user action, the
   request MUST NOT be repeated until it is requested by a separate user
   action.

Currently dCache returns "500 Internal Error" which provides no such
semantics.

Modification:

Introduce a new WebDavException, InsufficientStorageException, to allow
dCache to present the problem to milton.

Unfortunately, PoolManager does not return sufficient information to
distinguish between dCache being full and other failures; therefore, a
rather ugly work-around is employed that compares the String message
with known messages.

Result:

A full dCache results the expected 507 Insufficient Storage HTTP status.

Target: master
Require-notes: yes
Require-book: no
Request: 3.2
Request: 3.1
Request: 3.0
Request: 2.16
Patch: https://rb.dcache.org/r/10638/
Acked-by: Tigran Mkrtchyan

Conflicts:
	modules/dcache-webdav/src/main/java/org/dcache/webdav/DcacheDirectoryResource.java
  • Loading branch information
paulmillar committed Nov 21, 2017
1 parent 2fd15ed commit 1928311
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 2 deletions.
@@ -1,5 +1,6 @@
package org.dcache.webdav;

import com.google.common.collect.ImmutableSet;
import io.milton.http.Auth;
import io.milton.http.HttpManager;
import io.milton.http.LockInfo;
Expand Down Expand Up @@ -34,6 +35,7 @@
import diskCacheV111.util.FileExistsCacheException;
import diskCacheV111.util.FileNotFoundCacheException;
import diskCacheV111.util.FsPath;
import diskCacheV111.util.MissingResourceCacheException;
import diskCacheV111.util.PermissionDeniedCacheException;

import org.dcache.auth.Subjects;
Expand All @@ -48,6 +50,14 @@ public class DcacheDirectoryResource
implements PutableResource, GetableResource, DeletableResource,
MakeCollectionableResource, LockingCollectionResource
{
// FIXME update poolmanager to return the actual CacheException.
private static final ImmutableSet<String> FULL_POOL_MESSAGE = ImmutableSet.<String>builder()
.add("All pools full")
.add("All pools are full")
.add("Cost limit exceeded")
.add("Fallback cost exceeded")
.build();

public DcacheDirectoryResource(DcacheResourceFactory factory,
FsPath path, FileAttributes attributes)
{
Expand Down Expand Up @@ -111,6 +121,12 @@ public Resource createNew(String newName, InputStream inputStream,
throw WebDavExceptions.permissionDenied(this);
} catch (FileExistsCacheException e) {
throw new ConflictException(this);
} catch (MissingResourceCacheException e) {
if (FULL_POOL_MESSAGE.contains(e.getMessage())) {
throw new InsufficientStorageException(e.getMessage(), e, this);
} else {
throw new WebDavException(e.getMessage(), e, this);
}
} catch (CacheException e) {
throw new WebDavException(e.getMessage(), e, this);
} catch (InterruptedException e) {
Expand Down
Expand Up @@ -12,6 +12,7 @@
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.exceptions.NotFoundException;
import io.milton.http.quota.StorageChecker;
import io.milton.http.values.ValueAndType;
import io.milton.http.webdav.PropFindResponse;
import io.milton.http.webdav.PropFindResponse.NameAndError;
Expand Down Expand Up @@ -72,6 +73,7 @@ public class DcacheResponseHandler extends AbstractWrappingResponseHandler
.put(SC_UNAUTHORIZED, "UNAUTHORIZED")
.put(SC_METHOD_NOT_ALLOWED, "METHOD NOT ALLOWED")
.put(SC_NOT_FOUND, "FILE NOT FOUND")
.put(SC_INSUFFICIENT_STORAGE, "INSUFFICIENT STORAGE")
.build();

private AuthenticationService _authenticationService;
Expand Down Expand Up @@ -192,6 +194,12 @@ public void respondForbidden(Resource resource, Response response, Request reque
errorResponse(request, response, SC_FORBIDDEN);
}

@Override
public void respondInsufficientStorage(Request request, Response response, StorageChecker.StorageErrorReason storageErrorReason)
{
errorResponse(request, response, SC_INSUFFICIENT_STORAGE);
}

private void errorResponse(Request request, Response response, Response.Status status)
{
try {
Expand Down
Expand Up @@ -10,7 +10,8 @@
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.exceptions.NotFoundException;
import io.milton.http.http11.Http11ResponseHandler;
import io.milton.http.quota.StorageChecker;
import io.milton.http.webdav.WebDavResponseHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -34,7 +35,7 @@ public class DcacheStandardFilter implements Filter
public void process(FilterChain chain, Request request, Response response)
{
HttpManager manager = chain.getHttpManager();
Http11ResponseHandler responseHandler = manager.getResponseHandler();
WebDavResponseHandler responseHandler = (WebDavResponseHandler) manager.getResponseHandler();

try {
Request.Method method = request.getMethod();
Expand Down Expand Up @@ -69,6 +70,8 @@ public void process(FilterChain chain, Request request, Response response)
} catch (UncheckedBadRequestException e) {
log.debug("Client supplied bad request parameters: {}", e.getMessage());
responseHandler.respondBadRequest(e.getResource(), response, request);
} catch (InsufficientStorageException e) {
responseHandler.respondInsufficientStorage(request, response, StorageChecker.StorageErrorReason.SER_DISK_FULL);
} catch (ConflictException e) {
responseHandler.respondConflict(e.getResource(), response, request, e.getMessage());
} catch (NotAuthorizedException e) {
Expand Down
@@ -0,0 +1,15 @@
package org.dcache.webdav;

import io.milton.resource.Resource;

/**
* Indicates that the server should response with status code
* 507 Insufficient Storage (see RFC 4918).
*/
public class InsufficientStorageException extends WebDavException
{
public InsufficientStorageException(String message, Throwable cause, Resource resource)
{
super(message, cause, resource);
}
}

0 comments on commit 1928311

Please sign in to comment.