This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 82a1f68561 Remove WebDAV lock null support for compliance with RFC 4918
82a1f68561 is described below
commit 82a1f6856183f5d2267af1235f066cf0541dc3c0
Author: remm <[email protected]>
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.
Since this is a behavior change, I do not intend to backport.
---
.../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 57c1b56021..9fec057e5a 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.
*/
@@ -295,7 +286,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();
@@ -303,7 +293,6 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
LockInfo currentLock = collectionLocksIterator.next();
if (currentLock.hasExpired()) {
collectionLocksIterator.remove();
- removeLockNull(currentLock.path);
}
}
}
@@ -568,30 +557,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;
@@ -636,18 +601,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()) {
@@ -743,8 +696,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);
}
@@ -786,8 +737,6 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
super.doPut(req, resp);
- // Removing any lock-null resource which would be present
- removeLockNull(path);
}
@@ -1184,12 +1133,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);
@@ -1300,8 +1248,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);
}
}
@@ -1321,8 +1267,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;
}
}
@@ -1591,10 +1535,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;
}
@@ -1939,52 +1879,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);
@@ -2019,13 +1921,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);
@@ -2128,13 +2024,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);
@@ -2275,21 +2165,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();
@@ -2323,7 +2198,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 7b5a3cac1b..7d4e18af96 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -126,6 +126,11 @@
Remove default value (was <code>catalina</code>) for the
<code>secret</code> init parameter of the WebDAV Servlet. (remm)
</update>
+ <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>
<!-- Entries for backport and removal before 12.0.0-M1 below this line
-->
<update>
<bug>69374</bug>: Properly separate between table header and body
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]