View Javadoc
1   /*
2    * Copyright (C) 2015, christian.Halstrick <christian.halstrick@sap.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.http.test;
44  
45  import static org.junit.Assert.assertTrue;
46  import static org.junit.Assert.fail;
47  
48  import java.util.Collection;
49  import java.util.Collections;
50  
51  import javax.servlet.http.HttpServletRequest;
52  
53  import org.eclipse.jetty.servlet.ServletContextHandler;
54  import org.eclipse.jetty.servlet.ServletHolder;
55  import org.eclipse.jgit.errors.CorruptObjectException;
56  import org.eclipse.jgit.errors.RepositoryNotFoundException;
57  import org.eclipse.jgit.errors.TooLargePackException;
58  import org.eclipse.jgit.errors.TransportException;
59  import org.eclipse.jgit.http.server.GitServlet;
60  import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
61  import org.eclipse.jgit.junit.TestRepository;
62  import org.eclipse.jgit.junit.http.HttpTestCase;
63  import org.eclipse.jgit.lib.AnyObjectId;
64  import org.eclipse.jgit.lib.Constants;
65  import org.eclipse.jgit.lib.NullProgressMonitor;
66  import org.eclipse.jgit.lib.ObjectChecker;
67  import org.eclipse.jgit.lib.Repository;
68  import org.eclipse.jgit.lib.StoredConfig;
69  import org.eclipse.jgit.revwalk.RevBlob;
70  import org.eclipse.jgit.revwalk.RevCommit;
71  import org.eclipse.jgit.transport.PostReceiveHook;
72  import org.eclipse.jgit.transport.PreReceiveHook;
73  import org.eclipse.jgit.transport.ReceiveCommand;
74  import org.eclipse.jgit.transport.ReceivePack;
75  import org.eclipse.jgit.transport.RemoteRefUpdate;
76  import org.eclipse.jgit.transport.Transport;
77  import org.eclipse.jgit.transport.URIish;
78  import org.eclipse.jgit.transport.resolver.RepositoryResolver;
79  import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
80  import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
81  import org.junit.Before;
82  import org.junit.Test;
83  
84  /**
85   * Tests for correct responses of {@link GitServlet}. Especially error
86   * situations where the {@link GitServlet} faces exceptions during request
87   * processing are tested
88   */
89  public class GitServletResponseTests extends HttpTestCase {
90  	private Repository srvRepo;
91  
92  	private URIish srvURI;
93  
94  	private GitServlet gs;
95  
96  	private long maxPackSize = 0; // the maximum pack file size used by
97  									// the server
98  
99  	private PostReceiveHook postHook = null;
100 
101 	private PreReceiveHook preHook = null;
102 
103 	private ObjectChecker oc = null;
104 
105 	/**
106 	 * Setup a http server using {@link GitServlet}. Tests should be able to
107 	 * configure the maximum pack file size, the object checker and custom hooks
108 	 * just before they talk to the server.
109 	 */
110 	@Override
111 	@Before
112 	public void setUp() throws Exception {
113 		super.setUp();
114 
115 		final TestRepository<Repository> srv = createTestRepository();
116 		final String repoName = srv.getRepository().getDirectory().getName();
117 
118 		ServletContextHandler app = server.addContext("/git");
119 		gs = new GitServlet();
120 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
121 			@Override
122 			public Repository open(HttpServletRequest req, String name)
123 					throws RepositoryNotFoundException,
124 					ServiceNotEnabledException {
125 				if (!name.equals(repoName))
126 					throw new RepositoryNotFoundException(name);
127 
128 				final Repository db = srv.getRepository();
129 				db.incrementOpen();
130 				return db;
131 			}
132 		});
133 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
134 			@Override
135 			public ReceivePack create(HttpServletRequest req, Repository db)
136 					throws ServiceNotEnabledException,
137 					ServiceNotAuthorizedException {
138 				ReceivePack recv = super.create(req, db);
139 				if (maxPackSize > 0)
140 					recv.setMaxPackSizeLimit(maxPackSize);
141 				if (postHook != null)
142 					recv.setPostReceiveHook(postHook);
143 				if (preHook != null)
144 					recv.setPreReceiveHook(preHook);
145 				if (oc != null)
146 					recv.setObjectChecker(oc);
147 				return recv;
148 			}
149 
150 		});
151 		app.addServlet(new ServletHolder(gs), "/*");
152 
153 		server.setUp();
154 
155 		srvRepo = srv.getRepository();
156 		srvURI = toURIish(app, repoName);
157 
158 		StoredConfig cfg = srvRepo.getConfig();
159 		cfg.setBoolean("http", null, "receivepack", true);
160 		cfg.save();
161 	}
162 
163 	/**
164 	 * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
165 	 * during executing preReceiveHooks. This used to lead to exceptions with a
166 	 * description of "invalid channel 101" on the client side. Make sure
167 	 * clients receive the correct response on the correct sideband.
168 	 *
169 	 * @throws Exception
170 	 */
171 	@Test
172 	public void testRuntimeExceptionInPreReceiveHook() throws Exception {
173 		final TestRepository client = createTestRepository();
174 		final RevBlob Q_txt = client
175 				.blob("some blob content to measure pack size");
176 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
177 		final Repository clientRepo = client.getRepository();
178 		final String srvBranchName = Constants.R_HEADS + "new.branch";
179 
180 		maxPackSize = 0;
181 		postHook = null;
182 		preHook = new PreReceiveHook() {
183 			@Override
184 			public void onPreReceive(ReceivePack rp,
185 					Collection<ReceiveCommand> commands) {
186 				throw new IllegalStateException();
187 			}
188 		};
189 
190 		try (Transport t = Transport.open(clientRepo, srvURI)) {
191 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
192 					srvBranchName, false, null, null);
193 			try {
194 				t.push(NullProgressMonitor.INSTANCE,
195 						Collections.singleton(update));
196 				fail("should not reach this line");
197 			} catch (Exception e) {
198 				assertTrue(e instanceof TransportException);
199 			}
200 		}
201 	}
202 
203 	/**
204 	 * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
205 	 * during executing objectChecking.
206 	 *
207 	 * @throws Exception
208 	 */
209 	@Test
210 	public void testObjectCheckerException() throws Exception {
211 		final TestRepository client = createTestRepository();
212 		final RevBlob Q_txt = client
213 				.blob("some blob content to measure pack size");
214 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
215 		final Repository clientRepo = client.getRepository();
216 		final String srvBranchName = Constants.R_HEADS + "new.branch";
217 
218 		maxPackSize = 0;
219 		postHook = null;
220 		preHook = null;
221 		oc = new ObjectChecker() {
222 			@Override
223 			public void checkCommit(AnyObjectId id, byte[] raw)
224 					throws CorruptObjectException {
225 				throw new CorruptObjectException("refusing all commits");
226 			}
227 		};
228 
229 		try (Transport t = Transport.open(clientRepo, srvURI)) {
230 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
231 					srvBranchName, false, null, null);
232 			try {
233 				t.push(NullProgressMonitor.INSTANCE,
234 						Collections.singleton(update));
235 				fail("should not reach this line");
236 			} catch (Exception e) {
237 				assertTrue(e instanceof TransportException);
238 			}
239 		}
240 	}
241 
242 	/**
243 	 * Configure a {@link GitServlet} that faces a {@link TooLargePackException}
244 	 * during persisting the pack and a {@link IllegalStateException} during
245 	 * executing postReceiveHooks. This used to lead to exceptions with a
246 	 * description of "invalid channel 101" on the client side. Make sure
247 	 * clients receive the correct response about the too large pack on the
248 	 * correct sideband.
249 	 *
250 	 * @throws Exception
251 	 */
252 	@Test
253 	public void testUnpackErrorWithSubsequentExceptionInPostReceiveHook()
254 			throws Exception {
255 		final TestRepository client = createTestRepository();
256 		final RevBlob Q_txt = client
257 				.blob("some blob content to measure pack size");
258 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
259 		final Repository clientRepo = client.getRepository();
260 		final String srvBranchName = Constants.R_HEADS + "new.branch";
261 
262 		// this maxPackSize leads to an unPackError
263 		maxPackSize = 100;
264 		// this PostReceiveHook when called after an unsuccesfull unpack will
265 		// lead to an IllegalStateException
266 		postHook = new PostReceiveHook() {
267 			@Override
268 			public void onPostReceive(ReceivePack rp,
269 					Collection<ReceiveCommand> commands) {
270 				// the maxPackSize setting caused that the packfile couldn't be
271 				// saved to disk. Calling getPackSize() now will lead to a
272 				// IllegalStateException.
273 				rp.getPackSize();
274 			}
275 		};
276 
277 		try (Transport t = Transport.open(clientRepo, srvURI)) {
278 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
279 					srvBranchName, false, null, null);
280 			try {
281 				t.push(NullProgressMonitor.INSTANCE,
282 						Collections.singleton(update));
283 				fail("should not reach this line");
284 			} catch (Exception e) {
285 				assertTrue(e instanceof TooLargePackException);
286 			}
287 		}
288 	}
289 }