xref: /JGit/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
1 /*
2  * Copyright (C) 2009-2010, Google Inc. and others
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Distribution License v. 1.0 which is available at
6  * https://www.eclipse.org/org/documents/edl-v10.php.
7  *
8  * SPDX-License-Identifier: BSD-3-Clause
9  */
10 
11 package org.eclipse.jgit.http.server;
12 
13 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
14 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
15 import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
16 import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
17 import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK;
18 import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE;
19 import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE;
20 import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
21 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
22 import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
23 import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
24 import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
25 import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
26 
27 import java.io.IOException;
28 import java.text.MessageFormat;
29 import java.util.List;
30 
31 import javax.servlet.Filter;
32 import javax.servlet.FilterChain;
33 import javax.servlet.FilterConfig;
34 import javax.servlet.ServletException;
35 import javax.servlet.ServletRequest;
36 import javax.servlet.ServletResponse;
37 import javax.servlet.http.HttpServlet;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40 
41 import org.eclipse.jgit.annotations.Nullable;
42 import org.eclipse.jgit.errors.CorruptObjectException;
43 import org.eclipse.jgit.errors.PackProtocolException;
44 import org.eclipse.jgit.errors.UnpackException;
45 import org.eclipse.jgit.lib.Repository;
46 import org.eclipse.jgit.transport.InternalHttpServerGlue;
47 import org.eclipse.jgit.transport.ReceivePack;
48 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
49 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
50 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
51 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
52 
53 /** Server side implementation of smart push over HTTP. */
54 class ReceivePackServlet extends HttpServlet {
55 	private static final long serialVersionUID = 1L;
56 
57 	static class InfoRefs extends SmartServiceInfoRefs {
58 		private final ReceivePackFactory<HttpServletRequest> receivePackFactory;
59 
InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, List<Filter> filters)60 		InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory,
61 				List<Filter> filters) {
62 			super(RECEIVE_PACK, filters);
63 			this.receivePackFactory = receivePackFactory;
64 		}
65 
66 		@Override
begin(HttpServletRequest req, Repository db)67 		protected void begin(HttpServletRequest req, Repository db)
68 				throws IOException, ServiceNotEnabledException,
69 				ServiceNotAuthorizedException {
70 			ReceivePack rp = receivePackFactory.create(req, db);
71 			InternalHttpServerGlue.setPeerUserAgent(
72 					rp,
73 					req.getHeader(HDR_USER_AGENT));
74 			req.setAttribute(ATTRIBUTE_HANDLER, rp);
75 		}
76 
77 		@Override
advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)78 		protected void advertise(HttpServletRequest req,
79 				PacketLineOutRefAdvertiser pck) throws IOException,
80 				ServiceNotEnabledException, ServiceNotAuthorizedException {
81 			ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
82 			try {
83 				rp.sendAdvertisedRefs(pck);
84 			} finally {
85 				rp.getRevWalk().close();
86 			}
87 		}
88 	}
89 
90 	static class Factory implements Filter {
91 		private final ReceivePackFactory<HttpServletRequest> receivePackFactory;
92 
Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory)93 		Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory) {
94 			this.receivePackFactory = receivePackFactory;
95 		}
96 
97 		@Override
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)98 		public void doFilter(ServletRequest request, ServletResponse response,
99 				FilterChain chain) throws IOException, ServletException {
100 			HttpServletRequest req = (HttpServletRequest) request;
101 			HttpServletResponse rsp = (HttpServletResponse) response;
102 			ReceivePack rp;
103 			try {
104 				rp = receivePackFactory.create(req, getRepository(req));
105 			} catch (ServiceNotAuthorizedException e) {
106 				rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
107 				return;
108 			} catch (ServiceNotEnabledException e) {
109 				sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
110 				return;
111 			}
112 
113 			try {
114 				req.setAttribute(ATTRIBUTE_HANDLER, rp);
115 				chain.doFilter(req, rsp);
116 			} finally {
117 				req.removeAttribute(ATTRIBUTE_HANDLER);
118 			}
119 		}
120 
121 		@Override
init(FilterConfig filterConfig)122 		public void init(FilterConfig filterConfig) throws ServletException {
123 			// Nothing.
124 		}
125 
126 		@Override
destroy()127 		public void destroy() {
128 			// Nothing.
129 		}
130 	}
131 
132 	@Nullable
133 	private final ReceivePackErrorHandler handler;
134 
ReceivePackServlet(@ullable ReceivePackErrorHandler handler)135 	ReceivePackServlet(@Nullable ReceivePackErrorHandler handler) {
136 		this.handler = handler;
137 	}
138 
139 	/** {@inheritDoc} */
140 	@Override
doPost(final HttpServletRequest req, final HttpServletResponse rsp)141 	public void doPost(final HttpServletRequest req,
142 			final HttpServletResponse rsp) throws IOException {
143 		if (!RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType())) {
144 			rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
145 			return;
146 		}
147 
148 		SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
149 			@Override
150 			public void flush() throws IOException {
151 				doFlush();
152 			}
153 		};
154 
155 		ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
156 		rp.setBiDirectionalPipe(false);
157 		rsp.setContentType(RECEIVE_PACK_RESULT_TYPE);
158 
159 		if (handler != null) {
160 			handler.receive(req, rsp, () -> {
161 				rp.receiveWithExceptionPropagation(getInputStream(req), out,
162 						null);
163 				out.close();
164 			});
165 		} else {
166 			try {
167 				rp.receive(getInputStream(req), out, null);
168 				out.close();
169 			} catch (CorruptObjectException e ) {
170 				// This should be already reported to the client.
171 				getServletContext().log(MessageFormat.format(
172 						HttpServerText.get().receivedCorruptObject,
173 						e.getMessage(),
174 						ServletUtils.identify(rp.getRepository())));
175 				consumeRequestBody(req);
176 				out.close();
177 
178 			} catch (UnpackException | PackProtocolException e) {
179 				// This should be already reported to the client.
180 				log(rp.getRepository(), e.getCause());
181 				consumeRequestBody(req);
182 				out.close();
183 
184 			} catch (Throwable e) {
185 				log(rp.getRepository(), e);
186 				if (!rsp.isCommitted()) {
187 					rsp.reset();
188 					sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
189 				}
190 				return;
191 			}
192 		}
193 	}
194 
log(Repository git, Throwable e)195 	private void log(Repository git, Throwable e) {
196 		getServletContext().log(MessageFormat.format(
197 				HttpServerText.get().internalErrorDuringReceivePack,
198 				ServletUtils.identify(git)), e);
199 	}
200 }
201