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