diff options
author | James Moger <james.moger@gitblit.com> | 2013-05-03 19:06:50 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2013-05-03 19:09:25 -0400 |
commit | 40aa84507640cd2a980757e7910a63310474eb87 (patch) | |
tree | 088278936eb8722e4f162f2b77b91bbbffaeec87 /src/main | |
parent | 9985ed23ae251c1d024a223f9ea8372b75a122f2 (diff) | |
download | gitblit-40aa84507640cd2a980757e7910a63310474eb87.tar.gz gitblit-40aa84507640cd2a980757e7910a63310474eb87.zip |
Revised Git Daemon to improve thread stopping and to eliminate repository name hack
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/com/gitblit/GitBlit.java | 1 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/GitDaemon.java | 332 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/GitDaemonClient.java | 130 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/GitDaemonService.java | 165 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/GitblitReceivePackFactory.java | 13 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/GitblitUploadPackFactory.java | 5 | ||||
-rw-r--r-- | src/main/java/com/gitblit/git/RepositoryResolver.java | 18 |
7 files changed, 616 insertions, 48 deletions
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index 377a7b3d..0e4e2e91 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -3240,7 +3240,6 @@ public class GitBlit implements ServletContextListener { try {
gitDaemon = new GitDaemon(bindInterface, port, getRepositoriesFolder());
gitDaemon.start();
- logger.info(MessageFormat.format("Git daemon is listening on {0}:{1,number,0}", bindInterface, port));
} catch (IOException e) {
gitDaemon = null;
logger.error(MessageFormat.format("Failed to start Git daemon on {0}:{1,number,0}", bindInterface, port), e);
diff --git a/src/main/java/com/gitblit/git/GitDaemon.java b/src/main/java/com/gitblit/git/GitDaemon.java index 8ec05631..7050f878 100644 --- a/src/main/java/com/gitblit/git/GitDaemon.java +++ b/src/main/java/com/gitblit/git/GitDaemon.java @@ -1,36 +1,115 @@ /*
- * Copyright 2013 gitblit.com.
+ * Copyright (C) 2013 gitblit.com
+ * Copyright (C) 2008-2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * All rights reserved.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.gitblit.git;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.text.MessageFormat;
+import java.util.concurrent.atomic.AtomicBoolean;
-import org.eclipse.jgit.transport.Daemon;
-import org.eclipse.jgit.transport.DaemonClient;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.UploadPack;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.gitblit.utils.StringUtils;
/**
- * Gitblit's Git Daemon ignores any and all per-repository daemon settings
- * and integrates into Gitblit's security model.
+ * Gitblit's Git Daemon ignores any and all per-repository daemon settings and
+ * integrates into Gitblit's security model.
*
* @author James Moger
- *
+ *
*/
-public class GitDaemon extends Daemon {
+public class GitDaemon {
+
+ private final Logger logger = LoggerFactory.getLogger(GitDaemon.class);
+
+ /** 9418: IANA assigned port number for Git. */
+ public static final int DEFAULT_PORT = 9418;
+
+ private static final int BACKLOG = 5;
+
+ private InetSocketAddress myAddress;
+
+ private final GitDaemonService[] services;
+
+ private final ThreadGroup processors;
+
+ private AtomicBoolean run;
+
+ private ServerSocket acceptSocket;
+
+ private Thread acceptThread;
+
+ private int timeout;
+
+ private RepositoryResolver<GitDaemonClient> repositoryResolver;
+
+ private UploadPackFactory<GitDaemonClient> uploadPackFactory;
+
+ private ReceivePackFactory<GitDaemonClient> receivePackFactory;
+
+ /** Configure a daemon to listen on any available network port. */
+ public GitDaemon() {
+ this(null);
+ }
/**
* Construct the Gitblit Git daemon.
@@ -43,22 +122,213 @@ public class GitDaemon extends Daemon { * the folder to serve from
*/
public GitDaemon(String bindInterface, int port, File folder) {
- super(StringUtils.isEmpty(bindInterface) ? new InetSocketAddress(port) : new InetSocketAddress(bindInterface, port));
-
+ this(StringUtils.isEmpty(bindInterface) ? new InetSocketAddress(port)
+ : new InetSocketAddress(bindInterface, port));
+
// set the repository resolver and pack factories
- setRepositoryResolver(new RepositoryResolver<DaemonClient>(folder));
- setUploadPackFactory(new GitblitUploadPackFactory<DaemonClient>());
- setReceivePackFactory(new GitblitReceivePackFactory<DaemonClient>());
-
- // configure the git daemon to ignore the per-repository settings,
- // daemon.uploadpack and daemon.receivepack
- getService("git-upload-pack").setOverridable(false);
- getService("git-receive-pack").setOverridable(false);
-
- // enable both the upload and receive services and let the resolver,
- // pack factories, and receive hook handle security
- getService("git-upload-pack").setEnabled(true);
- getService("git-receive-pack").setEnabled(true);
+ repositoryResolver = new RepositoryResolver<GitDaemonClient>(folder);
}
+ /**
+ * Configure a new daemon for the specified network address.
+ *
+ * @param addr
+ * address to listen for connections on. If null, any available
+ * port will be chosen on all network interfaces.
+ */
+ public GitDaemon(final InetSocketAddress addr) {
+ myAddress = addr;
+ processors = new ThreadGroup("Git-Daemon");
+
+ run = new AtomicBoolean(false);
+ repositoryResolver = null;
+ uploadPackFactory = new GitblitUploadPackFactory<GitDaemonClient>();
+ receivePackFactory = new GitblitReceivePackFactory<GitDaemonClient>();
+
+ services = new GitDaemonService[] { new GitDaemonService("upload-pack", "uploadpack") {
+ {
+ setEnabled(true);
+ setOverridable(false);
+ }
+
+ @Override
+ protected void execute(final GitDaemonClient dc, final Repository db)
+ throws IOException, ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ UploadPack up = uploadPackFactory.create(dc, db);
+ InputStream in = dc.getInputStream();
+ OutputStream out = dc.getOutputStream();
+ up.upload(in, out, null);
+ }
+ }, new GitDaemonService("receive-pack", "receivepack") {
+ {
+ setEnabled(true);
+ setOverridable(false);
+ }
+
+ @Override
+ protected void execute(final GitDaemonClient dc, final Repository db)
+ throws IOException, ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ ReceivePack rp = receivePackFactory.create(dc, db);
+ InputStream in = dc.getInputStream();
+ OutputStream out = dc.getOutputStream();
+ rp.receive(in, out, null);
+ }
+ } };
+ }
+
+ /** @return timeout (in seconds) before aborting an IO operation. */
+ public int getTimeout() {
+ return timeout;
+ }
+
+ /**
+ * Set the timeout before willing to abort an IO call.
+ *
+ * @param seconds
+ * number of seconds to wait (with no data transfer occurring)
+ * before aborting an IO read or write operation with the
+ * connected client.
+ */
+ public void setTimeout(final int seconds) {
+ timeout = seconds;
+ }
+
+ /**
+ * Start this daemon on a background thread.
+ *
+ * @throws IOException
+ * the server socket could not be opened.
+ * @throws IllegalStateException
+ * the daemon is already running.
+ */
+ public synchronized void start() throws IOException {
+ if (acceptThread != null)
+ throw new IllegalStateException(JGitText.get().daemonAlreadyRunning);
+
+ final ServerSocket listenSock = new ServerSocket(myAddress != null ? myAddress.getPort()
+ : 0, BACKLOG, myAddress != null ? myAddress.getAddress() : null);
+ myAddress = (InetSocketAddress) listenSock.getLocalSocketAddress();
+
+ run.set(true);
+ acceptSocket = listenSock;
+ acceptThread = new Thread(processors, "Git-Daemon-Accept") {
+ public void run() {
+ while (isRunning()) {
+ try {
+ startClient(listenSock.accept());
+ } catch (InterruptedIOException e) {
+ // Test again to see if we should keep accepting.
+ } catch (IOException e) {
+ break;
+ }
+ }
+
+ try {
+ listenSock.close();
+ } catch (IOException err) {
+ //
+ } finally {
+ acceptSocket = null;
+ acceptThread = null;
+ }
+ }
+ };
+ acceptThread.start();
+
+ logger.info(MessageFormat.format("Git Daemon is listening on {0}:{1,number,0}", myAddress.getAddress().getHostAddress(), myAddress.getPort()));
+ }
+
+ /** @return true if this daemon is receiving connections. */
+ public boolean isRunning() {
+ return run.get();
+ }
+
+ /** Stop this daemon. */
+ public synchronized void stop() {
+ if (acceptThread != null) {
+ logger.info("Git Daemon stopping...");
+ run.set(false);
+ try {
+ // close the accept socket
+ // this throws a SocketException in the accept thread
+ acceptSocket.close();
+ } catch (IOException e1) {
+ }
+ try {
+ // join the accept thread
+ acceptThread.join();
+ logger.info("Git Daemon stopped.");
+ } catch (InterruptedException e) {
+ logger.error("Accept thread join interrupted", e);
+ }
+ }
+ }
+
+ private void startClient(final Socket s) {
+ final GitDaemonClient dc = new GitDaemonClient(this);
+
+ final SocketAddress peer = s.getRemoteSocketAddress();
+ if (peer instanceof InetSocketAddress)
+ dc.setRemoteAddress(((InetSocketAddress) peer).getAddress());
+
+ new Thread(processors, "Git-Daemon-Client " + peer.toString()) {
+ public void run() {
+ try {
+ dc.execute(s);
+ } catch (ServiceNotEnabledException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (ServiceNotAuthorizedException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (IOException e) {
+ // Ignore unexpected IO exceptions from clients
+ } finally {
+ try {
+ s.getInputStream().close();
+ } catch (IOException e) {
+ // Ignore close exceptions
+ }
+ try {
+ s.getOutputStream().close();
+ } catch (IOException e) {
+ // Ignore close exceptions
+ }
+ }
+ }
+ }.start();
+ }
+
+ synchronized GitDaemonService matchService(final String cmd) {
+ for (final GitDaemonService d : services) {
+ if (d.handles(cmd))
+ return d;
+ }
+ return null;
+ }
+
+ Repository openRepository(GitDaemonClient client, String name)
+ throws ServiceMayNotContinueException {
+ // Assume any attempt to use \ was by a Windows client
+ // and correct to the more typical / used in Git URIs.
+ //
+ name = name.replace('\\', '/');
+
+ // git://thishost/path should always be name="/path" here
+ //
+ if (!name.startsWith("/")) //$NON-NLS-1$
+ return null;
+
+ try {
+ return repositoryResolver.open(client, name.substring(1));
+ } catch (RepositoryNotFoundException e) {
+ // null signals it "wasn't found", which is all that is suitable
+ // for the remote client to know.
+ return null;
+ } catch (ServiceNotEnabledException e) {
+ // null signals it "wasn't found", which is all that is suitable
+ // for the remote client to know.
+ return null;
+ }
+ }
}
diff --git a/src/main/java/com/gitblit/git/GitDaemonClient.java b/src/main/java/com/gitblit/git/GitDaemonClient.java new file mode 100644 index 00000000..6972f314 --- /dev/null +++ b/src/main/java/com/gitblit/git/GitDaemonClient.java @@ -0,0 +1,130 @@ +package com.gitblit.git;
+
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.eclipse.jgit.transport.PacketLineIn;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
+
+/** Active network client of {@link Daemon}. */
+public class GitDaemonClient {
+ private final GitDaemon daemon;
+
+ private InetAddress peer;
+
+ private InputStream rawIn;
+
+ private OutputStream rawOut;
+
+ private String repositoryName;
+
+ GitDaemonClient(final GitDaemon d) {
+ daemon = d;
+ }
+
+ void setRemoteAddress(final InetAddress ia) {
+ peer = ia;
+ }
+
+ /** @return the daemon which spawned this client. */
+ public GitDaemon getDaemon() {
+ return daemon;
+ }
+
+ /** @return Internet address of the remote client. */
+ public InetAddress getRemoteAddress() {
+ return peer;
+ }
+
+ /** @return input stream to read from the connected client. */
+ public InputStream getInputStream() {
+ return rawIn;
+ }
+
+ /** @return output stream to send data to the connected client. */
+ public OutputStream getOutputStream() {
+ return rawOut;
+ }
+
+ public void setRepositoryName(String repositoryName) {
+ this.repositoryName = repositoryName;
+ }
+
+ /** @return the name of the requested repository. */
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+ void execute(final Socket sock) throws IOException,
+ ServiceNotEnabledException, ServiceNotAuthorizedException {
+ rawIn = new BufferedInputStream(sock.getInputStream());
+ rawOut = new SafeBufferedOutputStream(sock.getOutputStream());
+
+ if (0 < daemon.getTimeout())
+ sock.setSoTimeout(daemon.getTimeout() * 1000);
+ String cmd = new PacketLineIn(rawIn).readStringRaw();
+ final int nul = cmd.indexOf('\0');
+ if (nul >= 0) {
+ // Newer clients hide a "host" header behind this byte.
+ // Currently we don't use it for anything, so we ignore
+ // this portion of the command.
+ //
+ cmd = cmd.substring(0, nul);
+ }
+
+ final GitDaemonService srv = getDaemon().matchService(cmd);
+ if (srv == null)
+ return;
+ sock.setSoTimeout(0);
+ srv.execute(this, cmd);
+ }
+}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/git/GitDaemonService.java b/src/main/java/com/gitblit/git/GitDaemonService.java new file mode 100644 index 00000000..03c4e1cd --- /dev/null +++ b/src/main/java/com/gitblit/git/GitDaemonService.java @@ -0,0 +1,165 @@ +package com.gitblit.git;
+
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Config.SectionParser;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.Daemon;
+import org.eclipse.jgit.transport.PacketLineOut;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+
+/** A service exposed by {@link Daemon} over anonymous <code>git://</code>. */
+public abstract class GitDaemonService {
+ private final String command;
+
+ private final SectionParser<ServiceConfig> configKey;
+
+ private boolean enabled;
+
+ private boolean overridable;
+
+ GitDaemonService(final String cmdName, final String cfgName) {
+ command = cmdName.startsWith("git-") ? cmdName : "git-" + cmdName; //$NON-NLS-1$ //$NON-NLS-2$
+ configKey = new SectionParser<ServiceConfig>() {
+ public ServiceConfig parse(final Config cfg) {
+ return new ServiceConfig(GitDaemonService.this, cfg, cfgName);
+ }
+ };
+ overridable = true;
+ }
+
+ private static class ServiceConfig {
+ final boolean enabled;
+
+ ServiceConfig(final GitDaemonService service, final Config cfg,
+ final String name) {
+ enabled = cfg.getBoolean("daemon", name, service.isEnabled()); //$NON-NLS-1$
+ }
+ }
+
+ /** @return is this service enabled for invocation? */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * @param on
+ * true to allow this service to be used; false to deny it.
+ */
+ public void setEnabled(final boolean on) {
+ enabled = on;
+ }
+
+ /** @return can this service be configured in the repository config file? */
+ public boolean isOverridable() {
+ return overridable;
+ }
+
+ /**
+ * @param on
+ * true to permit repositories to override this service's enabled
+ * state with the <code>daemon.servicename</code> config setting.
+ */
+ public void setOverridable(final boolean on) {
+ overridable = on;
+ }
+
+ /** @return name of the command requested by clients. */
+ public String getCommandName() {
+ return command;
+ }
+
+ /**
+ * Determine if this service can handle the requested command.
+ *
+ * @param commandLine
+ * input line from the client.
+ * @return true if this command can accept the given command line.
+ */
+ public boolean handles(final String commandLine) {
+ return command.length() + 1 < commandLine.length()
+ && commandLine.charAt(command.length()) == ' '
+ && commandLine.startsWith(command);
+ }
+
+ void execute(final GitDaemonClient client, final String commandLine)
+ throws IOException, ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ final String name = commandLine.substring(command.length() + 1);
+ Repository db;
+ try {
+ db = client.getDaemon().openRepository(client, name);
+ } catch (ServiceMayNotContinueException e) {
+ // An error when opening the repo means the client is expecting a ref
+ // advertisement, so use that style of error.
+ PacketLineOut pktOut = new PacketLineOut(client.getOutputStream());
+ pktOut.writeString("ERR " + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ db = null;
+ }
+ if (db == null)
+ return;
+ try {
+ if (isEnabledFor(db))
+ execute(client, db);
+ } finally {
+ db.close();
+ }
+ }
+
+ private boolean isEnabledFor(final Repository db) {
+ if (isOverridable())
+ return db.getConfig().get(configKey).enabled;
+ return isEnabled();
+ }
+
+ abstract void execute(GitDaemonClient client, Repository db)
+ throws IOException, ServiceNotEnabledException,
+ ServiceNotAuthorizedException;
+}
diff --git a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java index 8b8ef0e4..77a3df63 100644 --- a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java +++ b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java @@ -19,7 +19,6 @@ import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.DaemonClient; import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; @@ -50,18 +49,15 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> { final ReceivePack rp = new ReceivePack(db); UserModel user = UserModel.ANONYMOUS; + String repositoryName = ""; String origin = ""; String gitblitUrl = ""; int timeout = 0; - // XXX extract the repository name from the config - // the name is injected by GitRepositoryResolver - String repositoryName = db.getConfig().getString("gitblit", null, "repositoryName"); - - if (req instanceof HttpServletRequest) { // http/https request may or may not be authenticated HttpServletRequest request = (HttpServletRequest) req; + repositoryName = request.getAttribute("gitblitRepositoryName").toString(); origin = request.getRemoteHost(); gitblitUrl = HttpUtils.getGitblitURL(request); @@ -74,9 +70,10 @@ public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> { user = new UserModel(username); } } - } else if (req instanceof DaemonClient) { + } else if (req instanceof GitDaemonClient) { // git daemon request is alway anonymous - DaemonClient client = (DaemonClient) req; + GitDaemonClient client = (GitDaemonClient) req; + repositoryName = client.getRepositoryName(); origin = client.getRemoteAddress().getHostAddress(); // set timeout from Git daemon timeout = client.getDaemon().getTimeout(); diff --git a/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java b/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java index 85750f84..e953ca49 100644 --- a/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java +++ b/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java @@ -21,7 +21,6 @@ import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.DaemonClient; import org.eclipse.jgit.transport.RefFilter; import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; @@ -56,9 +55,9 @@ public class GitblitUploadPackFactory<X> implements UploadPackFactory<X> { if (user == null) { user = UserModel.ANONYMOUS; } - } else if (req instanceof DaemonClient) { + } else if (req instanceof GitDaemonClient) { // git daemon request is always anonymous - DaemonClient client = (DaemonClient) req; + GitDaemonClient client = (GitDaemonClient) req; // set timeout from Git daemon timeout = client.getDaemon().getTimeout(); } diff --git a/src/main/java/com/gitblit/git/RepositoryResolver.java b/src/main/java/com/gitblit/git/RepositoryResolver.java index fb5db712..21a83760 100644 --- a/src/main/java/com/gitblit/git/RepositoryResolver.java +++ b/src/main/java/com/gitblit/git/RepositoryResolver.java @@ -23,7 +23,6 @@ import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.transport.DaemonClient; import org.eclipse.jgit.transport.resolver.FileResolver; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.slf4j.Logger; @@ -54,10 +53,19 @@ public class RepositoryResolver<X> extends FileResolver<X> { public Repository open(final X req, final String name) throws RepositoryNotFoundException, ServiceNotEnabledException { Repository repo = super.open(req, name); - // XXX Set repository name for the pack factories + + // Set repository name for the pack factories // We do this because the JGit API does not have a consistent way to // retrieve the repository name from the pack factories or the hooks. - repo.getConfig().setString("gitblit", null, "repositoryName", name); + if (req instanceof HttpServletRequest) { + // http/https request + HttpServletRequest client = (HttpServletRequest) req; + client.setAttribute("gitblitRepositoryName", name); + } else if (req instanceof GitDaemonClient) { + // git request + GitDaemonClient client = (GitDaemonClient) req; + client.setRepositoryName(name); + } return repo; } @@ -72,10 +80,10 @@ public class RepositoryResolver<X> extends FileResolver<X> { UserModel user = null; String origin = null; - if (req instanceof DaemonClient) { + if (req instanceof GitDaemonClient) { // git daemon request // this is an anonymous/unauthenticated protocol - DaemonClient client = (DaemonClient) req; + GitDaemonClient client = (GitDaemonClient) req; scheme = "git"; origin = client.getRemoteAddress().toString(); user = UserModel.ANONYMOUS; |