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 10:13:08 UTC
(tomcat) 01/18: 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 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit a627da2b9b77c88e1d1fdce5d27dc07ca38575da
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 fe5fbfc123..6b79fe3498 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;
@@ -222,16 +223,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.
*/
@@ -294,7 +285,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();
@@ -302,7 +292,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
LockInfo currentLock = collectionLocksIterator.next();
if (currentLock.hasExpired()) {
collectionLocksIterator.remove();
- removeLockNull(currentLock.path);
}
}
}
@@ -567,30 +556,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;
@@ -635,18 +600,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()) {
@@ -742,8 +695,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);
}
@@ -785,8 +736,6 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
super.doPut(req, resp);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
}
@@ -1190,12 +1139,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);
@@ -1307,8 +1255,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);
}
}
@@ -1328,8 +1274,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;
}
}
@@ -1598,10 +1542,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;
}
@@ -1946,52 +1886,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);
@@ -2026,13 +1928,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);
@@ -2135,13 +2031,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);
@@ -2282,21 +2172,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();
@@ -2330,7 +2205,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 22688b6ddd..df5cb2832b 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -442,7 +442,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 34f10491a2..8c83a8f990 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