summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.http.test
diff options
context:
space:
mode:
authorShawn O. Pearce <spearce@spearce.org>2010-01-12 12:30:42 -0800
committerShawn O. Pearce <spearce@spearce.org>2010-01-12 12:30:42 -0800
commitf5eb0d93660786213b98dadde7d93c5605454495 (patch)
tree4730c52648d239fc45e483f388be1bd1e874d106 /org.eclipse.jgit.http.test
parentd5bc8be743a6308509f36a2bd2f9414030eea9ea (diff)
downloadjgit-f5eb0d93660786213b98dadde7d93c5605454495.tar.gz
jgit-f5eb0d93660786213b98dadde7d93c5605454495.zip
Add JUnit tests for HTTP transport
No Eclipse support for this project is provided, because the Jetty project does not publish a complete P2 repository. Change-Id: Ic5fe2e79bb216e36920fd4a70ec15dd6ccfd1468 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.jgit.http.test')
-rw-r--r--org.eclipse.jgit.http.test/.gitignore1
-rw-r--r--org.eclipse.jgit.http.test/pom.xml108
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java136
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java198
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java160
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java246
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java261
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ErrorServletTest.java85
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java137
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletInitTest.java119
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java328
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java556
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AccessEvent.java181
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AppServer.java295
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java161
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/MockServletConfig.java85
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/RecordingLogger.java146
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/TestRequestLog.java71
18 files changed, 3274 insertions, 0 deletions
diff --git a/org.eclipse.jgit.http.test/.gitignore b/org.eclipse.jgit.http.test/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/org.eclipse.jgit.http.test/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
new file mode 100644
index 0000000000..fe9363a3ed
--- /dev/null
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2009-2010, Google Inc.
+ Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
+ 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit-parent</artifactId>
+ <version>0.6.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.eclipse.jgit.http.test</artifactId>
+ <name>JGit - HTTP Tests</name>
+
+ <description>
+ Tests for the HTTP transport.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</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>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.http.server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <testSourceDirectory>tst/</testSourceDirectory>
+
+ <testResources>
+ <testResource>
+ <directory>tst-rsrc/</directory>
+ </testResource>
+ </testResources>
+ </build>
+</project>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
new file mode 100644
index 0000000000..ea937481d4
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009-2010, 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.HttpServletRequestWrapper;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jgit.http.server.resolver.AsIsFileService;
+import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+
+public class AsIsServiceTest extends LocalDiskRepositoryTestCase {
+ private Repository db;
+
+ private AsIsFileService service;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ db = createBareRepository();
+ service = new AsIsFileService();
+ }
+
+ public void testDisabledSingleton() throws ServiceNotAuthorizedException {
+ service = AsIsFileService.DISABLED;
+ try {
+ service.access(new R(null, "1.2.3.4"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ service.access(new R("bob", "1.2.3.4"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_Default() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ service.access(new R(null, "1.2.3.4"), db);
+ service.access(new R("bob", "1.2.3.4"), db);
+ }
+
+ public void testCreate_Disabled() throws ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "getanyfile", false);
+
+ try {
+ service.access(new R(null, "1.2.3.4"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ service.access(new R("bob", "1.2.3.4"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_Enabled() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "getanyfile", true);
+ service.access(new R(null, "1.2.3.4"), db);
+ service.access(new R("bob", "1.2.3.4"), db);
+ }
+
+ private final class R extends HttpServletRequestWrapper {
+ private final String user;
+
+ private final String host;
+
+ R(final String user, final String host) {
+ super(new Request() /* can't pass null, sigh */);
+ this.user = user;
+ this.host = host;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return host;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return user;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
new file mode 100644
index 0000000000..5338caaa48
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2009-2010, 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.HttpServletRequestWrapper;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
+import org.eclipse.jgit.http.server.resolver.ReceivePackFactory;
+import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceivePack;
+
+public class DefaultReceivePackFactoryTest extends LocalDiskRepositoryTestCase {
+ private Repository db;
+
+ private ReceivePackFactory factory;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ db = createBareRepository();
+ factory = new DefaultReceivePackFactory();
+ }
+
+ public void testDisabledSingleton() throws ServiceNotAuthorizedException {
+ factory = ReceivePackFactory.DISABLED;
+
+ try {
+ factory.create(new R(null, "localhost"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("", "localhost"), db);
+ fail("Created session for anonymous user: \"\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("bob", "localhost"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_NullUser() throws ServiceNotEnabledException {
+ try {
+ factory.create(new R(null, "localhost"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotAuthorizedException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_EmptyStringUser() throws ServiceNotEnabledException {
+ try {
+ factory.create(new R("", "localhost"), db);
+ fail("Created session for anonymous user: \"\"");
+ } catch (ServiceNotAuthorizedException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_AuthUser() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ ReceivePack rp;
+ rp = factory.create(new R("bob", "1.2.3.4"), db);
+ assertNotNull("have ReceivePack", rp);
+ assertSame(db, rp.getRepository());
+
+ PersonIdent id = rp.getRefLogIdent();
+ assertNotNull(id);
+ assertEquals("bob", id.getName());
+ assertEquals("bob@1.2.3.4", id.getEmailAddress());
+
+ // Should have inherited off the current system, which is mocked
+ assertEquals(author.getTimeZoneOffset(), id.getTimeZoneOffset());
+ assertEquals(author.getWhen(), id.getWhen());
+ }
+
+ public void testCreate_Disabled() throws ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "receivepack", false);
+
+ try {
+ factory.create(new R(null, "localhost"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("", "localhost"), db);
+ fail("Created session for anonymous user: \"\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("bob", "localhost"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_Enabled() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "receivepack", true);
+ ReceivePack rp;
+
+ rp = factory.create(new R(null, "1.2.3.4"), db);
+ assertNotNull("have ReceivePack", rp);
+ assertSame(db, rp.getRepository());
+
+ PersonIdent id = rp.getRefLogIdent();
+ assertNotNull(id);
+ assertEquals("anonymous", id.getName());
+ assertEquals("anonymous@1.2.3.4", id.getEmailAddress());
+
+ // Should have inherited off the current system, which is mocked
+ assertEquals(author.getTimeZoneOffset(), id.getTimeZoneOffset());
+ assertEquals(author.getWhen(), id.getWhen());
+
+ rp = factory.create(new R("bob", "1.2.3.4"), db);
+ assertNotNull("have ReceivePack", rp);
+ }
+
+ private final class R extends HttpServletRequestWrapper {
+ private final String user;
+
+ private final String host;
+
+ R(final String user, final String host) {
+ super(new Request() /* can't pass null, sigh */);
+ this.user = user;
+ this.host = host;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return host;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return user;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
new file mode 100644
index 0000000000..8f57d40afe
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2009-2010, 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.HttpServletRequestWrapper;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
+import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.http.server.resolver.UploadPackFactory;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.UploadPack;
+
+public class DefaultUploadPackFactoryTest extends LocalDiskRepositoryTestCase {
+ private Repository db;
+
+ private UploadPackFactory factory;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ db = createBareRepository();
+ factory = new DefaultUploadPackFactory();
+ }
+
+ public void testDisabledSingleton() throws ServiceNotAuthorizedException {
+ factory = UploadPackFactory.DISABLED;
+
+ try {
+ factory.create(new R(null, "localhost"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("", "localhost"), db);
+ fail("Created session for anonymous user: \"\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("bob", "localhost"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_Default() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ UploadPack up;
+
+ up = factory.create(new R(null, "1.2.3.4"), db);
+ assertNotNull("have UploadPack", up);
+ assertSame(db, up.getRepository());
+
+ up = factory.create(new R("bob", "1.2.3.4"), db);
+ assertNotNull("have UploadPack", up);
+ assertSame(db, up.getRepository());
+ }
+
+ public void testCreate_Disabled() throws ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "uploadpack", false);
+
+ try {
+ factory.create(new R(null, "localhost"), db);
+ fail("Created session for anonymous user: null");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+
+ try {
+ factory.create(new R("bob", "localhost"), db);
+ fail("Created session for user: \"bob\"");
+ } catch (ServiceNotEnabledException e) {
+ // expected not authorized
+ }
+ }
+
+ public void testCreate_Enabled() throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ db.getConfig().setBoolean("http", null, "uploadpack", true);
+ UploadPack up;
+
+ up = factory.create(new R(null, "1.2.3.4"), db);
+ assertNotNull("have UploadPack", up);
+ assertSame(db, up.getRepository());
+
+ up = factory.create(new R("bob", "1.2.3.4"), db);
+ assertNotNull("have UploadPack", up);
+ assertSame(db, up.getRepository());
+ }
+
+ private final class R extends HttpServletRequestWrapper {
+ private final String user;
+
+ private final String host;
+
+ R(final String user, final String host) {
+ super(new Request() /* can't pass null, sigh */);
+ this.user = user;
+ this.host = host;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return host;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return user;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
new file mode 100644
index 0000000000..1ec3244284
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010, 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 static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
+import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
+import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.http.test.util.AccessEvent;
+import org.eclipse.jgit.http.test.util.HttpTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.TransportHttp;
+import org.eclipse.jgit.transport.URIish;
+
+public class DumbClientDumbServerTest extends HttpTestCase {
+ private Repository remoteRepository;
+
+ private URIish remoteURI;
+
+ private RevBlob A_txt;
+
+ private RevCommit A, B;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ final TestRepository src = createTestRepository();
+ final File srcGit = src.getRepository().getDirectory();
+ final URI base = srcGit.getParentFile().toURI();
+
+ ServletContextHandler app = server.addContext("/git");
+ app.setResourceBase(base.toString());
+ app.addServlet(DefaultServlet.class, "/");
+
+ server.setUp();
+
+ remoteRepository = src.getRepository();
+ remoteURI = toURIish(app, srcGit.getName());
+
+ 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);
+ }
+
+ public void testListRemote() throws IOException {
+ Repository dst = createBareRepository();
+
+ assertEquals("http", remoteURI.getScheme());
+
+ Map<String, Ref> map;
+ Transport t = Transport.open(dst, remoteURI);
+ try {
+ // I didn't make up these public interface names, I just
+ // approved them for inclusion into the code base. Sorry.
+ // --spearce
+ //
+ assertTrue("isa TransportHttp", t instanceof TransportHttp);
+ assertTrue("isa HttpTransport", t instanceof HttpTransport);
+
+ FetchConnection c = t.openFetch();
+ try {
+ map = c.getRefsMap();
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+
+ assertNotNull("have map of refs", map);
+ assertEquals(2, map.size());
+
+ assertNotNull("has " + master, map.get(master));
+ assertEquals(B, map.get(master).getObjectId());
+
+ assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
+ assertEquals(B, map.get(Constants.HEAD).getObjectId());
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+ assertEquals(0, getRequests(remoteURI, "git-upload-pack").size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(remoteURI, "info/refs"), info.getPath());
+ assertEquals(1, info.getParameters().size());
+ assertEquals("git-upload-pack", info.getParameter("service"));
+ assertEquals("no-cache", info.getRequestHeader(HDR_PRAGMA));
+ assertNotNull("has user-agent", info.getRequestHeader(HDR_USER_AGENT));
+ assertTrue("is jgit agent", info.getRequestHeader(HDR_USER_AGENT)
+ .startsWith("JGit/"));
+ assertEquals("application/x-git-upload-pack-advertisement, */*", info
+ .getRequestHeader(HDR_ACCEPT));
+ assertEquals(200, info.getStatus());
+
+ AccessEvent head = requests.get(1);
+ assertEquals("GET", head.getMethod());
+ assertEquals(join(remoteURI, "HEAD"), head.getPath());
+ assertEquals(0, head.getParameters().size());
+ assertEquals("no-cache", head.getRequestHeader(HDR_PRAGMA));
+ assertNotNull("has user-agent", head.getRequestHeader(HDR_USER_AGENT));
+ assertTrue("is jgit agent", head.getRequestHeader(HDR_USER_AGENT)
+ .startsWith("JGit/"));
+ assertEquals(200, head.getStatus());
+ }
+
+ public void testInitialClone_Loose() throws Exception {
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, remoteURI);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(dst.hasObject(A_txt));
+ assertEquals(B, dst.getRef(master).getObjectId());
+ fsck(dst, B);
+
+ List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
+ assertEquals(1, loose.size());
+ assertEquals("GET", loose.get(0).getMethod());
+ assertEquals(0, loose.get(0).getParameters().size());
+ assertEquals(200, loose.get(0).getStatus());
+ }
+
+ public void testInitialClone_Packed() throws Exception {
+ new TestRepository(remoteRepository).packAndPrune();
+
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, remoteURI);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(dst.hasObject(A_txt));
+ assertEquals(B, dst.getRef(master).getObjectId());
+ fsck(dst, B);
+
+ List<AccessEvent> req;
+ AccessEvent event;
+
+ req = getRequests(loose(remoteURI, B));
+ assertEquals(1, req.size());
+ event = req.get(0);
+ assertEquals("GET", event.getMethod());
+ assertEquals(0, event.getParameters().size());
+ assertEquals(404, event.getStatus());
+
+ req = getRequests(join(remoteURI, "objects/info/packs"));
+ assertEquals(1, req.size());
+ event = req.get(0);
+ assertEquals("GET", event.getMethod());
+ assertEquals(0, event.getParameters().size());
+ assertEquals("no-cache", event.getRequestHeader(HDR_PRAGMA));
+ assertNotNull("has user-agent", event.getRequestHeader(HDR_USER_AGENT));
+ assertTrue("is jgit agent", event.getRequestHeader(HDR_USER_AGENT)
+ .startsWith("JGit/"));
+ assertEquals(200, event.getStatus());
+ }
+
+ public void testPushNotSupported() throws Exception {
+ final TestRepository src = createTestRepository();
+ final RevCommit Q = src.commit().create();
+ final Repository db = src.getRepository();
+
+ Transport t = Transport.open(db, remoteURI);
+ try {
+ try {
+ t.push(NullProgressMonitor.INSTANCE, push(src, Q));
+ fail("push incorrectly completed against a dumb server");
+ } catch (NotSupportedException nse) {
+ String exp = "remote does not support smart HTTP push";
+ assertEquals(exp, nse.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+}
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
new file mode 100644
index 0000000000..0b28712f69
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2010, 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 static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
+import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
+
+import java.io.IOException;
+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.http.server.resolver.RepositoryResolver;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.http.test.util.AccessEvent;
+import org.eclipse.jgit.http.test.util.HttpTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.TransportHttp;
+import org.eclipse.jgit.transport.URIish;
+
+public class DumbClientSmartServerTest extends HttpTestCase {
+ private Repository remoteRepository;
+
+ private URIish remoteURI;
+
+ private RevBlob A_txt;
+
+ private RevCommit A, B;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ final TestRepository src = createTestRepository();
+ final String srcName = src.getRepository().getDirectory().getName();
+
+ ServletContextHandler app = server.addContext("/git");
+ GitServlet gs = new GitServlet();
+ gs.setRepositoryResolver(new RepositoryResolver() {
+ 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;
+ }
+ });
+ app.addServlet(new ServletHolder(gs), "/*");
+
+ server.setUp();
+
+ remoteRepository = src.getRepository();
+ remoteURI = toURIish(app, 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);
+ }
+
+ public void testListRemote() throws IOException {
+ Repository dst = createBareRepository();
+
+ assertEquals("http", remoteURI.getScheme());
+
+ Map<String, Ref> map;
+ Transport t = Transport.open(dst, remoteURI);
+ ((TransportHttp) t).setUseSmartHttp(false);
+ try {
+ // I didn't make up these public interface names, I just
+ // approved them for inclusion into the code base. Sorry.
+ // --spearce
+ //
+ assertTrue("isa TransportHttp", t instanceof TransportHttp);
+ assertTrue("isa HttpTransport", t instanceof HttpTransport);
+
+ FetchConnection c = t.openFetch();
+ try {
+ map = c.getRefsMap();
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+
+ assertNotNull("have map of refs", map);
+ assertEquals(2, map.size());
+
+ assertNotNull("has " + master, map.get(master));
+ assertEquals(B, map.get(master).getObjectId());
+
+ assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
+ assertEquals(B, map.get(Constants.HEAD).getObjectId());
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+ assertEquals(0, getRequests(remoteURI, "git-upload-pack").size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(remoteURI, "info/refs"), info.getPath());
+ assertEquals(0, info.getParameters().size());
+ assertNull("no service parameter", info.getParameter("service"));
+ assertEquals("no-cache", info.getRequestHeader(HDR_PRAGMA));
+ assertNotNull("has user-agent", info.getRequestHeader(HDR_USER_AGENT));
+ assertTrue("is jgit agent", info.getRequestHeader(HDR_USER_AGENT)
+ .startsWith("JGit/"));
+ assertEquals("*/*", info.getRequestHeader(HDR_ACCEPT));
+ assertEquals(200, info.getStatus());
+ assertEquals("text/plain;charset=UTF-8", info
+ .getResponseHeader(HDR_CONTENT_TYPE));
+
+ AccessEvent head = requests.get(1);
+ assertEquals("GET", head.getMethod());
+ assertEquals(join(remoteURI, "HEAD"), head.getPath());
+ assertEquals(0, head.getParameters().size());
+ assertEquals(200, head.getStatus());
+ assertEquals("text/plain", head.getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
+ public void testInitialClone_Small() throws Exception {
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, remoteURI);
+ ((TransportHttp) t).setUseSmartHttp(false);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(dst.hasObject(A_txt));
+ assertEquals(B, dst.getRef(master).getObjectId());
+ fsck(dst, B);
+
+ List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
+ assertEquals(1, loose.size());
+ assertEquals("GET", loose.get(0).getMethod());
+ assertEquals(0, loose.get(0).getParameters().size());
+ assertEquals(200, loose.get(0).getStatus());
+ assertEquals("application/x-git-loose-object", loose.get(0)
+ .getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
+ public void testInitialClone_Packed() throws Exception {
+ new TestRepository(remoteRepository).packAndPrune();
+
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, remoteURI);
+ ((TransportHttp) t).setUseSmartHttp(false);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(dst.hasObject(A_txt));
+ assertEquals(B, dst.getRef(master).getObjectId());
+ fsck(dst, B);
+
+ List<AccessEvent> req;
+
+ req = getRequests(loose(remoteURI, B));
+ assertEquals(1, req.size());
+ assertEquals("GET", req.get(0).getMethod());
+ assertEquals(0, req.get(0).getParameters().size());
+ assertEquals(404, req.get(0).getStatus());
+
+ req = getRequests(join(remoteURI, "objects/info/packs"));
+ assertEquals(1, req.size());
+ assertEquals("GET", req.get(0).getMethod());
+ assertEquals(0, req.get(0).getParameters().size());
+ assertEquals(200, req.get(0).getStatus());
+ assertEquals("text/plain;charset=UTF-8", req.get(0).getResponseHeader(
+ HDR_CONTENT_TYPE));
+ }
+
+ public void testPushNotSupported() throws Exception {
+ final TestRepository src = createTestRepository();
+ final RevCommit Q = src.commit().create();
+ final Repository db = src.getRepository();
+
+ Transport t = Transport.open(db, remoteURI);
+ ((TransportHttp) t).setUseSmartHttp(false);
+ try {
+ try {
+ t.push(NullProgressMonitor.INSTANCE, push(src, Q));
+ fail("push incorrectly completed against a smart server");
+ } catch (NotSupportedException nse) {
+ String exp = "smart HTTP push disabled";
+ assertEquals(exp, nse.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ErrorServletTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ErrorServletTest.java
new file mode 100644
index 0000000000..18c1bb8f6d
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ErrorServletTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009-2010, 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 java.net.HttpURLConnection;
+import java.net.URI;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.http.server.glue.ErrorServlet;
+import org.eclipse.jgit.http.test.util.AppServer;
+
+public class ErrorServletTest extends TestCase {
+ private AppServer server;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ server = new AppServer();
+
+ ServletContextHandler ctx = server.addContext("/");
+ ctx.addServlet(new ServletHolder(new ErrorServlet(404)), "/404");
+ ctx.addServlet(new ServletHolder(new ErrorServlet(500)), "/500");
+
+ server.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ if (server != null) {
+ server.tearDown();
+ }
+ super.tearDown();
+ }
+
+ public void testHandler() throws Exception {
+ final URI uri = server.getURI();
+ assertEquals(404, ((HttpURLConnection) uri.resolve("/404").toURL()
+ .openConnection()).getResponseCode());
+ assertEquals(500, ((HttpURLConnection) uri.resolve("/500").toURL()
+ .openConnection()).getResponseCode());
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
new file mode 100644
index 0000000000..d7ca5900f5
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010, 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 java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.http.server.resolver.FileResolver;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+
+public class FileResolverTest extends LocalDiskRepositoryTestCase {
+ public void testUnreasonableNames() throws ServiceNotEnabledException {
+ assertUnreasonable("");
+ assertUnreasonable("a\\b");
+ assertUnreasonable("../b");
+ assertUnreasonable("a/../b");
+ assertUnreasonable("a/./b");
+ assertUnreasonable("a//b");
+
+ if (new File("/foo").isAbsolute())
+ assertUnreasonable("/foo");
+
+ if (new File("//server/share").isAbsolute())
+ assertUnreasonable("//server/share");
+
+ if (new File("C:/windows").isAbsolute())
+ assertUnreasonable("C:/windows");
+ }
+
+ private void assertUnreasonable(String name)
+ throws ServiceNotEnabledException {
+ FileResolver r = new FileResolver(new File("."), false);
+ try {
+ r.open(null, name);
+ fail("Opened unreasonable name \"" + name + "\"");
+ } catch (RepositoryNotFoundException e) {
+ assertEquals("repository not found: " + name, e.getMessage());
+ assertNull("has no cause", e.getCause());
+ }
+ }
+
+ public void testExportOk() throws IOException {
+ final Repository a = createBareRepository();
+ final String name = a.getDirectory().getName();
+ final File base = a.getDirectory().getParentFile();
+ final File export = new File(a.getDirectory(), "git-daemon-export-ok");
+ FileResolver resolver;
+
+ assertFalse("no git-daemon-export-ok", export.exists());
+ resolver = new FileResolver(base, false /* require flag */);
+ try {
+ resolver.open(null, name);
+ fail("opened non-exported repository");
+ } catch (ServiceNotEnabledException e) {
+ assertEquals("Service not enabled", e.getMessage());
+ }
+
+ resolver = new FileResolver(base, true /* export all */);
+ try {
+ resolver.open(null, name).close();
+ } catch (ServiceNotEnabledException e) {
+ fail("did not honor export-all flag");
+ }
+
+ export.createNewFile();
+ assertTrue("has git-daemon-export-ok", export.exists());
+ resolver = new FileResolver(base, false /* require flag */);
+ try {
+ resolver.open(null, name).close();
+ } catch (ServiceNotEnabledException e) {
+ fail("did not honor git-daemon-export-ok");
+ }
+ }
+
+ public void testNotAGitRepository() throws IOException,
+ ServiceNotEnabledException {
+ final Repository a = createBareRepository();
+ final String name = a.getDirectory().getName() + "-not-a-git";
+ final File base = a.getDirectory().getParentFile();
+ FileResolver resolver = new FileResolver(base, false);
+
+ try {
+ resolver.open(null, name);
+ } catch (RepositoryNotFoundException e) {
+ assertEquals("repository not found: " + name, e.getMessage());
+
+ Throwable why = e.getCause();
+ assertNotNull("has cause", why);
+ assertEquals("repository not found: "
+ + new File(base, name).getAbsolutePath(), why.getMessage());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletInitTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletInitTest.java
new file mode 100644
index 0000000000..8407036782
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletInitTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010, 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 java.util.List;
+
+import javax.servlet.ServletException;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.http.server.GitServlet;
+import org.eclipse.jgit.http.test.util.AppServer;
+import org.eclipse.jgit.http.test.util.MockServletConfig;
+import org.eclipse.jgit.http.test.util.RecordingLogger;
+
+public class GitServletInitTest extends TestCase {
+ private AppServer server;
+
+ protected void tearDown() throws Exception {
+ if (server != null) {
+ server.tearDown();
+ server = null;
+ }
+ super.tearDown();
+ }
+
+ public void testDefaultConstructor_NoBasePath() throws Exception {
+ GitServlet s = new GitServlet();
+ try {
+ s.init(new MockServletConfig());
+ fail("Init did not crash due to missing parameter");
+ } catch (ServletException e) {
+ assertTrue(e.getMessage().contains("base-path"));
+ }
+ }
+
+ public void testDefaultConstructor_WithBasePath() throws Exception {
+ MockServletConfig c = new MockServletConfig();
+ c.setInitParameter("base-path", ".");
+ c.setInitParameter("export-all", "false");
+
+ GitServlet s = new GitServlet();
+ s.init(c);
+ s.destroy();
+ }
+
+ public void testInitUnderContainer_NoBasePath() throws Exception {
+ server = new AppServer();
+
+ ServletContextHandler app = server.addContext("/");
+ ServletHolder s = app.addServlet(GitServlet.class, "/git");
+ s.setInitOrder(1);
+
+ server.setUp();
+
+ List<RecordingLogger.Warning> events = RecordingLogger.getWarnings();
+ assertFalse("Servlet started without base-path", events.isEmpty());
+
+ Throwable why = events.get(0).getCause();
+ assertTrue("Caught ServletException", why instanceof ServletException);
+ assertTrue("Wanted base-path", why.getMessage().contains("base-path"));
+ }
+
+ public void testInitUnderContainer_WithBasePath() throws Exception {
+ server = new AppServer();
+
+ ServletContextHandler app = server.addContext("/");
+ ServletHolder s = app.addServlet(GitServlet.class, "/git");
+ s.setInitOrder(1);
+ s.setInitParameter("base-path", ".");
+ s.setInitParameter("export-all", "true");
+
+ server.setUp();
+ assertTrue("no warnings", RecordingLogger.getWarnings().isEmpty());
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
new file mode 100644
index 0000000000..729466df3b
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2009-2010, 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 java.io.File;
+import java.net.URI;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.errors.NoRemoteRepositoryException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.http.server.GitServlet;
+import org.eclipse.jgit.http.server.resolver.RepositoryResolver;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.http.test.util.AccessEvent;
+import org.eclipse.jgit.http.test.util.HttpTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+public class HttpClientTests extends HttpTestCase {
+ private TestRepository remoteRepository;
+
+ private URIish dumbAuthNoneURI;
+
+ private URIish dumbAuthBasicURI;
+
+ private URIish smartAuthNoneURI;
+
+ private URIish smartAuthBasicURI;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ remoteRepository = createTestRepository();
+ remoteRepository.update(master, remoteRepository.commit().create());
+
+ ServletContextHandler dNone = dumb("/dnone");
+ ServletContextHandler dBasic = server.authBasic(dumb("/dbasic"));
+
+ ServletContextHandler sNone = smart("/snone");
+ ServletContextHandler sBasic = server.authBasic(smart("/sbasic"));
+
+ server.setUp();
+
+ final String srcName = nameOf(remoteRepository);
+ dumbAuthNoneURI = toURIish(dNone, srcName);
+ dumbAuthBasicURI = toURIish(dBasic, srcName);
+
+ smartAuthNoneURI = toURIish(sNone, srcName);
+ smartAuthBasicURI = toURIish(sBasic, srcName);
+ }
+
+ private ServletContextHandler dumb(final String path) {
+ final File srcGit = remoteRepository.getRepository().getDirectory();
+ final URI base = srcGit.getParentFile().toURI();
+
+ ServletContextHandler ctx = server.addContext(path);
+ ctx.setResourceBase(base.toString());
+ ctx.addServlet(DefaultServlet.class, "/");
+ return ctx;
+ }
+
+ private ServletContextHandler smart(final String path) {
+ GitServlet gs = new GitServlet();
+ gs.setRepositoryResolver(new RepositoryResolver() {
+ public Repository open(HttpServletRequest req, String name)
+ throws RepositoryNotFoundException,
+ ServiceNotEnabledException {
+ if (!name.equals(nameOf(remoteRepository)))
+ throw new RepositoryNotFoundException(name);
+
+ final Repository db = remoteRepository.getRepository();
+ db.incrementOpen();
+ return db;
+ }
+ });
+
+ ServletContextHandler ctx = server.addContext(path);
+ ctx.addServlet(new ServletHolder(gs), "/*");
+ return ctx;
+ }
+
+ private static String nameOf(final TestRepository db) {
+ return db.getRepository().getDirectory().getName();
+ }
+
+ public void testRepositoryNotFound_Dumb() throws Exception {
+ URIish uri = toURIish("/dumb.none/not-found");
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, uri);
+ try {
+ try {
+ t.openFetch();
+ fail("connection opened to not found repository");
+ } catch (NoRemoteRepositoryException err) {
+ String exp = uri + ": " + uri
+ + "/info/refs?service=git-upload-pack not found";
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ public void testRepositoryNotFound_Smart() throws Exception {
+ URIish uri = toURIish("/smart.none/not-found");
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, uri);
+ try {
+ try {
+ t.openFetch();
+ fail("connection opened to not found repository");
+ } catch (NoRemoteRepositoryException err) {
+ String exp = uri + ": " + uri
+ + "/info/refs?service=git-upload-pack not found";
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ public void testListRemote_Dumb_DetachedHEAD() throws Exception {
+ Repository src = remoteRepository.getRepository();
+ RefUpdate u = src.updateRef(Constants.HEAD, true);
+ RevCommit Q = remoteRepository.commit().message("Q").create();
+ u.setNewObjectId(Q);
+ assertEquals(RefUpdate.Result.FORCED, u.forceUpdate());
+
+ Repository dst = createBareRepository();
+ Ref head;
+ Transport t = Transport.open(dst, dumbAuthNoneURI);
+ try {
+ FetchConnection c = t.openFetch();
+ try {
+ head = c.getRef(Constants.HEAD);
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+ assertNotNull("has " + Constants.HEAD, head);
+ assertEquals(Q, head.getObjectId());
+ }
+
+ public void testListRemote_Dumb_NoHEAD() throws Exception {
+ Repository src = remoteRepository.getRepository();
+ File headref = new File(src.getDirectory(), Constants.HEAD);
+ assertTrue("HEAD used to be present", headref.delete());
+ assertFalse("HEAD is gone", headref.exists());
+
+ Repository dst = createBareRepository();
+ Ref head;
+ Transport t = Transport.open(dst, dumbAuthNoneURI);
+ try {
+ FetchConnection c = t.openFetch();
+ try {
+ head = c.getRef(Constants.HEAD);
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+ assertNull("has no " + Constants.HEAD, head);
+ }
+
+ public void testListRemote_Smart_DetachedHEAD() throws Exception {
+ Repository src = remoteRepository.getRepository();
+ RefUpdate u = src.updateRef(Constants.HEAD, true);
+ RevCommit Q = remoteRepository.commit().message("Q").create();
+ u.setNewObjectId(Q);
+ assertEquals(RefUpdate.Result.FORCED, u.forceUpdate());
+
+ Repository dst = createBareRepository();
+ Ref head;
+ Transport t = Transport.open(dst, smartAuthNoneURI);
+ try {
+ FetchConnection c = t.openFetch();
+ try {
+ head = c.getRef(Constants.HEAD);
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+ assertNotNull("has " + Constants.HEAD, head);
+ assertEquals(Q, head.getObjectId());
+ }
+
+ public void testListRemote_Smart_WithQueryParameters() throws Exception {
+ URIish myURI = toURIish("/snone/do?r=1&p=test.git");
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, myURI);
+ try {
+ try {
+ t.openFetch();
+ fail("test did not fail to find repository as expected");
+ } catch (NoRemoteRepositoryException err) {
+ // expected
+ }
+ } finally {
+ t.close();
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(1, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals("/snone/do", info.getPath());
+ assertEquals(3, info.getParameters().size());
+ assertEquals("1", info.getParameter("r"));
+ assertEquals("test.git/info/refs", info.getParameter("p"));
+ assertEquals("git-upload-pack", info.getParameter("service"));
+ assertEquals(404, info.getStatus());
+ }
+
+ public void testListRemote_Dumb_NeedsAuth() throws Exception {
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, dumbAuthBasicURI);
+ try {
+ try {
+ t.openFetch();
+ fail("connection opened even info/refs needs auth basic");
+ } catch (TransportException err) {
+ String status = "401 Unauthorized";
+ String exp = dumbAuthBasicURI + ": " + status;
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ public void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, smartAuthBasicURI);
+ try {
+ try {
+ t.openFetch();
+ fail("connection opened even though service disabled");
+ } catch (TransportException err) {
+ String status = "401 Unauthorized";
+ String exp = smartAuthBasicURI + ": " + status;
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ public void testListRemote_Smart_UploadPackDisabled() throws Exception {
+ Repository src = remoteRepository.getRepository();
+ src.getConfig().setBoolean("http", null, "uploadpack", false);
+ src.getConfig().save();
+
+ Repository dst = createBareRepository();
+ Transport t = Transport.open(dst, smartAuthNoneURI);
+ try {
+ try {
+ t.openFetch();
+ fail("connection opened even though service disabled");
+ } catch (TransportException err) {
+ String exp = smartAuthNoneURI
+ + ": git-upload-pack not permitted";
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+ }
+}
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
new file mode 100644
index 0000000000..fd78bb4515
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2009-2010, 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 static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+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.FilterMapping;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.http.server.GitServlet;
+import org.eclipse.jgit.http.server.resolver.RepositoryResolver;
+import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.http.test.util.AccessEvent;
+import org.eclipse.jgit.http.test.util.HttpTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryConfig;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.TransportHttp;
+import org.eclipse.jgit.transport.URIish;
+
+public class SmartClientSmartServerTest extends HttpTestCase {
+ private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
+
+ private Repository remoteRepository;
+
+ private URIish remoteURI;
+
+ private URIish brokenURI;
+
+ private RevBlob A_txt;
+
+ private RevCommit A, B;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ final TestRepository src = createTestRepository();
+ final String srcName = src.getRepository().getDirectory().getName();
+
+ ServletContextHandler app = server.addContext("/git");
+ GitServlet gs = new GitServlet();
+ gs.setRepositoryResolver(new RepositoryResolver() {
+ 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;
+ }
+ });
+ app.addServlet(new ServletHolder(gs), "/*");
+
+ ServletContextHandler broken = server.addContext("/bad");
+ broken.addFilter(new FilterHolder(new Filter() {
+ public void doFilter(ServletRequest request,
+ ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ final HttpServletResponse r = (HttpServletResponse) response;
+ r.setContentType("text/plain");
+ r.setCharacterEncoding("UTF-8");
+ PrintWriter w = r.getWriter();
+ w.print("OK");
+ w.close();
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ //
+ }
+
+ public void destroy() {
+ //
+ }
+ }), "/" + srcName + "/git-upload-pack", FilterMapping.DEFAULT);
+ broken.addServlet(new ServletHolder(gs), "/*");
+
+ server.setUp();
+
+ remoteRepository = src.getRepository();
+ remoteURI = toURIish(app, srcName);
+ brokenURI = toURIish(broken, 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);
+
+ src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
+ }
+
+ public void testListRemote() throws IOException {
+ Repository dst = createBareRepository();
+
+ assertEquals("http", remoteURI.getScheme());
+
+ Map<String, Ref> map;
+ Transport t = Transport.open(dst, remoteURI);
+ try {
+ // I didn't make up these public interface names, I just
+ // approved them for inclusion into the code base. Sorry.
+ // --spearce
+ //
+ assertTrue("isa TransportHttp", t instanceof TransportHttp);
+ assertTrue("isa HttpTransport", t instanceof HttpTransport);
+
+ FetchConnection c = t.openFetch();
+ try {
+ map = c.getRefsMap();
+ } finally {
+ c.close();
+ }
+ } finally {
+ t.close();
+ }
+
+ assertNotNull("have map of refs", map);
+ assertEquals(3, map.size());
+
+ assertNotNull("has " + master, map.get(master));
+ assertEquals(B, map.get(master).getObjectId());
+
+ assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
+ assertEquals(B, map.get(Constants.HEAD).getObjectId());
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(1, requests.size());
+
+ AccessEvent info = requests.get(0);
+ 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));
+ }
+
+ public void testInitialClone_Small() throws Exception {
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, remoteURI);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(dst.hasObject(A_txt));
+ assertEquals(B, dst.getRef(master).getObjectId());
+ fsck(dst, B);
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+
+ AccessEvent info = requests.get(0);
+ 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(1);
+ 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));
+ assertNull("no compression (too small)", service
+ .getRequestHeader(HDR_CONTENT_ENCODING));
+
+ assertEquals(200, service.getStatus());
+ assertEquals("application/x-git-upload-pack-result", service
+ .getResponseHeader(HDR_CONTENT_TYPE));
+ assertNull("no compression (never compressed)", service
+ .getResponseHeader(HDR_CONTENT_ENCODING));
+ }
+
+ public void testFetchUpdateExisting() throws Exception {
+ // Bootstrap by doing the clone.
+ //
+ TestRepository dst = createTestRepository();
+ Transport t = Transport.open(dst.getRepository(), remoteURI);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+ assertEquals(B, dst.getRepository().getRef(master).getObjectId());
+ List<AccessEvent> cloneRequests = getRequests();
+
+ // Force enough into the local client that enumeration will
+ // need multiple packets, but not too many to overflow and
+ // not pick up the ACK_COMMON message.
+ //
+ TestRepository.BranchBuilder b = dst.branch(master);
+ for (int i = 0; i < 32 - 1; i++)
+ b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
+
+ // Create a new commit on the remote.
+ //
+ b = new TestRepository(remoteRepository).branch(master);
+ RevCommit Z = b.commit().message("Z").create();
+
+ // Now incrementally update.
+ //
+ t = Transport.open(dst.getRepository(), remoteURI);
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ } finally {
+ t.close();
+ }
+ assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
+
+ List<AccessEvent> requests = getRequests();
+ requests.removeAll(cloneRequests);
+ assertEquals(3, requests.size());
+
+ AccessEvent info = requests.get(0);
+ 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));
+
+ // We should have needed two requests to perform the fetch
+ // due to the high number of local unknown commits.
+ //
+ AccessEvent service = requests.get(1);
+ 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));
+
+ 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));
+ }
+
+ public void testInitialClone_BrokenServer() throws Exception {
+ Repository dst = createBareRepository();
+ assertFalse(dst.hasObject(A_txt));
+
+ Transport t = Transport.open(dst, brokenURI);
+ try {
+ try {
+ t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+ fail("fetch completed despite upload-pack being broken");
+ } catch (TransportException err) {
+ String exp = brokenURI + ": expected"
+ + " Content-Type application/x-git-upload-pack-result;"
+ + " received Content-Type text/plain;charset=UTF-8";
+ assertEquals(exp, err.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(brokenURI, "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));
+
+ AccessEvent service = requests.get(1);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
+ assertEquals(0, service.getParameters().size());
+ assertEquals(200, service.getStatus());
+ assertEquals("text/plain;charset=UTF-8", service
+ .getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
+ public void testPush_NotAuthorized() throws Exception {
+ final TestRepository src = createTestRepository();
+ final RevBlob Q_txt = src.blob("new text");
+ final RevCommit Q = src.commit().add("Q", Q_txt).create();
+ final Repository db = src.getRepository();
+ final String dstName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ // push anonymous shouldn't be allowed.
+ //
+ t = Transport.open(db, remoteURI);
+ try {
+ final String srcExpr = Q.name();
+ final boolean forceUpdate = false;
+ final String localName = null;
+ final ObjectId oldId = null;
+
+ RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
+ srcExpr, dstName, forceUpdate, localName, oldId);
+ try {
+ t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
+ fail("anonymous push incorrectly accepted without error");
+ } catch (TransportException e) {
+ final String status = "401 Unauthorized";
+ final String exp = remoteURI.toString() + ": " + status;
+ assertEquals(exp, e.getMessage());
+ }
+ } finally {
+ t.close();
+ }
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(1, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(remoteURI, "info/refs"), info.getPath());
+ assertEquals(1, info.getParameters().size());
+ assertEquals("git-receive-pack", info.getParameter("service"));
+ assertEquals(401, info.getStatus());
+ }
+
+ public void testPush_CreateBranch() throws Exception {
+ final TestRepository src = createTestRepository();
+ final RevBlob Q_txt = src.blob("new text");
+ final RevCommit Q = src.commit().add("Q", Q_txt).create();
+ final Repository db = src.getRepository();
+ final String dstName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ enableReceivePack();
+
+ t = Transport.open(db, remoteURI);
+ try {
+ final String srcExpr = Q.name();
+ final boolean forceUpdate = false;
+ final String localName = null;
+ final ObjectId oldId = null;
+
+ RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
+ srcExpr, dstName, forceUpdate, localName, oldId);
+ t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(remoteRepository.hasObject(Q_txt));
+ assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
+ assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ fsck(remoteRepository, Q);
+
+ final ReflogReader log = remoteRepository.getReflogReader(dstName);
+ assertNotNull("has log for " + dstName);
+
+ final ReflogReader.Entry last = log.getLastEntry();
+ assertNotNull("has last entry", last);
+ assertEquals(ObjectId.zeroId(), last.getOldId());
+ assertEquals(Q, last.getNewId());
+ assertEquals("anonymous", last.getWho().getName());
+
+ // Assumption: The host name we use to contact the server should
+ // be the server's own host name, because it should be the loopback
+ // network interface.
+ //
+ final String clientHost = remoteURI.getHost();
+ assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
+ assertEquals("push: created", last.getComment());
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(remoteURI, "info/refs"), info.getPath());
+ assertEquals(1, info.getParameters().size());
+ assertEquals("git-receive-pack", info.getParameter("service"));
+ assertEquals(200, info.getStatus());
+ assertEquals("application/x-git-receive-pack-advertisement", info
+ .getResponseHeader(HDR_CONTENT_TYPE));
+
+ AccessEvent service = requests.get(1);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(remoteURI, "git-receive-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-receive-pack-result", service
+ .getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
+ public void testPush_ChunkedEncoding() throws Exception {
+ final TestRepository src = createTestRepository();
+ final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
+ final RevCommit Q = src.commit().add("Q", Q_bin).create();
+ final Repository db = src.getRepository();
+ final String dstName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ enableReceivePack();
+
+ db.getConfig().setInt("core", null, "compression", 0);
+ db.getConfig().setInt("http", null, "postbuffer", 8 * 1024);
+ db.getConfig().save();
+
+ t = Transport.open(db, remoteURI);
+ try {
+ final String srcExpr = Q.name();
+ final boolean forceUpdate = false;
+ final String localName = null;
+ final ObjectId oldId = null;
+
+ RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
+ srcExpr, dstName, forceUpdate, localName, oldId);
+ t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
+ } finally {
+ t.close();
+ }
+
+ assertTrue(remoteRepository.hasObject(Q_bin));
+ assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
+ assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ fsck(remoteRepository, Q);
+
+ List<AccessEvent> requests = getRequests();
+ assertEquals(2, requests.size());
+
+ AccessEvent info = requests.get(0);
+ assertEquals("GET", info.getMethod());
+ assertEquals(join(remoteURI, "info/refs"), info.getPath());
+ assertEquals(1, info.getParameters().size());
+ assertEquals("git-receive-pack", info.getParameter("service"));
+ assertEquals(200, info.getStatus());
+ assertEquals("application/x-git-receive-pack-advertisement", info
+ .getResponseHeader(HDR_CONTENT_TYPE));
+
+ AccessEvent service = requests.get(1);
+ assertEquals("POST", service.getMethod());
+ assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
+ assertEquals(0, service.getParameters().size());
+ assertNull("no content-length", service
+ .getRequestHeader(HDR_CONTENT_LENGTH));
+ assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
+
+ assertEquals(200, service.getStatus());
+ assertEquals("application/x-git-receive-pack-result", service
+ .getResponseHeader(HDR_CONTENT_TYPE));
+ }
+
+ private void enableReceivePack() throws IOException {
+ final RepositoryConfig cfg = remoteRepository.getConfig();
+ cfg.setBoolean("http", null, "receivepack", true);
+ cfg.save();
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AccessEvent.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AccessEvent.java
new file mode 100644
index 0000000000..c6d531e5b3
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AccessEvent.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2010, 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.util;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+
+/** A single request made through {@link AppServer}. */
+public class AccessEvent {
+ private final String method;
+
+ private final String uri;
+
+ private final Map<String, String> requestHeaders;
+
+ private final Map<String, String[]> parameters;
+
+ private final int status;
+
+ private final Map<String, String> responseHeaders;
+
+ AccessEvent(final Request req, final Response rsp) {
+ method = req.getMethod();
+ uri = req.getRequestURI();
+ requestHeaders = cloneHeaders(req);
+ parameters = clone(req.getParameterMap());
+
+ status = rsp.getStatus();
+ responseHeaders = cloneHeaders(rsp);
+ }
+
+ private static Map<String, String> cloneHeaders(final Request req) {
+ Map<String, String> r = new TreeMap<String, String>();
+ Enumeration hn = req.getHeaderNames();
+ while (hn.hasMoreElements()) {
+ String key = (String) hn.nextElement();
+ if (!r.containsKey(key)) {
+ r.put(key, req.getHeader(key));
+ }
+ }
+ return Collections.unmodifiableMap(r);
+ }
+
+ private static Map<String, String> cloneHeaders(final Response rsp) {
+ Map<String, String> r = new TreeMap<String, String>();
+ Enumeration<String> hn = rsp.getHttpFields().getFieldNames();
+ while (hn.hasMoreElements()) {
+ String key = hn.nextElement();
+ if (!r.containsKey(key)) {
+ Enumeration<String> v = rsp.getHttpFields().getValues(key);
+ r.put(key, v.nextElement());
+ }
+ }
+ return Collections.unmodifiableMap(r);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map<String, String[]> clone(Map parameterMap) {
+ return new TreeMap<String, String[]>(parameterMap);
+ }
+
+ /** @return {@code "GET"} or {@code "POST"} */
+ public String getMethod() {
+ return method;
+ }
+
+ /** @return path of the file on the server, e.g. {@code /git/HEAD}. */
+ public String getPath() {
+ return uri;
+ }
+
+ /**
+ * @param name
+ * name of the request header to read.
+ * @return first value of the request header; null if not sent.
+ */
+ public String getRequestHeader(String name) {
+ return requestHeaders.get(name);
+ }
+
+ /**
+ * @param name
+ * name of the request parameter to read.
+ * @return first value of the request parameter; null if not sent.
+ */
+ public String getParameter(String name) {
+ String[] r = parameters.get(name);
+ return r != null && 1 <= r.length ? r[0] : null;
+ }
+
+ /** @return all parameters in the request. */
+ public Map<String, String[]> getParameters() {
+ return parameters;
+ }
+
+ /** @return HTTP status code of the response, e.g. 200, 403, 500. */
+ public int getStatus() {
+ return status;
+ }
+
+ /**
+ * @param name
+ * name of the response header to read.
+ * @return first value of the response header; null if not sent.
+ */
+ public String getResponseHeader(String name) {
+ return responseHeaders.get(name);
+ }
+
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append(method);
+ b.append(' ');
+ b.append(uri);
+ if (!parameters.isEmpty()) {
+ b.append('?');
+ boolean first = true;
+ for (Map.Entry<String, String[]> e : parameters.entrySet()) {
+ for (String val : e.getValue()) {
+ if (!first) {
+ b.append('&');
+ }
+ first = false;
+
+ b.append(e.getKey());
+ b.append('=');
+ b.append(val);
+ }
+ }
+ }
+ b.append(' ');
+ b.append(status);
+ return b.toString();
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AppServer.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AppServer.java
new file mode 100644
index 0000000000..74df7086ea
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AppServer.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2010, 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.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.eclipse.jetty.http.security.Constraint;
+import org.eclipse.jetty.http.security.Password;
+import org.eclipse.jetty.security.Authenticator;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.MappedLoginService;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Tiny web application server for unit testing.
+ * <p>
+ * Tests should start the server in their {@code setUp()} method and stop the
+ * server in their {@code tearDown()} method. Only while started the server's
+ * URL and/or port number can be obtained.
+ */
+public class AppServer {
+ /** Realm name for the secure access areas. */
+ public static final String realm = "Secure Area";
+
+ /** Username for secured access areas. */
+ public static final String username = "agitter";
+
+ /** Password for {@link #username} in secured access areas. */
+ public static final String password = "letmein";
+
+ static {
+ // Install a logger that throws warning messages.
+ //
+ final String prop = "org.eclipse.jetty.util.log.class";
+ System.setProperty(prop, RecordingLogger.class.getName());
+ }
+
+ private final Server server;
+
+ private final Connector connector;
+
+ private final ContextHandlerCollection contexts;
+
+ private final TestRequestLog log;
+
+ public AppServer() {
+ connector = new SelectChannelConnector();
+ connector.setPort(0);
+ try {
+ final InetAddress me = InetAddress.getByName("localhost");
+ connector.setHost(me.getHostAddress());
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Cannot find localhost", e);
+ }
+
+ // We need a handful of threads in the thread pool, otherwise
+ // our tests will deadlock when they can't open enough requests.
+ // In theory we only need 1 concurrent connection at a time, but
+ // I suspect the JRE isn't doing request pipelining on existing
+ // connections like we want it to.
+ //
+ final QueuedThreadPool pool = new QueuedThreadPool();
+ pool.setMinThreads(1);
+ pool.setMaxThreads(4);
+ pool.setMaxQueued(8);
+
+ contexts = new ContextHandlerCollection();
+
+ log = new TestRequestLog();
+
+ final RequestLogHandler logHandler = new RequestLogHandler();
+ logHandler.setHandler(contexts);
+ logHandler.setRequestLog(log);
+
+ server = new Server();
+ server.setConnectors(new Connector[] { connector });
+ server.setThreadPool(pool);
+ server.setHandler(logHandler);
+
+ server.setStopAtShutdown(false);
+ server.setGracefulShutdown(0);
+ }
+
+ /**
+ * Create a new servlet context within the server.
+ * <p>
+ * This method should be invoked before the server is started, once for each
+ * context the caller wants to register.
+ *
+ * @param path
+ * path of the context; use "/" for the root context if binding
+ * to the root is desired.
+ * @return the context to add servlets into.
+ */
+ public ServletContextHandler addContext(String path) {
+ assertNotYetSetUp();
+ if ("".equals(path))
+ path = "/";
+
+ ServletContextHandler ctx = new ServletContextHandler();
+ ctx.setContextPath(path);
+ contexts.addHandler(ctx);
+
+ return ctx;
+ }
+
+ public ServletContextHandler authBasic(ServletContextHandler ctx) {
+ assertNotYetSetUp();
+ auth(ctx, new BasicAuthenticator());
+ return ctx;
+ }
+
+ private void auth(ServletContextHandler ctx, Authenticator authType) {
+ final String role = "can-access";
+
+ MappedLoginService users = new MappedLoginService() {
+ @Override
+ protected UserIdentity loadUser(String who) {
+ return null;
+ }
+
+ @Override
+ protected void loadUsers() throws IOException {
+ putUser(username, new Password(password), new String[] { role });
+ }
+ };
+
+ ConstraintMapping cm = new ConstraintMapping();
+ cm.setConstraint(new Constraint());
+ cm.getConstraint().setAuthenticate(true);
+ cm.getConstraint().setDataConstraint(Constraint.DC_NONE);
+ cm.getConstraint().setRoles(new String[] { role });
+ cm.setPathSpec("/*");
+
+ ConstraintSecurityHandler sec = new ConstraintSecurityHandler();
+ sec.setStrict(false);
+ sec.setRealmName(realm);
+ sec.setAuthenticator(authType);
+ sec.setLoginService(users);
+ sec.setConstraintMappings(new ConstraintMapping[] { cm });
+ sec.setHandler(ctx);
+
+ contexts.removeHandler(ctx);
+ contexts.addHandler(sec);
+ }
+
+ /**
+ * Start the server on a random local port.
+ *
+ * @throws Exception
+ * the server cannot be started, testing is not possible.
+ */
+ public void setUp() throws Exception {
+ RecordingLogger.clear();
+ log.clear();
+ server.start();
+ }
+
+ /**
+ * Shutdown the server.
+ *
+ * @throws Exception
+ * the server refuses to halt, or wasn't running.
+ */
+ public void tearDown() throws Exception {
+ RecordingLogger.clear();
+ log.clear();
+ server.stop();
+ }
+
+ /**
+ * Get the URI to reference this server.
+ * <p>
+ * The returned URI includes the proper host name and port number, but does
+ * not contain a path.
+ *
+ * @return URI to reference this server's root context.
+ */
+ public URI getURI() {
+ assertAlreadySetUp();
+ String host = connector.getHost();
+ if (host.contains(":") && !host.startsWith("["))
+ host = "[" + host + "]";
+ final String uri = "http://" + host + ":" + getPort();
+ try {
+ return new URI(uri);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Unexpected URI error on " + uri, e);
+ }
+ }
+
+ /** @return the local port number the server is listening on. */
+ public int getPort() {
+ assertAlreadySetUp();
+ return ((SelectChannelConnector) connector).getLocalPort();
+ }
+
+ /** @return all requests since the server was started. */
+ public List<AccessEvent> getRequests() {
+ return new ArrayList<AccessEvent>(log.getEvents());
+ }
+
+ /**
+ * @param base
+ * base URI used to access the server.
+ * @param path
+ * the path to locate requests for, relative to {@code base}.
+ * @return all requests which match the given path.
+ */
+ public List<AccessEvent> getRequests(URIish base, String path) {
+ return getRequests(HttpTestCase.join(base, path));
+ }
+
+ /**
+ * @param path
+ * the path to locate requests for.
+ * @return all requests which match the given path.
+ */
+ public List<AccessEvent> getRequests(String path) {
+ ArrayList<AccessEvent> r = new ArrayList<AccessEvent>();
+ for (AccessEvent event : log.getEvents()) {
+ if (event.getPath().equals(path)) {
+ r.add(event);
+ }
+ }
+ return r;
+ }
+
+ private void assertNotYetSetUp() {
+ Assert.assertFalse("server is not running", server.isRunning());
+ }
+
+ private void assertAlreadySetUp() {
+ Assert.assertTrue("server is running", server.isRunning());
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
new file mode 100644
index 0000000000..e259757615
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2009-2010, 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.util;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.URIish;
+
+/** Base class for HTTP related transport testing. */
+public abstract class HttpTestCase extends LocalDiskRepositoryTestCase {
+ protected static final String master = Constants.R_HEADS + Constants.MASTER;
+
+ /** In-memory application server; subclass must start. */
+ protected AppServer server;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ server = new AppServer();
+ }
+
+ protected void tearDown() throws Exception {
+ server.tearDown();
+ super.tearDown();
+ }
+
+ protected TestRepository createTestRepository() throws Exception {
+ return new TestRepository(createBareRepository());
+ }
+
+ protected URIish toURIish(String path) throws URISyntaxException {
+ URI u = server.getURI().resolve(path);
+ return new URIish(u.toString());
+ }
+
+ protected URIish toURIish(ServletContextHandler app, String name)
+ throws URISyntaxException {
+ String p = app.getContextPath();
+ if (!p.endsWith("/") && !name.startsWith("/"))
+ p += "/";
+ p += name;
+ return toURIish(p);
+ }
+
+ protected List<AccessEvent> getRequests() {
+ return server.getRequests();
+ }
+
+ protected List<AccessEvent> getRequests(URIish base, String path) {
+ return server.getRequests(base, path);
+ }
+
+ protected List<AccessEvent> getRequests(String path) {
+ return server.getRequests(path);
+ }
+
+ protected static void fsck(Repository db, RevObject... tips)
+ throws Exception {
+ new TestRepository(db).fsck(tips);
+ }
+
+ protected static Set<RefSpec> mirror(String... refs) {
+ HashSet<RefSpec> r = new HashSet<RefSpec>();
+ for (String name : refs) {
+ RefSpec rs = new RefSpec(name);
+ rs = rs.setDestination(name);
+ rs = rs.setForceUpdate(true);
+ r.add(rs);
+ }
+ return r;
+ }
+
+ protected static Collection<RemoteRefUpdate> push(TestRepository from,
+ RevCommit q) throws IOException {
+ final Repository db = from.getRepository();
+ final String srcExpr = q.name();
+ final String dstName = master;
+ final boolean forceUpdate = true;
+ final String localName = null;
+ final ObjectId oldId = null;
+
+ RemoteRefUpdate u = new RemoteRefUpdate(db, srcExpr, dstName,
+ forceUpdate, localName, oldId);
+ return Collections.singleton(u);
+ }
+
+ public static String loose(URIish base, AnyObjectId id) {
+ final String objectName = id.name();
+ final String d = objectName.substring(0, 2);
+ final String f = objectName.substring(2);
+ return join(base, "objects/" + d + "/" + f);
+ }
+
+ public static String join(URIish base, String path) {
+ if (path.startsWith("/"))
+ fail("Cannot join absolute path " + path + " to URIish " + base);
+
+ String dir = base.getPath();
+ if (!dir.endsWith("/"))
+ dir += "/";
+ return dir + path;
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/MockServletConfig.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/MockServletConfig.java
new file mode 100644
index 0000000000..cf6f6f6a8f
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/MockServletConfig.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010, 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.util;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+public class MockServletConfig implements ServletConfig {
+ private final Map<String, String> parameters = new HashMap<String, String>();
+
+ public void setInitParameter(String name, String value) {
+ parameters.put(name, value);
+ }
+
+ public String getInitParameter(String name) {
+ return parameters.get(name);
+ }
+
+ public Enumeration getInitParameterNames() {
+ final Iterator<String> i = parameters.keySet().iterator();
+ return new Enumeration<String>() {
+ public boolean hasMoreElements() {
+ return i.hasNext();
+ }
+
+ public String nextElement() {
+ return i.next();
+ }
+ };
+ }
+
+ public String getServletName() {
+ return "MOCK_SERVLET";
+ }
+
+ public ServletContext getServletContext() {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/RecordingLogger.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/RecordingLogger.java
new file mode 100644
index 0000000000..24e125c22b
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/RecordingLogger.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010, 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.util;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jetty.util.log.Logger;
+
+/** Logs warnings into an array for later inspection. */
+public class RecordingLogger implements Logger {
+ private static List<Warning> warnings = new ArrayList<Warning>();
+
+ /** Clear the warnings, automatically done by {@link AppServer#setUp()} */
+ public static void clear() {
+ synchronized (warnings) {
+ warnings.clear();
+ }
+ }
+
+ /** @return the warnings (if any) from the last execution */
+ public static List<Warning> getWarnings() {
+ synchronized (warnings) {
+ ArrayList<Warning> copy = new ArrayList<Warning>(warnings);
+ return Collections.unmodifiableList(copy);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class Warning extends Exception {
+ public Warning(String msg) {
+ super(msg);
+ }
+
+ public Warning(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ }
+
+ private final String name;
+
+ public RecordingLogger() {
+ this("");
+ }
+
+ public RecordingLogger(final String name) {
+ this.name = name;
+ }
+
+ public Logger getLogger(@SuppressWarnings("hiding") String name) {
+ return new RecordingLogger(name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void warn(String msg, Object arg0, Object arg1) {
+ synchronized (warnings) {
+ warnings.add(new Warning(MessageFormat.format(msg, arg0, arg1)));
+ }
+ }
+
+ public void warn(String msg, Throwable th) {
+ synchronized (warnings) {
+ warnings.add(new Warning(msg, th));
+ }
+ }
+
+ public void warn(String msg) {
+ synchronized (warnings) {
+ warnings.add(new Warning(msg));
+ }
+ }
+
+ public void debug(String msg, Object arg0, Object arg1) {
+ // Ignore (not relevant to test failures)
+ }
+
+ public void debug(String msg, Throwable th) {
+ // Ignore (not relevant to test failures)
+ }
+
+ public void debug(String msg) {
+ // Ignore (not relevant to test failures)
+ }
+
+ public void info(String msg, Object arg0, Object arg1) {
+ // Ignore (not relevant to test failures)
+ }
+
+ public void info(String msg) {
+ // Ignore (not relevant to test failures)
+ }
+
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ public void setDebugEnabled(boolean enabled) {
+ // Ignore (not relevant to test failures)
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/TestRequestLog.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/TestRequestLog.java
new file mode 100644
index 0000000000..904f6aac8e
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/TestRequestLog.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010, 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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+
+/** Logs request made through {@link AppServer}. */
+class TestRequestLog extends AbstractLifeCycle implements RequestLog {
+ private final List<AccessEvent> events = new ArrayList<AccessEvent>();
+
+ /** Reset the log back to its original empty state. */
+ synchronized void clear() {
+ events.clear();
+ }
+
+ /** @return all of the events made since the last clear. */
+ synchronized List<AccessEvent> getEvents() {
+ return events;
+ }
+
+ public synchronized void log(Request request, Response response) {
+ events.add(new AccessEvent(request, response));
+ }
+}