diff options
author | Shawn O. Pearce <spearce@spearce.org> | 2011-03-15 14:00:43 -0700 |
---|---|---|
committer | Shawn O. Pearce <spearce@spearce.org> | 2011-04-01 17:40:33 -0400 |
commit | af3562f7f70a9017f6f90d266f2b2a6fc3b361ad (patch) | |
tree | 68762a6733c2e3053b1e4b4fdcf0af96c4cc2a5c /org.eclipse.jgit.http.server/src | |
parent | 8ac65d33ed7a94f77cb066271669feebf9b882fc (diff) | |
download | jgit-af3562f7f70a9017f6f90d266f2b2a6fc3b361ad.tar.gz jgit-af3562f7f70a9017f6f90d266f2b2a6fc3b361ad.zip |
Allow application filters on smart HTTP operations
Permit applications embedding GitServlet to wrap the
info/refs?service=$name and /$name operations with a
servlet Filter.
To help applications inspect state of the operation,
expose the UploadPack or ReceivePack object into a
request attribute. This can be useful for logging,
or to implement throttling of requests like Gerrit
Code Review uses to prevent server overload.
Change-Id: Ib8773c14e2b7a650769bd578aad745e6651210cb
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.jgit.http.server/src')
5 files changed, 228 insertions, 64 deletions
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java index 9be5fa7e9f..caa84e46ba 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java @@ -45,7 +45,10 @@ package org.eclipse.jgit.http.server; import java.io.File; import java.text.MessageFormat; +import java.util.LinkedList; +import java.util.List; +import javax.servlet.Filter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -55,9 +58,9 @@ import org.eclipse.jgit.http.server.glue.ErrorServlet; import org.eclipse.jgit.http.server.glue.MetaServlet; import org.eclipse.jgit.http.server.glue.RegexGroupFilter; import org.eclipse.jgit.http.server.glue.ServletBinder; +import org.eclipse.jgit.http.server.resolver.AsIsFileService; import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory; -import org.eclipse.jgit.http.server.resolver.AsIsFileService; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.UploadPack; @@ -114,6 +117,10 @@ public class GitServlet extends MetaServlet { private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory(); + private final List<Filter> uploadPackFilters = new LinkedList<Filter>(); + + private final List<Filter> receivePackFilters = new LinkedList<Filter>(); + /** * New servlet that will load its base directory from {@code web.xml}. * <p> @@ -161,6 +168,17 @@ public class GitServlet extends MetaServlet { } /** + * @param filter + * filter to apply before any of the UploadPack operations. The + * UploadPack instance is available in the request attribute + * {@link ServletUtils#ATTRIBUTE_HANDLER}. + */ + public void addUploadPackFilter(Filter filter) { + assertNotInitialized(); + uploadPackFilters.add(filter); + } + + /** * @param f * the factory to construct and configure a {@link ReceivePack} * session when a push is requested by a client. @@ -171,6 +189,17 @@ public class GitServlet extends MetaServlet { this.receivePackFactory = f != null ? f : (ReceivePackFactory<HttpServletRequest>)ReceivePackFactory.DISABLED; } + /** + * @param filter + * filter to apply before any of the ReceivePack operations. The + * ReceivePack instance is available in the request attribute + * {@link ServletUtils#ATTRIBUTE_HANDLER}. + */ + public void addReceivePackFilter(Filter filter) { + assertNotInitialized(); + receivePackFilters.add(filter); + } + private void assertNotInitialized() { if (initialized) throw new IllegalStateException(HttpServerText.get().alreadyInitializedByContainer); @@ -189,23 +218,29 @@ public class GitServlet extends MetaServlet { initialized = true; if (uploadPackFactory != UploadPackFactory.DISABLED) { - serve("*/git-upload-pack")// - .with(new UploadPackServlet(uploadPackFactory)); + ServletBinder b = serve("*/git-upload-pack"); + b = b.through(new UploadPackServlet.Factory(uploadPackFactory)); + for (Filter f : uploadPackFilters) + b = b.through(f); + b.with(new UploadPackServlet()); } if (receivePackFactory != ReceivePackFactory.DISABLED) { - serve("*/git-receive-pack")// - .with(new ReceivePackServlet(receivePackFactory)); + ServletBinder b = serve("*/git-receive-pack"); + b = b.through(new ReceivePackServlet.Factory(receivePackFactory)); + for (Filter f : receivePackFilters) + b = b.through(f); + b.with(new ReceivePackServlet()); } ServletBinder refs = serve("*/" + Constants.INFO_REFS); if (uploadPackFactory != UploadPackFactory.DISABLED) { - refs = refs.through(// - new UploadPackServlet.InfoRefs(uploadPackFactory)); + refs = refs.through(new UploadPackServlet.InfoRefs( + uploadPackFactory, uploadPackFilters)); } if (receivePackFactory != ReceivePackFactory.DISABLED) { - refs = refs.through(// - new ReceivePackServlet.InfoRefs(receivePackFactory)); + refs = refs.through(new ReceivePackServlet.InfoRefs( + receivePackFactory, receivePackFilters)); } if (asIs != AsIsFileService.DISABLED) { refs = refs.through(new IsLocalFilter()); diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java index bb4ae19e78..69b5aec25e 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java @@ -47,11 +47,19 @@ 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.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; import java.io.IOException; - +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; @@ -72,18 +80,27 @@ class ReceivePackServlet extends HttpServlet { private static final long serialVersionUID = 1L; static class InfoRefs extends SmartServiceInfoRefs { - private final ReceivePackFactory receivePackFactory; + private final ReceivePackFactory<HttpServletRequest> receivePackFactory; - InfoRefs(final ReceivePackFactory receivePackFactory) { - super("git-receive-pack"); + InfoRefs(ReceivePackFactory<HttpServletRequest> receivePackFactory, + List<Filter> filters) { + super("git-receive-pack", filters); this.receivePackFactory = receivePackFactory; } @Override - protected void advertise(HttpServletRequest req, Repository db, + protected void begin(HttpServletRequest req, Repository db) + throws IOException, ServiceNotEnabledException, + ServiceNotAuthorizedException { + ReceivePack rp = receivePackFactory.create(req, db); + req.setAttribute(ATTRIBUTE_HANDLER, rp); + } + + @Override + protected void advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck) throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException { - ReceivePack rp = receivePackFactory.create(req, db); + ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); try { rp.sendAdvertisedRefs(pck); } finally { @@ -92,10 +109,44 @@ class ReceivePackServlet extends HttpServlet { } } - private final ReceivePackFactory receivePackFactory; + static class Factory implements Filter { + private final ReceivePackFactory<HttpServletRequest> receivePackFactory; - ReceivePackServlet(final ReceivePackFactory receivePackFactory) { - this.receivePackFactory = receivePackFactory; + Factory(ReceivePackFactory<HttpServletRequest> receivePackFactory) { + this.receivePackFactory = receivePackFactory; + } + + 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); + return; + + } catch (ServiceNotEnabledException e) { + rsp.sendError(SC_FORBIDDEN); + return; + } + + try { + req.setAttribute(ATTRIBUTE_HANDLER, rp); + chain.doFilter(req, rsp); + } finally { + req.removeAttribute(ATTRIBUTE_HANDLER); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + // Nothing. + } + + public void destroy() { + // Nothing. + } } @Override @@ -106,9 +157,8 @@ class ReceivePackServlet extends HttpServlet { return; } - final Repository db = getRepository(req); + ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); try { - final ReceivePack rp = receivePackFactory.create(req, db); rp.setBiDirectionalPipe(false); rsp.setContentType(RSP_TYPE); @@ -120,15 +170,6 @@ class ReceivePackServlet extends HttpServlet { }; rp.receive(getInputStream(req), out, null); out.close(); - - } catch (ServiceNotAuthorizedException e) { - rsp.sendError(SC_UNAUTHORIZED); - return; - - } catch (ServiceNotEnabledException e) { - rsp.sendError(SC_FORBIDDEN); - return; - } catch (IOException e) { getServletContext().log(HttpServerText.get().internalErrorDuringReceivePack, e); rsp.sendError(SC_INTERNAL_SERVER_ERROR); diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java index 4e47c95ad0..2114655875 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java @@ -71,6 +71,9 @@ public final class ServletUtils { /** Request attribute which stores the {@link Repository} instance. */ public static final String ATTRIBUTE_REPOSITORY = "org.eclipse.jgit.Repository"; + /** Request attribute storing either UploadPack or ReceivePack. */ + public static final String ATTRIBUTE_HANDLER = "org.eclipse.jgit.transport.UploadPackOrReceivePack"; + /** * Get the selected repository from the request. * diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java index f9be95f04e..7152c88ed5 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java @@ -45,9 +45,11 @@ package org.eclipse.jgit.http.server; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; import java.io.IOException; +import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -68,8 +70,11 @@ import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; abstract class SmartServiceInfoRefs implements Filter { private final String svc; - SmartServiceInfoRefs(final String service) { + private final Filter[] filters; + + SmartServiceInfoRefs(final String service, final List<Filter> filters) { this.svc = service; + this.filters = filters.toArray(new Filter[filters.size()]); } public void init(FilterConfig config) throws ServletException { @@ -83,31 +88,71 @@ abstract class SmartServiceInfoRefs implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; + final HttpServletResponse rsp = (HttpServletResponse) response; if (svc.equals(req.getParameter("service"))) { - final HttpServletResponse rsp = (HttpServletResponse) response; + final Repository db = getRepository(req); try { - final Repository db = getRepository(req); - rsp.setContentType("application/x-" + svc + "-advertisement"); - - final SmartOutputStream buf = new SmartOutputStream(req, rsp); - final PacketLineOut out = new PacketLineOut(buf); - out.writeString("# service=" + svc + "\n"); - out.end(); - advertise(req, db, new PacketLineOutRefAdvertiser(out)); - buf.close(); + begin(req, db); } catch (ServiceNotAuthorizedException e) { rsp.sendError(SC_UNAUTHORIZED); - + return; } catch (ServiceNotEnabledException e) { rsp.sendError(SC_FORBIDDEN); + return; + } + + try { + if (filters.length == 0) + service(req, response); + else + new Chain().doFilter(request, response); + } finally { + req.removeAttribute(ATTRIBUTE_HANDLER); } } else { chain.doFilter(request, response); } } - protected abstract void advertise(HttpServletRequest req, Repository db, + private void service(ServletRequest request, ServletResponse response) + throws IOException { + final HttpServletRequest req = (HttpServletRequest) request; + final HttpServletResponse rsp = (HttpServletResponse) response; + try { + rsp.setContentType("application/x-" + svc + "-advertisement"); + + final SmartOutputStream buf = new SmartOutputStream(req, rsp); + final PacketLineOut out = new PacketLineOut(buf); + out.writeString("# service=" + svc + "\n"); + out.end(); + advertise(req, new PacketLineOutRefAdvertiser(out)); + buf.close(); + } catch (ServiceNotAuthorizedException e) { + rsp.sendError(SC_UNAUTHORIZED); + + } catch (ServiceNotEnabledException e) { + rsp.sendError(SC_FORBIDDEN); + } + } + + protected abstract void begin(HttpServletRequest req, Repository db) + throws IOException, ServiceNotEnabledException, + ServiceNotAuthorizedException; + + protected abstract void advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck) throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException; + + private class Chain implements FilterChain { + private int filterIdx; + + public void doFilter(ServletRequest req, ServletResponse rsp) + throws IOException, ServletException { + if (filterIdx < filters.length) + filters[filterIdx++].doFilter(req, rsp, this); + else + service(req, rsp); + } + } } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java index ff5e5d53c8..192adc56b4 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java @@ -47,18 +47,26 @@ 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.ServletUtils.ATTRIBUTE_HANDLER; import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; import static org.eclipse.jgit.http.server.ServletUtils.getRepository; import java.io.IOException; - +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.lib.Repository; -import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; +import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.eclipse.jgit.transport.resolver.UploadPackFactory; @@ -72,18 +80,27 @@ class UploadPackServlet extends HttpServlet { private static final long serialVersionUID = 1L; static class InfoRefs extends SmartServiceInfoRefs { - private final UploadPackFactory uploadPackFactory; + private final UploadPackFactory<HttpServletRequest> uploadPackFactory; - InfoRefs(final UploadPackFactory uploadPackFactory) { - super("git-upload-pack"); + InfoRefs(UploadPackFactory<HttpServletRequest> uploadPackFactory, + List<Filter> filters) { + super("git-upload-pack", filters); this.uploadPackFactory = uploadPackFactory; } @Override - protected void advertise(HttpServletRequest req, Repository db, + protected void begin(HttpServletRequest req, Repository db) + throws IOException, ServiceNotEnabledException, + ServiceNotAuthorizedException { + UploadPack up = uploadPackFactory.create(req, db); + req.setAttribute(ATTRIBUTE_HANDLER, up); + } + + @Override + protected void advertise(HttpServletRequest req, PacketLineOutRefAdvertiser pck) throws IOException, ServiceNotEnabledException, ServiceNotAuthorizedException { - UploadPack up = uploadPackFactory.create(req, db); + UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER); try { up.sendAdvertisedRefs(pck); } finally { @@ -92,10 +109,44 @@ class UploadPackServlet extends HttpServlet { } } - private final UploadPackFactory uploadPackFactory; + static class Factory implements Filter { + private final UploadPackFactory<HttpServletRequest> uploadPackFactory; + + Factory(UploadPackFactory<HttpServletRequest> uploadPackFactory) { + this.uploadPackFactory = uploadPackFactory; + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse rsp = (HttpServletResponse) response; + UploadPack rp; + try { + rp = uploadPackFactory.create(req, getRepository(req)); + } catch (ServiceNotAuthorizedException e) { + rsp.sendError(SC_UNAUTHORIZED); + return; + + } catch (ServiceNotEnabledException e) { + rsp.sendError(SC_FORBIDDEN); + return; + } + + try { + req.setAttribute(ATTRIBUTE_HANDLER, rp); + chain.doFilter(req, rsp); + } finally { + req.removeAttribute(ATTRIBUTE_HANDLER); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + // Nothing. + } - UploadPackServlet(final UploadPackFactory uploadPackFactory) { - this.uploadPackFactory = uploadPackFactory; + public void destroy() { + // Nothing. + } } @Override @@ -106,9 +157,8 @@ class UploadPackServlet extends HttpServlet { return; } - final Repository db = getRepository(req); + UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER); try { - final UploadPack up = uploadPackFactory.create(req, db); up.setBiDirectionalPipe(false); rsp.setContentType(RSP_TYPE); @@ -121,16 +171,6 @@ class UploadPackServlet extends HttpServlet { up.upload(getInputStream(req), out, null); out.close(); - } catch (ServiceNotAuthorizedException e) { - rsp.reset(); - rsp.sendError(SC_UNAUTHORIZED); - return; - - } catch (ServiceNotEnabledException e) { - rsp.reset(); - rsp.sendError(SC_FORBIDDEN); - return; - } catch (IOException e) { getServletContext().log(HttpServerText.get().internalErrorDuringUploadPack, e); rsp.reset(); |