You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2024/10/22 12:16:53 UTC
(tomcat) 01/19: Remove WebDAV lock null support for compliance with RFC 4918
This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 09a8845bfbd3dd377b7f0ce8a994bce70b49e4f1
Author: remm <re...@apache.org>
AuthorDate: Thu Oct 17 10:40:52 2024 +0200
Remove WebDAV lock null support for compliance with RFC 4918
Section 7.3 and annex D deprecate lock nulls and define a new behavior.
http://www.webdav.org/specs/rfc4918.html#lock-null
Instead a lock on a non existing resource will create an empty file
locked with a regular lock, which is of course far simpler. When
removing the lock, the corresponding file will not go away.
---
.../apache/catalina/servlets/WebdavServlet.java | 146 ++-------------------
.../catalina/servlets/TestWebdavServlet.java | 2 +-
webapps/docs/changelog.xml | 5 +
3 files changed, 16 insertions(+), 137 deletions(-)
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
index 8fc29b30c7..e9209629fa 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -16,6 +16,7 @@
*/
package org.apache.catalina.servlets;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
@@ -221,16 +222,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
private final ConcurrentHashMap<String,LockInfo> resourceLocks = new ConcurrentHashMap<>();
- /**
- * Repository of the lock-null resources.
- * <p>
- * Key : path of the collection containing the lock-null resource<br>
- * Value : List of lock-null resource which are members of the collection. Each element of the List is the path
- * associated with the lock-null resource.
- */
- private final ConcurrentHashMap<String,CopyOnWriteArrayList<String>> lockNullResources = new ConcurrentHashMap<>();
-
-
/**
* List of the inheritable collection locks.
*/
@@ -293,7 +284,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
for (LockInfo currentLock : resourceLocks.values()) {
if (currentLock.hasExpired()) {
resourceLocks.remove(currentLock.path);
- removeLockNull(currentLock.path);
}
}
Iterator<LockInfo> collectionLocksIterator = collectionLocks.iterator();
@@ -301,7 +291,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
LockInfo currentLock = collectionLocksIterator.next();
if (currentLock.hasExpired()) {
collectionLocksIterator.remove();
- removeLockNull(currentLock.path);
}
}
}
@@ -566,30 +555,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
return;
}
- if (!resource.exists()) {
- int slash = path.lastIndexOf('/');
- if (slash != -1) {
- String parentPath = path.substring(0, slash);
- List<String> currentLockNullResources = lockNullResources.get(parentPath);
- if (currentLockNullResources != null) {
- for (String lockNullPath : currentLockNullResources) {
- if (lockNullPath.equals(path)) {
- resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
- resp.setContentType("text/xml; charset=UTF-8");
- // Create multistatus object
- XMLWriter generatedXML = new XMLWriter(resp.getWriter());
- generatedXML.writeXMLHeader();
- generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING);
- parseLockNullProperties(req, generatedXML, lockNullPath, type, properties);
- generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING);
- generatedXML.sendData();
- return;
- }
- }
- }
- }
- }
-
if (!resource.exists()) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
@@ -634,18 +599,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
stackBelow.addFirst(newPath);
}
- // Displaying the lock-null resources present in that
- // collection
- String lockPath = currentPath;
- if (lockPath.endsWith("/")) {
- lockPath = lockPath.substring(0, lockPath.length() - 1);
- }
- List<String> currentLockNullResources = lockNullResources.get(lockPath);
- if (currentLockNullResources != null) {
- for (String lockNullPath : currentLockNullResources) {
- parseLockNullProperties(req, generatedXML, lockNullPath, type, properties);
- }
- }
}
if (stack.isEmpty()) {
@@ -741,8 +694,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
if (resources.mkdir(path)) {
resp.setStatus(WebdavStatus.SC_CREATED);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
} else {
resp.sendError(WebdavStatus.SC_CONFLICT);
}
@@ -784,8 +735,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
super.doPut(req, resp);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
}
@@ -1189,12 +1138,11 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
// Checking if a resource exists at this path
if (!resource.exists()) {
-
- // "Creating" a lock-null resource
- int slash = lock.path.lastIndexOf('/');
- String parentPath = lock.path.substring(0, slash);
-
- lockNullResources.computeIfAbsent(parentPath, k -> new CopyOnWriteArrayList<>()).add(lock.path);
+ // RFC 4918 removes lock null, instead an empty file is created
+ if (!resources.write(path, new ByteArrayInputStream(new byte[0]), false)) {
+ resp.sendError(WebdavStatus.SC_CONFLICT);
+ return;
+ }
}
lock.tokens.add(lockToken);
@@ -1306,8 +1254,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
if (lock.tokens.isEmpty()) {
resourceLocks.remove(path);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
}
}
@@ -1327,8 +1273,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
}
if (lock.tokens.isEmpty()) {
collectionLocks.remove(lock);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
break;
}
}
@@ -1597,10 +1541,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
resp.setStatus(WebdavStatus.SC_CREATED);
}
- // Removing any lock-null resource which would be present at
- // the destination path
- removeLockNull(destinationPath);
-
return true;
}
@@ -1945,52 +1885,14 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
String rewrittenUrl = rewriteUrl(href);
- generatePropFindResponse(generatedXML, rewrittenUrl, path, type, properties, resource.isFile(), false,
+ generatePropFindResponse(generatedXML, rewrittenUrl, path, type, properties, resource.isFile(),
resource.getCreation(), resource.getLastModified(), resource.getContentLength(),
getServletContext().getMimeType(resource.getName()), generateETag(resource));
}
- /**
- * Propfind helper method. Displays the properties of a lock-null resource.
- *
- * @param req The servlet request
- * @param generatedXML XML response to the Propfind request
- * @param path Path of the current resource
- * @param type Propfind type
- * @param properties If the propfind type is find properties by name, then this List contains those properties
- */
- private void parseLockNullProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type,
- List<String> properties) {
-
- // Exclude any resource in the /WEB-INF and /META-INF subdirectories
- if (isSpecialPath(path)) {
- return;
- }
-
- // Retrieving the lock associated with the lock-null resource
- LockInfo lock = resourceLocks.get(path);
-
- if (lock == null) {
- return;
- }
-
- String absoluteUri = req.getRequestURI();
- String relativePath = getRelativePath(req);
- String toAppend = path.substring(relativePath.length());
- if (!toAppend.startsWith("/")) {
- toAppend = "/" + toAppend;
- }
-
- String rewrittenUrl = rewriteUrl(RequestUtil.normalize(absoluteUri + toAppend));
-
- generatePropFindResponse(generatedXML, rewrittenUrl, path, type, properties, true, true,
- lock.creationDate.getTime(), lock.creationDate.getTime(), 0, "", "");
- }
-
-
private void generatePropFindResponse(XMLWriter generatedXML, String rewrittenUrl, String path, int propFindType,
- List<String> properties, boolean isFile, boolean isLockNull, long created, long lastModified,
+ List<String> properties, boolean isFile, long created, long lastModified,
long contentLength, String contentType, String eTag) {
generatedXML.writeElement("D", "response", XMLWriter.OPENING);
@@ -2025,13 +1927,7 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
generatedXML.writeProperty("D", "getcontenttype", contentType);
}
generatedXML.writeProperty("D", "getetag", eTag);
- if (isLockNull) {
- generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
- generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
- } else {
- generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
- }
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
} else {
generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified));
generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
@@ -2134,13 +2030,7 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
}
} else if (property.equals("resourcetype")) {
if (isFile) {
- if (isLockNull) {
- generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
- generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING);
- } else {
- generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
- }
+ generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT);
} else {
generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING);
generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT);
@@ -2281,21 +2171,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
}
- private void removeLockNull(String path) {
- int slash = path.lastIndexOf('/');
- if (slash >= 0) {
- String parentPath = path.substring(0, slash);
- List<String> paths = lockNullResources.get(parentPath);
- if (paths != null) {
- paths.remove(path);
- if (paths.isEmpty()) {
- lockNullResources.remove(parentPath);
- }
- }
- }
- }
-
-
private String getDAVNode(Node node) {
if (node.getNamespaceURI().equals(DEFAULT_NAMESPACE)) {
return node.getLocalName();
@@ -2329,7 +2204,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
String owner = "";
List<String> tokens = Collections.synchronizedList(new ArrayList<>());
long expiresAt = 0;
- Date creationDate = new Date();
// ----------------------------------------------------- Public Methods
diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java b/test/org/apache/catalina/servlets/TestWebdavServlet.java
index 8fe84abb85..a1df181f12 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -441,7 +441,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
SimpleHttpClient.CRLF + CONTENT });
client.connect();
client.processRequest(true);
- Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode());
+ Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode());
// Verify that this also removes the lock by doing another PUT without the token
client.setRequest(new String[] { "DELETE /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF +
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 67ffcee4ed..1210f018da 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -160,6 +160,11 @@
WebDAV Delete should remove any existing lock on successfully deleted
resources. (remm)
</fix>
+ <update>
+ Remove WebDAV lock null support in accordance with RFC 4918 section 7.3
+ and annex D. Instead a lock on a non existing resource will create an
+ empty file locked with a regular lock. (remm)
+ </update>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org