1
2
3
4
5
6
7
8
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_UNAUTHORIZED;
15 import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
16 import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK;
17 import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE;
18 import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE;
19 import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
20 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
21 import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
22 import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
23 import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
24 import static org.eclipse.jgit.http.server.UploadPackErrorHandler.statusCodeForThrowable;
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.PackProtocolException;
43 import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
44 import org.eclipse.jgit.lib.Repository;
45 import org.eclipse.jgit.transport.InternalHttpServerGlue;
46 import org.eclipse.jgit.transport.PacketLineOut;
47 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
48 import org.eclipse.jgit.transport.ServiceMayNotContinueException;
49 import org.eclipse.jgit.transport.UploadPack;
50 import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
51 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
52 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
53 import org.eclipse.jgit.transport.resolver.UploadPackFactory;
54
55
56 class UploadPackServlet extends HttpServlet {
57 private static final long serialVersionUID = 1L;
58
59 static class InfoRefs extends SmartServiceInfoRefs {
60 private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
61
62 InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory,
63 List<Filter> filters) {
64 super(UPLOAD_PACK, filters);
65 this.uploadPackFactory = uploadPackFactory;
66 }
67
68 @Override
69 protected void begin(HttpServletRequest req, Repository db)
70 throws IOException, ServiceNotEnabledException,
71 ServiceNotAuthorizedException {
72 UploadPack up = uploadPackFactory.create(req, db);
73 InternalHttpServerGlue.setPeerUserAgent(
74 up,
75 req.getHeader(HDR_USER_AGENT));
76 req.setAttribute(ATTRIBUTE_HANDLER, up);
77 }
78
79 @Override
80 protected void advertise(HttpServletRequest req,
81 PacketLineOutRefAdvertiser pck) throws IOException,
82 ServiceNotEnabledException, ServiceNotAuthorizedException {
83 UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
84 try {
85 up.setBiDirectionalPipe(false);
86 up.sendAdvertisedRefs(pck);
87 } finally {
88
89
90
91 up.getRevWalk().close();
92 }
93 }
94
95 @Override
96 protected void respond(HttpServletRequest req,
97 PacketLineOut pckOut, String serviceName) throws IOException,
98 ServiceNotEnabledException, ServiceNotAuthorizedException {
99 UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
100 try {
101 up.setBiDirectionalPipe(false);
102 up.sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut), serviceName);
103 } finally {
104
105
106
107 up.getRevWalk().close();
108 }
109 }
110 }
111
112 static class Factory implements Filter {
113 private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
114
115 Factory(UploadPackFactory<HttpServletRequest> uploadPackFactory) {
116 this.uploadPackFactory = uploadPackFactory;
117 }
118
119 @Override
120 public void doFilter(ServletRequest request, ServletResponse response,
121 FilterChain chain) throws IOException, ServletException {
122 HttpServletRequest req = (HttpServletRequest) request;
123 HttpServletResponse rsp = (HttpServletResponse) response;
124 UploadPack rp;
125 try {
126 rp = uploadPackFactory.create(req, getRepository(req));
127 } catch (ServiceNotAuthorizedException e) {
128 rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
129 return;
130 } catch (ServiceNotEnabledException e) {
131 sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
132 return;
133 }
134
135 try {
136 req.setAttribute(ATTRIBUTE_HANDLER, rp);
137 chain.doFilter(req, rsp);
138 } finally {
139 req.removeAttribute(ATTRIBUTE_HANDLER);
140 }
141 }
142
143 @Override
144 public void init(FilterConfig filterConfig) throws ServletException {
145
146 }
147
148 @Override
149 public void destroy() {
150
151 }
152 }
153
154 private final UploadPackErrorHandler handler;
155
156 UploadPackServlet(@Nullable UploadPackErrorHandler handler) {
157 this.handler = handler != null ? handler
158 : this::defaultUploadPackHandler;
159 }
160
161
162 @Override
163 public void doPost(HttpServletRequest req, HttpServletResponse rsp)
164 throws IOException {
165 if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) {
166 rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
167 return;
168 }
169
170 UploadPackRunnable r = () -> {
171 upload(req, rsp);
172 };
173
174 handler.upload(req, rsp, r);
175 }
176
177 private void upload(HttpServletRequest req, HttpServletResponse rsp)
178 throws IOException, ServiceMayNotContinueException {
179
180 @SuppressWarnings("resource")
181 SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
182 @Override
183 public void flush() throws IOException {
184 doFlush();
185 }
186 };
187 Repository repo = null;
188 try (UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER)) {
189 up.setBiDirectionalPipe(false);
190 rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
191 repo = up.getRepository();
192 up.uploadWithExceptionPropagation(getInputStream(req), out, null);
193 out.close();
194 } catch (ServiceMayNotContinueException e) {
195 if (e.isOutput()) {
196 consumeRequestBody(req);
197 out.close();
198 }
199 throw e;
200 } catch (UploadPackInternalServerErrorException e) {
201
202 log(repo, e.getCause());
203 consumeRequestBody(req);
204 out.close();
205 }
206 }
207
208 private void defaultUploadPackHandler(HttpServletRequest req,
209 HttpServletResponse rsp, UploadPackRunnable r) throws IOException {
210 try {
211 r.upload();
212 } catch (ServiceMayNotContinueException e) {
213 if (!e.isOutput() && !rsp.isCommitted()) {
214 rsp.reset();
215 sendError(req, rsp, e.getStatusCode(), e.getMessage());
216 }
217 } catch (Throwable e) {
218 UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
219 log(up.getRepository(), e);
220 if (!rsp.isCommitted()) {
221 rsp.reset();
222 String msg = null;
223 if (e instanceof PackProtocolException
224 || e instanceof ServiceNotEnabledException) {
225 msg = e.getMessage();
226 }
227 sendError(req, rsp, statusCodeForThrowable(e), msg);
228 }
229 }
230 }
231
232 private void log(Repository git, Throwable e) {
233 getServletContext().log(MessageFormat.format(
234 HttpServerText.get().internalErrorDuringUploadPack,
235 ServletUtils.identify(git)), e);
236 }
237 }