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 	@Before
111 	public void setUp() throws Exception {
112 		super.setUp();
113 
114 		final TestRepository<Repository> srv = createTestRepository();
115 		final String repoName = srv.getRepository().getDirectory().getName();
116 
117 		ServletContextHandler app = server.addContext("/git");
118 		gs = new GitServlet();
119 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
120 			public Repository open(HttpServletRequest req, String name)
121 					throws RepositoryNotFoundException,
122 					ServiceNotEnabledException {
123 				if (!name.equals(repoName))
124 					throw new RepositoryNotFoundException(name);
125 
126 				final Repository db = srv.getRepository();
127 				db.incrementOpen();
128 				return db;
129 			}
130 		});
131 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
132 			public ReceivePack create(HttpServletRequest req, Repository db)
133 					throws ServiceNotEnabledException,
134 					ServiceNotAuthorizedException {
135 				ReceivePack recv = super.create(req, db);
136 				if (maxPackSize > 0)
137 					recv.setMaxPackSizeLimit(maxPackSize);
138 				if (postHook != null)
139 					recv.setPostReceiveHook(postHook);
140 				if (preHook != null)
141 					recv.setPreReceiveHook(preHook);
142 				if (oc != null)
143 					recv.setObjectChecker(oc);
144 				return recv;
145 			}
146 
147 		});
148 		app.addServlet(new ServletHolder(gs), "/*");
149 
150 		server.setUp();
151 
152 		srvRepo = srv.getRepository();
153 		srvURI = toURIish(app, repoName);
154 
155 		StoredConfig cfg = srvRepo.getConfig();
156 		cfg.setBoolean("http", null, "receivepack", true);
157 		cfg.save();
158 	}
159 
160 	/**
161 	 * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
162 	 * during executing preReceiveHooks. This used to lead to exceptions with a
163 	 * description of "invalid channel 101" on the client side. Make sure
164 	 * clients receive the correct response on the correct sideband.
165 	 *
166 	 * @throws Exception
167 	 */
168 	@Test
169 	public void testRuntimeExceptionInPreReceiveHook() throws Exception {
170 		final TestRepository client = createTestRepository();
171 		final RevBlob Q_txt = client
172 				.blob("some blob content to measure pack size");
173 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
174 		final Repository clientRepo = client.getRepository();
175 		final String srvBranchName = Constants.R_HEADS + "new.branch";
176 		Transport t;
177 
178 		maxPackSize = 0;
179 		postHook = null;
180 		preHook = new PreReceiveHook() {
181 			@Override
182 			public void onPreReceive(ReceivePack rp,
183 					Collection<ReceiveCommand> commands) {
184 				throw new IllegalStateException();
185 			}
186 		};
187 
188 		t = Transport.open(clientRepo, srvURI);
189 		try {
190 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
191 					srvBranchName, false, null, null);
192 			try {
193 				t.push(NullProgressMonitor.INSTANCE,
194 						Collections.singleton(update));
195 				fail("should not reach this line");
196 			} catch (Exception e) {
197 				assertTrue(e instanceof TransportException);
198 			}
199 		} finally {
200 			t.close();
201 		}
202 	}
203 
204 	/**
205 	 * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
206 	 * during executing objectChecking.
207 	 *
208 	 * @throws Exception
209 	 */
210 	@Test
211 	public void testObjectCheckerException() throws Exception {
212 		final TestRepository client = createTestRepository();
213 		final RevBlob Q_txt = client
214 				.blob("some blob content to measure pack size");
215 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
216 		final Repository clientRepo = client.getRepository();
217 		final String srvBranchName = Constants.R_HEADS + "new.branch";
218 		Transport t;
219 
220 		maxPackSize = 0;
221 		postHook = null;
222 		preHook = null;
223 		oc = new ObjectChecker() {
224 			@Override
225 			public void checkCommit(AnyObjectId id, byte[] raw)
226 					throws CorruptObjectException {
227 				throw new CorruptObjectException("refusing all commits");
228 			}
229 		};
230 
231 		t = Transport.open(clientRepo, srvURI);
232 		try {
233 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
234 					srvBranchName, false, null, null);
235 			try {
236 				t.push(NullProgressMonitor.INSTANCE,
237 						Collections.singleton(update));
238 				fail("should not reach this line");
239 			} catch (Exception e) {
240 				assertTrue(e instanceof TransportException);
241 			}
242 		} finally {
243 			t.close();
244 		}
245 	}
246 
247 	/**
248 	 * Configure a {@link GitServlet} that faces a {@link TooLargePackException}
249 	 * during persisting the pack and a {@link IllegalStateException} during
250 	 * executing postReceiveHooks. This used to lead to exceptions with a
251 	 * description of "invalid channel 101" on the client side. Make sure
252 	 * clients receive the correct response about the too large pack on the
253 	 * correct sideband.
254 	 *
255 	 * @throws Exception
256 	 */
257 	@Test
258 	public void testUnpackErrorWithSubsequentExceptionInPostReceiveHook()
259 			throws Exception {
260 		final TestRepository client = createTestRepository();
261 		final RevBlob Q_txt = client
262 				.blob("some blob content to measure pack size");
263 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
264 		final Repository clientRepo = client.getRepository();
265 		final String srvBranchName = Constants.R_HEADS + "new.branch";
266 		Transport t;
267 
268 		// this maxPackSize leads to an unPackError
269 		maxPackSize = 400;
270 		// this PostReceiveHook when called after an unsuccesfull unpack will
271 		// lead to an IllegalStateException
272 		postHook = new PostReceiveHook() {
273 			public void onPostReceive(ReceivePack rp,
274 					Collection<ReceiveCommand> commands) {
275 				// the maxPackSize setting caused that the packfile couldn't be
276 				// saved to disk. Calling getPackSize() now will lead to a
277 				// IllegalStateException.
278 				rp.getPackSize();
279 			}
280 		};
281 
282 		t = Transport.open(clientRepo, srvURI);
283 		try {
284 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
285 					srvBranchName, false, null, null);
286 			try {
287 				t.push(NullProgressMonitor.INSTANCE,
288 						Collections.singleton(update));
289 				fail("should not reach this line");
290 			} catch (Exception e) {
291 				assertTrue(e instanceof TooLargePackException);
292 			}
293 		} finally {
294 			t.close();
295 		}
296 	}
297 }