181fea92eSShawn O. Pearce /* 2*5c5f7c6bSMatthias Sohn * Copyright (C) 2009-2010, Google Inc. and others 381fea92eSShawn 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. 781fea92eSShawn O. Pearce * 8*5c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 981fea92eSShawn O. Pearce */ 1081fea92eSShawn O. Pearce 1181fea92eSShawn O. Pearce package org.eclipse.jgit.http.server; 1281fea92eSShawn O. Pearce 1381fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; 1481fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 1581fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; 1681fea92eSShawn O. Pearce import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; 17867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK; 18867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE; 19867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_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; 2381fea92eSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; 2481fea92eSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 251c6c73c5SShawn O. Pearce import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT; 2681fea92eSShawn O. Pearce 2781fea92eSShawn O. Pearce import java.io.IOException; 28ae592cc6SShawn Pearce import java.text.MessageFormat; 29af3562f7SShawn O. Pearce import java.util.List; 3081fea92eSShawn 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; 3781fea92eSShawn O. Pearce import javax.servlet.http.HttpServlet; 3881fea92eSShawn O. Pearce import javax.servlet.http.HttpServletRequest; 3981fea92eSShawn O. Pearce import javax.servlet.http.HttpServletResponse; 4081fea92eSShawn O. Pearce 4175ec5b74SMasaya Suzuki import org.eclipse.jgit.annotations.Nullable; 42a870a8a0SShawn Pearce import org.eclipse.jgit.errors.CorruptObjectException; 436e4e34bbSDave Borowitz import org.eclipse.jgit.errors.PackProtocolException; 447ff6eb58SShawn O. Pearce import org.eclipse.jgit.errors.UnpackException; 4581fea92eSShawn O. Pearce import org.eclipse.jgit.lib.Repository; 464a984e20SShawn Pearce import org.eclipse.jgit.transport.InternalHttpServerGlue; 4781fea92eSShawn O. Pearce import org.eclipse.jgit.transport.ReceivePack; 4881fea92eSShawn O. Pearce import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; 491b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ReceivePackFactory; 501b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; 511b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; 5281fea92eSShawn O. Pearce 5381fea92eSShawn O. Pearce /** Server side implementation of smart push over HTTP. */ 5481fea92eSShawn O. Pearce class ReceivePackServlet extends HttpServlet { 5581fea92eSShawn O. Pearce private static final long serialVersionUID = 1L; 5681fea92eSShawn O. Pearce 5781fea92eSShawn O. Pearce static class InfoRefs extends SmartServiceInfoRefs { 58af3562f7SShawn O. Pearce private final ReceivePackFactory<HttpServletRequest> receivePackFactory; 5981fea92eSShawn O. Pearce InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, List<Filter> filters)60af3562f7SShawn O. Pearce InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, 61af3562f7SShawn O. Pearce List<Filter> filters) { 62867087bbSShawn O. Pearce super(RECEIVE_PACK, filters); 6381fea92eSShawn O. Pearce this.receivePackFactory = receivePackFactory; 6481fea92eSShawn O. Pearce } 6581fea92eSShawn O. Pearce 6681fea92eSShawn O. Pearce @Override begin(HttpServletRequest req, Repository db)67af3562f7SShawn O. Pearce protected void begin(HttpServletRequest req, Repository db) 68af3562f7SShawn O. Pearce throws IOException, ServiceNotEnabledException, 69af3562f7SShawn O. Pearce ServiceNotAuthorizedException { 70af3562f7SShawn O. Pearce ReceivePack rp = receivePackFactory.create(req, db); 714a984e20SShawn Pearce InternalHttpServerGlue.setPeerUserAgent( 724a984e20SShawn Pearce rp, 734a984e20SShawn Pearce req.getHeader(HDR_USER_AGENT)); 74af3562f7SShawn O. Pearce req.setAttribute(ATTRIBUTE_HANDLER, rp); 75af3562f7SShawn O. Pearce } 76af3562f7SShawn O. Pearce 77af3562f7SShawn O. Pearce @Override advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)78af3562f7SShawn O. Pearce protected void advertise(HttpServletRequest req, 7981fea92eSShawn O. Pearce PacketLineOutRefAdvertiser pck) throws IOException, 8081fea92eSShawn O. Pearce ServiceNotEnabledException, ServiceNotAuthorizedException { 81af3562f7SShawn O. Pearce ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); 82515deaf7SShawn O. Pearce try { 83515deaf7SShawn O. Pearce rp.sendAdvertisedRefs(pck); 84515deaf7SShawn O. Pearce } finally { 85686124beSMatthias Sohn rp.getRevWalk().close(); 86515deaf7SShawn O. Pearce } 8781fea92eSShawn O. Pearce } 8881fea92eSShawn O. Pearce } 8981fea92eSShawn O. Pearce 90af3562f7SShawn O. Pearce static class Factory implements Filter { 91af3562f7SShawn O. Pearce private final ReceivePackFactory<HttpServletRequest> receivePackFactory; 9281fea92eSShawn O. Pearce Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory)93af3562f7SShawn O. Pearce Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory) { 9481fea92eSShawn O. Pearce this.receivePackFactory = receivePackFactory; 9581fea92eSShawn O. Pearce } 9681fea92eSShawn O. Pearce 977ac182f4SDavid Pursehouse @Override doFilter(ServletRequest request, ServletResponse response, FilterChain chain)98af3562f7SShawn O. Pearce public void doFilter(ServletRequest request, ServletResponse response, 99af3562f7SShawn O. Pearce FilterChain chain) throws IOException, ServletException { 100af3562f7SShawn O. Pearce HttpServletRequest req = (HttpServletRequest) request; 101af3562f7SShawn O. Pearce HttpServletResponse rsp = (HttpServletResponse) response; 102af3562f7SShawn O. Pearce ReceivePack rp; 103af3562f7SShawn O. Pearce try { 104af3562f7SShawn O. Pearce rp = receivePackFactory.create(req, getRepository(req)); 105af3562f7SShawn O. Pearce } catch (ServiceNotAuthorizedException e) { 106cc4f4f2fSJonathan Nieder rsp.sendError(SC_UNAUTHORIZED, e.getMessage()); 107af3562f7SShawn O. Pearce return; 108af3562f7SShawn O. Pearce } catch (ServiceNotEnabledException e) { 109cc4f4f2fSJonathan Nieder sendError(req, rsp, SC_FORBIDDEN, e.getMessage()); 110af3562f7SShawn O. Pearce return; 111af3562f7SShawn O. Pearce } 112af3562f7SShawn O. Pearce 113af3562f7SShawn O. Pearce try { 114af3562f7SShawn O. Pearce req.setAttribute(ATTRIBUTE_HANDLER, rp); 115af3562f7SShawn O. Pearce chain.doFilter(req, rsp); 116af3562f7SShawn O. Pearce } finally { 117af3562f7SShawn O. Pearce req.removeAttribute(ATTRIBUTE_HANDLER); 118af3562f7SShawn O. Pearce } 119af3562f7SShawn O. Pearce } 120af3562f7SShawn O. Pearce 1217ac182f4SDavid Pursehouse @Override init(FilterConfig filterConfig)122af3562f7SShawn O. Pearce public void init(FilterConfig filterConfig) throws ServletException { 123af3562f7SShawn O. Pearce // Nothing. 124af3562f7SShawn O. Pearce } 125af3562f7SShawn O. Pearce 1267ac182f4SDavid Pursehouse @Override destroy()127af3562f7SShawn O. Pearce public void destroy() { 128af3562f7SShawn O. Pearce // Nothing. 129af3562f7SShawn O. Pearce } 130af3562f7SShawn O. Pearce } 131af3562f7SShawn O. Pearce 13275ec5b74SMasaya Suzuki @Nullable 13375ec5b74SMasaya Suzuki private final ReceivePackErrorHandler handler; 13475ec5b74SMasaya Suzuki ReceivePackServlet(@ullable ReceivePackErrorHandler handler)13575ec5b74SMasaya Suzuki ReceivePackServlet(@Nullable ReceivePackErrorHandler handler) { 13675ec5b74SMasaya Suzuki this.handler = handler; 13775ec5b74SMasaya Suzuki } 13875ec5b74SMasaya Suzuki 1393b00041cSMatthias Sohn /** {@inheritDoc} */ 14081fea92eSShawn O. Pearce @Override doPost(final HttpServletRequest req, final HttpServletResponse rsp)14181fea92eSShawn O. Pearce public void doPost(final HttpServletRequest req, 14281fea92eSShawn O. Pearce final HttpServletResponse rsp) throws IOException { 143867087bbSShawn O. Pearce if (!RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType())) { 14481fea92eSShawn O. Pearce rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); 14581fea92eSShawn O. Pearce return; 14681fea92eSShawn O. Pearce } 14781fea92eSShawn O. Pearce 14820e3779eSShawn Pearce SmartOutputStream out = new SmartOutputStream(req, rsp, false) { 149bd531eb9SShawn O. Pearce @Override 150bd531eb9SShawn O. Pearce public void flush() throws IOException { 151bd531eb9SShawn O. Pearce doFlush(); 152bd531eb9SShawn O. Pearce } 153bd531eb9SShawn O. Pearce }; 154ac6cda95SShawn O. Pearce 155ac6cda95SShawn O. Pearce ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); 156ac6cda95SShawn O. Pearce rp.setBiDirectionalPipe(false); 157ac6cda95SShawn O. Pearce rsp.setContentType(RECEIVE_PACK_RESULT_TYPE); 158ac6cda95SShawn O. Pearce 15975ec5b74SMasaya Suzuki if (handler != null) { 16075ec5b74SMasaya Suzuki handler.receive(req, rsp, () -> { 16175ec5b74SMasaya Suzuki rp.receiveWithExceptionPropagation(getInputStream(req), out, 16275ec5b74SMasaya Suzuki null); 16375ec5b74SMasaya Suzuki out.close(); 16475ec5b74SMasaya Suzuki }); 16575ec5b74SMasaya Suzuki } else { 16675ec5b74SMasaya Suzuki try { 16781fea92eSShawn O. Pearce rp.receive(getInputStream(req), out, null); 168c0f09389SShawn O. Pearce out.close(); 169a870a8a0SShawn Pearce } catch (CorruptObjectException e ) { 170a870a8a0SShawn Pearce // This should be already reported to the client. 171a870a8a0SShawn Pearce getServletContext().log(MessageFormat.format( 172a870a8a0SShawn Pearce HttpServerText.get().receivedCorruptObject, 173a870a8a0SShawn Pearce e.getMessage(), 174a870a8a0SShawn Pearce ServletUtils.identify(rp.getRepository()))); 175a870a8a0SShawn Pearce consumeRequestBody(req); 176a870a8a0SShawn Pearce out.close(); 177a870a8a0SShawn Pearce 1786e4e34bbSDave Borowitz } catch (UnpackException | PackProtocolException e) { 1797ff6eb58SShawn O. Pearce // This should be already reported to the client. 180ae592cc6SShawn Pearce log(rp.getRepository(), e.getCause()); 181db00632dSShawn O. Pearce consumeRequestBody(req); 182ac6cda95SShawn O. Pearce out.close(); 1837ff6eb58SShawn O. Pearce 184ac6cda95SShawn O. Pearce } catch (Throwable e) { 185ae592cc6SShawn Pearce log(rp.getRepository(), e); 1867ff6eb58SShawn O. Pearce if (!rsp.isCommitted()) { 1877ff6eb58SShawn O. Pearce rsp.reset(); 188867087bbSShawn O. Pearce sendError(req, rsp, SC_INTERNAL_SERVER_ERROR); 1897ff6eb58SShawn O. Pearce } 19081fea92eSShawn O. Pearce return; 19181fea92eSShawn O. Pearce } 19281fea92eSShawn O. Pearce } 19375ec5b74SMasaya Suzuki } 194ae592cc6SShawn Pearce log(Repository git, Throwable e)195ae592cc6SShawn Pearce private void log(Repository git, Throwable e) { 196ae592cc6SShawn Pearce getServletContext().log(MessageFormat.format( 197ae592cc6SShawn Pearce HttpServerText.get().internalErrorDuringReceivePack, 198ae592cc6SShawn Pearce ServletUtils.identify(git)), e); 199ae592cc6SShawn Pearce } 20081fea92eSShawn O. Pearce } 201