Author: jpeck
Date: Fri Jul 3 11:22:47 2026
New Revision: 1935831
Log:
Fix mod_dav_svn write-through proxy mangling of COPY/MOVE Destination headers.
See: https://lists.apache.org/thread/blwxmspvj4k97zdhhqs43sml39hmy04x
When proxying a COPY or MOVE to the master, the Destination header still
pointed at the slave and was never translated, so cross-location commits
(and paths containing characters like spaces) failed or were mis-encoded.
Adds a new helper function proxy_request_fixup_destination() that rewrites
a request's destination header from the slave location to the master URI,
keeping the path encoded exactly once.
Modified:
subversion/branches/proxy-move-copy-fix/subversion/mod_dav_svn/mirror.c
Modified:
subversion/branches/proxy-move-copy-fix/subversion/mod_dav_svn/mirror.c
==============================================================================
--- subversion/branches/proxy-move-copy-fix/subversion/mod_dav_svn/mirror.c
Fri Jul 3 11:11:39 2026 (r1935830)
+++ subversion/branches/proxy-move-copy-fix/subversion/mod_dav_svn/mirror.c
Fri Jul 3 11:22:47 2026 (r1935831)
@@ -33,6 +33,53 @@
#include "dav_svn.h"
+/* If the request carries a Destination header (as COPY and MOVE do), rewrite
+ it to target the master server instead of this slave. MASTER_URI is the
+ SVNMasterURI configuration value (not URI-encoded). This is the
+ request-side counterpart to the Location-header rewrite performed by
+ dav_svn__location_header_filter() (the two are conceptual inverses, though
+ they differ in how they juggle the encoded/decoded root). */
+static void
+proxy_request_fixup_destination(request_rec *r, const char *master_uri)
+{
+ const char *destination = apr_table_get(r->headers_in, "Destination");
+ apr_uri_t dest_uri;
+ const char *dest_path, *root_dir, *rel;
+
+ if (destination == NULL
+ || apr_uri_parse(r->pool, destination, &dest_uri) != APR_SUCCESS
+ || dest_uri.path == NULL)
+ {
+ return;
+ }
+
+ /* apr_uri_parse() leaves dest_uri.path URI-encoded but
+ dav_svn__get_root_dir() is decoded. Encode the root so the comparison
+ and the rebuilt header both stay in the encoded domain. rel is then
+ already encoded and must not be re-encoded, only master_uri is, just
+ as dav_svn__location_header_filter() encodes it on the way back. */
+ dest_path = svn_urlpath__canonicalize(dest_uri.path, r->pool);
+ root_dir = svn_path_uri_encode(dav_svn__get_root_dir(r), r->pool);
+ root_dir = svn_urlpath__canonicalize(root_dir, r->pool);
+
+ rel = svn_urlpath__skip_ancestor(root_dir, dest_path);
+ if (rel == NULL)
+ {
+ /* The Destination isn't under this slave's location, so we have no
+ way to translate it to the master. Leave it untouched (the master
+ will reject it) but log for diagnosability. */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Not rewriting Destination '%s': outside slave root
'%s'",
+ dest_path, root_dir);
+ return;
+ }
+
+ apr_table_set(r->headers_in, "Destination",
+ apr_pstrcat(r->pool, svn_path_uri_encode(master_uri,
r->pool),
+ "/", rel, SVN_VA_NULL));
+}
+
+
/* Tweak the request record R, and add the necessary filters, so that
the request is ready to be proxied away. MASTER_URI is the URI
specified in the SVNMasterURI Apache configuration value.
@@ -60,6 +107,8 @@ static int proxy_request_fixup(request_r
r->pool);
r->handler = "proxy-server";
+ proxy_request_fixup_destination(r, master_uri);
+
/* ### FIXME: Seems we could avoid adding some or all of these
filters altogether when the root_dir (that is, the slave's
location, relative to the server root) and path portion of