You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UploadPackServlet.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. package org.eclipse.jgit.http.server;
  11. import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
  12. import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
  13. import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
  14. import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
  15. import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK;
  16. import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE;
  17. import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE;
  18. import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
  19. import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
  20. import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
  21. import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
  22. import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
  23. import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
  24. import java.io.IOException;
  25. import java.text.MessageFormat;
  26. import java.util.List;
  27. import javax.servlet.Filter;
  28. import javax.servlet.FilterChain;
  29. import javax.servlet.FilterConfig;
  30. import javax.servlet.ServletException;
  31. import javax.servlet.ServletRequest;
  32. import javax.servlet.ServletResponse;
  33. import javax.servlet.http.HttpServlet;
  34. import javax.servlet.http.HttpServletRequest;
  35. import javax.servlet.http.HttpServletResponse;
  36. import org.eclipse.jgit.annotations.Nullable;
  37. import org.eclipse.jgit.errors.PackProtocolException;
  38. import org.eclipse.jgit.http.server.UploadPackErrorHandler.UploadPackRunnable;
  39. import org.eclipse.jgit.lib.Repository;
  40. import org.eclipse.jgit.transport.InternalHttpServerGlue;
  41. import org.eclipse.jgit.transport.PacketLineOut;
  42. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  43. import org.eclipse.jgit.transport.ServiceMayNotContinueException;
  44. import org.eclipse.jgit.transport.UploadPack;
  45. import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
  46. import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
  47. import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
  48. import org.eclipse.jgit.transport.resolver.UploadPackFactory;
  49. /** Server side implementation of smart fetch over HTTP. */
  50. class UploadPackServlet extends HttpServlet {
  51. private static final long serialVersionUID = 1L;
  52. static class InfoRefs extends SmartServiceInfoRefs {
  53. private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
  54. InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory,
  55. List<Filter> filters) {
  56. super(UPLOAD_PACK, filters);
  57. this.uploadPackFactory = uploadPackFactory;
  58. }
  59. @Override
  60. protected void begin(HttpServletRequest req, Repository db)
  61. throws IOException, ServiceNotEnabledException,
  62. ServiceNotAuthorizedException {
  63. UploadPack up = uploadPackFactory.create(req, db);
  64. InternalHttpServerGlue.setPeerUserAgent(
  65. up,
  66. req.getHeader(HDR_USER_AGENT));
  67. req.setAttribute(ATTRIBUTE_HANDLER, up);
  68. }
  69. @Override
  70. protected void advertise(HttpServletRequest req,
  71. PacketLineOutRefAdvertiser pck) throws IOException,
  72. ServiceNotEnabledException, ServiceNotAuthorizedException {
  73. UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
  74. try {
  75. up.setBiDirectionalPipe(false);
  76. up.sendAdvertisedRefs(pck);
  77. } finally {
  78. // TODO(jonathantanmy): Move responsibility for closing the
  79. // RevWalk to UploadPack, either by making it AutoCloseable
  80. // or by making sendAdvertisedRefs clean up after itself.
  81. up.getRevWalk().close();
  82. }
  83. }
  84. @Override
  85. protected void respond(HttpServletRequest req,
  86. PacketLineOut pckOut, String serviceName) throws IOException,
  87. ServiceNotEnabledException, ServiceNotAuthorizedException {
  88. UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
  89. try {
  90. up.setBiDirectionalPipe(false);
  91. up.sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut), serviceName);
  92. } finally {
  93. // TODO(jonathantanmy): Move responsibility for closing the
  94. // RevWalk to UploadPack, either by making it AutoCloseable
  95. // or by making sendAdvertisedRefs clean up after itself.
  96. up.getRevWalk().close();
  97. }
  98. }
  99. }
  100. static class Factory implements Filter {
  101. private final UploadPackFactory<HttpServletRequest> uploadPackFactory;
  102. Factory(UploadPackFactory<HttpServletRequest> uploadPackFactory) {
  103. this.uploadPackFactory = uploadPackFactory;
  104. }
  105. @Override
  106. public void doFilter(ServletRequest request, ServletResponse response,
  107. FilterChain chain) throws IOException, ServletException {
  108. HttpServletRequest req = (HttpServletRequest) request;
  109. HttpServletResponse rsp = (HttpServletResponse) response;
  110. UploadPack rp;
  111. try {
  112. rp = uploadPackFactory.create(req, getRepository(req));
  113. } catch (ServiceNotAuthorizedException e) {
  114. rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
  115. return;
  116. } catch (ServiceNotEnabledException e) {
  117. sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
  118. return;
  119. }
  120. try {
  121. req.setAttribute(ATTRIBUTE_HANDLER, rp);
  122. chain.doFilter(req, rsp);
  123. } finally {
  124. req.removeAttribute(ATTRIBUTE_HANDLER);
  125. }
  126. }
  127. @Override
  128. public void init(FilterConfig filterConfig) throws ServletException {
  129. // Nothing.
  130. }
  131. @Override
  132. public void destroy() {
  133. // Nothing.
  134. }
  135. }
  136. private final UploadPackErrorHandler handler;
  137. UploadPackServlet(@Nullable UploadPackErrorHandler handler) {
  138. this.handler = handler != null ? handler
  139. : this::defaultUploadPackHandler;
  140. }
  141. /** {@inheritDoc} */
  142. @Override
  143. public void doPost(HttpServletRequest req, HttpServletResponse rsp)
  144. throws IOException {
  145. if (!UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType())) {
  146. rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE);
  147. return;
  148. }
  149. UploadPackRunnable r = () -> {
  150. UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
  151. @SuppressWarnings("resource")
  152. SmartOutputStream out = new SmartOutputStream(req, rsp, false) {
  153. @Override
  154. public void flush() throws IOException {
  155. doFlush();
  156. }
  157. };
  158. up.setBiDirectionalPipe(false);
  159. rsp.setContentType(UPLOAD_PACK_RESULT_TYPE);
  160. try {
  161. up.uploadWithExceptionPropagation(getInputStream(req), out,
  162. null);
  163. out.close();
  164. } catch (ServiceMayNotContinueException e) {
  165. if (e.isOutput()) {
  166. consumeRequestBody(req);
  167. out.close();
  168. }
  169. throw e;
  170. } catch (UploadPackInternalServerErrorException e) {
  171. // Special case exception, error message was sent to client.
  172. log(up.getRepository(), e.getCause());
  173. consumeRequestBody(req);
  174. out.close();
  175. }
  176. };
  177. handler.upload(req, rsp, r);
  178. }
  179. private void defaultUploadPackHandler(HttpServletRequest req,
  180. HttpServletResponse rsp, UploadPackRunnable r) throws IOException {
  181. try {
  182. r.upload();
  183. } catch (ServiceMayNotContinueException e) {
  184. if (!e.isOutput() && !rsp.isCommitted()) {
  185. rsp.reset();
  186. sendError(req, rsp, e.getStatusCode(), e.getMessage());
  187. }
  188. } catch (Throwable e) {
  189. UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
  190. log(up.getRepository(), e);
  191. if (!rsp.isCommitted()) {
  192. rsp.reset();
  193. String msg = e instanceof PackProtocolException ? e.getMessage()
  194. : null;
  195. sendError(req, rsp, SC_INTERNAL_SERVER_ERROR, msg);
  196. }
  197. }
  198. }
  199. private void log(Repository git, Throwable e) {
  200. getServletContext().log(MessageFormat.format(
  201. HttpServerText.get().internalErrorDuringUploadPack,
  202. ServletUtils.identify(git)), e);
  203. }
  204. }