View Javadoc
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  
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
52  	public void init(FilterConfig config) throws ServletException {
53  		// Do nothing.
54  	}
55  
56  	/** {@inheritDoc} */
57  	@Override
58  	public void destroy() {
59  		// Do nothing.
60  	}
61  
62  	/** {@inheritDoc} */
63  	@Override
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  
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 SmartOutputStreamtOutputStream.html#SmartOutputStream">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 	 */
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 	 */
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 	 */
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
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 }