Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

UploadPack.java 44KB

UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
PackWriter: Support reuse of entire packs The most expensive part of packing a repository for transport to another system is enumerating all of the objects in the repository. Once this gets to the size of the linux-2.6 repository (1.8 million objects), enumeration can take several CPU minutes and costs a lot of temporary working set memory. Teach PackWriter to efficiently reuse an existing "cached pack" by answering a clone request with a thin pack followed by a larger cached pack appended to the end. This requires the repository owner to first construct the cached pack by hand, and record the tip commits inside of $GIT_DIR/objects/info/cached-packs: cd $GIT_DIR root=$(git rev-parse master) tmp=objects/.tmp-$$ names=$(echo $root | git pack-objects --keep-true-parents --revs $tmp) for n in $names; do chmod a-w $tmp-$n.pack $tmp-$n.idx touch objects/pack/pack-$n.keep mv $tmp-$n.pack objects/pack/pack-$n.pack mv $tmp-$n.idx objects/pack/pack-$n.idx done (echo "+ $root"; for n in $names; do echo "P $n"; done; echo) >>objects/info/cached-packs git repack -a -d When a clone request needs to include $root, the corresponding cached pack will be copied as-is, rather than enumerating all of the objects that are reachable from $root. For a linux-2.6 kernel repository that should be about 376 MiB, the above process creates two packs of 368 MiB and 38 MiB[1]. This is a local disk usage increase of ~26 MiB, due to reduced delta compression between the large cached pack and the smaller recent activity pack. The overhead is similar to 1 full copy of the compressed project sources. With this cached pack in hand, JGit daemon completes a clone request in 1m17s less time, but a slightly larger data transfer (+2.39 MiB): Before: remote: Counting objects: 1861830, done remote: Finding sources: 100% (1861830/1861830) remote: Getting sizes: 100% (88243/88243) remote: Compressing objects: 100% (88184/88184) Receiving objects: 100% (1861830/1861830), 376.01 MiB | 19.01 MiB/s, done. remote: Total 1861830 (delta 4706), reused 1851053 (delta 1553844) Resolving deltas: 100% (1564621/1564621), done. real 3m19.005s After: remote: Counting objects: 1601, done remote: Counting objects: 1828460, done remote: Finding sources: 100% (50475/50475) remote: Getting sizes: 100% (18843/18843) remote: Compressing objects: 100% (7585/7585) remote: Total 1861830 (delta 2407), reused 1856197 (delta 37510) Receiving objects: 100% (1861830/1861830), 378.40 MiB | 31.31 MiB/s, done. Resolving deltas: 100% (1559477/1559477), done. real 2m2.938s Repository owners can periodically refresh their cached packs by repacking their repository, folding all newer objects into a larger cached pack. Since repacking is already considered to be a normal Git maintenance activity, this isn't a very big burden. [1] In this test $root was set back about two weeks. Change-Id: Ib87131d5c4b5e8c5cacb0f4fe16ff4ece554734b Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 13 gadiem
PackWriter: Don't reuse commit or tag deltas JGit doesn't generate deltas for commit or tag objects when it packs a repository from scratch. This is an explicit design decision that is (mostly) justified by the fact that these objects do not delta compress well. Annotated tags are made once on stable points of the project history, it is unlikely they will ever appear again with sufficient common text to justify using a delta over just deflating the raw content. JGit never tries to delta compress annotated tags and I take the stance that these are best stored as non-deltas given how frequently they might be accessed by repository viewers. Commits only have sufficient common text when they are cherry-picked to forward-port or back-port a change from one branch to another. Even in these cases the distance between the commits as returned by the log traversal has to be small enough that they would both appear in the delta search window at the same time in order to delta compress one of the messages against the other. JGit never tries to delta compress commits, as it requires a lot of CPU time but typically does not produce a smaller pack file. Avoid reusing deltas for either of these types when constructing a new pack. To avoid killing performance during serving of network clients, UploadPack disables this code change by allowing PackWriter to reuse delta commits. Repositories that were already repacked by C Git will not have their delta commits decompressed and recompressed on the fly during object writing, saving server-side CPU resources. Change-Id: I749407e7c5c677e05e4d054b40db7656cfa7fca8 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 13 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
PackWriter: Hoist and cluster reference targets Many source browsers and network related tools like UploadPack need to find and parse the target of all branches and annotated tags within the repository during their startup phase. Clustering these together into the same part of the pack file will improve locality, reducing thrashing when an application starts and needs to load all of these into memory at once. To prevent bottlenecking basic log viewing tools that are scannning backwards from the tip of a current branch (and don't need tags) we place this cluster of older targets after 4096 newer commits have already been placed into the pack stream. 4096 was chosen as a rough guess, but was based on a few factors: - log viewers typically show 5-200 commits per page - users only view the first page or two - DHT can cram 2200-4000 commits per 1 MiB chunk thus these will fall into the second commit chunk (roughly) Unfortunately this placement hurts history tools that are scanning backwards through the commit graph and completely ignored tags or branch heads when they started. An ancient tagged commit is no longer positioned behind its first child (its now much earlier), resulting in a page fault for the parser to reload this cluster of objects on demand. This may be an acceptable loss. If a user is walking backwards and has already scanned through more than 4096 commits of history, waiting for the region to reload isn't really that bad compared to the amount of time already spent. If the repository is so small that there are less than 4096 commits, this change has no impact on the placement of objects. Change-Id: If3052e430d305e17878d94145c93754f56b74c61 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 13 gadiem
UploadPack: Fix races in smart HTTP negotiation Clients cache the set of advertised references at the start of a negotiation, and keep replaying the same "want SHA1" list to the server on each negotiation step. If another client pushes into a branch and moves it by fast-forward, any request to obtain that branch's prior SHA-1 is still valid, the commit is reachable from the new position of the reference. Unfortunately the fast-forward causes smart HTTP negotations to fail, as the server no longer is advertising that prior SHA-1. Instead of causing clients to fail out with a "want invalid" error and forcing the end-user retry, possibly getting into a never ending try-fail-retry race while other clients are pushing into the same busy repository, allow the slightly stale want request so long as it is still reachable. C Git implemented this same change recently to fix races on the smart HTTP protocol when the C Git git-http-backend is used. The new RequestPolicy feature also allows server authors to make an even more lenient configuration that exports any SHA-1 to the client. This might be useful in certain settings where a server has authenticated the client as the "repository owner" and wants to allow them to grab any content from the server as a complete unbroken history chain. The new setAdvertisedRefs() method allows server authors to manually fix the references that are advertised, possibly bypassing the getAllRefs() call on the Repository object. Change-Id: I7cdb563bf9c55c83653f217f6e53c3add55a0541 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
pirms 12 gadiem
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import static org.eclipse.jgit.lib.RefDatabase.ALL;
  45. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  46. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  47. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  48. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  49. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  50. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  51. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  52. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  53. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  54. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  55. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  56. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  57. import java.io.EOFException;
  58. import java.io.IOException;
  59. import java.io.InputStream;
  60. import java.io.OutputStream;
  61. import java.text.MessageFormat;
  62. import java.util.ArrayList;
  63. import java.util.Collection;
  64. import java.util.Collections;
  65. import java.util.HashSet;
  66. import java.util.List;
  67. import java.util.Map;
  68. import java.util.Set;
  69. import org.eclipse.jgit.errors.CorruptObjectException;
  70. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  71. import org.eclipse.jgit.errors.MissingObjectException;
  72. import org.eclipse.jgit.errors.PackProtocolException;
  73. import org.eclipse.jgit.internal.JGitText;
  74. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  75. import org.eclipse.jgit.lib.Constants;
  76. import org.eclipse.jgit.lib.NullProgressMonitor;
  77. import org.eclipse.jgit.lib.ObjectId;
  78. import org.eclipse.jgit.lib.ProgressMonitor;
  79. import org.eclipse.jgit.lib.Ref;
  80. import org.eclipse.jgit.lib.Repository;
  81. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  82. import org.eclipse.jgit.revwalk.DepthWalk;
  83. import org.eclipse.jgit.revwalk.ObjectWalk;
  84. import org.eclipse.jgit.revwalk.RevCommit;
  85. import org.eclipse.jgit.revwalk.RevFlag;
  86. import org.eclipse.jgit.revwalk.RevFlagSet;
  87. import org.eclipse.jgit.revwalk.RevObject;
  88. import org.eclipse.jgit.revwalk.RevTag;
  89. import org.eclipse.jgit.revwalk.RevWalk;
  90. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  91. import org.eclipse.jgit.storage.pack.PackConfig;
  92. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  93. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  94. import org.eclipse.jgit.util.io.InterruptTimer;
  95. import org.eclipse.jgit.util.io.NullOutputStream;
  96. import org.eclipse.jgit.util.io.TimeoutInputStream;
  97. import org.eclipse.jgit.util.io.TimeoutOutputStream;
  98. /**
  99. * Implements the server side of a fetch connection, transmitting objects.
  100. */
  101. public class UploadPack {
  102. /** Policy the server uses to validate client requests */
  103. public static enum RequestPolicy {
  104. /** Client may only ask for objects the server advertised a reference for. */
  105. ADVERTISED,
  106. /**
  107. * Client may ask for any commit reachable from a reference advertised by
  108. * the server.
  109. */
  110. REACHABLE_COMMIT,
  111. /**
  112. * Client may ask for objects that are the tip of any reference, even if not
  113. * advertised.
  114. * <p>
  115. * This may happen, for example, when a custom {@link RefFilter} is set.
  116. *
  117. * @since 3.1
  118. */
  119. TIP,
  120. /**
  121. * Client may ask for any commit reachable from any reference, even if that
  122. * reference wasn't advertised.
  123. *
  124. * @since 3.1
  125. */
  126. REACHABLE_COMMIT_TIP,
  127. /** Client may ask for any SHA-1 in the repository. */
  128. ANY;
  129. }
  130. /**
  131. * Validator for client requests.
  132. *
  133. * @since 3.1
  134. */
  135. public interface RequestValidator {
  136. /**
  137. * Check a list of client wants against the request policy.
  138. *
  139. * @param up
  140. * {@link UploadPack} instance.
  141. * @param wants
  142. * objects the client requested that were not advertised.
  143. *
  144. * @throws PackProtocolException
  145. * if one or more wants is not valid.
  146. * @throws IOException
  147. * if a low-level exception occurred.
  148. * @since 3.1
  149. */
  150. void checkWants(UploadPack up, List<ObjectId> wants)
  151. throws PackProtocolException, IOException;
  152. }
  153. /** Data in the first line of a request, the line itself plus options. */
  154. public static class FirstLine {
  155. private final String line;
  156. private final Set<String> options;
  157. /**
  158. * Parse the first line of a receive-pack request.
  159. *
  160. * @param line
  161. * line from the client.
  162. */
  163. public FirstLine(String line) {
  164. if (line.length() > 45) {
  165. final HashSet<String> opts = new HashSet<String>();
  166. String opt = line.substring(45);
  167. if (opt.startsWith(" ")) //$NON-NLS-1$
  168. opt = opt.substring(1);
  169. for (String c : opt.split(" ")) //$NON-NLS-1$
  170. opts.add(c);
  171. this.line = line.substring(0, 45);
  172. this.options = Collections.unmodifiableSet(opts);
  173. } else {
  174. this.line = line;
  175. this.options = Collections.emptySet();
  176. }
  177. }
  178. /** @return non-capabilities part of the line. */
  179. public String getLine() {
  180. return line;
  181. }
  182. /** @return options parsed from the line. */
  183. public Set<String> getOptions() {
  184. return options;
  185. }
  186. }
  187. /** Database we read the objects from. */
  188. private final Repository db;
  189. /** Revision traversal support over {@link #db}. */
  190. private final RevWalk walk;
  191. /** Configuration to pass into the PackWriter. */
  192. private PackConfig packConfig;
  193. /** Configuration for various transfer options. */
  194. private TransferConfig transferConfig;
  195. /** Timeout in seconds to wait for client interaction. */
  196. private int timeout;
  197. /**
  198. * Is the client connection a bi-directional socket or pipe?
  199. * <p>
  200. * If true, this class assumes it can perform multiple read and write cycles
  201. * with the client over the input and output streams. This matches the
  202. * functionality available with a standard TCP/IP connection, or a local
  203. * operating system or in-memory pipe.
  204. * <p>
  205. * If false, this class runs in a read everything then output results mode,
  206. * making it suitable for single round-trip systems RPCs such as HTTP.
  207. */
  208. private boolean biDirectionalPipe = true;
  209. /** Timer to manage {@link #timeout}. */
  210. private InterruptTimer timer;
  211. private InputStream rawIn;
  212. private OutputStream rawOut;
  213. private PacketLineIn pckIn;
  214. private PacketLineOut pckOut;
  215. private OutputStream msgOut = NullOutputStream.INSTANCE;
  216. /** The refs we advertised as existing at the start of the connection. */
  217. private Map<String, Ref> refs;
  218. /** Hook used while advertising the refs to the client. */
  219. private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  220. /** Filter used while advertising the refs to the client. */
  221. private RefFilter refFilter = RefFilter.DEFAULT;
  222. /** Hook handling the various upload phases. */
  223. private PreUploadHook preUploadHook = PreUploadHook.NULL;
  224. /** Capabilities requested by the client. */
  225. private Set<String> options;
  226. String userAgent;
  227. /** Raw ObjectIds the client has asked for, before validating them. */
  228. private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
  229. /** Objects the client wants to obtain. */
  230. private final Set<RevObject> wantAll = new HashSet<RevObject>();
  231. /** Objects on both sides, these don't have to be sent. */
  232. private final Set<RevObject> commonBase = new HashSet<RevObject>();
  233. /** Shallow commits the client already has. */
  234. private final Set<ObjectId> clientShallowCommits = new HashSet<ObjectId>();
  235. /** Shallow commits on the client which are now becoming unshallow */
  236. private final List<ObjectId> unshallowCommits = new ArrayList<ObjectId>();
  237. /** Desired depth from the client on a shallow request. */
  238. private int depth;
  239. /** Commit time of the oldest common commit, in seconds. */
  240. private int oldestTime;
  241. /** null if {@link #commonBase} should be examined again. */
  242. private Boolean okToGiveUp;
  243. private boolean sentReady;
  244. /** Objects we sent in our advertisement list, clients can ask for these. */
  245. private Set<ObjectId> advertised;
  246. /** Marked on objects the client has asked us to give them. */
  247. private final RevFlag WANT;
  248. /** Marked on objects both we and the client have. */
  249. private final RevFlag PEER_HAS;
  250. /** Marked on objects in {@link #commonBase}. */
  251. private final RevFlag COMMON;
  252. /** Objects where we found a path from the want list to a common base. */
  253. private final RevFlag SATISFIED;
  254. private final RevFlagSet SAVE;
  255. private RequestValidator requestValidator = new AdvertisedRequestValidator();
  256. private MultiAck multiAck = MultiAck.OFF;
  257. private boolean noDone;
  258. private PackWriter.Statistics statistics;
  259. private UploadPackLogger logger = UploadPackLogger.NULL;
  260. /**
  261. * Create a new pack upload for an open repository.
  262. *
  263. * @param copyFrom
  264. * the source repository.
  265. */
  266. public UploadPack(final Repository copyFrom) {
  267. db = copyFrom;
  268. walk = new RevWalk(db);
  269. walk.setRetainBody(false);
  270. WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  271. PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  272. COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  273. SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  274. walk.carry(PEER_HAS);
  275. SAVE = new RevFlagSet();
  276. SAVE.add(WANT);
  277. SAVE.add(PEER_HAS);
  278. SAVE.add(COMMON);
  279. SAVE.add(SATISFIED);
  280. setTransferConfig(null);
  281. }
  282. /** @return the repository this upload is reading from. */
  283. public final Repository getRepository() {
  284. return db;
  285. }
  286. /** @return the RevWalk instance used by this connection. */
  287. public final RevWalk getRevWalk() {
  288. return walk;
  289. }
  290. /**
  291. * Get refs which were advertised to the client.
  292. *
  293. * @return all refs which were advertised to the client, or null if
  294. * {@link #setAdvertisedRefs(Map)} has not been called yet.
  295. */
  296. public final Map<String, Ref> getAdvertisedRefs() {
  297. return refs;
  298. }
  299. /**
  300. * Set the refs advertised by this UploadPack.
  301. * <p>
  302. * Intended to be called from a {@link PreUploadHook}.
  303. *
  304. * @param allRefs
  305. * explicit set of references to claim as advertised by this
  306. * UploadPack instance. This overrides any references that
  307. * may exist in the source repository. The map is passed
  308. * to the configured {@link #getRefFilter()}. If null, assumes
  309. * all refs were advertised.
  310. */
  311. public void setAdvertisedRefs(Map<String, Ref> allRefs) {
  312. if (allRefs != null)
  313. refs = allRefs;
  314. else
  315. refs = db.getAllRefs();
  316. if (refFilter == RefFilter.DEFAULT)
  317. refs = transferConfig.getRefFilter().filter(refs);
  318. else
  319. refs = refFilter.filter(refs);
  320. }
  321. /** @return timeout (in seconds) before aborting an IO operation. */
  322. public int getTimeout() {
  323. return timeout;
  324. }
  325. /**
  326. * Set the timeout before willing to abort an IO call.
  327. *
  328. * @param seconds
  329. * number of seconds to wait (with no data transfer occurring)
  330. * before aborting an IO read or write operation with the
  331. * connected client.
  332. */
  333. public void setTimeout(final int seconds) {
  334. timeout = seconds;
  335. }
  336. /**
  337. * @return true if this class expects a bi-directional pipe opened between
  338. * the client and itself. The default is true.
  339. */
  340. public boolean isBiDirectionalPipe() {
  341. return biDirectionalPipe;
  342. }
  343. /**
  344. * @param twoWay
  345. * if true, this class will assume the socket is a fully
  346. * bidirectional pipe between the two peers and takes advantage
  347. * of that by first transmitting the known refs, then waiting to
  348. * read commands. If false, this class assumes it must read the
  349. * commands before writing output and does not perform the
  350. * initial advertising.
  351. */
  352. public void setBiDirectionalPipe(final boolean twoWay) {
  353. biDirectionalPipe = twoWay;
  354. }
  355. /**
  356. * @return policy used by the service to validate client requests, or null for
  357. * a custom request validator.
  358. */
  359. public RequestPolicy getRequestPolicy() {
  360. if (requestValidator instanceof AdvertisedRequestValidator)
  361. return RequestPolicy.ADVERTISED;
  362. if (requestValidator instanceof ReachableCommitRequestValidator)
  363. return RequestPolicy.REACHABLE_COMMIT;
  364. if (requestValidator instanceof TipRequestValidator)
  365. return RequestPolicy.TIP;
  366. if (requestValidator instanceof ReachableCommitTipRequestValidator)
  367. return RequestPolicy.REACHABLE_COMMIT_TIP;
  368. if (requestValidator instanceof AnyRequestValidator)
  369. return RequestPolicy.ANY;
  370. return null;
  371. }
  372. /**
  373. * @param policy
  374. * the policy used to enforce validation of a client's want list.
  375. * By default the policy is {@link RequestPolicy#ADVERTISED},
  376. * which is the Git default requiring clients to only ask for an
  377. * object that a reference directly points to. This may be relaxed
  378. * to {@link RequestPolicy#REACHABLE_COMMIT} or
  379. * {@link RequestPolicy#REACHABLE_COMMIT_TIP} when callers have
  380. * {@link #setBiDirectionalPipe(boolean)} set to false.
  381. * Overrides any policy specified in a {@link TransferConfig}.
  382. */
  383. public void setRequestPolicy(RequestPolicy policy) {
  384. switch (policy) {
  385. case ADVERTISED:
  386. default:
  387. requestValidator = new AdvertisedRequestValidator();
  388. break;
  389. case REACHABLE_COMMIT:
  390. requestValidator = new ReachableCommitRequestValidator();
  391. break;
  392. case TIP:
  393. requestValidator = new TipRequestValidator();
  394. break;
  395. case REACHABLE_COMMIT_TIP:
  396. requestValidator = new ReachableCommitTipRequestValidator();
  397. break;
  398. case ANY:
  399. requestValidator = new AnyRequestValidator();
  400. break;
  401. }
  402. }
  403. /**
  404. * @param validator
  405. * custom validator for client want list.
  406. * @since 3.1
  407. */
  408. public void setRequestValidator(RequestValidator validator) {
  409. requestValidator = validator != null ? validator
  410. : new AdvertisedRequestValidator();
  411. }
  412. /** @return the hook used while advertising the refs to the client */
  413. public AdvertiseRefsHook getAdvertiseRefsHook() {
  414. return advertiseRefsHook;
  415. }
  416. /** @return the filter used while advertising the refs to the client */
  417. public RefFilter getRefFilter() {
  418. return refFilter;
  419. }
  420. /**
  421. * Set the hook used while advertising the refs to the client.
  422. * <p>
  423. * If the {@link AdvertiseRefsHook} chooses to call
  424. * {@link #setAdvertisedRefs(Map)}, only refs set by this hook <em>and</em>
  425. * selected by the {@link RefFilter} will be shown to the client.
  426. *
  427. * @param advertiseRefsHook
  428. * the hook; may be null to show all refs.
  429. */
  430. public void setAdvertiseRefsHook(final AdvertiseRefsHook advertiseRefsHook) {
  431. if (advertiseRefsHook != null)
  432. this.advertiseRefsHook = advertiseRefsHook;
  433. else
  434. this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  435. }
  436. /**
  437. * Set the filter used while advertising the refs to the client.
  438. * <p>
  439. * Only refs allowed by this filter will be sent to the client.
  440. * The filter is run against the refs specified by the
  441. * {@link AdvertiseRefsHook} (if applicable). If null or not set, uses the
  442. * filter implied by the {@link TransferConfig}.
  443. *
  444. * @param refFilter
  445. * the filter; may be null to show all refs.
  446. */
  447. public void setRefFilter(final RefFilter refFilter) {
  448. this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  449. }
  450. /** @return the configured upload hook. */
  451. public PreUploadHook getPreUploadHook() {
  452. return preUploadHook;
  453. }
  454. /**
  455. * Set the hook that controls how this instance will behave.
  456. *
  457. * @param hook
  458. * the hook; if null no special actions are taken.
  459. */
  460. public void setPreUploadHook(PreUploadHook hook) {
  461. preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  462. }
  463. /**
  464. * Set the configuration used by the pack generator.
  465. *
  466. * @param pc
  467. * configuration controlling packing parameters. If null the
  468. * source repository's settings will be used.
  469. */
  470. public void setPackConfig(PackConfig pc) {
  471. this.packConfig = pc;
  472. }
  473. /**
  474. * @param tc
  475. * configuration controlling transfer options. If null the source
  476. * repository's settings will be used.
  477. * @since 3.1
  478. */
  479. public void setTransferConfig(TransferConfig tc) {
  480. this.transferConfig = tc != null ? tc : new TransferConfig(db);
  481. setRequestPolicy(transferConfig.isAllowTipSha1InWant()
  482. ? RequestPolicy.TIP : RequestPolicy.ADVERTISED);
  483. }
  484. /** @return the configured logger. */
  485. public UploadPackLogger getLogger() {
  486. return logger;
  487. }
  488. /**
  489. * Set the logger.
  490. *
  491. * @param logger
  492. * the logger instance. If null, no logging occurs.
  493. */
  494. public void setLogger(UploadPackLogger logger) {
  495. this.logger = logger;
  496. }
  497. /**
  498. * Check whether the client expects a side-band stream.
  499. *
  500. * @return true if the client has advertised a side-band capability, false
  501. * otherwise.
  502. * @throws RequestNotYetReadException
  503. * if the client's request has not yet been read from the wire, so
  504. * we do not know if they expect side-band. Note that the client
  505. * may have already written the request, it just has not been
  506. * read.
  507. */
  508. public boolean isSideBand() throws RequestNotYetReadException {
  509. if (options == null)
  510. throw new RequestNotYetReadException();
  511. return (options.contains(OPTION_SIDE_BAND)
  512. || options.contains(OPTION_SIDE_BAND_64K));
  513. }
  514. /**
  515. * Execute the upload task on the socket.
  516. *
  517. * @param input
  518. * raw input to read client commands from. Caller must ensure the
  519. * input is buffered, otherwise read performance may suffer.
  520. * @param output
  521. * response back to the Git network client, to write the pack
  522. * data onto. Caller must ensure the output is buffered,
  523. * otherwise write performance may suffer.
  524. * @param messages
  525. * secondary "notice" channel to send additional messages out
  526. * through. When run over SSH this should be tied back to the
  527. * standard error channel of the command execution. For most
  528. * other network connections this should be null.
  529. * @throws IOException
  530. */
  531. public void upload(final InputStream input, final OutputStream output,
  532. final OutputStream messages) throws IOException {
  533. try {
  534. rawIn = input;
  535. rawOut = output;
  536. if (messages != null)
  537. msgOut = messages;
  538. if (timeout > 0) {
  539. final Thread caller = Thread.currentThread();
  540. timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  541. TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  542. TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
  543. i.setTimeout(timeout * 1000);
  544. o.setTimeout(timeout * 1000);
  545. rawIn = i;
  546. rawOut = o;
  547. }
  548. pckIn = new PacketLineIn(rawIn);
  549. pckOut = new PacketLineOut(rawOut);
  550. service();
  551. } finally {
  552. msgOut = NullOutputStream.INSTANCE;
  553. walk.release();
  554. if (timer != null) {
  555. try {
  556. timer.terminate();
  557. } finally {
  558. timer = null;
  559. }
  560. }
  561. }
  562. }
  563. /**
  564. * Get the PackWriter's statistics if a pack was sent to the client.
  565. *
  566. * @return statistics about pack output, if a pack was sent. Null if no pack
  567. * was sent, such as during the negotation phase of a smart HTTP
  568. * connection, or if the client was already up-to-date.
  569. * @since 3.0
  570. */
  571. public PackWriter.Statistics getPackStatistics() {
  572. return statistics;
  573. }
  574. private Map<String, Ref> getAdvertisedOrDefaultRefs() {
  575. if (refs == null)
  576. setAdvertisedRefs(null);
  577. return refs;
  578. }
  579. private void service() throws IOException {
  580. if (biDirectionalPipe)
  581. sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  582. else if (requestValidator instanceof AnyRequestValidator)
  583. advertised = Collections.emptySet();
  584. else
  585. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  586. boolean sendPack;
  587. try {
  588. recvWants();
  589. if (wantIds.isEmpty()) {
  590. preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
  591. preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
  592. return;
  593. }
  594. if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
  595. multiAck = MultiAck.DETAILED;
  596. noDone = options.contains(OPTION_NO_DONE);
  597. } else if (options.contains(OPTION_MULTI_ACK))
  598. multiAck = MultiAck.CONTINUE;
  599. else
  600. multiAck = MultiAck.OFF;
  601. if (depth != 0)
  602. processShallow();
  603. if (!clientShallowCommits.isEmpty())
  604. walk.assumeShallow(clientShallowCommits);
  605. sendPack = negotiate();
  606. } catch (PackProtocolException err) {
  607. reportErrorDuringNegotiate(err.getMessage());
  608. throw err;
  609. } catch (ServiceMayNotContinueException err) {
  610. if (!err.isOutput() && err.getMessage() != null) {
  611. try {
  612. pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  613. err.setOutput();
  614. } catch (Throwable err2) {
  615. // Ignore this secondary failure (and not mark output).
  616. }
  617. }
  618. throw err;
  619. } catch (IOException err) {
  620. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  621. throw err;
  622. } catch (RuntimeException err) {
  623. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  624. throw err;
  625. } catch (Error err) {
  626. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  627. throw err;
  628. }
  629. if (sendPack)
  630. sendPack();
  631. }
  632. private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  633. Set<ObjectId> ids = new HashSet<ObjectId>(refs.size());
  634. for (Ref ref : refs) {
  635. if (ref.getObjectId() != null)
  636. ids.add(ref.getObjectId());
  637. }
  638. return ids;
  639. }
  640. private void reportErrorDuringNegotiate(String msg) {
  641. try {
  642. pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  643. } catch (Throwable err) {
  644. // Ignore this secondary failure.
  645. }
  646. }
  647. private void processShallow() throws IOException {
  648. DepthWalk.RevWalk depthWalk =
  649. new DepthWalk.RevWalk(walk.getObjectReader(), depth);
  650. // Find all the commits which will be shallow
  651. for (ObjectId o : wantIds) {
  652. try {
  653. depthWalk.markRoot(depthWalk.parseCommit(o));
  654. } catch (IncorrectObjectTypeException notCommit) {
  655. // Ignore non-commits in this loop.
  656. }
  657. }
  658. RevCommit o;
  659. while ((o = depthWalk.next()) != null) {
  660. DepthWalk.Commit c = (DepthWalk.Commit) o;
  661. // Commits at the boundary which aren't already shallow in
  662. // the client need to be marked as such
  663. if (c.getDepth() == depth && !clientShallowCommits.contains(c))
  664. pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
  665. // Commits not on the boundary which are shallow in the client
  666. // need to become unshallowed
  667. if (c.getDepth() < depth && clientShallowCommits.remove(c)) {
  668. unshallowCommits.add(c.copy());
  669. pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
  670. }
  671. }
  672. pckOut.end();
  673. }
  674. /**
  675. * Generate an advertisement of available refs and capabilities.
  676. *
  677. * @param adv
  678. * the advertisement formatter.
  679. * @throws IOException
  680. * the formatter failed to write an advertisement.
  681. * @throws ServiceMayNotContinueException
  682. * the hook denied advertisement.
  683. */
  684. public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
  685. ServiceMayNotContinueException {
  686. try {
  687. advertiseRefsHook.advertiseRefs(this);
  688. } catch (ServiceMayNotContinueException fail) {
  689. if (fail.getMessage() != null) {
  690. adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
  691. fail.setOutput();
  692. }
  693. throw fail;
  694. }
  695. adv.init(db);
  696. adv.advertiseCapability(OPTION_INCLUDE_TAG);
  697. adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  698. adv.advertiseCapability(OPTION_MULTI_ACK);
  699. adv.advertiseCapability(OPTION_OFS_DELTA);
  700. adv.advertiseCapability(OPTION_SIDE_BAND);
  701. adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  702. adv.advertiseCapability(OPTION_THIN_PACK);
  703. adv.advertiseCapability(OPTION_NO_PROGRESS);
  704. adv.advertiseCapability(OPTION_SHALLOW);
  705. if (!biDirectionalPipe)
  706. adv.advertiseCapability(OPTION_NO_DONE);
  707. RequestPolicy policy = getRequestPolicy();
  708. if (policy == RequestPolicy.TIP
  709. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  710. || policy == null)
  711. adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  712. adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
  713. adv.setDerefTags(true);
  714. Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
  715. findSymrefs(adv, refs);
  716. advertised = adv.send(refs);
  717. if (adv.isEmpty())
  718. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  719. adv.end();
  720. }
  721. /**
  722. * Send a message to the client, if it supports receiving them.
  723. * <p>
  724. * If the client doesn't support receiving messages, the message will be
  725. * discarded, with no other indication to the caller or to the client.
  726. *
  727. * @param what
  728. * string describing the problem identified by the hook. The
  729. * string must not end with an LF, and must not contain an LF.
  730. * @since 3.1
  731. */
  732. public void sendMessage(String what) {
  733. try {
  734. msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  735. } catch (IOException e) {
  736. // Ignore write failures.
  737. }
  738. }
  739. /**
  740. * @return an underlying stream for sending messages to the client, or null.
  741. * @since 3.1
  742. */
  743. public OutputStream getMessageOutputStream() {
  744. return msgOut;
  745. }
  746. private void recvWants() throws IOException {
  747. boolean isFirst = true;
  748. for (;;) {
  749. String line;
  750. try {
  751. line = pckIn.readString();
  752. } catch (EOFException eof) {
  753. if (isFirst)
  754. break;
  755. throw eof;
  756. }
  757. if (line == PacketLineIn.END)
  758. break;
  759. if (line.startsWith("deepen ")) { //$NON-NLS-1$
  760. depth = Integer.parseInt(line.substring(7));
  761. continue;
  762. }
  763. if (line.startsWith("shallow ")) { //$NON-NLS-1$
  764. clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
  765. continue;
  766. }
  767. if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$
  768. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
  769. if (isFirst) {
  770. if (line.length() > 45) {
  771. FirstLine firstLine = new FirstLine(line);
  772. options = firstLine.getOptions();
  773. line = firstLine.getLine();
  774. } else
  775. options = Collections.emptySet();
  776. }
  777. wantIds.add(ObjectId.fromString(line.substring(5)));
  778. isFirst = false;
  779. }
  780. }
  781. /**
  782. * Returns the clone/fetch depth. Valid only after calling recvWants().
  783. *
  784. * @return the depth requested by the client, or 0 if unbounded.
  785. * @since 4.0
  786. */
  787. public int getDepth() {
  788. if (options == null)
  789. throw new RequestNotYetReadException();
  790. return depth;
  791. }
  792. /**
  793. * Get the user agent of the client.
  794. * <p>
  795. * If the client is new enough to use {@code agent=} capability that value
  796. * will be returned. Older HTTP clients may also supply their version using
  797. * the HTTP {@code User-Agent} header. The capability overrides the HTTP
  798. * header if both are available.
  799. * <p>
  800. * When an HTTP request has been received this method returns the HTTP
  801. * {@code User-Agent} header value until capabilities have been parsed.
  802. *
  803. * @return user agent supplied by the client. Available only if the client
  804. * is new enough to advertise its user agent.
  805. * @since 4.0
  806. */
  807. public String getPeerUserAgent() {
  808. return UserAgent.getAgent(options, userAgent);
  809. }
  810. private boolean negotiate() throws IOException {
  811. okToGiveUp = Boolean.FALSE;
  812. ObjectId last = ObjectId.zeroId();
  813. List<ObjectId> peerHas = new ArrayList<ObjectId>(64);
  814. for (;;) {
  815. String line;
  816. try {
  817. line = pckIn.readString();
  818. } catch (EOFException eof) {
  819. // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  820. // means the client asked for the updated shallow/unshallow data,
  821. // disconnected, and will try another request with actual want/have.
  822. // Don't report the EOF here, its a bug in the protocol that the client
  823. // just disconnects without sending an END.
  824. if (!biDirectionalPipe && depth > 0)
  825. return false;
  826. throw eof;
  827. }
  828. if (line == PacketLineIn.END) {
  829. last = processHaveLines(peerHas, last);
  830. if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  831. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  832. if (noDone && sentReady) {
  833. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  834. return true;
  835. }
  836. if (!biDirectionalPipe)
  837. return false;
  838. pckOut.flush();
  839. } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  840. peerHas.add(ObjectId.fromString(line.substring(5)));
  841. } else if (line.equals("done")) { //$NON-NLS-1$
  842. last = processHaveLines(peerHas, last);
  843. if (commonBase.isEmpty())
  844. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  845. else if (multiAck != MultiAck.OFF)
  846. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  847. return true;
  848. } else {
  849. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  850. }
  851. }
  852. }
  853. private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last)
  854. throws IOException {
  855. preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  856. if (wantAll.isEmpty() && !wantIds.isEmpty())
  857. parseWants();
  858. if (peerHas.isEmpty())
  859. return last;
  860. sentReady = false;
  861. int haveCnt = 0;
  862. walk.getObjectReader().setAvoidUnreachableObjects(true);
  863. AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  864. try {
  865. for (;;) {
  866. RevObject obj;
  867. try {
  868. obj = q.next();
  869. } catch (MissingObjectException notFound) {
  870. continue;
  871. }
  872. if (obj == null)
  873. break;
  874. last = obj;
  875. haveCnt++;
  876. if (obj instanceof RevCommit) {
  877. RevCommit c = (RevCommit) obj;
  878. if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  879. oldestTime = c.getCommitTime();
  880. }
  881. if (obj.has(PEER_HAS))
  882. continue;
  883. obj.add(PEER_HAS);
  884. if (obj instanceof RevCommit)
  885. ((RevCommit) obj).carry(PEER_HAS);
  886. addCommonBase(obj);
  887. // If both sides have the same object; let the client know.
  888. //
  889. switch (multiAck) {
  890. case OFF:
  891. if (commonBase.size() == 1)
  892. pckOut.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  893. break;
  894. case CONTINUE:
  895. pckOut.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  896. break;
  897. case DETAILED:
  898. pckOut.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  899. break;
  900. }
  901. }
  902. } finally {
  903. q.release();
  904. walk.getObjectReader().setAvoidUnreachableObjects(false);
  905. }
  906. int missCnt = peerHas.size() - haveCnt;
  907. // If we don't have one of the objects but we're also willing to
  908. // create a pack at this point, let the client know so it stops
  909. // telling us about its history.
  910. //
  911. boolean didOkToGiveUp = false;
  912. if (0 < missCnt) {
  913. for (int i = peerHas.size() - 1; i >= 0; i--) {
  914. ObjectId id = peerHas.get(i);
  915. if (walk.lookupOrNull(id) == null) {
  916. didOkToGiveUp = true;
  917. if (okToGiveUp()) {
  918. switch (multiAck) {
  919. case OFF:
  920. break;
  921. case CONTINUE:
  922. pckOut.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  923. break;
  924. case DETAILED:
  925. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  926. sentReady = true;
  927. break;
  928. }
  929. }
  930. break;
  931. }
  932. }
  933. }
  934. if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
  935. ObjectId id = peerHas.get(peerHas.size() - 1);
  936. sentReady = true;
  937. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  938. sentReady = true;
  939. }
  940. preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  941. peerHas.clear();
  942. return last;
  943. }
  944. private void parseWants() throws IOException {
  945. List<ObjectId> notAdvertisedWants = null;
  946. for (ObjectId obj : wantIds) {
  947. if (!advertised.contains(obj)) {
  948. if (notAdvertisedWants == null)
  949. notAdvertisedWants = new ArrayList<ObjectId>();
  950. notAdvertisedWants.add(obj);
  951. }
  952. }
  953. if (notAdvertisedWants != null)
  954. requestValidator.checkWants(this, notAdvertisedWants);
  955. AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  956. try {
  957. RevObject obj;
  958. while ((obj = q.next()) != null) {
  959. want(obj);
  960. if (!(obj instanceof RevCommit))
  961. obj.add(SATISFIED);
  962. if (obj instanceof RevTag) {
  963. obj = walk.peel(obj);
  964. if (obj instanceof RevCommit)
  965. want(obj);
  966. }
  967. }
  968. wantIds.clear();
  969. } catch (MissingObjectException notFound) {
  970. ObjectId id = notFound.getObjectId();
  971. throw new PackProtocolException(MessageFormat.format(
  972. JGitText.get().wantNotValid, id.name()), notFound);
  973. } finally {
  974. q.release();
  975. }
  976. }
  977. private void want(RevObject obj) {
  978. if (!obj.has(WANT)) {
  979. obj.add(WANT);
  980. wantAll.add(obj);
  981. }
  982. }
  983. /**
  984. * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  985. *
  986. * @since 3.1
  987. */
  988. public static final class AdvertisedRequestValidator
  989. implements RequestValidator {
  990. public void checkWants(UploadPack up, List<ObjectId> wants)
  991. throws PackProtocolException, IOException {
  992. if (!up.isBiDirectionalPipe())
  993. new ReachableCommitRequestValidator().checkWants(up, wants);
  994. else if (!wants.isEmpty())
  995. throw new PackProtocolException(MessageFormat.format(
  996. JGitText.get().wantNotValid, wants.iterator().next().name()));
  997. }
  998. }
  999. /**
  1000. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  1001. *
  1002. * @since 3.1
  1003. */
  1004. public static final class ReachableCommitRequestValidator
  1005. implements RequestValidator {
  1006. public void checkWants(UploadPack up, List<ObjectId> wants)
  1007. throws PackProtocolException, IOException {
  1008. checkNotAdvertisedWants(up.getRevWalk(), wants,
  1009. refIdSet(up.getAdvertisedRefs().values()));
  1010. }
  1011. }
  1012. /**
  1013. * Validator corresponding to {@link RequestPolicy#TIP}.
  1014. *
  1015. * @since 3.1
  1016. */
  1017. public static final class TipRequestValidator implements RequestValidator {
  1018. public void checkWants(UploadPack up, List<ObjectId> wants)
  1019. throws PackProtocolException, IOException {
  1020. if (!up.isBiDirectionalPipe())
  1021. new ReachableCommitTipRequestValidator().checkWants(up, wants);
  1022. else if (!wants.isEmpty()) {
  1023. Set<ObjectId> refIds =
  1024. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values());
  1025. for (ObjectId obj : wants) {
  1026. if (!refIds.contains(obj))
  1027. throw new PackProtocolException(MessageFormat.format(
  1028. JGitText.get().wantNotValid, obj.name()));
  1029. }
  1030. }
  1031. }
  1032. }
  1033. /**
  1034. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1035. *
  1036. * @since 3.1
  1037. */
  1038. public static final class ReachableCommitTipRequestValidator
  1039. implements RequestValidator {
  1040. public void checkWants(UploadPack up, List<ObjectId> wants)
  1041. throws PackProtocolException, IOException {
  1042. checkNotAdvertisedWants(up.getRevWalk(), wants,
  1043. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values()));
  1044. }
  1045. }
  1046. /**
  1047. * Validator corresponding to {@link RequestPolicy#ANY}.
  1048. *
  1049. * @since 3.1
  1050. */
  1051. public static final class AnyRequestValidator implements RequestValidator {
  1052. public void checkWants(UploadPack up, List<ObjectId> wants)
  1053. throws PackProtocolException, IOException {
  1054. // All requests are valid.
  1055. }
  1056. }
  1057. private static void checkNotAdvertisedWants(RevWalk walk,
  1058. List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
  1059. throws MissingObjectException, IncorrectObjectTypeException, IOException {
  1060. // Walk the requested commits back to the provided set of commits. If any
  1061. // commit exists, a branch was deleted or rewound and the repository owner
  1062. // no longer exports that requested item. If the requested commit is merged
  1063. // into an advertised branch it will be marked UNINTERESTING and no commits
  1064. // return.
  1065. AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
  1066. try {
  1067. RevObject obj;
  1068. while ((obj = q.next()) != null) {
  1069. if (!(obj instanceof RevCommit))
  1070. throw new PackProtocolException(MessageFormat.format(
  1071. JGitText.get().wantNotValid, obj.name()));
  1072. walk.markStart((RevCommit) obj);
  1073. }
  1074. } catch (MissingObjectException notFound) {
  1075. ObjectId id = notFound.getObjectId();
  1076. throw new PackProtocolException(MessageFormat.format(
  1077. JGitText.get().wantNotValid, id.name()), notFound);
  1078. } finally {
  1079. q.release();
  1080. }
  1081. for (ObjectId id : reachableFrom) {
  1082. try {
  1083. walk.markUninteresting(walk.parseCommit(id));
  1084. } catch (IncorrectObjectTypeException notCommit) {
  1085. continue;
  1086. }
  1087. }
  1088. RevCommit bad = walk.next();
  1089. if (bad != null) {
  1090. throw new PackProtocolException(MessageFormat.format(
  1091. JGitText.get().wantNotValid,
  1092. bad.name()));
  1093. }
  1094. walk.reset();
  1095. }
  1096. private void addCommonBase(final RevObject o) {
  1097. if (!o.has(COMMON)) {
  1098. o.add(COMMON);
  1099. commonBase.add(o);
  1100. okToGiveUp = null;
  1101. }
  1102. }
  1103. private boolean okToGiveUp() throws PackProtocolException {
  1104. if (okToGiveUp == null)
  1105. okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1106. return okToGiveUp.booleanValue();
  1107. }
  1108. private boolean okToGiveUpImp() throws PackProtocolException {
  1109. if (commonBase.isEmpty())
  1110. return false;
  1111. try {
  1112. for (RevObject obj : wantAll) {
  1113. if (!wantSatisfied(obj))
  1114. return false;
  1115. }
  1116. return true;
  1117. } catch (IOException e) {
  1118. throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1119. }
  1120. }
  1121. private boolean wantSatisfied(final RevObject want) throws IOException {
  1122. if (want.has(SATISFIED))
  1123. return true;
  1124. walk.resetRetain(SAVE);
  1125. walk.markStart((RevCommit) want);
  1126. if (oldestTime != 0)
  1127. walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1128. for (;;) {
  1129. final RevCommit c = walk.next();
  1130. if (c == null)
  1131. break;
  1132. if (c.has(PEER_HAS)) {
  1133. addCommonBase(c);
  1134. want.add(SATISFIED);
  1135. return true;
  1136. }
  1137. }
  1138. return false;
  1139. }
  1140. private void sendPack() throws IOException {
  1141. final boolean sideband = options.contains(OPTION_SIDE_BAND)
  1142. || options.contains(OPTION_SIDE_BAND_64K);
  1143. if (!biDirectionalPipe) {
  1144. // Ensure the request was fully consumed. Any remaining input must
  1145. // be a protocol error. If we aren't at EOF the implementation is broken.
  1146. int eof = rawIn.read();
  1147. if (0 <= eof)
  1148. throw new CorruptObjectException(MessageFormat.format(
  1149. JGitText.get().expectedEOFReceived,
  1150. "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  1151. }
  1152. if (sideband) {
  1153. try {
  1154. sendPack(true);
  1155. } catch (ServiceMayNotContinueException noPack) {
  1156. // This was already reported on (below).
  1157. throw noPack;
  1158. } catch (IOException err) {
  1159. if (reportInternalServerErrorOverSideband())
  1160. throw new UploadPackInternalServerErrorException(err);
  1161. else
  1162. throw err;
  1163. } catch (RuntimeException err) {
  1164. if (reportInternalServerErrorOverSideband())
  1165. throw new UploadPackInternalServerErrorException(err);
  1166. else
  1167. throw err;
  1168. } catch (Error err) {
  1169. if (reportInternalServerErrorOverSideband())
  1170. throw new UploadPackInternalServerErrorException(err);
  1171. else
  1172. throw err;
  1173. }
  1174. } else {
  1175. sendPack(false);
  1176. }
  1177. }
  1178. private boolean reportInternalServerErrorOverSideband() {
  1179. try {
  1180. @SuppressWarnings("resource" /* java 7 */)
  1181. SideBandOutputStream err = new SideBandOutputStream(
  1182. SideBandOutputStream.CH_ERROR,
  1183. SideBandOutputStream.SMALL_BUF,
  1184. rawOut);
  1185. err.write(Constants.encode(JGitText.get().internalServerError));
  1186. err.flush();
  1187. return true;
  1188. } catch (Throwable cannotReport) {
  1189. // Ignore the reason. This is a secondary failure.
  1190. return false;
  1191. }
  1192. }
  1193. private void sendPack(final boolean sideband) throws IOException {
  1194. ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1195. OutputStream packOut = rawOut;
  1196. if (sideband) {
  1197. int bufsz = SideBandOutputStream.SMALL_BUF;
  1198. if (options.contains(OPTION_SIDE_BAND_64K))
  1199. bufsz = SideBandOutputStream.MAX_BUF;
  1200. packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
  1201. bufsz, rawOut);
  1202. if (!options.contains(OPTION_NO_PROGRESS)) {
  1203. msgOut = new SideBandOutputStream(
  1204. SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1205. pm = new SideBandProgressMonitor(msgOut);
  1206. }
  1207. }
  1208. try {
  1209. if (wantAll.isEmpty()) {
  1210. preUploadHook.onSendPack(this, wantIds, commonBase);
  1211. } else {
  1212. preUploadHook.onSendPack(this, wantAll, commonBase);
  1213. }
  1214. msgOut.flush();
  1215. } catch (ServiceMayNotContinueException noPack) {
  1216. if (sideband && noPack.getMessage() != null) {
  1217. noPack.setOutput();
  1218. @SuppressWarnings("resource" /* java 7 */)
  1219. SideBandOutputStream err = new SideBandOutputStream(
  1220. SideBandOutputStream.CH_ERROR,
  1221. SideBandOutputStream.SMALL_BUF, rawOut);
  1222. err.write(Constants.encode(noPack.getMessage()));
  1223. err.flush();
  1224. }
  1225. throw noPack;
  1226. }
  1227. PackConfig cfg = packConfig;
  1228. if (cfg == null)
  1229. cfg = new PackConfig(db);
  1230. final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
  1231. try {
  1232. pw.setIndexDisabled(true);
  1233. pw.setUseCachedPacks(true);
  1234. pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
  1235. pw.setReuseDeltaCommits(true);
  1236. pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
  1237. pw.setThin(options.contains(OPTION_THIN_PACK));
  1238. pw.setReuseValidatingObjects(false);
  1239. if (commonBase.isEmpty() && refs != null) {
  1240. Set<ObjectId> tagTargets = new HashSet<ObjectId>();
  1241. for (Ref ref : refs.values()) {
  1242. if (ref.getPeeledObjectId() != null)
  1243. tagTargets.add(ref.getPeeledObjectId());
  1244. else if (ref.getObjectId() == null)
  1245. continue;
  1246. else if (ref.getName().startsWith(Constants.R_HEADS))
  1247. tagTargets.add(ref.getObjectId());
  1248. }
  1249. pw.setTagTargets(tagTargets);
  1250. }
  1251. if (depth > 0)
  1252. pw.setShallowPack(depth, unshallowCommits);
  1253. RevWalk rw = walk;
  1254. if (wantAll.isEmpty()) {
  1255. pw.preparePack(pm, wantIds, commonBase);
  1256. } else {
  1257. walk.reset();
  1258. ObjectWalk ow = walk.toObjectWalkWithSameObjects();
  1259. pw.preparePack(pm, ow, wantAll, commonBase);
  1260. rw = ow;
  1261. }
  1262. if (options.contains(OPTION_INCLUDE_TAG) && refs != null) {
  1263. for (Ref ref : refs.values()) {
  1264. ObjectId objectId = ref.getObjectId();
  1265. // If the object was already requested, skip it.
  1266. if (wantAll.isEmpty()) {
  1267. if (wantIds.contains(objectId))
  1268. continue;
  1269. } else {
  1270. RevObject obj = rw.lookupOrNull(objectId);
  1271. if (obj != null && obj.has(WANT))
  1272. continue;
  1273. }
  1274. if (!ref.isPeeled())
  1275. ref = db.peel(ref);
  1276. ObjectId peeledId = ref.getPeeledObjectId();
  1277. if (peeledId == null)
  1278. continue;
  1279. objectId = ref.getObjectId();
  1280. if (pw.willInclude(peeledId) && !pw.willInclude(objectId))
  1281. pw.addObject(rw.parseAny(objectId));
  1282. }
  1283. }
  1284. pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
  1285. if (msgOut != NullOutputStream.INSTANCE) {
  1286. String msg = pw.getStatistics().getMessage() + '\n';
  1287. msgOut.write(Constants.encode(msg));
  1288. msgOut.flush();
  1289. }
  1290. } finally {
  1291. statistics = pw.getStatistics();
  1292. if (statistics != null)
  1293. logger.onPackStatistics(statistics);
  1294. pw.release();
  1295. }
  1296. if (sideband)
  1297. pckOut.end();
  1298. }
  1299. private void findSymrefs(
  1300. final RefAdvertiser adv, final Map<String, Ref> refs) {
  1301. Ref head = refs.get(Constants.HEAD);
  1302. if (head != null && head.isSymbolic()) {
  1303. adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  1304. }
  1305. }
  1306. }