/** * This file is part of git-as-svn. It is subject to the license terms * in the LICENSE file found in the top-level directory of this distribution * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn, * including this file, may be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ package svnserver.ext.gitlfs.server; import com.google.common.collect.ImmutableMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNException; import ru.bozaro.gitlfs.server.ContentManager; import ru.bozaro.gitlfs.server.ContentServlet; import ru.bozaro.gitlfs.server.PointerServlet; import svnserver.api.lfs.Lfs; import svnserver.context.Local; import svnserver.context.LocalContext; import svnserver.context.Shared; import svnserver.ext.api.ServiceRegistry; import svnserver.ext.gitlfs.api.LfsRpc; import svnserver.ext.gitlfs.config.LfsConfig; import svnserver.ext.gitlfs.storage.LfsStorage; import svnserver.ext.web.server.WebServer; import javax.servlet.Servlet; import java.io.IOException; import java.net.URI; import java.text.MessageFormat; import java.util.Collection; /** * LFS server. * * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com> */ public class LfsServer implements Shared { @NotNull public static final String SERVLET_BASE = "info/lfs"; @NotNull public static final String SERVLET_AUTH = "auth/lfs"; @NotNull public static final String SERVLET_CONTENT = SERVLET_BASE + "/storage"; @NotNull public static final String SERVLET_POINTER = SERVLET_BASE + "/objects"; @NotNull private final String pathFormat; @Nullable private final String privateToken; private int tokenExpireSec; private float tokenEnsureTime; public LfsServer(@NotNull String pathFormat, @Nullable String privateToken, int tokenExpireSec, float tokenEnsureTime) { this.pathFormat = pathFormat; this.privateToken = privateToken; this.tokenExpireSec = tokenExpireSec > 0 ? tokenExpireSec : LfsConfig.DEFAULT_TOKEN_EXPIRE_SEC; this.tokenEnsureTime = Math.max(0.0f, Math.min(tokenEnsureTime, 1.0f)); } public void register(@NotNull LocalContext localContext, @NotNull LfsStorage storage) throws IOException, SVNException { final WebServer webServer = WebServer.get(localContext.getShared()); final String name = localContext.getName(); final String pathSpec = ("/" + MessageFormat.format(pathFormat, name) + "/").replaceAll("/+", "/"); final ContentManager pointerManager = new LfsContentManager(localContext, storage, tokenExpireSec, tokenEnsureTime); final ContentManager contentManager = new LfsContentManager(localContext, storage, tokenExpireSec, 0.0f); final Collection<WebServer.Holder> servletsInfo = webServer.addServlets( ImmutableMap.<String, Servlet>builder() .put(pathSpec + SERVLET_AUTH, new LfsAuthServlet(localContext, pathSpec + SERVLET_BASE, privateToken, tokenExpireSec, tokenEnsureTime)) .put(pathSpec + SERVLET_POINTER + "/*", new PointerServlet(pointerManager, pathSpec + SERVLET_CONTENT)) .put(pathSpec + SERVLET_CONTENT + "/*", new ContentServlet(contentManager)) .build() ); localContext.add(LfsServerHolder.class, new LfsServerHolder(webServer, servletsInfo)); ServiceRegistry.get(localContext).addService(Lfs.newReflectiveBlockingService(new LfsRpc(URI.create(pathSpec + SERVLET_BASE), localContext, tokenExpireSec, tokenEnsureTime))); } public void unregister(@NotNull LocalContext localContext) throws IOException, SVNException { LfsServerHolder holder = localContext.remove(LfsServerHolder.class); if (holder != null) { holder.close(); } } private static class LfsServerHolder implements Local { @NotNull private final WebServer webServer; @NotNull private final Collection<WebServer.Holder> servlets; public LfsServerHolder(@NotNull WebServer webServer, @NotNull Collection<WebServer.Holder> servlets) { this.webServer = webServer; this.servlets = servlets; } @Override public void close() { webServer.removeServlets(servlets); } } }