xref: /JGit/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java (revision 5c5f7c6b146b24f2bd4afae1902df85ad6e57ea3)
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