xref: /JGit/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
12e521446SShawn O. Pearce /*
2*5c5f7c6bSMatthias Sohn  * Copyright (C) 2009-2010, Google Inc. and others
32e521446SShawn 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.
72e521446SShawn O. Pearce  *
8*5c5f7c6bSMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
92e521446SShawn O. Pearce  */
102e521446SShawn O. Pearce 
112e521446SShawn O. Pearce package org.eclipse.jgit.http.server;
122e521446SShawn O. Pearce 
132e521446SShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
142e521446SShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
152e521446SShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
162e521446SShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
17867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK;
18867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE;
19867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_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;
232e521446SShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
242e521446SShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
251c6c73c5SShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
262e521446SShawn O. Pearce 
272e521446SShawn O. Pearce import java.io.IOException;
28ae592cc6SShawn Pearce import java.text.MessageFormat;
29af3562f7SShawn O. Pearce import java.util.List;
302e521446SShawn 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;
372e521446SShawn O. Pearce import javax.servlet.http.HttpServlet;
382e521446SShawn O. Pearce import javax.servlet.http.HttpServletRequest;
392e521446SShawn O. Pearce import javax.servlet.http.HttpServletResponse;
40d72d6a6bSMasaya Suzuki 
41abedaf0dSMasaya Suzuki import org.eclipse.jgit.annotations.Nullable;
42d72d6a6bSMasaya Suzuki import org.eclipse.jgit.errors.PackProtocolException;
43abedaf0dSMasaya Suzuki import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
442e521446SShawn O. Pearce import org.eclipse.jgit.lib.Repository;
454a984e20SShawn Pearce import org.eclipse.jgit.transport.InternalHttpServerGlue;
46f516c1dfSJonathan Tan import org.eclipse.jgit.transport.PacketLineOut;
472e521446SShawn O. Pearce import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
48ae592cc6SShawn Pearce import org.eclipse.jgit.transport.ServiceMayNotContinueException;
49af3562f7SShawn O. Pearce import org.eclipse.jgit.transport.UploadPack;
507ff6eb58SShawn O. Pearce import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
511b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
521b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
531b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.UploadPackFactory;
542e521446SShawn O. Pearce 
552e521446SShawn O. Pearce /** Server side implementation of smart fetch over HTTP. */
562e521446SShawn O. Pearce class UploadPackServlet extends HttpServlet {
572e521446SShawn O. Pearce 	private static final long serialVersionUID = 1L;
582e521446SShawn O. Pearce 
592e521446SShawn O. Pearce 	static class InfoRefs extends SmartServiceInfoRefs {
60af3562f7SShawn O. Pearce 		private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
612e521446SShawn O. Pearce 
InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory, List<Filter> filters)62af3562f7SShawn O. Pearce 		InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory,
63af3562f7SShawn O. Pearce 				List<Filter> filters) {
64867087bbSShawn O. Pearce 			super(UPLOAD_PACK, filters);
652e521446SShawn O. Pearce 			this.uploadPackFactory = uploadPackFactory;
662e521446SShawn O. Pearce 		}
672e521446SShawn O. Pearce 
682e521446SShawn O. Pearce 		@Override
begin(HttpServletRequest req, Repository db)69af3562f7SShawn O. Pearce 		protected void begin(HttpServletRequest req, Repository db)
70af3562f7SShawn O. Pearce 				throws IOException, ServiceNotEnabledException,
71af3562f7SShawn O. Pearce 				ServiceNotAuthorizedException {
72af3562f7SShawn O. Pearce 			UploadPack up = uploadPackFactory.create(req, db);
734a984e20SShawn Pearce 			InternalHttpServerGlue.setPeerUserAgent(
744a984e20SShawn Pearce 					up,
754a984e20SShawn Pearce 					req.getHeader(HDR_USER_AGENT));
76af3562f7SShawn O. Pearce 			req.setAttribute(ATTRIBUTE_HANDLER, up);
77af3562f7SShawn O. Pearce 		}
78af3562f7SShawn O. Pearce 
79af3562f7SShawn O. Pearce 		@Override
advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)80af3562f7SShawn O. Pearce 		protected void advertise(HttpServletRequest req,
812e521446SShawn O. Pearce 				PacketLineOutRefAdvertiser pck) throws IOException,
822e521446SShawn O. Pearce 				ServiceNotEnabledException, ServiceNotAuthorizedException {
83af3562f7SShawn O. Pearce 			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
84515deaf7SShawn O. Pearce 			try {
85b209671dSShawn O. Pearce 				up.setBiDirectionalPipe(false);
86515deaf7SShawn O. Pearce 				up.sendAdvertisedRefs(pck);
87515deaf7SShawn O. Pearce 			} finally {
88f516c1dfSJonathan Tan 				// TODO(jonathantanmy): Move responsibility for closing the
89f516c1dfSJonathan Tan 				// RevWalk to UploadPack, either by making it AutoCloseable
90f516c1dfSJonathan Tan 				// or by making sendAdvertisedRefs clean up after itself.
91f516c1dfSJonathan Tan 				up.getRevWalk().close();
92f516c1dfSJonathan Tan 			}
93f516c1dfSJonathan Tan 		}
94f516c1dfSJonathan Tan 
95f516c1dfSJonathan Tan 		@Override
respond(HttpServletRequest req, PacketLineOut pckOut, String serviceName)96f516c1dfSJonathan Tan 		protected void respond(HttpServletRequest req,
97f516c1dfSJonathan Tan 				PacketLineOut pckOut, String serviceName) throws IOException,
98f516c1dfSJonathan Tan 				ServiceNotEnabledException, ServiceNotAuthorizedException {
99f516c1dfSJonathan Tan 			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
100f516c1dfSJonathan Tan 			try {
101f516c1dfSJonathan Tan 				up.setBiDirectionalPipe(false);
102f516c1dfSJonathan Tan 				up.sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut), serviceName);
103f516c1dfSJonathan Tan 			} finally {
104f516c1dfSJonathan Tan 				// TODO(jonathantanmy): Move responsibility for closing the
105f516c1dfSJonathan Tan 				// RevWalk to UploadPack, either by making it AutoCloseable
106f516c1dfSJonathan Tan 				// or by making sendAdvertisedRefs clean up after itself.
107686124beSMatthias Sohn 				up.getRevWalk().close();
108515deaf7SShawn O. Pearce 			}
1092e521446SShawn O. Pearce 		}
1102e521446SShawn O. Pearce 	}
1112e521446SShawn O. Pearce 
112af3562f7SShawn O. Pearce 	static class Factory implements Filter {
113af3562f7SShawn O. Pearce 		private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
1142e521446SShawn O. Pearce 
Factory(UploadPackFactory<HttpServletRequest> uploadPackFactory)115af3562f7SShawn O. Pearce 		Factory(UploadPackFactory<HttpServletRequest> uploadPackFactory) {
1162e521446SShawn O. Pearce 			this.uploadPackFactory = uploadPackFactory;
1172e521446SShawn O. Pearce 		}
1182e521446SShawn O. Pearce 
1197ac182f4SDavid Pursehouse 		@Override
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)120af3562f7SShawn O. Pearce 		public void doFilter(ServletRequest request, ServletResponse response,
121af3562f7SShawn O. Pearce 				FilterChain chain) throws IOException, ServletException {
122af3562f7SShawn O. Pearce 			HttpServletRequest req = (HttpServletRequest) request;
123af3562f7SShawn O. Pearce 			HttpServletResponse rsp = (HttpServletResponse) response;
124af3562f7SShawn O. Pearce 			UploadPack rp;
125af3562f7SShawn O. Pearce 			try {
126af3562f7SShawn O. Pearce 				rp = uploadPackFactory.create(req, getRepository(req));
127af3562f7SShawn O. Pearce 			} catch (ServiceNotAuthorizedException e) {
128cc4f4f2fSJonathan Nieder 				rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
129af3562f7SShawn O. Pearce 				return;
130af3562f7SShawn O. Pearce 			} catch (ServiceNotEnabledException e) {
131cc4f4f2fSJonathan Nieder 				sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
132af3562f7SShawn O. Pearce 				return;
133af3562f7SShawn O. Pearce 			}
134af3562f7SShawn O. Pearce 
135af3562f7SShawn O. Pearce 			try {
136af3562f7SShawn O. Pearce 				req.setAttribute(ATTRIBUTE_HANDLER, rp);
137af3562f7SShawn O. Pearce 				chain.doFilter(req, rsp);
138af3562f7SShawn O. Pearce 			} finally {
139af3562f7SShawn O. Pearce 				req.removeAttribute(ATTRIBUTE_HANDLER);
140af3562f7SShawn O. Pearce 			}
141af3562f7SShawn O. Pearce 		}
142af3562f7SShawn O. Pearce 
1437ac182f4SDavid Pursehouse 		@Override
init(FilterConfig filterConfig)144af3562f7SShawn O. Pearce 		public void init(FilterConfig filterConfig) throws ServletException {
145af3562f7SShawn O. Pearce 			// Nothing.
146af3562f7SShawn O. Pearce 		}
147af3562f7SShawn O. Pearce 
1487ac182f4SDavid Pursehouse 		@Override
destroy()149af3562f7SShawn O. Pearce 		public void destroy() {
150af3562f7SShawn O. Pearce 			// Nothing.
151af3562f7SShawn O. Pearce 		}
152af3562f7SShawn O. Pearce 	}
153af3562f7SShawn O. Pearce 
154abedaf0dSMasaya Suzuki 	private final UploadPackErrorHandler handler;
155abedaf0dSMasaya Suzuki 
UploadPackServlet(@ullable UploadPackErrorHandler handler)156abedaf0dSMasaya Suzuki 	UploadPackServlet(@Nullable UploadPackErrorHandler handler) {
157abedaf0dSMasaya Suzuki 		this.handler = handler != null ? handler
158abedaf0dSMasaya Suzuki 				: this::defaultUploadPackHandler;
159abedaf0dSMasaya Suzuki 	}
160abedaf0dSMasaya Suzuki 
1613b00041cSMatthias Sohn 	/** {@inheritDoc} */
1622e521446SShawn O. Pearce 	@Override
doPost(HttpServletRequest req, HttpServletResponse rsp)163abedaf0dSMasaya Suzuki 	public void doPost(HttpServletRequest req, HttpServletResponse rsp)
164abedaf0dSMasaya Suzuki 			throws IOException {
165867087bbSShawn O. Pearce 		if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) {
1662e521446SShawn O. Pearce 			rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
1672e521446SShawn O. Pearce 			return;
1682e521446SShawn O. Pearce 		}
1692e521446SShawn O. Pearce 
170abedaf0dSMasaya Suzuki 		UploadPackRunnable r = () -> {
171abedaf0dSMasaya Suzuki 			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
172abedaf0dSMasaya Suzuki 			@SuppressWarnings("resource")
173f93a6a72SShawn O. Pearce 			SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
174ccd0c0c9SShawn O. Pearce 				@Override
175ccd0c0c9SShawn O. Pearce 				public void flush() throws IOException {
176ccd0c0c9SShawn O. Pearce 					doFlush();
177ccd0c0c9SShawn O. Pearce 				}
178ccd0c0c9SShawn O. Pearce 			};
179ac6cda95SShawn O. Pearce 
180ac6cda95SShawn O. Pearce 			up.setBiDirectionalPipe(false);
181ac6cda95SShawn O. Pearce 			rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
182ac6cda95SShawn O. Pearce 
183abedaf0dSMasaya Suzuki 			try {
184d72d6a6bSMasaya Suzuki 				up.uploadWithExceptionPropagation(getInputStream(req), out,
185d72d6a6bSMasaya Suzuki 						null);
186c0f09389SShawn O. Pearce 				out.close();
1871f2022e3SDave Borowitz 			} catch (ServiceMayNotContinueException e) {
188ac6cda95SShawn O. Pearce 				if (e.isOutput()) {
189db00632dSShawn O. Pearce 					consumeRequestBody(req);
190ac6cda95SShawn O. Pearce 					out.close();
1917ff6eb58SShawn O. Pearce 				}
192abedaf0dSMasaya Suzuki 				throw e;
1937ff6eb58SShawn O. Pearce 			} catch (UploadPackInternalServerErrorException e) {
194867087bbSShawn O. Pearce 				// Special case exception, error message was sent to client.
195ae592cc6SShawn Pearce 				log(up.getRepository(), e.getCause());
196db00632dSShawn O. Pearce 				consumeRequestBody(req);
197ac6cda95SShawn O. Pearce 				out.close();
198abedaf0dSMasaya Suzuki 			}
199abedaf0dSMasaya Suzuki 		};
2007ff6eb58SShawn O. Pearce 
201abedaf0dSMasaya Suzuki 		handler.upload(req, rsp, r);
202abedaf0dSMasaya Suzuki 	}
203abedaf0dSMasaya Suzuki 
defaultUploadPackHandler(HttpServletRequest req, HttpServletResponse rsp, UploadPackRunnable r)204abedaf0dSMasaya Suzuki 	private void defaultUploadPackHandler(HttpServletRequest req,
205abedaf0dSMasaya Suzuki 			HttpServletResponse rsp, UploadPackRunnable r) throws IOException {
206abedaf0dSMasaya Suzuki 		try {
207abedaf0dSMasaya Suzuki 			r.upload();
208abedaf0dSMasaya Suzuki 		} catch (ServiceMayNotContinueException e) {
209abedaf0dSMasaya Suzuki 			if (!e.isOutput() && !rsp.isCommitted()) {
210abedaf0dSMasaya Suzuki 				rsp.reset();
211abedaf0dSMasaya Suzuki 				sendError(req, rsp, e.getStatusCode(), e.getMessage());
212abedaf0dSMasaya Suzuki 			}
213ac6cda95SShawn O. Pearce 		} catch (Throwable e) {
214abedaf0dSMasaya Suzuki 			UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
215ae592cc6SShawn Pearce 			log(up.getRepository(), e);
2167ff6eb58SShawn O. Pearce 			if (!rsp.isCommitted()) {
2172e521446SShawn O. Pearce 				rsp.reset();
218d72d6a6bSMasaya Suzuki 				String msg = e instanceof PackProtocolException ? e.getMessage()
219d72d6a6bSMasaya Suzuki 						: null;
220d72d6a6bSMasaya Suzuki 				sendError(req, rsp, SC_INTERNAL_SERVER_ERROR, msg);
2217ff6eb58SShawn O. Pearce 			}
2222e521446SShawn O. Pearce 		}
2232e521446SShawn O. Pearce 	}
224ae592cc6SShawn Pearce 
log(Repository git, Throwable e)225ae592cc6SShawn Pearce 	private void log(Repository git, Throwable e) {
226ae592cc6SShawn Pearce 		getServletContext().log(MessageFormat.format(
227ae592cc6SShawn Pearce 				HttpServerText.get().internalErrorDuringUploadPack,
228ae592cc6SShawn Pearce 				ServletUtils.identify(git)), e);
229ae592cc6SShawn Pearce 	}
2302e521446SShawn O. Pearce }
231