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_UNAUTHORIZED; 15867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.infoRefsResultType; 16867087bbSShawn O. Pearce import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; 17af3562f7SShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; 1881fea92eSShawn O. Pearce import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 1981fea92eSShawn O. Pearce 2081fea92eSShawn O. Pearce import java.io.IOException; 21af3562f7SShawn O. Pearce import java.util.List; 2281fea92eSShawn O. Pearce 2381fea92eSShawn O. Pearce import javax.servlet.Filter; 2481fea92eSShawn O. Pearce import javax.servlet.FilterChain; 2581fea92eSShawn O. Pearce import javax.servlet.FilterConfig; 2681fea92eSShawn O. Pearce import javax.servlet.ServletException; 2781fea92eSShawn O. Pearce import javax.servlet.ServletRequest; 2881fea92eSShawn O. Pearce import javax.servlet.ServletResponse; 2981fea92eSShawn O. Pearce import javax.servlet.http.HttpServletRequest; 3081fea92eSShawn O. Pearce import javax.servlet.http.HttpServletResponse; 3181fea92eSShawn O. Pearce 3281fea92eSShawn O. Pearce import org.eclipse.jgit.lib.Repository; 3381fea92eSShawn O. Pearce import org.eclipse.jgit.transport.PacketLineOut; 3481fea92eSShawn O. Pearce import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; 351f2022e3SDave Borowitz import org.eclipse.jgit.transport.ServiceMayNotContinueException; 361b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; 371b7a5a29SShawn O. Pearce import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; 3881fea92eSShawn O. Pearce 3981fea92eSShawn O. Pearce /** Filter in front of {@link InfoRefsServlet} to catch smart service requests. */ 4081fea92eSShawn O. Pearce abstract class SmartServiceInfoRefs implements Filter { 4181fea92eSShawn O. Pearce private final String svc; 4281fea92eSShawn O. Pearce 43af3562f7SShawn O. Pearce private final Filter[] filters; 44af3562f7SShawn O. Pearce SmartServiceInfoRefs(String service, List<Filter> filters)45f3ec7cf3SHan-Wen Nienhuys SmartServiceInfoRefs(String service, List<Filter> filters) { 4681fea92eSShawn O. Pearce this.svc = service; 472fc00af4SMichael Keppler this.filters = filters.toArray(new Filter[0]); 4881fea92eSShawn O. Pearce } 4981fea92eSShawn O. Pearce 503b00041cSMatthias Sohn /** {@inheritDoc} */ 517ac182f4SDavid Pursehouse @Override init(FilterConfig config)5281fea92eSShawn O. Pearce public void init(FilterConfig config) throws ServletException { 5381fea92eSShawn O. Pearce // Do nothing. 5481fea92eSShawn O. Pearce } 5581fea92eSShawn O. Pearce 563b00041cSMatthias Sohn /** {@inheritDoc} */ 577ac182f4SDavid Pursehouse @Override destroy()5881fea92eSShawn O. Pearce public void destroy() { 5981fea92eSShawn O. Pearce // Do nothing. 6081fea92eSShawn O. Pearce } 6181fea92eSShawn O. Pearce 623b00041cSMatthias Sohn /** {@inheritDoc} */ 637ac182f4SDavid Pursehouse @Override doFilter(ServletRequest request, ServletResponse response, FilterChain chain)6481fea92eSShawn O. Pearce public void doFilter(ServletRequest request, ServletResponse response, 6581fea92eSShawn O. Pearce FilterChain chain) throws IOException, ServletException { 6681fea92eSShawn O. Pearce final HttpServletRequest req = (HttpServletRequest) request; 67867087bbSShawn O. Pearce final HttpServletResponse res = (HttpServletResponse) response; 6881fea92eSShawn O. Pearce 6981fea92eSShawn O. Pearce if (svc.equals(req.getParameter("service"))) { 70af3562f7SShawn O. Pearce final Repository db = getRepository(req); 71af3562f7SShawn O. Pearce try { 72af3562f7SShawn O. Pearce begin(req, db); 73af3562f7SShawn O. Pearce } catch (ServiceNotAuthorizedException e) { 74cc4f4f2fSJonathan Nieder res.sendError(SC_UNAUTHORIZED, e.getMessage()); 75af3562f7SShawn O. Pearce return; 76af3562f7SShawn O. Pearce } catch (ServiceNotEnabledException e) { 77cbb61760SHector Oswaldo Caballero sendError(req, res, SC_FORBIDDEN, e.getMessage()); 78af3562f7SShawn O. Pearce return; 79af3562f7SShawn O. Pearce } 80af3562f7SShawn O. Pearce 81af3562f7SShawn O. Pearce try { 82af3562f7SShawn O. Pearce if (filters.length == 0) 83af3562f7SShawn O. Pearce service(req, response); 84af3562f7SShawn O. Pearce else 85af3562f7SShawn O. Pearce new Chain().doFilter(request, response); 86af3562f7SShawn O. Pearce } finally { 87af3562f7SShawn O. Pearce req.removeAttribute(ATTRIBUTE_HANDLER); 88af3562f7SShawn O. Pearce } 89af3562f7SShawn O. Pearce } else { 90af3562f7SShawn O. Pearce chain.doFilter(request, response); 91af3562f7SShawn O. Pearce } 92af3562f7SShawn O. Pearce } 93af3562f7SShawn O. Pearce service(ServletRequest request, ServletResponse response)94af3562f7SShawn O. Pearce private void service(ServletRequest request, ServletResponse response) 95af3562f7SShawn O. Pearce throws IOException { 96af3562f7SShawn O. Pearce final HttpServletRequest req = (HttpServletRequest) request; 97867087bbSShawn O. Pearce final HttpServletResponse res = (HttpServletResponse) response; 98f93a6a72SShawn O. Pearce final SmartOutputStream buf = new SmartOutputStream(req, res, true); 9981fea92eSShawn O. Pearce try { 100867087bbSShawn O. Pearce res.setContentType(infoRefsResultType(svc)); 101c0f09389SShawn O. Pearce 10281fea92eSShawn O. Pearce final PacketLineOut out = new PacketLineOut(buf); 103c32a62cdSJonathan Tan respond(req, out, svc); 104c0f09389SShawn O. Pearce buf.close(); 10581fea92eSShawn O. Pearce } catch (ServiceNotAuthorizedException e) { 106cc4f4f2fSJonathan Nieder res.sendError(SC_UNAUTHORIZED, e.getMessage()); 10781fea92eSShawn O. Pearce } catch (ServiceNotEnabledException e) { 108cc4f4f2fSJonathan Nieder sendError(req, res, SC_FORBIDDEN, e.getMessage()); 1091f2022e3SDave Borowitz } catch (ServiceMayNotContinueException e) { 11064b524e3SShawn O. Pearce if (e.isOutput()) 11164b524e3SShawn O. Pearce buf.close(); 11264b524e3SShawn O. Pearce else 113c4e209b2SMasaya Suzuki sendError(req, res, e.getStatusCode(), e.getMessage()); 11481fea92eSShawn O. Pearce } 11581fea92eSShawn O. Pearce } 11681fea92eSShawn O. Pearce 1173b00041cSMatthias Sohn /** 1183b00041cSMatthias Sohn * Begin service. 1193b00041cSMatthias Sohn * 1203b00041cSMatthias Sohn * @param req 1213b00041cSMatthias Sohn * request 1223b00041cSMatthias Sohn * @param db 1233b00041cSMatthias Sohn * repository 1243b00041cSMatthias Sohn * @throws IOException 1253b00041cSMatthias Sohn * @throws ServiceNotEnabledException 1263b00041cSMatthias Sohn * @throws ServiceNotAuthorizedException 1273b00041cSMatthias Sohn */ begin(HttpServletRequest req, Repository db)128af3562f7SShawn O. Pearce protected abstract void begin(HttpServletRequest req, Repository db) 129af3562f7SShawn O. Pearce throws IOException, ServiceNotEnabledException, 130af3562f7SShawn O. Pearce ServiceNotAuthorizedException; 131af3562f7SShawn O. Pearce 1323b00041cSMatthias Sohn /** 1333b00041cSMatthias Sohn * Advertise. 1343b00041cSMatthias Sohn * 1353b00041cSMatthias Sohn * @param req 1363b00041cSMatthias Sohn * request 1373b00041cSMatthias Sohn * @param pck 1383b00041cSMatthias Sohn * @throws IOException 1393b00041cSMatthias Sohn * @throws ServiceNotEnabledException 1403b00041cSMatthias Sohn * @throws ServiceNotAuthorizedException 1413b00041cSMatthias Sohn */ advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)142af3562f7SShawn O. Pearce protected abstract void advertise(HttpServletRequest req, 14381fea92eSShawn O. Pearce PacketLineOutRefAdvertiser pck) throws IOException, 14481fea92eSShawn O. Pearce ServiceNotEnabledException, ServiceNotAuthorizedException; 145af3562f7SShawn O. Pearce 146c32a62cdSJonathan Tan /** 147c32a62cdSJonathan Tan * Writes the appropriate response to an info/refs request received by 148c32a62cdSJonathan Tan * a smart service. In protocol v0, this starts with "# 149c32a62cdSJonathan Tan * service=serviceName" followed by a flush packet, but this is not 150c32a62cdSJonathan Tan * necessarily the case in other protocol versions. 151c32a62cdSJonathan Tan * <p> 152c32a62cdSJonathan Tan * The default implementation writes "# service=serviceName" and a 153c32a62cdSJonathan Tan * flush packet, then calls {@link #advertise}. Subclasses should 154c32a62cdSJonathan Tan * override this method if they support protocol versions other than 155c32a62cdSJonathan Tan * protocol v0. 156c32a62cdSJonathan Tan * 157c32a62cdSJonathan Tan * @param req 158c32a62cdSJonathan Tan * request 159c32a62cdSJonathan Tan * @param pckOut 160c32a62cdSJonathan Tan * destination of response 161c32a62cdSJonathan Tan * @param serviceName 162c32a62cdSJonathan Tan * service name to be written out in protocol v0; may or may 163c32a62cdSJonathan Tan * not be used in other versions 164c32a62cdSJonathan Tan * @throws IOException 165c32a62cdSJonathan Tan * @throws ServiceNotEnabledException 166c32a62cdSJonathan Tan * @throws ServiceNotAuthorizedException 167c32a62cdSJonathan Tan */ respond(HttpServletRequest req, PacketLineOut pckOut, String serviceName)168c32a62cdSJonathan Tan protected void respond(HttpServletRequest req, 169c32a62cdSJonathan Tan PacketLineOut pckOut, String serviceName) 170c32a62cdSJonathan Tan throws IOException, ServiceNotEnabledException, 171c32a62cdSJonathan Tan ServiceNotAuthorizedException { 172c32a62cdSJonathan Tan pckOut.writeString("# service=" + svc + '\n'); //$NON-NLS-1$ 173c32a62cdSJonathan Tan pckOut.end(); 174c32a62cdSJonathan Tan advertise(req, new PacketLineOutRefAdvertiser(pckOut)); 175c32a62cdSJonathan Tan } 176c32a62cdSJonathan Tan 177af3562f7SShawn O. Pearce private class Chain implements FilterChain { 178af3562f7SShawn O. Pearce private int filterIdx; 179af3562f7SShawn O. Pearce 1807ac182f4SDavid Pursehouse @Override doFilter(ServletRequest req, ServletResponse rsp)181af3562f7SShawn O. Pearce public void doFilter(ServletRequest req, ServletResponse rsp) 182af3562f7SShawn O. Pearce throws IOException, ServletException { 183af3562f7SShawn O. Pearce if (filterIdx < filters.length) 184af3562f7SShawn O. Pearce filters[filterIdx++].doFilter(req, rsp, this); 185af3562f7SShawn O. Pearce else 186af3562f7SShawn O. Pearce service(req, rsp); 187af3562f7SShawn O. Pearce } 188af3562f7SShawn O. Pearce } 18981fea92eSShawn O. Pearce } 190