[WAGON-583] WebDavWagon and tests now adhere to RFC 4918#845
[WAGON-583] WebDavWagon and tests now adhere to RFC 4918#845slachiewicz wants to merge 2 commits into
Conversation
slachiewicz
commented
Dec 30, 2025
- Handle 409 Conflict status when parent collections don't exist
- Handle redirects on MKCOL operations
- Accept 200 OK status for already-existing collections
- Fix tests to use WebDAV PROPFIND instead of filesystem checks
There was a problem hiding this comment.
Pull request overview
This PR updates the WebDavWagon implementation to comply with RFC 4918 WebDAV specifications, improving the robustness of collection creation operations and refactoring tests to use proper WebDAV PROPFIND requests instead of direct filesystem checks.
- Enhances MKCOL operation to handle additional RFC 4918 status codes (200 OK, 409 Conflict)
- Adds redirect handling for MKCOL requests
- Introduces a new collectionExists method for WebDAV-based collection verification
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| WebDavWagon.java | Added RFC 4918 compliance for status code handling in mkdirs(), implemented redirect handling in doMkCol(), and added collectionExists() method for WebDAV-based collection verification |
| WebDavWagonTest.java | Refactored testMkdirs() and testMkdirsWithNoBasedir() to use WebDAV PROPFIND via collectionExists() instead of filesystem File.exists() checks |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (statusCode >= HttpStatus.SC_MULTIPLE_CHOICES && statusCode < HttpStatus.SC_BAD_REQUEST) { | ||
| org.apache.http.Header locationHeader = closeableHttpResponse.getFirstHeader("Location"); | ||
| if (locationHeader != null) { | ||
| String redirectUrl = locationHeader.getValue(); | ||
| // Recursive call to handle redirect - execute() will handle the redirect automatically | ||
| // but we need to return the final status | ||
| return doMkCol(redirectUrl); | ||
| } |
There was a problem hiding this comment.
The redirect handling logic has a potential infinite recursion issue. If a server responds with a redirect that points back to the original URL or creates a redirect loop, this will cause a stack overflow. The recursive call should include protection against redirect loops, such as tracking visited URLs or limiting the maximum number of redirects.
| String repositoryUrl = testRepository.getUrl(); | ||
|
|
||
| // test leading / | ||
| assertFalse(new File(dir, "foo").exists()); | ||
| String fooUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "foo"; | ||
| assertFalse("Collection should not exist before creation", wagon.collectionExists(fooUrl)); | ||
| wagon.mkdirs("/foo"); | ||
| assertTrue(new File(dir, "foo").exists()); | ||
| assertTrue("Collection should exist after creation", wagon.collectionExists(fooUrl)); | ||
|
|
||
| // test trailing / | ||
| assertFalse(new File(dir, "bar").exists()); | ||
| String barUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "bar"; | ||
| assertFalse("Collection should not exist before creation", wagon.collectionExists(barUrl)); | ||
| wagon.mkdirs("bar/"); | ||
| assertTrue(new File(dir, "bar").exists()); | ||
| assertTrue("Collection should exist after creation", wagon.collectionExists(barUrl)); | ||
|
|
||
| // test when already exists | ||
| // test when already exists (should not fail) | ||
| wagon.mkdirs("bar"); | ||
| assertTrue("Collection should still exist", wagon.collectionExists(barUrl)); | ||
|
|
||
| // test several parts | ||
| assertFalse(new File(dir, "1/2/3/4").exists()); | ||
| String deepUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "1/2/3/4"; | ||
| assertFalse("Deep collection should not exist before creation", wagon.collectionExists(deepUrl)); | ||
| wagon.mkdirs("1/2/3/4"); | ||
| assertTrue(new File(dir, "1/2/3/4").exists()); | ||
| assertTrue("Deep collection should exist after creation", wagon.collectionExists(deepUrl)); | ||
|
|
||
| // test additional part and trailing / | ||
| assertFalse(new File(dir, "1/2/3/4/5").exists()); | ||
| String deeperUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "1/2/3/4/5"; | ||
| assertFalse("Deeper collection should not exist before creation", wagon.collectionExists(deeperUrl)); | ||
| wagon.mkdirs("1/2/3/4/5/"); | ||
| assertTrue(new File(dir, "1/2/3/4").exists()); | ||
| assertTrue("Deeper collection should exist after creation", wagon.collectionExists(deeperUrl)); |
There was a problem hiding this comment.
The URL construction pattern is repetitive and error-prone. The same logic for appending paths to repositoryUrl is duplicated multiple times. Consider extracting this into a helper method to reduce duplication and improve maintainability.
- Handle 409 Conflict status when parent collections don't exist - Handle redirects on MKCOL operations - Accept 200 OK status for already-existing collections - Fix tests to use WebDAV PROPFIND instead of filesystem checks
f8c7ea0 to
3f095c3
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| // RFC 4918: Accept 201 Created or 200 OK (if collection already exists from another request) | ||
| if (status != HttpStatus.SC_CREATED && status != HttpStatus.SC_OK) { |
| try (CloseableHttpResponse closeableHttpResponse = execute(method)) { | ||
| return closeableHttpResponse.getStatusLine().getStatusCode(); | ||
| int statusCode = closeableHttpResponse.getStatusLine().getStatusCode(); | ||
|
|
||
| // RFC 4918: Handle redirects for MKCOL | ||
| // 3xx redirects should be followed to the new location | ||
| if (statusCode >= HttpStatus.SC_MULTIPLE_CHOICES && statusCode < HttpStatus.SC_BAD_REQUEST) { | ||
| org.apache.http.Header locationHeader = closeableHttpResponse.getFirstHeader("Location"); | ||
| if (locationHeader != null) { | ||
| String redirectUrl = locationHeader.getValue(); | ||
| // Follow the redirect by recursively invoking doMkCol on the new URL | ||
| // and return the status code from the final resolved location | ||
| return doMkCol(redirectUrl); | ||
| } |
| int statusCode = closeableHttpResponse.getStatusLine().getStatusCode(); | ||
|
|
||
| // RFC 4918: Handle redirects for MKCOL | ||
| // 3xx redirects should be followed to the new location | ||
| if (statusCode >= HttpStatus.SC_MULTIPLE_CHOICES && statusCode < HttpStatus.SC_BAD_REQUEST) { | ||
| org.apache.http.Header locationHeader = closeableHttpResponse.getFirstHeader("Location"); | ||
| if (locationHeader != null) { | ||
| String redirectUrl = locationHeader.getValue(); | ||
| // Follow the redirect by recursively invoking doMkCol on the new URL | ||
| // and return the status code from the final resolved location | ||
| return doMkCol(redirectUrl); | ||
| } | ||
| } | ||
|
|
||
| return statusCode; |