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_UNAUTHORIZED; 15 import static org.eclipse.jgit.http.server.GitSmartHttpTools.infoRefsResultType; 16 import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; 17 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; 18 import static org.eclipse.jgit.http.server.ServletUtils.getRepository; 19 20 import java.io.IOException; 21 import java.util.List; 22 23 import javax.servlet.Filter; 24 import javax.servlet.FilterChain; 25 import javax.servlet.FilterConfig; 26 import javax.servlet.ServletException; 27 import javax.servlet.ServletRequest; 28 import javax.servlet.ServletResponse; 29 import javax.servlet.http.HttpServletRequest; 30 import javax.servlet.http.HttpServletResponse; 31 32 import org.eclipse.jgit.lib.Repository; 33 import org.eclipse.jgit.transport.PacketLineOut; 34 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; 35 import org.eclipse.jgit.transport.ServiceMayNotContinueException; 36 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; 37 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; 38 39 /** Filter in front of {@link InfoRefsServlet} to catch smart service requests. */ 40 abstract class SmartServiceInfoRefs implements Filter { 41 private final String svc; 42 43 private final Filter[] filters; 44 SmartServiceInfoRefs(String service, List<Filter> filters)45 SmartServiceInfoRefs(String service, List<Filter> filters) { 46 this.svc = service; 47 this.filters = filters.toArray(new Filter[0]); 48 } 49 50 /** {@inheritDoc} */ 51 @Override init(FilterConfig config)52 public void init(FilterConfig config) throws ServletException { 53 // Do nothing. 54 } 55 56 /** {@inheritDoc} */ 57 @Override destroy()58 public void destroy() { 59 // Do nothing. 60 } 61 62 /** {@inheritDoc} */ 63 @Override doFilter(ServletRequest request, ServletResponse response, FilterChain chain)64 public void doFilter(ServletRequest request, ServletResponse response, 65 FilterChain chain) throws IOException, ServletException { 66 final HttpServletRequest req = (HttpServletRequest) request; 67 final HttpServletResponse res = (HttpServletResponse) response; 68 69 if (svc.equals(req.getParameter("service"))) { 70 final Repository db = getRepository(req); 71 try { 72 begin(req, db); 73 } catch (ServiceNotAuthorizedException e) { 74 res.sendError(SC_UNAUTHORIZED, e.getMessage()); 75 return; 76 } catch (ServiceNotEnabledException e) { 77 sendError(req, res, SC_FORBIDDEN, e.getMessage()); 78 return; 79 } 80 81 try { 82 if (filters.length == 0) 83 service(req, response); 84 else 85 new Chain().doFilter(request, response); 86 } finally { 87 req.removeAttribute(ATTRIBUTE_HANDLER); 88 } 89 } else { 90 chain.doFilter(request, response); 91 } 92 } 93 service(ServletRequest request, ServletResponse response)94 private void service(ServletRequest request, ServletResponse response) 95 throws IOException { 96 final HttpServletRequest req = (HttpServletRequest) request; 97 final HttpServletResponse res = (HttpServletResponse) response; 98 final SmartOutputStream buf = new SmartOutputStream(req, res, true); 99 try { 100 res.setContentType(infoRefsResultType(svc)); 101 102 final PacketLineOut out = new PacketLineOut(buf); 103 respond(req, out, svc); 104 buf.close(); 105 } catch (ServiceNotAuthorizedException e) { 106 res.sendError(SC_UNAUTHORIZED, e.getMessage()); 107 } catch (ServiceNotEnabledException e) { 108 sendError(req, res, SC_FORBIDDEN, e.getMessage()); 109 } catch (ServiceMayNotContinueException e) { 110 if (e.isOutput()) 111 buf.close(); 112 else 113 sendError(req, res, e.getStatusCode(), e.getMessage()); 114 } 115 } 116 117 /** 118 * Begin service. 119 * 120 * @param req 121 * request 122 * @param db 123 * repository 124 * @throws IOException 125 * @throws ServiceNotEnabledException 126 * @throws ServiceNotAuthorizedException 127 */ begin(HttpServletRequest req, Repository db)128 protected abstract void begin(HttpServletRequest req, Repository db) 129 throws IOException, ServiceNotEnabledException, 130 ServiceNotAuthorizedException; 131 132 /** 133 * Advertise. 134 * 135 * @param req 136 * request 137 * @param pck 138 * @throws IOException 139 * @throws ServiceNotEnabledException 140 * @throws ServiceNotAuthorizedException 141 */ advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck)142 protected abstract void advertise(HttpServletRequest req, 143 PacketLineOutRefAdvertiser pck) throws IOException, 144 ServiceNotEnabledException, ServiceNotAuthorizedException; 145 146 /** 147 * Writes the appropriate response to an info/refs request received by 148 * a smart service. In protocol v0, this starts with "# 149 * service=serviceName" followed by a flush packet, but this is not 150 * necessarily the case in other protocol versions. 151 * <p> 152 * The default implementation writes "# service=serviceName" and a 153 * flush packet, then calls {@link #advertise}. Subclasses should 154 * override this method if they support protocol versions other than 155 * protocol v0. 156 * 157 * @param req 158 * request 159 * @param pckOut 160 * destination of response 161 * @param serviceName 162 * service name to be written out in protocol v0; may or may 163 * not be used in other versions 164 * @throws IOException 165 * @throws ServiceNotEnabledException 166 * @throws ServiceNotAuthorizedException 167 */ respond(HttpServletRequest req, PacketLineOut pckOut, String serviceName)168 protected void respond(HttpServletRequest req, 169 PacketLineOut pckOut, String serviceName) 170 throws IOException, ServiceNotEnabledException, 171 ServiceNotAuthorizedException { 172 pckOut.writeString("# service=" + svc + '\n'); //$NON-NLS-1$ 173 pckOut.end(); 174 advertise(req, new PacketLineOutRefAdvertiser(pckOut)); 175 } 176 177 private class Chain implements FilterChain { 178 private int filterIdx; 179 180 @Override doFilter(ServletRequest req, ServletResponse rsp)181 public void doFilter(ServletRequest req, ServletResponse rsp) 182 throws IOException, ServletException { 183 if (filterIdx < filters.length) 184 filters[filterIdx++].doFilter(req, rsp, this); 185 else 186 service(req, rsp); 187 } 188 } 189 } 190