Browse Source

Modify refs in UploadPack/ReceivePack using a hook interface

This is intended to replace the RefFilter interface (but does not yet,
for backwards compatibility). That interface required lots of extra
scanning and copying in filter cases such as only advertising a subtree
of the refs directory. Instead, provide a hook that can be executed
right before ref advertisement, using the public methods on
UploadPack/ReceivePack to explicitly set the map of advertised refs.

Change-Id: I0067019a191c8148af2cfb71a675f2258c5af0ca
tags/v2.0.0.201206130900-r
Dave Borowitz 12 years ago
parent
commit
1f2022e3a7

+ 2
- 2
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java View File

@@ -65,7 +65,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PacketLineOut;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.UploadPackMayNotContinueException;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;

@@ -137,7 +137,7 @@ abstract class SmartServiceInfoRefs implements Filter {
} catch (ServiceNotEnabledException e) {
sendError(req, res, SC_FORBIDDEN);

} catch (UploadPackMayNotContinueException e) {
} catch (ServiceMayNotContinueException e) {
if (e.isOutput())
buf.close();
else

+ 2
- 2
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java View File

@@ -73,7 +73,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
import org.eclipse.jgit.transport.UploadPackMayNotContinueException;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
@@ -176,7 +176,7 @@ class UploadPackServlet extends HttpServlet {
up.upload(getInputStream(req), out, null);
out.close();

} catch (UploadPackMayNotContinueException e) {
} catch (ServiceMayNotContinueException e) {
if (e.isOutput()) {
consumeRequestBody(req);
out.close();

org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java → org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java View File

@@ -74,6 +74,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.ObjectDirectory;
import org.eclipse.jgit.storage.pack.BinaryDelta;
import org.eclipse.jgit.util.NB;
@@ -82,7 +83,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCase {
private static final NullProgressMonitor PM = NullProgressMonitor.INSTANCE;

private static final String R_MASTER = Constants.R_HEADS + Constants.MASTER;
@@ -150,7 +151,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
dst.incrementOpen();

final ReceivePack rp = super.createReceivePack(dst);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
return rp;
}
};
@@ -215,7 +216,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = super.createReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
return rp;
}
};
@@ -259,7 +260,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = new ReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
try {
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
@@ -317,7 +318,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = new ReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
try {
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
@@ -367,7 +368,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = new ReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
try {
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
@@ -418,7 +419,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = new ReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
try {
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
@@ -466,7 +467,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
final ReceivePack rp = new ReceivePack(dst);
rp.setCheckReceivedObjects(true);
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new HidePrivateFilter());
rp.setAdvertiseRefsHook(new HidePrivateHook());
try {
receive(rp, inBuf, outBuf);
fail("Expected UnpackException");
@@ -560,11 +561,11 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
return new PacketLineIn(new ByteArrayInputStream(buf.toByteArray()));
}

private static final class HidePrivateFilter implements RefFilter {
public Map<String, Ref> filter(Map<String, Ref> refs) {
Map<String, Ref> r = new HashMap<String, Ref>(refs);
assertNotNull(r.remove(R_PRIVATE));
return r;
private static final class HidePrivateHook extends AbstractAdvertiseRefsHook {
public Map<String, Ref> getAdvertisedRefs(Repository r, RevWalk revWalk) {
Map<String, Ref> refs = new HashMap<String, Ref>(r.getAllRefs());
assertNotNull(refs.remove(R_PRIVATE));
return refs;
}
}


+ 105
- 0
org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2012, 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.
*/

package org.eclipse.jgit.transport;

import java.util.Map;
import java.util.Set;

import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;

/**
* Implementation of {@link AdvertiseRefsHook} that advertises the same refs for
* upload-pack and receive-pack.
*/
public abstract class AbstractAdvertiseRefsHook implements AdvertiseRefsHook {
public void advertiseRefs(UploadPack uploadPack)
throws ServiceMayNotContinueException {
uploadPack.setAdvertisedRefs(getAdvertisedRefs(
uploadPack.getRepository(), uploadPack.getRevWalk()));
}

public void advertiseRefs(ReceivePack receivePack)
throws ServiceMayNotContinueException {
Map<String, Ref> refs = getAdvertisedRefs(receivePack.getRepository(),
receivePack.getRevWalk());
Set<ObjectId> haves = getAdvertisedHaves(receivePack.getRepository(),
receivePack.getRevWalk());
receivePack.setAdvertisedRefs(refs, haves);
}

/**
* Get the refs to advertise.
*
* @param repository
* repository instance.
* @param revWalk
* open rev walk on the repository.
* @return set of refs to advertise.
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
protected abstract Map<String, Ref> getAdvertisedRefs(
Repository repository, RevWalk revWalk)
throws ServiceMayNotContinueException;

/**
* Get the additional haves to advertise.
*
* @param repository
* repository instance.
* @param revWalk
* open rev walk on the repository.
* @return set of additional haves; see
* {@link ReceivePack#getAdvertisedObjects()}.
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
protected Set<ObjectId> getAdvertisedHaves(
Repository repository, RevWalk revWalk)
throws ServiceMayNotContinueException {
return null;
}
}

+ 87
- 0
org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2012, 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.
*/

package org.eclipse.jgit.transport;

/** Hook to allow callers to take over advertising refs to the client. */
public interface AdvertiseRefsHook {
/**
* A simple hook that advertises the default refs.
* <p>
* The method implementations do nothing to preserve the default behavior; see
* {@link UploadPack#setAdvertisedRefs(java.util.Map)} and
* {@link ReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
*/
public static final AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
public void advertiseRefs(UploadPack uploadPack) {
// Do nothing.
}

public void advertiseRefs(ReceivePack receivePack) {
// Do nothing.
}
};

/**
* Advertise refs for upload-pack.
*
* @param uploadPack instance on which to call
* {@link UploadPack#setAdvertisedRefs(java.util.Map)}
* if necessary.
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void advertiseRefs(UploadPack uploadPack)
throws ServiceMayNotContinueException;

/**
* Advertise refs for receive-pack.
*
* @param receivePack instance on which to call
* {@link ReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}
* if necessary.
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void advertiseRefs(ReceivePack receivePack)
throws ServiceMayNotContinueException;
}

+ 17
- 13
org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java View File

@@ -50,7 +50,7 @@ import org.eclipse.jgit.lib.ObjectId;
/**
* Hook invoked by {@link UploadPack} before during critical phases.
* <p>
* If any hook function throws {@link UploadPackMayNotContinueException} then
* If any hook function throws {@link ServiceMayNotContinueException} then
* processing stops immediately and the exception is thrown up the call stack.
* Most phases of UploadPack will try to report the exception's message text to
* the end-user over the client's protocol connection.
@@ -59,41 +59,45 @@ public interface PreUploadHook {
/** A simple no-op hook. */
public static final PreUploadHook NULL = new PreUploadHook() {
public void onPreAdvertiseRefs(UploadPack up)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
// Do nothing.
}

public void onBeginNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntOffered)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
// Do nothing.
}

public void onEndNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntCommon,
int cntNotFound, boolean ready)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
// Do nothing.
}

public void onSendPack(UploadPack up,
Collection<? extends ObjectId> wants,
Collection<? extends ObjectId> haves)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
// Do nothing.
}
};

/**
* Invoked just before {@link UploadPack#sendAdvertisedRefs(RefAdvertiser)}.
* <p>
* New code should prefer implementing
* {@link AdvertiseRefsHook#advertiseRefs(UploadPack)}, which is more powerful
* and may replace this method in the future.
*
* @param up
* the upload pack instance handling the connection.
* @throws UploadPackMayNotContinueException
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void onPreAdvertiseRefs(UploadPack up)
throws UploadPackMayNotContinueException;
throws ServiceMayNotContinueException;

/**
* Invoked before negotiation round is started.
@@ -104,12 +108,12 @@ public interface PreUploadHook {
* the list of wanted objects.
* @param cntOffered
* number of objects the client has offered.
* @throws UploadPackMayNotContinueException
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void onBeginNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntOffered)
throws UploadPackMayNotContinueException;
throws ServiceMayNotContinueException;

/**
* Invoked after a negotiation round is completed.
@@ -128,13 +132,13 @@ public interface PreUploadHook {
* @param ready
* true if a pack is ready to be sent (the commit graph was
* successfully cut).
* @throws UploadPackMayNotContinueException
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void onEndNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntCommon,
int cntNotFound, boolean ready)
throws UploadPackMayNotContinueException;
throws ServiceMayNotContinueException;

/**
* Invoked just before a pack will be sent to the client.
@@ -149,10 +153,10 @@ public interface PreUploadHook {
* the list of common objects. Empty on an initial clone request.
* These may be RevObject or RevCommit if the processed parsed
* them. Implementors should not rely on the values being parsed.
* @throws UploadPackMayNotContinueException
* @throws ServiceMayNotContinueException
* abort; the message will be sent to the user.
*/
public void onSendPack(UploadPack up, Collection<? extends ObjectId> wants,
Collection<? extends ObjectId> haves)
throws UploadPackMayNotContinueException;
throws ServiceMayNotContinueException;
}

+ 4
- 4
org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java View File

@@ -80,14 +80,14 @@ public class PreUploadHookChain implements PreUploadHook {
}

public void onPreAdvertiseRefs(UploadPack up)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
for (int i = 0; i < count; i++)
hooks[i].onPreAdvertiseRefs(up);
}

public void onBeginNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntOffered)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
for (int i = 0; i < count; i++)
hooks[i].onBeginNegotiateRound(up, wants, cntOffered);
}
@@ -95,7 +95,7 @@ public class PreUploadHookChain implements PreUploadHook {
public void onEndNegotiateRound(UploadPack up,
Collection<? extends ObjectId> wants, int cntCommon,
int cntNotFound, boolean ready)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
for (int i = 0; i < count; i++)
hooks[i].onEndNegotiateRound(up, wants, cntCommon, cntNotFound, ready);
}
@@ -103,7 +103,7 @@ public class PreUploadHookChain implements PreUploadHook {
public void onSendPack(UploadPack up,
Collection<? extends ObjectId> wants,
Collection<? extends ObjectId> haves)
throws UploadPackMayNotContinueException {
throws ServiceMayNotContinueException {
for (int i = 0; i < count; i++)
hooks[i].onSendPack(up, wants, haves);
}

+ 103
- 24
org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java View File

@@ -134,6 +134,9 @@ public class ReceivePack {
/** Identity to record action as within the reflog. */
private PersonIdent refLogIdent;

/** Hook used while advertising the refs to the client. */
private AdvertiseRefsHook advertiseRefsHook;

/** Filter used while advertising the refs to the client. */
private RefFilter refFilter;

@@ -213,6 +216,7 @@ public class ReceivePack {
allowDeletes = cfg.allowDeletes;
allowNonFastForwards = cfg.allowNonFastForwards;
allowOfsDelta = cfg.allowOfsDelta;
advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
refFilter = RefFilter.DEFAULT;
preReceive = PreReceiveHook.NULL;
postReceive = PostReceiveHook.NULL;
@@ -304,27 +308,59 @@ public class ReceivePack {
return walk;
}

/** @return all refs which were advertised to the client. */
/**
* Get refs which were advertised to the client.
*
* @return all refs which were advertised to the client, or null if
* {@link #setAdvertisedRefs(Map, Set)} has not been called yet.
*/
public final Map<String, Ref> getAdvertisedRefs() {
if (refs == null) {
refs = refFilter.filter(db.getAllRefs());

Ref head = refs.get(Constants.HEAD);
if (head != null && head.isSymbolic())
refs.remove(Constants.HEAD);
return refs;
}

for (Ref ref : refs.values()) {
if (ref.getObjectId() != null)
advertisedHaves.add(ref.getObjectId());
}
advertisedHaves.addAll(db.getAdditionalHaves());
/**
* Set the refs advertised by this ReceivePack.
* <p>
* Intended to be called from a {@link PreReceiveHook}.
*
* @param allRefs
* explicit set of references to claim as advertised by this
* ReceivePack instance. This overrides any references that
* may exist in the source repository. The map is passed
* to the configured {@link #getRefFilter()}. If null, assumes
* all refs were advertised.
* @param additionalHaves
* explicit set of additional haves to claim as advertised. If
* null, assumes the default set of additional haves from the
* repository.
*/
public void setAdvertisedRefs(Map<String, Ref> allRefs,
Set<ObjectId> additionalHaves) {
refs = allRefs != null ? allRefs : db.getAllRefs();
refs = refFilter.filter(refs);

Ref head = refs.get(Constants.HEAD);
if (head != null && head.isSymbolic())
refs.remove(Constants.HEAD);

for (Ref ref : refs.values()) {
if (ref.getObjectId() != null)
advertisedHaves.add(ref.getObjectId());
}
return refs;
if (additionalHaves != null)
advertisedHaves.addAll(additionalHaves);
else
advertisedHaves.addAll(db.getAdditionalHaves());
}

/** @return the set of objects advertised as present in this repository. */
/**
* Get objects advertised to the client.
*
* @return the set of objects advertised to the as present in this repository,
* or null if {@link #setAdvertisedRefs(Map, Set)} has not been called
* yet.
*/
public final Set<ObjectId> getAdvertisedObjects() {
getAdvertisedRefs();
return advertisedHaves;
}

@@ -342,13 +378,13 @@ public class ReceivePack {
* <p>
* If enabled, this instance will verify that references to objects not
* contained within the received pack are already reachable through at least
* one other reference selected by the {@link #getRefFilter()} and displayed
* as part of {@link #getAdvertisedRefs()}.
* one other reference displayed as part of {@link #getAdvertisedRefs()}.
* <p>
* This feature is useful when the application doesn't trust the client to
* not provide a forged SHA-1 reference to an object, in an attempt to
* access parts of the DAG that they aren't allowed to see and which have
* been hidden from them via the configured {@link RefFilter}.
* been hidden from them via the configured {@link AdvertiseRefsHook} or
* {@link RefFilter}.
* <p>
* Enabling this feature may imply at least some, if not all, of the same
* functionality performed by {@link #setCheckReceivedObjects(boolean)}.
@@ -464,18 +500,42 @@ public class ReceivePack {
refLogIdent = pi;
}

/** @return the hook used while advertising the refs to the client */
public AdvertiseRefsHook getAdvertiseRefsHook() {
return advertiseRefsHook;
}

/** @return the filter used while advertising the refs to the client */
public RefFilter getRefFilter() {
return refFilter;
}

/**
* Set the hook used while advertising the refs to the client.
* <p>
* If the {@link AdvertiseRefsHook} chooses to call
* {@link #setAdvertisedRefs(Map,Set)}, only refs set by this hook
* <em>and</em> selected by the {@link RefFilter} will be shown to the client.
* Clients may still attempt to create or update a reference not advertised by
* the configured {@link AdvertiseRefsHook}. These attempts should be rejected
* by a matching {@link PreReceiveHook}.
*
* @param advertiseRefsHook
* the hook; may be null to show all refs.
*/
public void setAdvertiseRefsHook(final AdvertiseRefsHook advertiseRefsHook) {
if (advertiseRefsHook != null)
this.advertiseRefsHook = advertiseRefsHook;
else
this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
}

/**
* Set the filter used while advertising the refs to the client.
* <p>
* Only refs allowed by this filter will be shown to the client.
* Clients may still attempt to create or update a reference hidden
* by the configured {@link RefFilter}. These attempts should be
* rejected by a matching {@link PreReceiveHook}.
* The filter is run against the refs specified by the
* {@link AdvertiseRefsHook} (if applicable).
*
* @param refFilter
* the filter; may be null to show all refs.
@@ -706,12 +766,18 @@ public class ReceivePack {
}
}

private Map<String, Ref> getAdvertisedOrDefaultRefs() {
if (refs == null)
setAdvertisedRefs(null, null);
return refs;
}

private void service() throws IOException {
if (biDirectionalPipe) {
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
pckOut.flush();
} else
getAdvertisedRefs();
getAdvertisedOrDefaultRefs();
if (advertiseError != null)
return;
recvCommands();
@@ -776,20 +842,33 @@ public class ReceivePack {
* the advertisement formatter.
* @throws IOException
* the formatter failed to write an advertisement.
* @throws ServiceMayNotContinueException
* the hook denied advertisement.
*/
public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
ServiceMayNotContinueException {
if (advertiseError != null) {
adv.writeOne("ERR " + advertiseError);
return;
}

try {
advertiseRefsHook.advertiseRefs(this);
} catch (ServiceMayNotContinueException fail) {
if (fail.getMessage() != null) {
adv.writeOne("ERR " + fail.getMessage());
fail.setOutput();
}
throw fail;
}

adv.init(db);
adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
adv.advertiseCapability(CAPABILITY_DELETE_REFS);
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
adv.send(getAdvertisedRefs());
adv.send(getAdvertisedOrDefaultRefs());
for (ObjectId obj : advertisedHaves)
adv.advertiseHave(obj);
if (adv.isEmpty())

+ 77
- 0
org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2011-2012, 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.
*/

package org.eclipse.jgit.transport;

import java.io.IOException;

/** Indicates a transport service may not continue execution. */
public class ServiceMayNotContinueException extends IOException {
private static final long serialVersionUID = 1L;

private boolean output;

/** Initialize with no message. */
public ServiceMayNotContinueException() {
// Do not set a message.
}

/**
* @param msg
* a message explaining why it cannot continue. This message may
* be shown to an end-user.
*/
public ServiceMayNotContinueException(String msg) {
super(msg);
}

/** @return true if the message was already output to the client. */
public boolean isOutput() {
return output;
}

/** Mark this message has being sent to the client. */
public void setOutput() {
output = true;
}
}

+ 61
- 19
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java View File

@@ -157,8 +157,11 @@ public class UploadPack {
/** The refs we advertised as existing at the start of the connection. */
private Map<String, Ref> refs;

/** Hook used while advertising the refs to the client. */
private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;

/** Filter used while advertising the refs to the client. */
private RefFilter refFilter;
private RefFilter refFilter = RefFilter.DEFAULT;

/** Hook handling the various upload phases. */
private PreUploadHook preUploadHook = PreUploadHook.NULL;
@@ -241,7 +244,6 @@ public class UploadPack {
SAVE.add(PEER_HAS);
SAVE.add(COMMON);
SAVE.add(SATISFIED);
refFilter = RefFilter.DEFAULT;
}

/** @return the repository this upload is reading from. */
@@ -254,22 +256,34 @@ public class UploadPack {
return walk;
}

/** @return all refs which were advertised to the client. */
/**
* Get refs which were advertised to the client.
*
* @return all refs which were advertised to the client, or null if
* {@link #setAdvertisedRefs(Map)} has not been called yet.
*/
public final Map<String, Ref> getAdvertisedRefs() {
if (refs == null)
setAdvertisedRefs(db.getAllRefs());
return refs;
}

/**
* Set the refs advertised by this UploadPack.
* <p>
* Intended to be called from a {@link PreUploadHook}.
*
* @param allRefs
* explicit set of references to claim as advertised by this
* UploadPack instance. This overrides any references that
* may exist in the source repository. The map is passed
* to the configured {@link #getRefFilter()}.
* to the configured {@link #getRefFilter()}. If null, assumes
* all refs were advertised.
*/
public void setAdvertisedRefs(Map<String, Ref> allRefs) {
refs = refFilter.filter(allRefs);
if (allRefs != null)
refs = allRefs;
else
refs = db.getAllRefs();
refs = refFilter.filter(refs);
}

/** @return timeout (in seconds) before aborting an IO operation. */
@@ -330,18 +344,39 @@ public class UploadPack {
requestPolicy = policy != null ? policy : RequestPolicy.ADVERTISED;
}

/** @return the hook used while advertising the refs to the client */
public AdvertiseRefsHook getAdvertiseRefsHook() {
return advertiseRefsHook;
}

/** @return the filter used while advertising the refs to the client */
public RefFilter getRefFilter() {
return refFilter;
}

/**
* Set the hook used while advertising the refs to the client.
* <p>
* If the {@link AdvertiseRefsHook} chooses to call
* {@link #setAdvertisedRefs(Map)}, only refs set by this hook <em>and</em>
* selected by the {@link RefFilter} will be shown to the client.
*
* @param advertiseRefsHook
* the hook; may be null to show all refs.
*/
public void setAdvertiseRefsHook(final AdvertiseRefsHook advertiseRefsHook) {
if (advertiseRefsHook != null)
this.advertiseRefsHook = advertiseRefsHook;
else
this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
}

/**
* Set the filter used while advertising the refs to the client.
* <p>
* Only refs allowed by this filter will be sent to the client. This can
* be used by a server to restrict the list of references the client can
* obtain through clone or fetch, effectively limiting the access to only
* certain refs.
* Only refs allowed by this filter will be sent to the client.
* The filter is run against the refs specified by the
* {@link AdvertiseRefsHook} (if applicable).
*
* @param refFilter
* the filter; may be null to show all refs.
@@ -451,6 +486,12 @@ public class UploadPack {
return statistics;
}

private Map<String, Ref> getAdvertisedOrDefaultRefs() {
if (refs == null)
setAdvertisedRefs(null);
return refs;
}

private void service() throws IOException {
if (biDirectionalPipe)
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -458,7 +499,7 @@ public class UploadPack {
advertised = Collections.emptySet();
else {
advertised = new HashSet<ObjectId>();
for (Ref ref : getAdvertisedRefs().values()) {
for (Ref ref : getAdvertisedOrDefaultRefs().values()) {
if (ref.getObjectId() != null)
advertised.add(ref.getObjectId());
}
@@ -488,7 +529,7 @@ public class UploadPack {
reportErrorDuringNegotiate(err.getMessage());
throw err;

} catch (UploadPackMayNotContinueException err) {
} catch (ServiceMayNotContinueException err) {
if (!err.isOutput() && err.getMessage() != null) {
try {
pckOut.writeString("ERR " + err.getMessage() + "\n");
@@ -562,14 +603,15 @@ public class UploadPack {
* the advertisement formatter.
* @throws IOException
* the formatter failed to write an advertisement.
* @throws UploadPackMayNotContinueException
* @throws ServiceMayNotContinueException
* the hook denied advertisement.
*/
public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
UploadPackMayNotContinueException {
ServiceMayNotContinueException {
try {
preUploadHook.onPreAdvertiseRefs(this);
} catch (UploadPackMayNotContinueException fail) {
advertiseRefsHook.advertiseRefs(this);
} catch (ServiceMayNotContinueException fail) {
if (fail.getMessage() != null) {
adv.writeOne("ERR " + fail.getMessage());
fail.setOutput();
@@ -590,7 +632,7 @@ public class UploadPack {
if (!biDirectionalPipe)
adv.advertiseCapability(OPTION_NO_DONE);
adv.setDerefTags(true);
advertised = adv.send(getAdvertisedRefs());
advertised = adv.send(getAdvertisedOrDefaultRefs());
adv.end();
}

@@ -959,7 +1001,7 @@ public class UploadPack {
if (sideband) {
try {
sendPack(true);
} catch (UploadPackMayNotContinueException noPack) {
} catch (ServiceMayNotContinueException noPack) {
// This was already reported on (below).
throw noPack;
} catch (IOException err) {
@@ -1023,7 +1065,7 @@ public class UploadPack {
} else {
preUploadHook.onSendPack(this, wantAll, commonBase);
}
} catch (UploadPackMayNotContinueException noPack) {
} catch (ServiceMayNotContinueException noPack) {
if (sideband && noPack.getMessage() != null) {
noPack.setOutput();
SideBandOutputStream err = new SideBandOutputStream(

+ 7
- 16
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java View File

@@ -43,14 +43,15 @@

package org.eclipse.jgit.transport;

import java.io.IOException;

/** Indicates UploadPack may not continue execution. */
public class UploadPackMayNotContinueException extends IOException {
/**
* Indicates UploadPack may not continue execution.
*
* @deprecated use {@link ServiceMayNotContinueException} instead.
*/
@Deprecated
public class UploadPackMayNotContinueException extends ServiceMayNotContinueException {
private static final long serialVersionUID = 1L;

private boolean output;

/** Initialize with no message. */
public UploadPackMayNotContinueException() {
// Do not set a message.
@@ -64,14 +65,4 @@ public class UploadPackMayNotContinueException extends IOException {
public UploadPackMayNotContinueException(String msg) {
super(msg);
}

/** @return true if the message was already output to the client. */
public boolean isOutput() {
return output;
}

/** Mark this message has being sent to the client. */
public void setOutput() {
output = true;
}
}

Loading…
Cancel
Save