| /* | |
| * Copyright (C) 2009-2010, Google Inc. and others | |
| * | |
| * This program and the accompanying materials are made available under the | |
| * terms of the Eclipse Distribution License v. 1.0 which is available at | |
| * https://www.eclipse.org/org/documents/edl-v10.php. | |
| * | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| */ | |
| package org.eclipse.jgit.http.server; | |
| import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; | |
| import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; | |
| import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; | |
| import static jakarta.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; | |
| import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK; | |
| import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE; | |
| import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE; | |
| import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; | |
| import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; | |
| import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody; | |
| import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; | |
| import static org.eclipse.jgit.http.server.ServletUtils.getRepository; | |
| import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT; | |
| import java.io.IOException; | |
| import java.text.MessageFormat; | |
| import java.util.List; | |
| import jakarta.servlet.Filter; | |
| import jakarta.servlet.FilterChain; | |
| import jakarta.servlet.FilterConfig; | |
| import jakarta.servlet.ServletException; | |
| import jakarta.servlet.ServletRequest; | |
| import jakarta.servlet.ServletResponse; | |
| import jakarta.servlet.http.HttpServlet; | |
| import jakarta.servlet.http.HttpServletRequest; | |
| import jakarta.servlet.http.HttpServletResponse; | |
| import org.eclipse.jgit.annotations.Nullable; | |
| import org.eclipse.jgit.errors.CorruptObjectException; | |
| import org.eclipse.jgit.errors.PackProtocolException; | |
| import org.eclipse.jgit.errors.UnpackException; | |
| import org.eclipse.jgit.lib.Repository; | |
| import org.eclipse.jgit.transport.InternalHttpServerGlue; | |
| import org.eclipse.jgit.transport.ReceivePack; | |
| import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; | |
| import org.eclipse.jgit.transport.resolver.ReceivePackFactory; | |
| import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |
| import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |
| /** Server side implementation of smart push over HTTP. */ | |
| class ReceivePackServlet extends HttpServlet { | |
| private static final long serialVersionUID = 1L; | |
| static class InfoRefs extends SmartServiceInfoRefs { | |
| private final ReceivePackFactory<HttpServletRequest> receivePackFactory; | |
| InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, | |
| List<Filter> filters) { | |
| super(RECEIVE_PACK, filters); | |
| this.receivePackFactory = receivePackFactory; | |
| } | |
| @Override | |
| protected void begin(HttpServletRequest req, Repository db) | |
| throws IOException, ServiceNotEnabledException, | |
| ServiceNotAuthorizedException { | |
| ReceivePack rp = receivePackFactory.create(req, db); | |
| InternalHttpServerGlue.setPeerUserAgent( | |
| rp, | |
| req.getHeader(HDR_USER_AGENT)); | |
| req.setAttribute(ATTRIBUTE_HANDLER, rp); | |
| } | |
| @Override | |
| protected void advertise(HttpServletRequest req, | |
| PacketLineOutRefAdvertiser pck) throws IOException, | |
| ServiceNotEnabledException, ServiceNotAuthorizedException { | |
| ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); | |
| try { | |
| rp.sendAdvertisedRefs(pck); | |
| } finally { | |
| rp.getRevWalk().close(); | |
| } | |
| } | |
| } | |
| static class Factory implements Filter { | |
| private final ReceivePackFactory<HttpServletRequest> receivePackFactory; | |
| Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory) { | |
| this.receivePackFactory = receivePackFactory; | |
| } | |
| @Override | |
| public void doFilter(ServletRequest request, ServletResponse response, | |
| FilterChain chain) throws IOException, ServletException { | |
| HttpServletRequest req = (HttpServletRequest) request; | |
| HttpServletResponse rsp = (HttpServletResponse) response; | |
| ReceivePack rp; | |
| try { | |
| rp = receivePackFactory.create(req, getRepository(req)); | |
| } catch (ServiceNotAuthorizedException e) { | |
| rsp.sendError(SC_UNAUTHORIZED, e.getMessage()); | |
| return; | |
| } catch (ServiceNotEnabledException e) { | |
| sendError(req, rsp, SC_FORBIDDEN, e.getMessage()); | |
| return; | |
| } | |
| try { | |
| req.setAttribute(ATTRIBUTE_HANDLER, rp); | |
| chain.doFilter(req, rsp); | |
| } finally { | |
| req.removeAttribute(ATTRIBUTE_HANDLER); | |
| } | |
| } | |
| @Override | |
| public void init(FilterConfig filterConfig) throws ServletException { | |
| // Nothing. | |
| } | |
| @Override | |
| public void destroy() { | |
| // Nothing. | |
| } | |
| } | |
| @Nullable | |
| private final ReceivePackErrorHandler handler; | |
| ReceivePackServlet(@Nullable ReceivePackErrorHandler handler) { | |
| this.handler = handler; | |
| } | |
| @Override | |
| public void doPost(final HttpServletRequest req, | |
| final HttpServletResponse rsp) throws IOException { | |
| if (!RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType())) { | |
| rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); | |
| return; | |
| } | |
| SmartOutputStream out = new SmartOutputStream(req, rsp, false) { | |
| @Override | |
| public void flush() throws IOException { | |
| doFlush(); | |
| } | |
| }; | |
| ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); | |
| rp.setBiDirectionalPipe(false); | |
| rsp.setContentType(RECEIVE_PACK_RESULT_TYPE); | |
| if (handler != null) { | |
| handler.receive(req, rsp, () -> { | |
| rp.receiveWithExceptionPropagation(getInputStream(req), out, | |
| null); | |
| out.close(); | |
| }); | |
| } else { | |
| try { | |
| rp.receive(getInputStream(req), out, null); | |
| out.close(); | |
| } catch (CorruptObjectException e ) { | |
| // This should be already reported to the client. | |
| getServletContext().log(MessageFormat.format( | |
| HttpServerText.get().receivedCorruptObject, | |
| e.getMessage(), | |
| ServletUtils.identify(rp.getRepository()))); | |
| consumeRequestBody(req); | |
| out.close(); | |
| } catch (UnpackException | PackProtocolException e) { | |
| // This should be already reported to the client. | |
| log(rp.getRepository(), e.getCause()); | |
| consumeRequestBody(req); | |
| out.close(); | |
| } catch (Throwable e) { | |
| log(rp.getRepository(), e); | |
| if (!rsp.isCommitted()) { | |
| rsp.reset(); | |
| sendError(req, rsp, SC_INTERNAL_SERVER_ERROR); | |
| } | |
| return; | |
| } | |
| } | |
| } | |
| private void log(Repository git, Throwable e) { | |
| getServletContext().log(MessageFormat.format( | |
| HttpServerText.get().internalErrorDuringReceivePack, | |
| ServletUtils.identify(git)), e); | |
| } | |
| } |