diff options
Diffstat (limited to 'org.eclipse.jgit.http.test')
9 files changed, 1075 insertions, 92 deletions
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..fef3713825 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -39,11 +41,12 @@ sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class= sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true sp_cleanup.remove_unused_imports=false sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false sp_cleanup.use_blocks=false sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.pde.api.tools.prefs index cd148d9049..c0030ded71 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.pde.api.tools.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.pde.api.tools.prefs @@ -1,4 +1,4 @@ -#Tue Oct 18 00:52:01 CEST 2011 +ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error @@ -8,6 +8,10 @@ API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error +API_USE_SCAN_FIELD_SEVERITY=Error +API_USE_SCAN_METHOD_SEVERITY=Error +API_USE_SCAN_TYPE_SEVERITY=Error +CLASS_ELEMENT_TYPE_ADDED_FIELD=Error CLASS_ELEMENT_TYPE_ADDED_METHOD=Error CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error @@ -47,6 +51,7 @@ ILLEGAL_IMPLEMENT=Warning ILLEGAL_INSTANTIATE=Warning ILLEGAL_OVERRIDE=Warning ILLEGAL_REFERENCE=Warning +INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error @@ -58,6 +63,7 @@ INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +INVALID_ANNOTATION=Ignore INVALID_JAVADOC_TAG=Ignore INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error LEAK_EXTEND=Warning @@ -75,6 +81,7 @@ METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +MISSING_EE_DESCRIPTIONS=Warning TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error @@ -83,10 +90,13 @@ TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error UNUSED_PROBLEM_FILTERS=Warning automatically_removed_unused_problem_filters=false +changed_execution_env=Error eclipse.preferences.version=1 incompatible_api_component_version=Error incompatible_api_component_version_include_major_without_breaking_change=Disabled incompatible_api_component_version_include_minor_without_api_change=Disabled +incompatible_api_component_version_report_major_without_breaking_change=Warning +incompatible_api_component_version_report_minor_without_api_change=Ignore invalid_since_tag_version=Error malformed_since_tag=Error missing_since_tag=Error diff --git a/org.eclipse.jgit.http.test/BUILD b/org.eclipse.jgit.http.test/BUILD index ce2d6112f1..85a22422be 100644 --- a/org.eclipse.jgit.http.test/BUILD +++ b/org.eclipse.jgit.http.test/BUILD @@ -34,6 +34,7 @@ java_library( srcs = glob(["src/**/*.java"]), deps = [ "//lib:junit", + "//lib:servlet-api", "//org.eclipse.jgit.http.server:jgit-servlet", "//org.eclipse.jgit:jgit", "//org.eclipse.jgit.junit.http:junit-http", diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 811585ec69..442c4ec45c 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -2,12 +2,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 4.8.1.qualifier +Bundle-Version: 4.9.10.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: javax.servlet;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)", + org.apache.commons.codec;version="[1.6.0,2.0.0)", + org.apache.commons.codec.binary;version="[1.6.0,2.0.0)", org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)", org.eclipse.jetty.http;version="[9.4.5,10.0.0)", org.eclipse.jetty.io;version="[9.4.5,10.0.0)", @@ -22,25 +24,27 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.errors;version="[4.8.1,4.9.0)", - org.eclipse.jgit.http.server;version="[4.8.1,4.9.0)", - org.eclipse.jgit.http.server.glue;version="[4.8.1,4.9.0)", - org.eclipse.jgit.http.server.resolver;version="[4.8.1,4.9.0)", - org.eclipse.jgit.internal;version="[4.8.1,4.9.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.8.1,4.9.0)", - org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)", - org.eclipse.jgit.junit;version="[4.8.1,4.9.0)", - org.eclipse.jgit.junit.http;version="[4.8.1,4.9.0)", - org.eclipse.jgit.lib;version="[4.8.1,4.9.0)", - org.eclipse.jgit.nls;version="[4.8.1,4.9.0)", - org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)", - org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)", - org.eclipse.jgit.transport;version="[4.8.1,4.9.0)", - org.eclipse.jgit.transport.http;version="[4.8.1,4.9.0)", - org.eclipse.jgit.transport.http.apache;version="[4.8.1,4.9.0)", - org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)", - org.eclipse.jgit.util;version="[4.8.1,4.9.0)", + org.eclipse.jgit.errors;version="[4.9.10,4.10.0)", + org.eclipse.jgit.http.server;version="[4.9.10,4.10.0)", + org.eclipse.jgit.http.server.glue;version="[4.9.10,4.10.0)", + org.eclipse.jgit.http.server.resolver;version="[4.9.10,4.10.0)", + org.eclipse.jgit.internal;version="[4.9.10,4.10.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.9.10,4.10.0)", + org.eclipse.jgit.internal.storage.file;version="[4.9.10,4.10.0)", + org.eclipse.jgit.junit;version="[4.9.10,4.10.0)", + org.eclipse.jgit.junit.http;version="[4.9.10,4.10.0)", + org.eclipse.jgit.lib;version="[4.9.10,4.10.0)", + org.eclipse.jgit.nls;version="[4.9.10,4.10.0)", + org.eclipse.jgit.revwalk;version="[4.9.10,4.10.0)", + org.eclipse.jgit.storage.file;version="[4.9.10,4.10.0)", + org.eclipse.jgit.transport;version="[4.9.10,4.10.0)", + org.eclipse.jgit.transport.http;version="[4.9.10,4.10.0)", + org.eclipse.jgit.transport.http.apache;version="[4.9.10,4.10.0)", + org.eclipse.jgit.transport.resolver;version="[4.9.10,4.10.0)", + org.eclipse.jgit.util;version="[4.9.10,4.10.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.0.0,5.0.0)", + org.junit.rules;version="[4.0.0,5.0.0)", org.junit.runner;version="[4.0.0,5.0.0)", org.junit.runners;version="[4.0.0,5.0.0)" +Require-Bundle: org.hamcrest.library;bundle-version="[1.1.0,2.0.0)" diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 1538601cce..c76368153c 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.8.1-SNAPSHOT</version> + <version>4.9.10-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.test</artifactId> @@ -71,6 +71,13 @@ </dependency> <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <scope>test</scope> + <version>[1.1.0,2.0.0)</version> + </dependency> + + <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit</artifactId> <version>${project.version}</version> @@ -87,14 +94,12 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.junit.http</artifactId> <version>${project.version}</version> - <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.junit</artifactId> <version>${project.version}</version> - <scope>test</scope> </dependency> <dependency> @@ -107,7 +112,6 @@ <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> - <scope>test</scope> </dependency> </dependencies> diff --git a/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/TestRepositoryResolver.java b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/TestRepositoryResolver.java new file mode 100644 index 0000000000..334e57c6d1 --- /dev/null +++ b/org.eclipse.jgit.http.test/src/org/eclipse/jgit/http/test/TestRepositoryResolver.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016, 2017 Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.http.test; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.resolver.RepositoryResolver; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; + +/** A simple repository resolver for tests. */ +public final class TestRepositoryResolver + implements RepositoryResolver<HttpServletRequest> { + + private final TestRepository<Repository> repo; + + private final String repoName; + + /** + * Creates a new {@link TestRepositoryResolver} that resolves the given name to + * the given repository. + * + * @param repo + * to resolve to + * @param repoName + * to match + */ + public TestRepositoryResolver(TestRepository<Repository> repo, String repoName) { + this.repo = repo; + this.repoName = repoName; + } + + @Override + public Repository open(HttpServletRequest req, String name) + throws RepositoryNotFoundException, ServiceNotEnabledException { + if (!name.equals(repoName)) { + throw new RepositoryNotFoundException(name); + } + Repository db = repo.getRepository(); + db.incrementOpen(); + return db; + } +} diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java index 06bfd7988b..727f9bab00 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Google Inc. + * Copyright (C) 2010, 2017 Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -60,12 +60,9 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; - import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jgit.errors.NotSupportedException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.http.server.GitServlet; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.http.AccessEvent; @@ -84,8 +81,6 @@ import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.http.HttpConnectionFactory; import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; -import org.eclipse.jgit.transport.resolver.RepositoryResolver; -import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -124,19 +119,7 @@ public class DumbClientSmartServerTest extends HttpTestCase { ServletContextHandler app = server.addContext("/git"); GitServlet gs = new GitServlet(); - gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() { - @Override - public Repository open(HttpServletRequest req, String name) - throws RepositoryNotFoundException, - ServiceNotEnabledException { - if (!name.equals(srcName)) - throw new RepositoryNotFoundException(name); - - final Repository db = src.getRepository(); - db.incrementOpen(); - return db; - } - }); + gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName)); app.addServlet(new ServletHolder(gs), "/*"); server.setUp(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java new file mode 100644 index 0000000000..7deb0d85a0 --- /dev/null +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerSslTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; + +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.errors.UnsupportedCredentialItem; +import org.eclipse.jgit.http.server.GitServlet; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.junit.http.AccessEvent; +import org.eclipse.jgit.junit.http.AppServer; +import org.eclipse.jgit.junit.http.HttpTestCase; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevBlob; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.HttpTransport; +import org.eclipse.jgit.transport.Transport; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; +import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; +import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; +import org.eclipse.jgit.util.HttpSupport; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SmartClientSmartServerSslTest extends HttpTestCase { + + // We run these tests with a server on localhost with a self-signed + // certificate. We don't do authentication tests here, so there's no need + // for username and password. + // + // But the server certificate will not validate. We know that Transport will + // ask whether we trust the server all the same. This credentials provider + // blindly trusts the self-signed certificate by answering "Yes" to all + // questions. + private CredentialsProvider testCredentials = new CredentialsProvider() { + + @Override + public boolean isInteractive() { + return false; + } + + @Override + public boolean supports(CredentialItem... items) { + for (CredentialItem item : items) { + if (item instanceof CredentialItem.InformationalMessage) { + continue; + } + if (item instanceof CredentialItem.YesNoType) { + continue; + } + return false; + } + return true; + } + + @Override + public boolean get(URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + for (CredentialItem item : items) { + if (item instanceof CredentialItem.InformationalMessage) { + continue; + } + if (item instanceof CredentialItem.YesNoType) { + ((CredentialItem.YesNoType) item).setValue(true); + continue; + } + return false; + } + return true; + } + }; + + private URIish remoteURI; + + private URIish secureURI; + + private RevBlob A_txt; + + private RevCommit A, B; + + @Parameters + public static Collection<Object[]> data() { + // run all tests with both connection factories we have + return Arrays.asList(new Object[][] { + { new JDKHttpConnectionFactory() }, + { new HttpClientConnectionFactory() } }); + } + + public SmartClientSmartServerSslTest(HttpConnectionFactory cf) { + HttpTransport.setConnectionFactory(cf); + } + + @Override + protected AppServer createServer() { + return new AppServer(0, 0); + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + final TestRepository<Repository> src = createTestRepository(); + final String srcName = src.getRepository().getDirectory().getName(); + src.getRepository() + .getConfig() + .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); + + GitServlet gs = new GitServlet(); + + ServletContextHandler app = addNormalContext(gs, src, srcName); + + server.setUp(); + + remoteURI = toURIish(app, srcName); + secureURI = new URIish(rewriteUrl(remoteURI.toString(), "https", + server.getSecurePort())); + + A_txt = src.blob("A"); + A = src.commit().add("A_txt", A_txt).create(); + B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create(); + src.update(master, B); + + src.update("refs/garbage/a/very/long/ref/name/to/compress", B); + } + + private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) { + ServletContextHandler app = server.addContext("/git"); + app.addFilter(new FilterHolder(new Filter() { + + @Override + public void init(FilterConfig filterConfig) + throws ServletException { + // empty + } + + // Redirects http to https for requests containing "/https/". + @Override + public void doFilter(ServletRequest request, + ServletResponse response, FilterChain chain) + throws IOException, ServletException { + final HttpServletResponse httpServletResponse = (HttpServletResponse) response; + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + final StringBuffer fullUrl = httpServletRequest.getRequestURL(); + if (httpServletRequest.getQueryString() != null) { + fullUrl.append("?") + .append(httpServletRequest.getQueryString()); + } + String urlString = rewriteUrl(fullUrl.toString(), "https", + server.getSecurePort()); + httpServletResponse + .setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, + urlString.replace("/https/", "/")); + } + + @Override + public void destroy() { + // empty + } + }), "/https/*", EnumSet.of(DispatcherType.REQUEST)); + app.addFilter(new FilterHolder(new Filter() { + + @Override + public void init(FilterConfig filterConfig) + throws ServletException { + // empty + } + + // Redirects https back to http for requests containing "/back/". + @Override + public void doFilter(ServletRequest request, + ServletResponse response, FilterChain chain) + throws IOException, ServletException { + final HttpServletResponse httpServletResponse = (HttpServletResponse) response; + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + final StringBuffer fullUrl = httpServletRequest.getRequestURL(); + if (httpServletRequest.getQueryString() != null) { + fullUrl.append("?") + .append(httpServletRequest.getQueryString()); + } + String urlString = rewriteUrl(fullUrl.toString(), "http", + server.getPort()); + httpServletResponse + .setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, + urlString.replace("/back/", "/")); + } + + @Override + public void destroy() { + // empty + } + }), "/back/*", EnumSet.of(DispatcherType.REQUEST)); + gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName)); + app.addServlet(new ServletHolder(gs), "/*"); + return app; + } + + @Test + public void testInitialClone_ViaHttps() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, secureURI)) { + t.setCredentialsProvider(testCredentials); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(2, requests.size()); + } + + @Test + public void testInitialClone_RedirectToHttps() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(remoteURI, "/https"); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.setCredentialsProvider(testCredentials); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(3, requests.size()); + } + + @Test + public void testInitialClone_RedirectBackToHttp() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(secureURI, "/back"); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.setCredentialsProvider(testCredentials); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (redirect from https to http)"); + } catch (TransportException e) { + assertTrue(e.getMessage().contains("not allowed")); + } + } + + @Test + public void testInitialClone_SslFailure() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, secureURI)) { + // Set a credentials provider that doesn't handle questions + t.setCredentialsProvider( + new UsernamePasswordCredentialsProvider("any", "anypwd")); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (SSL certificate not trusted)"); + } catch (TransportException e) { + assertTrue(e.getMessage().contains("Secure connection")); + } + } + +} diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index ed223c96ef..caa172e138 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Google Inc. + * Copyright (C) 2010, 2017 Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -57,17 +57,21 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; +import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -78,9 +82,10 @@ import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jgit.errors.RemoteRepositoryException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.http.server.GitServlet; +import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.junit.TestRepository; @@ -101,20 +106,35 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook; +import org.eclipse.jgit.transport.AdvertiseRefsHook; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.FetchConnection; import org.eclipse.jgit.transport.HttpTransport; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.TransportHttp; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.UploadPack; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.transport.http.HttpConnectionFactory; import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; -import org.eclipse.jgit.transport.resolver.RepositoryResolver; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import org.eclipse.jgit.transport.resolver.UploadPackFactory; +import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.HttpSupport; +import org.eclipse.jgit.util.SystemReader; +import org.hamcrest.Matchers; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -123,17 +143,29 @@ import org.junit.runners.Parameterized.Parameters; public class SmartClientSmartServerTest extends HttpTestCase { private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding"; + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private AdvertiseRefsHook advertiseRefsHook; + private Repository remoteRepository; + private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider( + AppServer.username, AppServer.password); + private URIish remoteURI; private URIish brokenURI; private URIish redirectURI; + private URIish authURI; + + private URIish authOnPostURI; + private RevBlob A_txt; - private RevCommit A, B; + private RevCommit A, B, unreachableCommit; @Parameters public static Collection<Object[]> data() { @@ -160,12 +192,29 @@ public class SmartClientSmartServerTest extends HttpTestCase { ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); GitServlet gs = new GitServlet(); + gs.setUploadPackFactory(new UploadPackFactory<HttpServletRequest>() { + @Override + public UploadPack create(HttpServletRequest req, Repository db) + throws ServiceNotEnabledException, + ServiceNotAuthorizedException { + DefaultUploadPackFactory f = new DefaultUploadPackFactory(); + UploadPack up = f.create(req, db); + if (advertiseRefsHook != null) { + up.setAdvertiseRefsHook(advertiseRefsHook); + } + return up; + } + }); ServletContextHandler app = addNormalContext(gs, src, srcName); ServletContextHandler broken = addBrokenContext(gs, src, srcName); - ServletContextHandler redirect = addRedirectContext(gs, src, srcName); + ServletContextHandler redirect = addRedirectContext(gs); + + ServletContextHandler auth = addAuthContext(gs, "auth"); + + ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST"); server.setUp(); @@ -173,18 +222,67 @@ public class SmartClientSmartServerTest extends HttpTestCase { remoteURI = toURIish(app, srcName); brokenURI = toURIish(broken, srcName); redirectURI = toURIish(redirect, srcName); + authURI = toURIish(auth, srcName); + authOnPostURI = toURIish(authOnPost, srcName); A_txt = src.blob("A"); A = src.commit().add("A_txt", A_txt).create(); B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create(); src.update(master, B); + unreachableCommit = src.commit().add("A_txt", A_txt).create(); + src.update("refs/garbage/a/very/long/ref/name/to/compress", B); } private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) { ServletContextHandler app = server.addContext("/git"); - gs.setRepositoryResolver(new TestRepoResolver(src, srcName)); + app.addFilter(new FilterHolder(new Filter() { + + @Override + public void init(FilterConfig filterConfig) + throws ServletException { + // empty + } + + // Does an internal forward for GET requests containing "/post/", + // and issues a 301 redirect on POST requests for such URLs. Used + // in the POST redirect tests. + @Override + public void doFilter(ServletRequest request, + ServletResponse response, FilterChain chain) + throws IOException, ServletException { + final HttpServletResponse httpServletResponse = (HttpServletResponse) response; + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + final StringBuffer fullUrl = httpServletRequest.getRequestURL(); + if (httpServletRequest.getQueryString() != null) { + fullUrl.append("?") + .append(httpServletRequest.getQueryString()); + } + String urlString = fullUrl.toString(); + if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) { + httpServletResponse.setStatus( + HttpServletResponse.SC_MOVED_PERMANENTLY); + httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, + urlString.replace("/post/", "/")); + } else { + String path = httpServletRequest.getPathInfo(); + path = path.replace("/post/", "/"); + if (httpServletRequest.getQueryString() != null) { + path += '?' + httpServletRequest.getQueryString(); + } + RequestDispatcher dispatcher = httpServletRequest + .getRequestDispatcher(path); + dispatcher.forward(httpServletRequest, httpServletResponse); + } + } + + @Override + public void destroy() { + // empty + } + }), "/post/*", EnumSet.of(DispatcherType.REQUEST)); + gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName)); app.addServlet(new ServletHolder(gs), "/*"); return app; } @@ -222,12 +320,28 @@ public class SmartClientSmartServerTest extends HttpTestCase { return broken; } - @SuppressWarnings("unused") - private ServletContextHandler addRedirectContext(GitServlet gs, - TestRepository<Repository> src, String srcName) { + private ServletContextHandler addAuthContext(GitServlet gs, + String contextPath, String... methods) { + ServletContextHandler auth = server.addContext('/' + contextPath); + auth.addServlet(new ServletHolder(gs), "/*"); + return server.authBasic(auth, methods); + } + + private ServletContextHandler addRedirectContext(GitServlet gs) { ServletContextHandler redirect = server.addContext("/redirect"); redirect.addFilter(new FilterHolder(new Filter() { + // Enables tests for different codes, and for multiple redirects. + // First parameter is the number of redirects, second one is the + // redirect status code that should be used + private Pattern responsePattern = Pattern + .compile("/response/(\\d+)/(30[1237])/"); + + // Enables tests to specify the context that the request should be + // redirected to in the end. If not present, redirects got to the + // normal /git context. + private Pattern targetPattern = Pattern.compile("/target(/\\w+)/"); + @Override public void init(FilterConfig filterConfig) throws ServletException { @@ -245,10 +359,50 @@ public class SmartClientSmartServerTest extends HttpTestCase { fullUrl.append("?") .append(httpServletRequest.getQueryString()); } - httpServletResponse - .setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + String urlString = fullUrl.toString(); + if (urlString.contains("/loop/")) { + urlString = urlString.replace("/loop/", "/loop/x/"); + if (urlString.contains("/loop/x/x/x/x/x/x/x/x/")) { + // Go back to initial. + urlString = urlString.replace("/loop/x/x/x/x/x/x/x/x/", + "/loop/"); + } + httpServletResponse.setStatus( + HttpServletResponse.SC_MOVED_TEMPORARILY); + httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, + urlString); + return; + } + int responseCode = HttpServletResponse.SC_MOVED_PERMANENTLY; + int nofRedirects = 0; + Matcher matcher = responsePattern.matcher(urlString); + if (matcher.find()) { + nofRedirects = Integer + .parseUnsignedInt(matcher.group(1)); + responseCode = Integer.parseUnsignedInt(matcher.group(2)); + if (--nofRedirects <= 0) { + urlString = urlString.substring(0, matcher.start()) + + '/' + urlString.substring(matcher.end()); + } else { + urlString = urlString.substring(0, matcher.start()) + + "/response/" + nofRedirects + "/" + + responseCode + '/' + + urlString.substring(matcher.end()); + } + } + httpServletResponse.setStatus(responseCode); + if (nofRedirects <= 0) { + String targetContext = "/git"; + matcher = targetPattern.matcher(urlString); + if (matcher.find()) { + urlString = urlString.substring(0, matcher.start()) + + '/' + urlString.substring(matcher.end()); + targetContext = matcher.group(1); + } + urlString = urlString.replace("/redirect", targetContext); + } httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, - fullUrl.toString().replace("/redirect", "/git")); + urlString); } @Override @@ -334,6 +488,56 @@ public class SmartClientSmartServerTest extends HttpTestCase { } @Test + public void testFetchBySHA1() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, remoteURI)) { + t.fetch(NullProgressMonitor.INSTANCE, + Collections.singletonList(new RefSpec(B.name()))); + } + + assertTrue(dst.hasObject(A_txt)); + } + + @Test + public void testFetchBySHA1Unreachable() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, remoteURI)) { + thrown.expect(TransportException.class); + thrown.expectMessage(Matchers.containsString( + "want " + unreachableCommit.name() + " not valid")); + t.fetch(NullProgressMonitor.INSTANCE, Collections + .singletonList(new RefSpec(unreachableCommit.name()))); + } + } + + @Test + public void testFetchBySHA1UnreachableByAdvertiseRefsHook() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + advertiseRefsHook = new AbstractAdvertiseRefsHook() { + @Override + protected Map<String, Ref> getAdvertisedRefs(Repository repository, + RevWalk revWalk) { + return Collections.emptyMap(); + } + }; + + try (Transport t = Transport.open(dst, remoteURI)) { + thrown.expect(TransportException.class); + thrown.expectMessage(Matchers.containsString( + "want " + A.name() + " not valid")); + t.fetch(NullProgressMonitor.INSTANCE, Collections + .singletonList(new RefSpec(A.name()))); + } + } + + @Test public void testInitialClone_Small() throws Exception { Repository dst = createBareRepository(); assertFalse(dst.hasObject(A_txt)); @@ -373,13 +577,332 @@ public class SmartClientSmartServerTest extends HttpTestCase { .getResponseHeader(HDR_CONTENT_TYPE)); } + private void initialClone_Redirect(int nofRedirects, int code) + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = redirectURI; + if (code != 301 || nofRedirects > 1) { + cloneFrom = extendPath(cloneFrom, + "/response/" + nofRedirects + "/" + code); + } + try (Transport t = Transport.open(dst, cloneFrom)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(2 + nofRedirects, requests.size()); + + int n = 0; + while (n < nofRedirects) { + AccessEvent redirect = requests.get(n++); + assertEquals(code, redirect.getStatus()); + } + + AccessEvent info = requests.get(n++); + assertEquals("GET", info.getMethod()); + assertEquals(join(remoteURI, "info/refs"), info.getPath()); + assertEquals(1, info.getParameters().size()); + assertEquals("git-upload-pack", info.getParameter("service")); + assertEquals(200, info.getStatus()); + assertEquals("application/x-git-upload-pack-advertisement", + info.getResponseHeader(HDR_CONTENT_TYPE)); + assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING)); + + AccessEvent service = requests.get(n++); + assertEquals("POST", service.getMethod()); + assertEquals(join(remoteURI, "git-upload-pack"), service.getPath()); + assertEquals(0, service.getParameters().size()); + assertNotNull("has content-length", + service.getRequestHeader(HDR_CONTENT_LENGTH)); + assertNull("not chunked", + service.getRequestHeader(HDR_TRANSFER_ENCODING)); + + assertEquals(200, service.getStatus()); + assertEquals("application/x-git-upload-pack-result", + service.getResponseHeader(HDR_CONTENT_TYPE)); + } + + @Test + public void testInitialClone_Redirect301Small() throws Exception { + initialClone_Redirect(1, 301); + } + + @Test + public void testInitialClone_Redirect302Small() throws Exception { + initialClone_Redirect(1, 302); + } + + @Test + public void testInitialClone_Redirect303Small() throws Exception { + initialClone_Redirect(1, 303); + } + + @Test + public void testInitialClone_Redirect307Small() throws Exception { + initialClone_Redirect(1, 307); + } + + @Test + public void testInitialClone_RedirectMultiple() throws Exception { + initialClone_Redirect(4, 302); + } + + @Test + public void testInitialClone_RedirectMax() throws Exception { + FileBasedConfig userConfig = SystemReader.getInstance() + .openUserConfig(null, FS.DETECTED); + userConfig.setInt("http", null, "maxRedirects", 4); + userConfig.save(); + initialClone_Redirect(4, 302); + } + + @Test + public void testInitialClone_RedirectTooOften() throws Exception { + FileBasedConfig userConfig = SystemReader.getInstance() + .openUserConfig(null, FS.DETECTED); + userConfig.setInt("http", null, "maxRedirects", 3); + userConfig.save(); + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(redirectURI, "/response/4/302"); + String remoteUri = cloneFrom.toString(); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (too many redirects)"); + } catch (TransportException e) { + String expectedMessageBegin = remoteUri.toString() + ": " + + MessageFormat.format(JGitText.get().redirectLimitExceeded, + "3", remoteUri.replace("/4/", "/1/") + '/', ""); + String message = e.getMessage(); + if (message.length() > expectedMessageBegin.length()) { + message = message.substring(0, expectedMessageBegin.length()); + } + assertEquals(expectedMessageBegin, message); + } + } + + @Test + public void testInitialClone_RedirectLoop() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(redirectURI, "/loop"); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (redirect loop)"); + } catch (TransportException e) { + assertTrue(e.getMessage().contains("Redirected more than")); + } + } + @Test - public void testInitialClone_RedirectSmall() throws Exception { + public void testInitialClone_RedirectOnPostAllowed() throws Exception { + FileBasedConfig userConfig = SystemReader.getInstance() + .openUserConfig(null, FS.DETECTED); + userConfig.setString("http", null, "followRedirects", "true"); + userConfig.save(); + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(remoteURI, "/post"); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(3, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(join(cloneFrom, "info/refs"), info.getPath()); + assertEquals(1, info.getParameters().size()); + assertEquals("git-upload-pack", info.getParameter("service")); + assertEquals(200, info.getStatus()); + assertEquals("application/x-git-upload-pack-advertisement", + info.getResponseHeader(HDR_CONTENT_TYPE)); + assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING)); + + AccessEvent redirect = requests.get(1); + assertEquals("POST", redirect.getMethod()); + assertEquals(301, redirect.getStatus()); + + AccessEvent service = requests.get(2); + assertEquals("POST", service.getMethod()); + assertEquals(join(remoteURI, "git-upload-pack"), service.getPath()); + assertEquals(0, service.getParameters().size()); + assertNotNull("has content-length", + service.getRequestHeader(HDR_CONTENT_LENGTH)); + assertNull("not chunked", + service.getRequestHeader(HDR_TRANSFER_ENCODING)); + + assertEquals(200, service.getStatus()); + assertEquals("application/x-git-upload-pack-result", + service.getResponseHeader(HDR_CONTENT_TYPE)); + } + + @Test + public void testInitialClone_RedirectOnPostForbidden() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(remoteURI, "/post"); + try (Transport t = Transport.open(dst, cloneFrom)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (redirect on POST)"); + } catch (TransportException e) { + assertTrue(e.getMessage().contains("301")); + } + } + + @Test + public void testInitialClone_RedirectForbidden() throws Exception { + FileBasedConfig userConfig = SystemReader.getInstance() + .openUserConfig(null, FS.DETECTED); + userConfig.setString("http", null, "followRedirects", "false"); + userConfig.save(); + Repository dst = createBareRepository(); assertFalse(dst.hasObject(A_txt)); try (Transport t = Transport.open(dst, redirectURI)) { t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should have failed (redirects forbidden)"); + } catch (TransportException e) { + assertTrue( + e.getMessage().contains("http.followRedirects is false")); + } + } + + @Test + public void testInitialClone_WithAuthentication() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.setCredentialsProvider(testCredentials); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(3, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(401, info.getStatus()); + + info = requests.get(1); + assertEquals("GET", info.getMethod()); + assertEquals(join(authURI, "info/refs"), info.getPath()); + assertEquals(1, info.getParameters().size()); + assertEquals("git-upload-pack", info.getParameter("service")); + assertEquals(200, info.getStatus()); + assertEquals("application/x-git-upload-pack-advertisement", + info.getResponseHeader(HDR_CONTENT_TYPE)); + assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING)); + + AccessEvent service = requests.get(2); + assertEquals("POST", service.getMethod()); + assertEquals(join(authURI, "git-upload-pack"), service.getPath()); + assertEquals(0, service.getParameters().size()); + assertNotNull("has content-length", + service.getRequestHeader(HDR_CONTENT_LENGTH)); + assertNull("not chunked", + service.getRequestHeader(HDR_TRANSFER_ENCODING)); + + assertEquals(200, service.getStatus()); + assertEquals("application/x-git-upload-pack-result", + service.getResponseHeader(HDR_CONTENT_TYPE)); + } + + @Test + public void testInitialClone_WithAuthenticationNoCredentials() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should not have succeeded -- no authentication"); + } catch (TransportException e) { + String msg = e.getMessage(); + assertTrue("Unexpected exception message: " + msg, + msg.contains("no CredentialsProvider")); + } + List<AccessEvent> requests = getRequests(); + assertEquals(1, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(401, info.getStatus()); + } + + @Test + public void testInitialClone_WithAuthenticationWrongCredentials() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.setCredentialsProvider(new UsernamePasswordCredentialsProvider( + AppServer.username, "wrongpassword")); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should not have succeeded -- wrong password"); + } catch (TransportException e) { + String msg = e.getMessage(); + assertTrue("Unexpected exception message: " + msg, + msg.contains("auth")); + } + List<AccessEvent> requests = getRequests(); + // Once without authentication plus three re-tries with authentication + assertEquals(4, requests.size()); + + for (AccessEvent event : requests) { + assertEquals("GET", event.getMethod()); + assertEquals(401, event.getStatus()); + } + } + + @Test + public void testInitialClone_WithAuthenticationAfterRedirect() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(redirectURI, "/target/auth"); + CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider( + "unknown", "none") { + @Override + public boolean get(URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + // Only return the true credentials if the uri path starts with + // /auth. This ensures that we do provide the correct + // credentials only for the URi after the redirect, making the + // test fail if we should be asked for the credentials for the + // original URI. + if (uri.getPath().startsWith("/auth")) { + return testCredentials.get(uri, items); + } + return super.get(uri, items); + } + }; + try (Transport t = Transport.open(dst, cloneFrom)) { + t.setCredentialsProvider(uriSpecificCredentialsProvider); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); } assertTrue(dst.hasObject(A_txt)); @@ -389,12 +912,19 @@ public class SmartClientSmartServerTest extends HttpTestCase { List<AccessEvent> requests = getRequests(); assertEquals(4, requests.size()); - AccessEvent firstRedirect = requests.get(0); - assertEquals(301, firstRedirect.getStatus()); + AccessEvent redirect = requests.get(0); + assertEquals("GET", redirect.getMethod()); + assertEquals(join(cloneFrom, "info/refs"), redirect.getPath()); + assertEquals(301, redirect.getStatus()); AccessEvent info = requests.get(1); assertEquals("GET", info.getMethod()); - assertEquals(join(remoteURI, "info/refs"), info.getPath()); + assertEquals(join(authURI, "info/refs"), info.getPath()); + assertEquals(401, info.getStatus()); + + info = requests.get(2); + assertEquals("GET", info.getMethod()); + assertEquals(join(authURI, "info/refs"), info.getPath()); assertEquals(1, info.getParameters().size()); assertEquals("git-upload-pack", info.getParameter("service")); assertEquals(200, info.getStatus()); @@ -402,12 +932,56 @@ public class SmartClientSmartServerTest extends HttpTestCase { info.getResponseHeader(HDR_CONTENT_TYPE)); assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING)); - AccessEvent secondRedirect = requests.get(2); - assertEquals(301, secondRedirect.getStatus()); - AccessEvent service = requests.get(3); assertEquals("POST", service.getMethod()); - assertEquals(join(remoteURI, "git-upload-pack"), service.getPath()); + assertEquals(join(authURI, "git-upload-pack"), service.getPath()); + assertEquals(0, service.getParameters().size()); + assertNotNull("has content-length", + service.getRequestHeader(HDR_CONTENT_LENGTH)); + assertNull("not chunked", + service.getRequestHeader(HDR_TRANSFER_ENCODING)); + + assertEquals(200, service.getStatus()); + assertEquals("application/x-git-upload-pack-result", + service.getResponseHeader(HDR_CONTENT_TYPE)); + } + + @Test + public void testInitialClone_WithAuthenticationOnPostOnly() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authOnPostURI)) { + t.setCredentialsProvider(testCredentials); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } + + assertTrue(dst.hasObject(A_txt)); + assertEquals(B, dst.exactRef(master).getObjectId()); + fsck(dst, B); + + List<AccessEvent> requests = getRequests(); + assertEquals(3, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(join(authOnPostURI, "info/refs"), info.getPath()); + assertEquals(1, info.getParameters().size()); + assertEquals("git-upload-pack", info.getParameter("service")); + assertEquals(200, info.getStatus()); + assertEquals("application/x-git-upload-pack-advertisement", + info.getResponseHeader(HDR_CONTENT_TYPE)); + assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING)); + + AccessEvent service = requests.get(1); + assertEquals("POST", service.getMethod()); + assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath()); + assertEquals(401, service.getStatus()); + + service = requests.get(2); + assertEquals("POST", service.getMethod()); + assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath()); assertEquals(0, service.getParameters().size()); assertNotNull("has content-length", service.getRequestHeader(HDR_CONTENT_LENGTH)); @@ -619,7 +1193,7 @@ public class SmartClientSmartServerTest extends HttpTestCase { ServletContextHandler app = noRefServer.addContext("/git"); GitServlet gs = new GitServlet(); - gs.setRepositoryResolver(new TestRepoResolver(repo, repoName)); + gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName)); app.addServlet(new ServletHolder(gs), "/*"); noRefServer.setUp(); @@ -822,28 +1396,4 @@ public class SmartClientSmartServerTest extends HttpTestCase { cfg.save(); } - private final class TestRepoResolver - implements RepositoryResolver<HttpServletRequest> { - - private final TestRepository<Repository> repo; - - private final String repoName; - - private TestRepoResolver(TestRepository<Repository> repo, - String repoName) { - this.repo = repo; - this.repoName = repoName; - } - - @Override - public Repository open(HttpServletRequest req, String name) - throws RepositoryNotFoundException, ServiceNotEnabledException { - if (!name.equals(repoName)) - throw new RepositoryNotFoundException(name); - - Repository db = repo.getRepository(); - db.incrementOpen(); - return db; - } - } } |