diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java index 9db9e44994..fd7649a4d2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java @@ -50,7 +50,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -531,8 +533,11 @@ public class PushReplication implements ReplicationQueue { final List r = new ArrayList(remote.getURIs().size()); for (URIish uri : remote.getURIs()) { if (matches(uri, urlMatch)) { - final String replacedPath = - replace(uri.getPath(), "name", project.get()); + String name = project.get(); + if (needsUrlEncoding(uri)) { + name = encode(name); + } + String replacedPath = replace(uri.getPath(), "name", name); if (replacedPath != null) { uri = uri.setPath(replacedPath); r.add(uri); @@ -542,6 +547,27 @@ public class PushReplication implements ReplicationQueue { return r; } + static boolean needsUrlEncoding(URIish uri) { + return "http".equalsIgnoreCase(uri.getScheme()) + || "https".equalsIgnoreCase(uri.getScheme()) + || "amazon-s3".equalsIgnoreCase(uri.getScheme()); + } + + static String encode(String str) { + try { + // Some cleanup is required. The '/' character is always encoded as %2F + // however remote servers will expect it to be not encoded as part of the + // path used to the repository. Space is incorrectly encoded as '+' for this + // context. In the path part of a URI space should be %20, but in form data + // space is '+'. Our cleanup replace fixes these two issues. + return URLEncoder.encode(str, "UTF-8") + .replaceAll("%2[fF]", "/") + .replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + String[] getAdminUrls() { return this.adminUrls; } diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/PushReplicationTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/PushReplicationTest.java new file mode 100644 index 0000000000..b95c7dac87 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/PushReplicationTest.java @@ -0,0 +1,42 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import static com.google.gerrit.server.git.PushReplication.ReplicationConfig.*; +import junit.framework.TestCase; + +import org.eclipse.jgit.transport.URIish; + +import java.net.URISyntaxException; + +public class PushReplicationTest extends TestCase { + public void testNeedsUrlEncoding() throws URISyntaxException { + assertTrue(needsUrlEncoding(new URIish("http://host/path"))); + assertTrue(needsUrlEncoding(new URIish("https://host/path"))); + assertTrue(needsUrlEncoding(new URIish("amazon-s3://config/bucket/path"))); + + assertFalse(needsUrlEncoding(new URIish("host:path"))); + assertFalse(needsUrlEncoding(new URIish("user@host:path"))); + assertFalse(needsUrlEncoding(new URIish("git://host/path"))); + assertFalse(needsUrlEncoding(new URIish("ssh://host/path"))); + } + + public void testUrlEncoding() { + assertEquals("foo/bar/thing", encode("foo/bar/thing")); + assertEquals("--%20All%20Projects%20--", encode("-- All Projects --")); + assertEquals("name/with%20a%20space", encode("name/with a space")); + assertEquals("name%0Awith-LF", encode("name\nwith-LF")); + } +}