]> source.dussan.org Git - jgit.git/commitdiff
Add JUnit tests for HTTP transport 79/179/6
authorShawn O. Pearce <spearce@spearce.org>
Tue, 12 Jan 2010 20:30:42 +0000 (12:30 -0800)
committerShawn O. Pearce <spearce@spearce.org>
Tue, 12 Jan 2010 20:30:42 +0000 (12:30 -0800)
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>
22 files changed:
org.eclipse.jgit.http.test/.gitignore [new file with mode: 0644]
org.eclipse.jgit.http.test/pom.xml [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ErrorServletTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletInitTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AccessEvent.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/AppServer.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/MockServletConfig.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/RecordingLogger.java [new file with mode: 0644]
org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/TestRequestLog.java [new file with mode: 0644]
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
pom.xml

diff --git a/org.eclipse.jgit.http.test/.gitignore b/org.eclipse.jgit.http.test/.gitignore
new file mode 100644 (file)
index 0000000..ea8c4bf
--- /dev/null
@@ -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 (file)
index 0000000..fe9363a
--- /dev/null
@@ -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 (file)
index 0000000..ea93748
--- /dev/null
@@ -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 (file)
index 0000000..5338caa
--- /dev/null
@@ -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 (file)
index 0000000..8f57d40
--- /dev/null
@@ -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 (file)
index 0000000..1ec3244
--- /dev/null
@@ -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 (file)
index 0000000..0b28712
--- /dev/null
@@ -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 (file)
index 0000000..18c1bb8
--- /dev/null
@@ -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 (file)
index 0000000..d7ca590
--- /dev/null
@@ -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 (file)
index 0000000..8407036
--- /dev/null
@@ -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 (file)
index 0000000..729466d
--- /dev/null
@@ -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 (file)
index 0000000..fd78bb4
--- /dev/null
@@ -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 (file)
index 0000000..c6d531e
--- /dev/null
@@ -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 (file)
index 0000000..74df708
--- /dev/null
@@ -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 (file)
index 0000000..e259757
--- /dev/null
@@ -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 (file)
index 0000000..cf6f6f6
--- /dev/null
@@ -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 (file)
index 0000000..24e125c
--- /dev/null
@@ -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 (file)
index 0000000..904f6aa
--- /dev/null
@@ -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));
+       }
+}
index ec9b1d7ac361b483194b39c536ca5dd2653a81d7..ddace0df2ed12f7a4391d764ff359123354ff89a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
  * and other copyright owners as documented in the project's IP log.
@@ -57,8 +57,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
+import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileBasedConfig;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -404,6 +406,14 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
                return new String(body, 0, body.length, "UTF-8");
        }
 
+       protected static void assertEquals(AnyObjectId exp, AnyObjectId act) {
+               if (exp != null)
+                       exp = exp.copy();
+               if (act != null)
+                       act = act.copy();
+               Assert.assertEquals(exp, act);
+       }
+
        private static String[] toEnvArray(final Map<String, String> env) {
                final String[] envp = new String[env.size()];
                int i = 0;
index ce8b3e6ee6bd9d6a946368ab3422b54f9400e532..e738276bd84a443262052c5f23cc910d588a71e7 100644 (file)
 package org.eclipse.jgit.junit;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import junit.framework.Assert;
 import junit.framework.AssertionFailedError;
@@ -60,21 +64,30 @@ import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
 import org.eclipse.jgit.dircache.DirCacheEditor.DeleteTree;
 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.ObjectWritingException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Commit;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.LockFile;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectChecker;
+import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectDirectory;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.PackFile;
+import org.eclipse.jgit.lib.PackWriter;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefWriter;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
@@ -428,26 +441,25 @@ public class TestRepository {
         * @throws Exception
         */
        public void updateServerInfo() throws Exception {
-               if (db.getObjectDatabase() instanceof ObjectDirectory) {
+               final ObjectDatabase odb = db.getObjectDatabase();
+               if (odb instanceof ObjectDirectory) {
                        RefWriter rw = new RefWriter(db.getAllRefs().values()) {
                                @Override
                                protected void writeFile(final String name, final byte[] bin)
                                                throws IOException {
-                                       final File p = new File(db.getDirectory(), name);
-                                       final LockFile lck = new LockFile(p);
-                                       if (!lck.lock())
-                                               throw new ObjectWritingException("Can't write " + p);
-                                       try {
-                                               lck.write(bin);
-                                       } catch (IOException ioe) {
-                                               throw new ObjectWritingException("Can't write " + p);
-                                       }
-                                       if (!lck.commit())
-                                               throw new ObjectWritingException("Can't write " + p);
+                                       TestRepository.this.writeFile(name, bin);
                                }
                        };
                        rw.writePackedRefs();
                        rw.writeInfoRefs();
+
+                       final StringBuilder w = new StringBuilder();
+                       for (PackFile p : ((ObjectDirectory) odb).getPacks()) {
+                               w.append("P ");
+                               w.append(p.getPackFile().getName());
+                               w.append('\n');
+                       }
+                       writeFile("objects/info/packs", Constants.encodeASCII(w.toString()));
                }
        }
 
@@ -484,6 +496,132 @@ public class TestRepository {
                return new BranchBuilder(ref);
        }
 
+       /**
+        * Run consistency checks against the object database.
+        * <p>
+        * This method completes silently if the checks pass. A temporary revision
+        * pool is constructed during the checking.
+        *
+        * @param tips
+        *            the tips to start checking from; if not supplied the refs of
+        *            the repository are used instead.
+        * @throws MissingObjectException
+        * @throws IncorrectObjectTypeException
+        * @throws IOException
+        */
+       public void fsck(RevObject... tips) throws MissingObjectException,
+                       IncorrectObjectTypeException, IOException {
+               ObjectWalk ow = new ObjectWalk(db);
+               if (tips.length != 0) {
+                       for (RevObject o : tips)
+                               ow.markStart(ow.parseAny(o));
+               } else {
+                       for (Ref r : db.getAllRefs().values())
+                               ow.markStart(ow.parseAny(r.getObjectId()));
+               }
+
+               ObjectChecker oc = new ObjectChecker();
+               for (;;) {
+                       final RevCommit o = ow.next();
+                       if (o == null)
+                               break;
+
+                       final byte[] bin = db.openObject(o).getCachedBytes();
+                       oc.checkCommit(bin);
+                       assertHash(o, bin);
+               }
+
+               for (;;) {
+                       final RevObject o = ow.nextObject();
+                       if (o == null)
+                               break;
+
+                       final byte[] bin = db.openObject(o).getCachedBytes();
+                       oc.check(o.getType(), bin);
+                       assertHash(o, bin);
+               }
+       }
+
+       private static void assertHash(RevObject id, byte[] bin) {
+               MessageDigest md = Constants.newMessageDigest();
+               md.update(Constants.encodedTypeString(id.getType()));
+               md.update((byte) ' ');
+               md.update(Constants.encodeASCII(bin.length));
+               md.update((byte) 0);
+               md.update(bin);
+               Assert.assertEquals(id.copy(), ObjectId.fromRaw(md.digest()));
+       }
+
+       /**
+        * Pack all reachable objects in the repository into a single pack file.
+        * <p>
+        * All loose objects are automatically pruned. Existing packs however are
+        * not removed.
+        *
+        * @throws Exception
+        */
+       public void packAndPrune() throws Exception {
+               final ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
+               final PackWriter pw = new PackWriter(db, NullProgressMonitor.INSTANCE);
+
+               Set<ObjectId> all = new HashSet<ObjectId>();
+               for (Ref r : db.getAllRefs().values())
+                       all.add(r.getObjectId());
+               pw.preparePack(all, Collections.<ObjectId> emptySet());
+
+               final ObjectId name = pw.computeName();
+               FileOutputStream out;
+
+               final File pack = nameFor(odb, name, ".pack");
+               out = new FileOutputStream(pack);
+               try {
+                       pw.writePack(out);
+               } finally {
+                       out.close();
+               }
+               pack.setReadOnly();
+
+               final File idx = nameFor(odb, name, ".idx");
+               out = new FileOutputStream(idx);
+               try {
+                       pw.writeIndex(out);
+               } finally {
+                       out.close();
+               }
+               idx.setReadOnly();
+
+               odb.openPack(pack, idx);
+               updateServerInfo();
+               prunePacked(odb);
+       }
+
+       private void prunePacked(ObjectDirectory odb) {
+               for (PackFile p : odb.getPacks()) {
+                       for (MutableEntry e : p)
+                               odb.fileFor(e.toObjectId()).delete();
+               }
+       }
+
+       private static File nameFor(ObjectDirectory odb, ObjectId name, String t) {
+               File packdir = new File(odb.getDirectory(), "pack");
+               return new File(packdir, "pack-" + name.name() + t);
+       }
+
+       private void writeFile(final String name, final byte[] bin)
+                       throws IOException, ObjectWritingException {
+               final File p = new File(db.getDirectory(), name);
+               final LockFile lck = new LockFile(p);
+               if (!lck.lock())
+                       throw new ObjectWritingException("Can't write " + p);
+               try {
+                       lck.write(bin);
+               } catch (IOException ioe) {
+                       throw new ObjectWritingException("Can't write " + p);
+               }
+               if (!lck.commit())
+                       throw new ObjectWritingException("Can't write " + p);
+       }
+
        /** Helper to build a branch with one or more commits */
        public class BranchBuilder {
                private final String ref;
index 62303de9f0bd3458ccbeb3125aa197306c91f3bb..c521e805146dd44869525c5c496c14d18721d0b9 100644 (file)
@@ -155,6 +155,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 
        private final ProxySelector proxySelector;
 
+       private boolean useSmartHttp = true;
+
        TransportHttp(final Repository local, final URIish uri)
                        throws NotSupportedException {
                super(local, uri);
@@ -171,6 +173,20 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
                proxySelector = ProxySelector.getDefault();
        }
 
+       /**
+        * Toggle whether or not smart HTTP transport should be used.
+        * <p>
+        * This flag exists primarily to support backwards compatibility testing
+        * within a testing framework, there is no need to modify it in most
+        * applications.
+        *
+        * @param on
+        *            if {@code true} (default), smart HTTP is enabled.
+        */
+       public void setUseSmartHttp(final boolean on) {
+               useSmartHttp = on;
+       }
+
        @Override
        public FetchConnection openFetch() throws TransportException,
                        NotSupportedException {
@@ -271,6 +287,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
                                        readSmartHeaders(in, service);
                                        return new SmartHttpPushConnection(in);
 
+                               } else if (!useSmartHttp) {
+                                       final String msg = "smart HTTP push disabled";
+                                       throw new NotSupportedException(msg);
+
                                } else {
                                        final String msg = "remote does not support smart HTTP push";
                                        throw new NotSupportedException(msg);
@@ -303,9 +323,11 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
                                b.append('/');
                        b.append(Constants.INFO_REFS);
 
-                       b.append(b.indexOf("?") < 0 ? '?' : '&');
-                       b.append("service=");
-                       b.append(service);
+                       if (useSmartHttp) {
+                               b.append(b.indexOf("?") < 0 ? '?' : '&');
+                               b.append("service=");
+                               b.append(service);
+                       }
 
                        u = new URL(b.toString());
                } catch (MalformedURLException e) {
@@ -314,8 +336,12 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 
                try {
                        final HttpURLConnection conn = httpOpen(u);
-                       String expType = "application/x-" + service + "-advertisement";
-                       conn.setRequestProperty(HDR_ACCEPT, expType + ", */*");
+                       if (useSmartHttp) {
+                               String expType = "application/x-" + service + "-advertisement";
+                               conn.setRequestProperty(HDR_ACCEPT, expType + ", */*");
+                       } else {
+                               conn.setRequestProperty(HDR_ACCEPT, "*/*");
+                       }
                        final int status = HttpSupport.response(conn);
                        switch (status) {
                        case HttpURLConnection.HTTP_OK:
diff --git a/pom.xml b/pom.xml
index d3467204f4c794ae5fbe95a8d3524d45bdd9bf1d..d02cafbbe9a8a2709da86e1dd89f4372335d28ac 100644 (file)
--- a/pom.xml
+++ b/pom.xml
 
     <servlet-api-CQ>CQ 3565</servlet-api-CQ>
     <servlet-api-version>2.5</servlet-api-version>
+
+    <jetty-version>7.0.1.v20091125</jetty-version>
   </properties>
 
   <build>
         <artifactId>servlet-api</artifactId>
         <version>${servlet-api-version}</version>
       </dependency>
+
+      <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-servlet</artifactId>
+        <version>${jetty-version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
     <module>org.eclipse.jgit.http.server</module>
     <module>org.eclipse.jgit.pgm</module>
     <module>org.eclipse.jgit.junit</module>
+
     <module>org.eclipse.jgit.test</module>
+    <module>org.eclipse.jgit.http.test</module>
   </modules>
 </project>