| /* |
| * 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 javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; |
| import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; |
| import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; |
| import static javax.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 javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.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; |
| } |
| |
| /** {@inheritDoc} */ |
| @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); |
| } |
| } |