You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
10 KiB
315 lines
10 KiB
/* |
|
* Copyright (C) 2009-2010, Google Inc. and others |
|
* |
|
* This program and the accompanying materials are made available under the |
|
* terms of the Eclipse Distribution License v. 1.0 which is available at |
|
* https://www.eclipse.org/org/documents/edl-v10.php. |
|
* |
|
* SPDX-License-Identifier: BSD-3-Clause |
|
*/ |
|
|
|
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.FilterConfig; |
|
import javax.servlet.ServletException; |
|
import javax.servlet.http.HttpServletRequest; |
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
import org.eclipse.jgit.http.server.glue.ErrorServlet; |
|
import org.eclipse.jgit.http.server.glue.MetaFilter; |
|
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.lib.Constants; |
|
import org.eclipse.jgit.transport.resolver.FileResolver; |
|
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; |
|
import org.eclipse.jgit.transport.resolver.RepositoryResolver; |
|
import org.eclipse.jgit.transport.resolver.UploadPackFactory; |
|
import org.eclipse.jgit.util.StringUtils; |
|
|
|
/** |
|
* Handles Git repository access over HTTP. |
|
* <p> |
|
* Applications embedding this filter should map a directory path within the |
|
* application to this filter. For a servlet version, see |
|
* {@link org.eclipse.jgit.http.server.GitServlet}. |
|
* <p> |
|
* Applications may wish to add additional repository action URLs to this |
|
* servlet by taking advantage of its extension from |
|
* {@link org.eclipse.jgit.http.server.glue.MetaFilter}. Callers may register |
|
* their own URL suffix translations through {@link #serve(String)}, or their |
|
* regex translations through {@link #serveRegex(String)}. Each translation |
|
* should contain a complete filter pipeline which ends with the HttpServlet |
|
* that should handle the requested action. |
|
*/ |
|
public class GitFilter extends MetaFilter { |
|
private volatile boolean initialized; |
|
|
|
private RepositoryResolver<HttpServletRequest> resolver; |
|
|
|
private AsIsFileService asIs = new AsIsFileService(); |
|
|
|
private UploadPackFactory<HttpServletRequest> uploadPackFactory = new DefaultUploadPackFactory(); |
|
|
|
private UploadPackErrorHandler uploadPackErrorHandler; |
|
|
|
private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory(); |
|
|
|
private ReceivePackErrorHandler receivePackErrorHandler; |
|
|
|
private final List<Filter> uploadPackFilters = new LinkedList<>(); |
|
|
|
private final List<Filter> receivePackFilters = new LinkedList<>(); |
|
|
|
/** |
|
* New servlet that will load its base directory from {@code web.xml}. |
|
* <p> |
|
* The required parameter {@code base-path} must be configured to point to |
|
* the local filesystem directory where all served Git repositories reside. |
|
*/ |
|
public GitFilter() { |
|
// Initialized above by field declarations. |
|
} |
|
|
|
/** |
|
* New servlet configured with a specific resolver. |
|
* |
|
* @param resolver |
|
* the resolver to use when matching URL to Git repository. If |
|
* null the {@code base-path} parameter will be looked for in the |
|
* parameter table during init, which usually comes from the |
|
* {@code web.xml} file of the web application. |
|
*/ |
|
public void setRepositoryResolver(RepositoryResolver<HttpServletRequest> resolver) { |
|
assertNotInitialized(); |
|
this.resolver = resolver; |
|
} |
|
|
|
/** |
|
* Set AsIsFileService |
|
* |
|
* @param f |
|
* the filter to validate direct access to repository files |
|
* through a dumb client. If {@code null} then dumb client |
|
* support is completely disabled. |
|
*/ |
|
public void setAsIsFileService(AsIsFileService f) { |
|
assertNotInitialized(); |
|
this.asIs = f != null ? f : AsIsFileService.DISABLED; |
|
} |
|
|
|
/** |
|
* Set upload-pack factory |
|
* |
|
* @param f |
|
* the factory to construct and configure an |
|
* {@link org.eclipse.jgit.transport.UploadPack} session when a |
|
* fetch or clone is requested by a client. |
|
*/ |
|
@SuppressWarnings("unchecked") |
|
public void setUploadPackFactory(UploadPackFactory<HttpServletRequest> f) { |
|
assertNotInitialized(); |
|
this.uploadPackFactory = f != null ? f : (UploadPackFactory<HttpServletRequest>)UploadPackFactory.DISABLED; |
|
} |
|
|
|
/** |
|
* Set a custom error handler for git-upload-pack. |
|
* |
|
* @param h |
|
* A custom error handler for git-upload-pack. |
|
*/ |
|
public void setUploadPackErrorHandler(UploadPackErrorHandler h) { |
|
assertNotInitialized(); |
|
this.uploadPackErrorHandler = h; |
|
} |
|
|
|
/** |
|
* Add upload-pack filter |
|
* |
|
* @param filter |
|
* filter to apply before any of the UploadPack operations. The |
|
* UploadPack instance is available in the request attribute |
|
* {@link org.eclipse.jgit.http.server.ServletUtils#ATTRIBUTE_HANDLER}. |
|
*/ |
|
public void addUploadPackFilter(Filter filter) { |
|
assertNotInitialized(); |
|
uploadPackFilters.add(filter); |
|
} |
|
|
|
/** |
|
* Set the receive-pack factory |
|
* |
|
* @param f |
|
* the factory to construct and configure a |
|
* {@link org.eclipse.jgit.transport.ReceivePack} session when a |
|
* push is requested by a client. |
|
*/ |
|
@SuppressWarnings("unchecked") |
|
public void setReceivePackFactory(ReceivePackFactory<HttpServletRequest> f) { |
|
assertNotInitialized(); |
|
this.receivePackFactory = f != null ? f : (ReceivePackFactory<HttpServletRequest>)ReceivePackFactory.DISABLED; |
|
} |
|
|
|
/** |
|
* Set a custom error handler for git-receive-pack. |
|
* |
|
* @param h |
|
* A custom error handler for git-receive-pack. |
|
*/ |
|
public void setReceivePackErrorHandler(ReceivePackErrorHandler h) { |
|
assertNotInitialized(); |
|
this.receivePackErrorHandler = h; |
|
} |
|
|
|
/** |
|
* Add receive-pack filter |
|
* |
|
* @param filter |
|
* filter to apply before any of the ReceivePack operations. The |
|
* ReceivePack instance is available in the request attribute |
|
* {@link org.eclipse.jgit.http.server.ServletUtils#ATTRIBUTE_HANDLER}. |
|
*/ |
|
public void addReceivePackFilter(Filter filter) { |
|
assertNotInitialized(); |
|
receivePackFilters.add(filter); |
|
} |
|
|
|
private void assertNotInitialized() { |
|
if (initialized) |
|
throw new IllegalStateException(HttpServerText.get().alreadyInitializedByContainer); |
|
} |
|
|
|
/** {@inheritDoc} */ |
|
@Override |
|
public void init(FilterConfig filterConfig) throws ServletException { |
|
super.init(filterConfig); |
|
|
|
if (resolver == null) { |
|
File root = getFile(filterConfig, "base-path"); |
|
boolean exportAll = getBoolean(filterConfig, "export-all"); |
|
setRepositoryResolver(new FileResolver<>(root, exportAll)); |
|
} |
|
|
|
initialized = true; |
|
|
|
if (uploadPackFactory != UploadPackFactory.DISABLED) { |
|
ServletBinder b = serve("*/" + GitSmartHttpTools.UPLOAD_PACK); |
|
b = b.through(new UploadPackServlet.Factory(uploadPackFactory)); |
|
for (Filter f : uploadPackFilters) |
|
b = b.through(f); |
|
b.with(new UploadPackServlet(uploadPackErrorHandler)); |
|
} |
|
|
|
if (receivePackFactory != ReceivePackFactory.DISABLED) { |
|
ServletBinder b = serve("*/" + GitSmartHttpTools.RECEIVE_PACK); |
|
b = b.through(new ReceivePackServlet.Factory(receivePackFactory)); |
|
for (Filter f : receivePackFilters) |
|
b = b.through(f); |
|
b.with(new ReceivePackServlet(receivePackErrorHandler)); |
|
} |
|
|
|
ServletBinder refs = serve("*/" + Constants.INFO_REFS); |
|
if (uploadPackFactory != UploadPackFactory.DISABLED) { |
|
refs = refs.through(new UploadPackServlet.InfoRefs( |
|
uploadPackFactory, uploadPackFilters)); |
|
} |
|
if (receivePackFactory != ReceivePackFactory.DISABLED) { |
|
refs = refs.through(new ReceivePackServlet.InfoRefs( |
|
receivePackFactory, receivePackFilters)); |
|
} |
|
if (asIs != AsIsFileService.DISABLED) { |
|
refs = refs.through(new IsLocalFilter()); |
|
refs = refs.through(new AsIsFileFilter(asIs)); |
|
refs.with(new InfoRefsServlet()); |
|
} else |
|
refs.with(new ErrorServlet(HttpServletResponse.SC_NOT_ACCEPTABLE)); |
|
|
|
if (asIs != AsIsFileService.DISABLED) { |
|
final IsLocalFilter mustBeLocal = new IsLocalFilter(); |
|
final AsIsFileFilter enabled = new AsIsFileFilter(asIs); |
|
|
|
serve("*/" + Constants.HEAD)// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.with(new TextFileServlet(Constants.HEAD)); |
|
|
|
final String info_alternates = Constants.OBJECTS + "/" + Constants.INFO_ALTERNATES; |
|
serve("*/" + info_alternates)// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.with(new TextFileServlet(info_alternates)); |
|
|
|
final String http_alternates = Constants.OBJECTS + "/" + Constants.INFO_HTTP_ALTERNATES; |
|
serve("*/" + http_alternates)// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.with(new TextFileServlet(http_alternates)); |
|
|
|
serve("*/objects/info/packs")// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.with(new InfoPacksServlet()); |
|
|
|
serveRegex("^/(.*)/objects/([0-9a-f]{2}/[0-9a-f]{38})$")// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.through(new RegexGroupFilter(2))// |
|
.with(new ObjectFileServlet.Loose()); |
|
|
|
serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.pack)$")// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.through(new RegexGroupFilter(2))// |
|
.with(new ObjectFileServlet.Pack()); |
|
|
|
serveRegex("^/(.*)/objects/(pack/pack-[0-9a-f]{40}\\.idx)$")// |
|
.through(mustBeLocal)// |
|
.through(enabled)// |
|
.through(new RegexGroupFilter(2))// |
|
.with(new ObjectFileServlet.PackIdx()); |
|
} |
|
} |
|
|
|
private static File getFile(FilterConfig cfg, String param) |
|
throws ServletException { |
|
String n = cfg.getInitParameter(param); |
|
if (n == null || "".equals(n)) |
|
throw new ServletException(MessageFormat.format(HttpServerText.get().parameterNotSet, param)); |
|
|
|
File path = new File(n); |
|
if (!path.exists()) |
|
throw new ServletException(MessageFormat.format(HttpServerText.get().pathForParamNotFound, path, param)); |
|
return path; |
|
} |
|
|
|
private static boolean getBoolean(FilterConfig cfg, String param) |
|
throws ServletException { |
|
String n = cfg.getInitParameter(param); |
|
if (n == null) |
|
return false; |
|
try { |
|
return StringUtils.toBoolean(n); |
|
} catch (IllegalArgumentException err) { |
|
throw new ServletException(MessageFormat.format( |
|
HttpServerText.get().invalidBoolean, param, n), err); |
|
} |
|
} |
|
|
|
/** {@inheritDoc} */ |
|
@Override |
|
protected ServletBinder register(ServletBinder binder) { |
|
if (resolver == null) |
|
throw new IllegalStateException(HttpServerText.get().noResolverAvailable); |
|
binder = binder.through(new NoCacheFilter()); |
|
binder = binder.through(new RepositoryFilter(resolver)); |
|
return binder; |
|
} |
|
}
|
|
|