Patch bugfix.
I discovered the previous patch had a URL encoding issue that caused
COPY/MOVE
operations to fail for paths containing special characters (e.g., spaces).

The Destination header value is already URL-encoded by the client
(e.g., "/svn/repo/file%20name.txt"). The fix now properly handles this by:

1. Encoding root_dir before comparison with the already-encoded path
2. Encoding master_path before concatenation
3. NOT re-encoding the final path (which would double-encode the
   remainder portion, turning %20 into %2520)

This follows the same pattern used by the existing body rewrite filters
(dav_svn__location_in_filter and dav_svn__location_body_filter), which
also encode their search patterns because "incoming request body has
it encoded already."

I have attached the updated patch.

Thanks,
Jordan


On Wed, 21 Jan 2026 at 10:30, Daniel Sahlberg <[email protected]>
wrote:

> Den tis 20 jan. 2026 kl 20:56 skrev Jordan Peck via dev <
> [email protected]>:
>
>> Small update, after some testing I'm now running our company SVN server
>> with this patch. Everything is working smoothly and the proxy issues are
>> resolved.
>>
>> I've attached an updated version of the patch which fixes some code
>> formatting.
>>
>> Thanks,
>> Jordan
>>
>
> Thank you so much for the patch and the detailed report!
>
> I've taken a quick look and it all seems good, but I need to study the
> surrounding code in more detail to fully understand it. However I havn't
> found the time yet.
>
> To any other takers! We have a test for this in
> https://svn.apache.org/repos/asf/subversion/trunk/subversion/tests/cmdline/dav-mirror-autocheck.sh
>
> Updating the settings around lines 369-376 (ie, changing the
> MASTER_LOCATION and SLAVE_LOCATION as indicated by the comments):
> [[[
> # location directive elements for master,slave,sync
> # tests currently work if master==slave,fail if different
> # ** Should different locations for each work?
> #MASTER_LOCATION="master"
> #SLAVE_LOCATION="slave"
> MASTER_LOCATION="repo"
> SLAVE_LOCATION="repo"
> SYNC_LOCATION="sync"
> ]]]
>
> Without the patch, the test fails with:
> [[[
> ...
> dav-mirror-autocheck.sh: running svnmucc test to
> http://127.0.0.1:1989/slave
> r1 committed by jrandom at 2026-01-21T10:20:43.736512Z
> r2 committed by jrandom at 2026-01-21T10:20:43.861355Z
> svnmucc: E175015: The HTTP method 'COPY' is not allowed on
> '/slave/!svn/rvr/2/trunk'
> svnmucc: E160013: Can't delete node at 'branch' as it does not exist
> ...
> ]]]
>
> With the patch, the test succeeds, as it does when the MASTER_LOCATION and
> SLAVE_LOCATION are the same.
>
> dav-mirror-autocheck.sh is run from a checkout of /trunk as follows:
> $ APACHE_MPM=event ./subversion/tests/cmdline/dav-mirror-autocheck.sh
>  /tmp/test-work
>
> davautocheck is a separate target in Makefile. I'd like to add
> dav-mirror-autocheck the same way (it seems to be easy enough) possibly
> also making a "check it all" target for release testing.
>
> Cheers,
> Daniel
>
>
>
>> On Mon, 19 Jan 2026 at 23:38, C. Michael Pilato <[email protected]>
>> wrote:
>>
>>> Not really.  I suspect the pool of reviewers is just small and occupied
>>> with other activity.  All volunteers 'round here.
>>>
>>> On Mon, Jan 19, 2026 at 12:48 PM Jordan Peck <[email protected]>
>>> wrote:
>>>
>>>> Hi Mike,
>>>> Thanks for having a look, is there anything else I have to do for this
>>>> to be considered for merging into trunk?
>>>>
>>>> Thanks,
>>>> Jordan
>>>>
>>>> On Wed, 14 Jan 2026 at 20:22, C. Michael Pilato <[email protected]>
>>>> wrote:
>>>>
>>>>> I'm too long out of the game to offer meaningful review and testing on
>>>>> this, but I definitely appreciate the thorough write-up.  And given the
>>>>> comments I left sprinkled in that source file (so many years ago...) about
>>>>> how inefficient some of the filtering was when the master/slave repository
>>>>> paths were identical, chances are pretty good that I did most of my 
>>>>> testing
>>>>> in exactly that configuration.
>>>>>
>>>>> -- Mike
>>>>>
>>>>> On Wed, Jan 14, 2026 at 7:18 AM Jordan Peck via dev <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I've identified and fixed a bug in mod_dav_svn's master/slave proxy
>>>>>> functionality (SVNMasterURI) that causes COPY and MOVE operations to
>>>>>> fail with a 400 error when the slave and master repositories are
>>>>>> hosted at different paths.
>>>>>>
>>>>>>
>>>>>> STEPS TO REPRODUCE
>>>>>> ==================
>>>>>>
>>>>>> 1. Configure a master SVN server with the repository at /repo:
>>>>>>
>>>>>>    # Master server (master-server.example.com)
>>>>>>    <Location /repo>
>>>>>>        DAV svn
>>>>>>        SVNPath /var/svn/myrepo
>>>>>>    </Location>
>>>>>>
>>>>>> 2. Configure a slave SVN server with the repository at a different
>>>>>>    path (/svn/repo) and SVNMasterURI pointing to the master:
>>>>>>
>>>>>>    # Slave server (slave-server.example.com)
>>>>>>    <Location /svn/repo>
>>>>>>        DAV svn
>>>>>>        SVNPath /var/svn/myrepo-slave
>>>>>>        SVNMasterURI http://master-server.example.com/repo
>>>>>>    </Location>
>>>>>>
>>>>>> 3. Ensure the slave repository is a synced mirror of the master
>>>>>>    (via svnsync).
>>>>>>
>>>>>> 4. Attempt a COPY operation through the slave to create a branch:
>>>>>>
>>>>>>    svn copy http://slave-server.example.com/svn/repo/trunk \
>>>>>>
>>>>>> http://slave-server.example.com/svn/repo/branches/new-feature \
>>>>>>             -m "Creating new feature branch"
>>>>>>
>>>>>>
>>>>>> EXPECTED RESULT
>>>>>> ===============
>>>>>>
>>>>>> The COPY request is proxied to the master and the branch is created
>>>>>> successfully.
>>>>>>
>>>>>>
>>>>>> ACTUAL RESULT
>>>>>> =============
>>>>>>
>>>>>> The operation fails with HTTP 400 Bad Request. Server logs show the
>>>>>> master receiving a Destination header with path
>>>>>> /svn/repo/branches/new-feature which does not exist on the master
>>>>>> (the correct path would be /repo/branches/new-feature).
>>>>>>
>>>>>>
>>>>>> ROOT CAUSE
>>>>>> ==========
>>>>>>
>>>>>> In subversion/mod_dav_svn/mirror.c, the proxy_request_fixup() function
>>>>>> correctly rewrites the Request-URI when proxying requests to the
>>>>>> master.
>>>>>> However, WebDAV COPY and MOVE operations specify their destination in
>>>>>> the Destination HTTP header, which is not rewritten.
>>>>>>
>>>>>> The existing filters handle:
>>>>>> - Request body rewriting (dav_svn__location_in_filter)
>>>>>> - Response body rewriting (dav_svn__location_body_filter)
>>>>>> - Response Location header rewriting (dav_svn__location_header_filter)
>>>>>>
>>>>>> But there is no rewriting for the incoming Destination request header.
>>>>>>
>>>>>> When paths are identical on slave and master, this bug is masked
>>>>>> because the path portion extracted from the Destination header happens
>>>>>> to be valid on both servers. The bug only manifests when the
>>>>>> repository
>>>>>> paths differ.
>>>>>>
>>>>>>
>>>>>> FIX
>>>>>> ===
>>>>>>
>>>>>> The attached patch adds rewriting of the Destination header in
>>>>>> proxy_request_fixup(). It parses the header, replaces the slave's root
>>>>>> path with the master's path, and updates the header before the request
>>>>>> is proxied.
>>>>>>
>>>>>> I have tested this fix and can confirm it fixes the issue in my
>>>>>> testing.
>>>>>>
>>>>>> Please review the attached patch.
>>>>>>
>>>>>> Thanks,
>>>>>> Jordan Peck
>>>>>>
>>>>>

Attachment: move-copy-proxy-v3.patch
Description: Binary data

Reply via email to