xref: /JGit/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
181fea92eSShawn O. Pearce /*
2*5c5f7c6bSMatthias Sohn  * Copyright (C) 2009-2010, Google Inc. and others
381fea92eSShawn O. Pearce  *
4*5c5f7c6bSMatthias Sohn  * This program and the accompanying materials are made available under the
5*5c5f7c6bSMatthias Sohn  * terms of the Eclipse Distribution License v. 1.0 which is available at
6*5c5f7c6bSMatthias Sohn  * https://www.eclipse.org/org/documents/edl-v10.php.
781fea92eSShawn O. Pearce  *
8*5c5f7c6bSMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
981fea92eSShawn O. Pearce  */
1081fea92eSShawn O. Pearce 
1181fea92eSShawn O. Pearce package org.eclipse.jgit.http.server;
1281fea92eSShawn O. Pearce 
1381fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
1481fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
1581fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
1681fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
17867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK;
18867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE;
19867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE;
20867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
21af3562f7SShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
22db00632dSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
2381fea92eSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
2481fea92eSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
251c6c73c5SShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
2681fea92eSShawn O. Pearce 
2781fea92eSShawn O. Pearce import java.io.IOException;
28ae592cc6SShawn Pearce import java.text.MessageFormat;
29af3562f7SShawn O. Pearce import java.util.List;
3081fea92eSShawn O. Pearce 
31af3562f7SShawn O. Pearce import javax.servlet.Filter;
32af3562f7SShawn O. Pearce import javax.servlet.FilterChain;
33af3562f7SShawn O. Pearce import javax.servlet.FilterConfig;
34af3562f7SShawn O. Pearce import javax.servlet.ServletException;
35af3562f7SShawn O. Pearce import javax.servlet.ServletRequest;
36af3562f7SShawn O. Pearce import javax.servlet.ServletResponse;
3781fea92eSShawn O. Pearce import javax.servlet.http.HttpServlet;
3881fea92eSShawn O. Pearce import javax.servlet.http.HttpServletRequest;
3981fea92eSShawn O. Pearce import javax.servlet.http.HttpServletResponse;
4081fea92eSShawn O. Pearce 
4175ec5b74SMasaya Suzuki import org.eclipse.jgit.annotations.Nullable;
42a870a8a0SShawn Pearce import org.eclipse.jgit.errors.CorruptObjectException;
436e4e34bbSDave Borowitz import org.eclipse.jgit.errors.PackProtocolException;
447ff6eb58SShawn O. Pearce import org.eclipse.jgit.errors.UnpackException;
4581fea92eSShawn O. Pearce import org.eclipse.jgit.lib.Repository;
464a984e20SShawn Pearce import org.eclipse.jgit.transport.InternalHttpServerGlue;
4781fea92eSShawn O. Pearce import org.eclipse.jgit.transport.ReceivePack;
4881fea92eSShawn O. Pearce import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
491b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
501b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
511b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
5281fea92eSShawn O. Pearce 
5381fea92eSShawn O. Pearce /** Server side implementation of smart push over HTTP. */
5481fea92eSShawn O. Pearce class ReceivePackServlet extends HttpServlet {
5581fea92eSShawn O. Pearce 	private static final long serialVersionUID = 1L;
5681fea92eSShawn O. Pearce 
5781fea92eSShawn O. Pearce 	static class InfoRefs extends SmartServiceInfoRefs {
58af3562f7SShawn O. Pearce 		private final ReceivePackFactory<HttpServletRequest> receivePackFactory;
5981fea92eSShawn O. Pearce 
InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, List<Filter> filters)60af3562f7SShawn O. Pearce 		InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory,
61af3562f7SShawn O. Pearce 				List<Filter> filters) {
62867087bbSShawn O. Pearce 			super(RECEIVE_PACK, filters);
6381fea92eSShawn O. Pearce 			this.receivePackFactory = receivePackFactory;
6481fea92eSShawn O. Pearce 		}
6581fea92eSShawn O. Pearce 
6681fea92eSShawn O. Pearce 		@Override
begin(HttpServletRequest req, Repository db)67af3562f7SShawn O. Pearce 		protected void begin(HttpServletRequest req, Repository db)
68af3562f7SShawn O. Pearce 				throws IOException, ServiceNotEnabledException,
69af3562f7SShawn O. Pearce 				ServiceNotAuthorizedException {
70af3562f7SShawn O. Pearce 			ReceivePack rp = receivePackFactory.create(req, db);
714a984e20SShawn Pearce 			InternalHttpServerGlue.setPeerUserAgent(
724a984e20SShawn Pearce 					rp,
734a984e20SShawn Pearce 					req.getHeader(HDR_USER_AGENT));
74af3562f7SShawn O. Pearce 			req.setAttribute(ATTRIBUTE_HANDLER, rp);
75af3562f7SShawn O. Pearce 		}
76af3562f7SShawn O. Pearce 
77af3562f7SShawn O. Pearce 		@Override
advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)78af3562f7SShawn O. Pearce 		protected void advertise(HttpServletRequest req,
7981fea92eSShawn O. Pearce 				PacketLineOutRefAdvertiser pck) throws IOException,
8081fea92eSShawn O. Pearce 				ServiceNotEnabledException, ServiceNotAuthorizedException {
81af3562f7SShawn O. Pearce 			ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
82515deaf7SShawn O. Pearce 			try {
83515deaf7SShawn O. Pearce 				rp.sendAdvertisedRefs(pck);
84515deaf7SShawn O. Pearce 			} finally {
85686124beSMatthias Sohn 				rp.getRevWalk().close();
86515deaf7SShawn O. Pearce 			}
8781fea92eSShawn O. Pearce 		}
8881fea92eSShawn O. Pearce 	}
8981fea92eSShawn O. Pearce 
90af3562f7SShawn O. Pearce 	static class Factory implements Filter {
91af3562f7SShawn O. Pearce 		private final ReceivePackFactory<HttpServletRequest> receivePackFactory;
9281fea92eSShawn O. Pearce 
Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory)93af3562f7SShawn O. Pearce 		Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory) {
9481fea92eSShawn O. Pearce 			this.receivePackFactory = receivePackFactory;
9581fea92eSShawn O. Pearce 		}
9681fea92eSShawn O. Pearce 
977ac182f4SDavid Pursehouse 		@Override
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)98af3562f7SShawn O. Pearce 		public void doFilter(ServletRequest request, ServletResponse response,
99af3562f7SShawn O. Pearce 				FilterChain chain) throws IOException, ServletException {
100af3562f7SShawn O. Pearce 			HttpServletRequest req = (HttpServletRequest) request;
101af3562f7SShawn O. Pearce 			HttpServletResponse rsp = (HttpServletResponse) response;
102af3562f7SShawn O. Pearce 			ReceivePack rp;
103af3562f7SShawn O. Pearce 			try {
104af3562f7SShawn O. Pearce 				rp = receivePackFactory.create(req, getRepository(req));
105af3562f7SShawn O. Pearce 			} catch (ServiceNotAuthorizedException e) {
106cc4f4f2fSJonathan Nieder 				rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
107af3562f7SShawn O. Pearce 				return;
108af3562f7SShawn O. Pearce 			} catch (ServiceNotEnabledException e) {
109cc4f4f2fSJonathan Nieder 				sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
110af3562f7SShawn O. Pearce 				return;
111af3562f7SShawn O. Pearce 			}
112af3562f7SShawn O. Pearce 
113af3562f7SShawn O. Pearce 			try {
114af3562f7SShawn O. Pearce 				req.setAttribute(ATTRIBUTE_HANDLER, rp);
115af3562f7SShawn O. Pearce 				chain.doFilter(req, rsp);
116af3562f7SShawn O. Pearce 			} finally {
117af3562f7SShawn O. Pearce 				req.removeAttribute(ATTRIBUTE_HANDLER);
118af3562f7SShawn O. Pearce 			}
119af3562f7SShawn O. Pearce 		}
120af3562f7SShawn O. Pearce 
1217ac182f4SDavid Pursehouse 		@Override
init(FilterConfig filterConfig)122af3562f7SShawn O. Pearce 		public void init(FilterConfig filterConfig) throws ServletException {
123af3562f7SShawn O. Pearce 			// Nothing.
124af3562f7SShawn O. Pearce 		}
125af3562f7SShawn O. Pearce 
1267ac182f4SDavid Pursehouse 		@Override
destroy()127af3562f7SShawn O. Pearce 		public void destroy() {
128af3562f7SShawn O. Pearce 			// Nothing.
129af3562f7SShawn O. Pearce 		}
130af3562f7SShawn O. Pearce 	}
131af3562f7SShawn O. Pearce 
13275ec5b74SMasaya Suzuki 	@Nullable
13375ec5b74SMasaya Suzuki 	private final ReceivePackErrorHandler handler;
13475ec5b74SMasaya Suzuki 
ReceivePackServlet(@ullable ReceivePackErrorHandler handler)13575ec5b74SMasaya Suzuki 	ReceivePackServlet(@Nullable ReceivePackErrorHandler handler) {
13675ec5b74SMasaya Suzuki 		this.handler = handler;
13775ec5b74SMasaya Suzuki 	}
13875ec5b74SMasaya Suzuki 
1393b00041cSMatthias Sohn 	/** {@inheritDoc} */
14081fea92eSShawn O. Pearce 	@Override
doPost(final HttpServletRequest req, final HttpServletResponse rsp)14181fea92eSShawn O. Pearce 	public void doPost(final HttpServletRequest req,
14281fea92eSShawn O. Pearce 			final HttpServletResponse rsp) throws IOException {
143867087bbSShawn O. Pearce 		if (!RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType())) {
14481fea92eSShawn O. Pearce 			rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
14581fea92eSShawn O. Pearce 			return;
14681fea92eSShawn O. Pearce 		}
14781fea92eSShawn O. Pearce 
14820e3779eSShawn Pearce 		SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
149bd531eb9SShawn O. Pearce 			@Override
150bd531eb9SShawn O. Pearce 			public void flush() throws IOException {
151bd531eb9SShawn O. Pearce 				doFlush();
152bd531eb9SShawn O. Pearce 			}
153bd531eb9SShawn O. Pearce 		};
154ac6cda95SShawn O. Pearce 
155ac6cda95SShawn O. Pearce 		ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
156ac6cda95SShawn O. Pearce 		rp.setBiDirectionalPipe(false);
157ac6cda95SShawn O. Pearce 		rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
158ac6cda95SShawn O. Pearce 
15975ec5b74SMasaya Suzuki 		if (handler != null) {
16075ec5b74SMasaya Suzuki 			handler.receive(req, rsp, () -> {
16175ec5b74SMasaya Suzuki 				rp.receiveWithExceptionPropagation(getInputStream(req), out,
16275ec5b74SMasaya Suzuki 						null);
16375ec5b74SMasaya Suzuki 				out.close();
16475ec5b74SMasaya Suzuki 			});
16575ec5b74SMasaya Suzuki 		} else {
16675ec5b74SMasaya Suzuki 			try {
16781fea92eSShawn O. Pearce 				rp.receive(getInputStream(req), out, null);
168c0f09389SShawn O. Pearce 				out.close();
169a870a8a0SShawn Pearce 			} catch (CorruptObjectException e ) {
170a870a8a0SShawn Pearce 				// This should be already reported to the client.
171a870a8a0SShawn Pearce 				getServletContext().log(MessageFormat.format(
172a870a8a0SShawn Pearce 						HttpServerText.get().receivedCorruptObject,
173a870a8a0SShawn Pearce 						e.getMessage(),
174a870a8a0SShawn Pearce 						ServletUtils.identify(rp.getRepository())));
175a870a8a0SShawn Pearce 				consumeRequestBody(req);
176a870a8a0SShawn Pearce 				out.close();
177a870a8a0SShawn Pearce 
1786e4e34bbSDave Borowitz 			} catch (UnpackException | PackProtocolException e) {
1797ff6eb58SShawn O. Pearce 				// This should be already reported to the client.
180ae592cc6SShawn Pearce 				log(rp.getRepository(), e.getCause());
181db00632dSShawn O. Pearce 				consumeRequestBody(req);
182ac6cda95SShawn O. Pearce 				out.close();
1837ff6eb58SShawn O. Pearce 
184ac6cda95SShawn O. Pearce 			} catch (Throwable e) {
185ae592cc6SShawn Pearce 				log(rp.getRepository(), e);
1867ff6eb58SShawn O. Pearce 				if (!rsp.isCommitted()) {
1877ff6eb58SShawn O. Pearce 					rsp.reset();
188867087bbSShawn O. Pearce 					sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
1897ff6eb58SShawn O. Pearce 				}
19081fea92eSShawn O. Pearce 				return;
19181fea92eSShawn O. Pearce 			}
19281fea92eSShawn O. Pearce 		}
19375ec5b74SMasaya Suzuki 	}
194ae592cc6SShawn Pearce 
log(Repository git, Throwable e)195ae592cc6SShawn Pearce 	private void log(Repository git, Throwable e) {
196ae592cc6SShawn Pearce 		getServletContext().log(MessageFormat.format(
197ae592cc6SShawn Pearce 				HttpServerText.get().internalErrorDuringReceivePack,
198ae592cc6SShawn Pearce 				ServletUtils.identify(git)), e);
199ae592cc6SShawn Pearce 	}
20081fea92eSShawn O. Pearce }
201