You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UploadPack.java 75KB

Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
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>
12 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
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>
12 years ago
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>
12 years ago
Move reachability checker generation into the ObjectReader object Reachability checkers are retrieved from RevWalk and ObjectWalk objects: * RevWalk.createReachabilityChecker() * ObjectWalk.createObjectReachabilityChecker() Since RevWalks and ObjectWalks are themselves directly instantiated in hundreds of places (e.g. UploadPack...) overriding them in a consistent way requires overloading 100s of methods, which isn't feasible. Moving reachability checker generation to a more central place solves that problem. The ObjectReader object seems a good place from which to get reachability checkers, because reachability checkers return information about relationships between objects. ObjectDatabases delegate many operations to ObjectReaders, and reachability bitmaps are attached to ObjectReaders. The Bitmapped and Pedestrian reachability checker objects were package private in the org.eclipse.jgit.revwalk package. This change makes them public and moves them to the org.eclipse.jgit.internal.revwalk package. Corresponding tests are also moved. Motivation: 1) Reachability checking algorithms need to scale. One of the internal Android repositories has ~2.4 million refs/changes/* references, causing bad long tail performance in reachability checks. 2) Reachability check performance is impacted by repository topography: number of refs, number of objects, amounts of related vs. unrelated history. 3) Reachability check performance is also affected by per-branch access (Gerrit branch permissions) since different users can see different branches. 4) Reachability check performance isn't affected by any state in a RevWalk or ObjectWalk. I don't yet know if a single algorithm will work for all cases in #2 and #3. We may need to evolve the ReachabilityChecker interfaces over time to solve the Gerrit branch permissions case, or use Gerrit-specific identity information to solve that in an efficient way. This change takes the existing public API and moves it to the ObjectReader/whole repository level, which is where we can do consistent customizations for #2 and #3. We intend to upstream the best of whatever works, but anticipate the need for multiple rounds of experimentation. Change-Id: I9185feff43551fb387957c436112d5250486833d Signed-off-by: Terry Parker <tparker@google.com>
3 years ago
Move reachability checker generation into the ObjectReader object Reachability checkers are retrieved from RevWalk and ObjectWalk objects: * RevWalk.createReachabilityChecker() * ObjectWalk.createObjectReachabilityChecker() Since RevWalks and ObjectWalks are themselves directly instantiated in hundreds of places (e.g. UploadPack...) overriding them in a consistent way requires overloading 100s of methods, which isn't feasible. Moving reachability checker generation to a more central place solves that problem. The ObjectReader object seems a good place from which to get reachability checkers, because reachability checkers return information about relationships between objects. ObjectDatabases delegate many operations to ObjectReaders, and reachability bitmaps are attached to ObjectReaders. The Bitmapped and Pedestrian reachability checker objects were package private in the org.eclipse.jgit.revwalk package. This change makes them public and moves them to the org.eclipse.jgit.internal.revwalk package. Corresponding tests are also moved. Motivation: 1) Reachability checking algorithms need to scale. One of the internal Android repositories has ~2.4 million refs/changes/* references, causing bad long tail performance in reachability checks. 2) Reachability check performance is impacted by repository topography: number of refs, number of objects, amounts of related vs. unrelated history. 3) Reachability check performance is also affected by per-branch access (Gerrit branch permissions) since different users can see different branches. 4) Reachability check performance isn't affected by any state in a RevWalk or ObjectWalk. I don't yet know if a single algorithm will work for all cases in #2 and #3. We may need to evolve the ReachabilityChecker interfaces over time to solve the Gerrit branch permissions case, or use Gerrit-specific identity information to solve that in an efficient way. This change takes the existing public API and moves it to the ObjectReader/whole repository level, which is where we can do consistent customizations for #2 and #3. We intend to upstream the best of whatever works, but anticipate the need for multiple rounds of experimentation. Change-Id: I9185feff43551fb387957c436112d5250486833d Signed-off-by: Terry Parker <tparker@google.com>
3 years ago
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>
12 years ago
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>
13 years ago
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>
12 years ago
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>
13 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466
  1. /*
  2. * Copyright (C) 2008, 2020 Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.transport;
  11. import static java.util.Collections.unmodifiableMap;
  12. import static java.util.Objects.requireNonNull;
  13. import static org.eclipse.jgit.lib.Constants.R_TAGS;
  14. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
  15. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
  16. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
  17. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
  18. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  19. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
  20. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  21. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
  22. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
  23. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  24. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  25. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  26. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  27. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  28. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  29. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  30. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
  31. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  32. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  33. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  34. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
  35. import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
  36. import static org.eclipse.jgit.util.RefMap.toRefMap;
  37. import java.io.ByteArrayOutputStream;
  38. import java.io.EOFException;
  39. import java.io.IOException;
  40. import java.io.InputStream;
  41. import java.io.OutputStream;
  42. import java.io.UncheckedIOException;
  43. import java.text.MessageFormat;
  44. import java.time.Duration;
  45. import java.time.Instant;
  46. import java.util.ArrayList;
  47. import java.util.Collection;
  48. import java.util.Collections;
  49. import java.util.HashSet;
  50. import java.util.List;
  51. import java.util.Map;
  52. import java.util.Objects;
  53. import java.util.Optional;
  54. import java.util.Set;
  55. import java.util.TreeMap;
  56. import java.util.function.Predicate;
  57. import java.util.stream.Collectors;
  58. import java.util.stream.Stream;
  59. import org.eclipse.jgit.annotations.NonNull;
  60. import org.eclipse.jgit.annotations.Nullable;
  61. import org.eclipse.jgit.errors.CorruptObjectException;
  62. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  63. import org.eclipse.jgit.errors.MissingObjectException;
  64. import org.eclipse.jgit.errors.PackProtocolException;
  65. import org.eclipse.jgit.internal.JGitText;
  66. import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
  67. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  68. import org.eclipse.jgit.internal.transport.parser.FirstWant;
  69. import org.eclipse.jgit.lib.Constants;
  70. import org.eclipse.jgit.lib.NullProgressMonitor;
  71. import org.eclipse.jgit.lib.ObjectId;
  72. import org.eclipse.jgit.lib.ObjectReader;
  73. import org.eclipse.jgit.lib.ProgressMonitor;
  74. import org.eclipse.jgit.lib.Ref;
  75. import org.eclipse.jgit.lib.RefDatabase;
  76. import org.eclipse.jgit.lib.Repository;
  77. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  78. import org.eclipse.jgit.revwalk.DepthWalk;
  79. import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
  80. import org.eclipse.jgit.revwalk.ObjectWalk;
  81. import org.eclipse.jgit.revwalk.ReachabilityChecker;
  82. import org.eclipse.jgit.revwalk.RevCommit;
  83. import org.eclipse.jgit.revwalk.RevFlag;
  84. import org.eclipse.jgit.revwalk.RevFlagSet;
  85. import org.eclipse.jgit.revwalk.RevObject;
  86. import org.eclipse.jgit.revwalk.RevTag;
  87. import org.eclipse.jgit.revwalk.RevWalk;
  88. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  89. import org.eclipse.jgit.storage.pack.PackConfig;
  90. import org.eclipse.jgit.storage.pack.PackStatistics;
  91. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  92. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  93. import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
  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 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. /**
  154. * Data in the first line of a want-list, the line itself plus options.
  155. *
  156. * @deprecated Use {@link FirstWant} instead
  157. */
  158. @Deprecated
  159. public static class FirstLine {
  160. private final FirstWant firstWant;
  161. /**
  162. * @param line
  163. * line from the client.
  164. */
  165. public FirstLine(String line) {
  166. try {
  167. firstWant = FirstWant.fromLine(line);
  168. } catch (PackProtocolException e) {
  169. throw new UncheckedIOException(e);
  170. }
  171. }
  172. /** @return non-capabilities part of the line. */
  173. public String getLine() {
  174. return firstWant.getLine();
  175. }
  176. /** @return capabilities parsed from the line. */
  177. public Set<String> getOptions() {
  178. if (firstWant.getAgent() != null) {
  179. Set<String> caps = new HashSet<>(firstWant.getCapabilities());
  180. caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
  181. return caps;
  182. }
  183. return firstWant.getCapabilities();
  184. }
  185. }
  186. /*
  187. * {@link java.util.function.Consumer} doesn't allow throwing checked
  188. * exceptions. Define our own to propagate IOExceptions.
  189. */
  190. @FunctionalInterface
  191. private static interface IOConsumer<R> {
  192. void accept(R t) throws IOException;
  193. }
  194. /** Database we read the objects from. */
  195. private final Repository db;
  196. /** Revision traversal support over {@link #db}. */
  197. private final RevWalk walk;
  198. /** Configuration to pass into the PackWriter. */
  199. private PackConfig packConfig;
  200. /** Configuration for various transfer options. */
  201. private TransferConfig transferConfig;
  202. /** Timeout in seconds to wait for client interaction. */
  203. private int timeout;
  204. /**
  205. * Is the client connection a bi-directional socket or pipe?
  206. * <p>
  207. * If true, this class assumes it can perform multiple read and write cycles
  208. * with the client over the input and output streams. This matches the
  209. * functionality available with a standard TCP/IP connection, or a local
  210. * operating system or in-memory pipe.
  211. * <p>
  212. * If false, this class runs in a read everything then output results mode,
  213. * making it suitable for single round-trip systems RPCs such as HTTP.
  214. */
  215. private boolean biDirectionalPipe = true;
  216. /** Timer to manage {@link #timeout}. */
  217. private InterruptTimer timer;
  218. /**
  219. * Whether the client requested to use protocol V2 through a side
  220. * channel (such as the Git-Protocol HTTP header).
  221. */
  222. private boolean clientRequestedV2;
  223. private InputStream rawIn;
  224. private ResponseBufferedOutputStream rawOut;
  225. private PacketLineIn pckIn;
  226. private OutputStream msgOut = NullOutputStream.INSTANCE;
  227. private ErrorWriter errOut = new PackProtocolErrorWriter();
  228. /**
  229. * Refs eligible for advertising to the client, set using
  230. * {@link #setAdvertisedRefs}.
  231. */
  232. private Map<String, Ref> refs;
  233. /** Hook used while processing Git protocol v2 requests. */
  234. private ProtocolV2Hook protocolV2Hook = ProtocolV2Hook.DEFAULT;
  235. /** Hook used while advertising the refs to the client. */
  236. private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  237. /** Whether the {@link #advertiseRefsHook} has been invoked. */
  238. private boolean advertiseRefsHookCalled;
  239. /** Filter used while advertising the refs to the client. */
  240. private RefFilter refFilter = RefFilter.DEFAULT;
  241. /** Hook handling the various upload phases. */
  242. private PreUploadHook preUploadHook = PreUploadHook.NULL;
  243. /** Hook for taking post upload actions. */
  244. private PostUploadHook postUploadHook = PostUploadHook.NULL;
  245. /** Caller user agent */
  246. String userAgent;
  247. /** Raw ObjectIds the client has asked for, before validating them. */
  248. private Set<ObjectId> wantIds = new HashSet<>();
  249. /** Objects the client wants to obtain. */
  250. private final Set<RevObject> wantAll = new HashSet<>();
  251. /** Objects on both sides, these don't have to be sent. */
  252. private final Set<RevObject> commonBase = new HashSet<>();
  253. /** Commit time of the oldest common commit, in seconds. */
  254. private int oldestTime;
  255. /** null if {@link #commonBase} should be examined again. */
  256. private Boolean okToGiveUp;
  257. private boolean sentReady;
  258. /** Objects we sent in our advertisement list. */
  259. private Set<ObjectId> advertised;
  260. /** Marked on objects the client has asked us to give them. */
  261. private final RevFlag WANT;
  262. /** Marked on objects both we and the client have. */
  263. private final RevFlag PEER_HAS;
  264. /** Marked on objects in {@link #commonBase}. */
  265. private final RevFlag COMMON;
  266. /** Objects where we found a path from the want list to a common base. */
  267. private final RevFlag SATISFIED;
  268. private final RevFlagSet SAVE;
  269. private RequestValidator requestValidator = new AdvertisedRequestValidator();
  270. private MultiAck multiAck = MultiAck.OFF;
  271. private boolean noDone;
  272. private PackStatistics statistics;
  273. /**
  274. * Request this instance is handling.
  275. *
  276. * We need to keep a reference to it for {@link PreUploadHook pre upload
  277. * hooks}. They receive a reference this instance and invoke methods like
  278. * getDepth() to get information about the request.
  279. */
  280. private FetchRequest currentRequest;
  281. private CachedPackUriProvider cachedPackUriProvider;
  282. /**
  283. * Create a new pack upload for an open repository.
  284. *
  285. * @param copyFrom
  286. * the source repository.
  287. */
  288. public UploadPack(Repository copyFrom) {
  289. db = copyFrom;
  290. walk = new RevWalk(db);
  291. walk.setRetainBody(false);
  292. WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  293. PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  294. COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  295. SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  296. walk.carry(PEER_HAS);
  297. SAVE = new RevFlagSet();
  298. SAVE.add(WANT);
  299. SAVE.add(PEER_HAS);
  300. SAVE.add(COMMON);
  301. SAVE.add(SATISFIED);
  302. setTransferConfig(null);
  303. }
  304. /**
  305. * Get the repository this upload is reading from.
  306. *
  307. * @return the repository this upload is reading from.
  308. */
  309. public final Repository getRepository() {
  310. return db;
  311. }
  312. /**
  313. * Get the RevWalk instance used by this connection.
  314. *
  315. * @return the RevWalk instance used by this connection.
  316. */
  317. public final RevWalk getRevWalk() {
  318. return walk;
  319. }
  320. /**
  321. * Get refs which were advertised to the client.
  322. *
  323. * @return all refs which were advertised to the client. Only valid during
  324. * the negotiation phase. Will return {@code null} if
  325. * {@link #setAdvertisedRefs(Map)} has not been called yet or if
  326. * {@code #sendPack()} has been called.
  327. */
  328. public final Map<String, Ref> getAdvertisedRefs() {
  329. return refs;
  330. }
  331. /**
  332. * Set the refs advertised by this UploadPack.
  333. * <p>
  334. * Intended to be called from a
  335. * {@link org.eclipse.jgit.transport.PreUploadHook}.
  336. *
  337. * @param allRefs
  338. * explicit set of references to claim as advertised by this
  339. * UploadPack instance. This overrides any references that may
  340. * exist in the source repository. The map is passed to the
  341. * configured {@link #getRefFilter()}. If null, assumes all refs
  342. * were advertised.
  343. */
  344. public void setAdvertisedRefs(@Nullable Map<String, Ref> allRefs) {
  345. if (allRefs != null)
  346. refs = allRefs;
  347. else
  348. refs = db.getAllRefs();
  349. if (refFilter == RefFilter.DEFAULT)
  350. refs = transferConfig.getRefFilter().filter(refs);
  351. else
  352. refs = refFilter.filter(refs);
  353. }
  354. /**
  355. * Get timeout (in seconds) before aborting an IO operation.
  356. *
  357. * @return timeout (in seconds) before aborting an IO operation.
  358. */
  359. public int getTimeout() {
  360. return timeout;
  361. }
  362. /**
  363. * Set the timeout before willing to abort an IO call.
  364. *
  365. * @param seconds
  366. * number of seconds to wait (with no data transfer occurring)
  367. * before aborting an IO read or write operation with the
  368. * connected client.
  369. */
  370. public void setTimeout(int seconds) {
  371. timeout = seconds;
  372. }
  373. /**
  374. * Whether this class expects a bi-directional pipe opened between the
  375. * client and itself.
  376. *
  377. * @return true if this class expects a bi-directional pipe opened between
  378. * the client and itself. The default is true.
  379. */
  380. public boolean isBiDirectionalPipe() {
  381. return biDirectionalPipe;
  382. }
  383. /**
  384. * Set whether this class will assume the socket is a fully bidirectional
  385. * pipe between the two peers
  386. *
  387. * @param twoWay
  388. * if true, this class will assume the socket is a fully
  389. * bidirectional pipe between the two peers and takes advantage
  390. * of that by first transmitting the known refs, then waiting to
  391. * read commands. If false, this class assumes it must read the
  392. * commands before writing output and does not perform the
  393. * initial advertising.
  394. */
  395. public void setBiDirectionalPipe(boolean twoWay) {
  396. biDirectionalPipe = twoWay;
  397. }
  398. /**
  399. * Get policy used by the service to validate client requests
  400. *
  401. * @return policy used by the service to validate client requests, or null
  402. * for a custom request validator.
  403. */
  404. public RequestPolicy getRequestPolicy() {
  405. if (requestValidator instanceof AdvertisedRequestValidator)
  406. return RequestPolicy.ADVERTISED;
  407. if (requestValidator instanceof ReachableCommitRequestValidator)
  408. return RequestPolicy.REACHABLE_COMMIT;
  409. if (requestValidator instanceof TipRequestValidator)
  410. return RequestPolicy.TIP;
  411. if (requestValidator instanceof ReachableCommitTipRequestValidator)
  412. return RequestPolicy.REACHABLE_COMMIT_TIP;
  413. if (requestValidator instanceof AnyRequestValidator)
  414. return RequestPolicy.ANY;
  415. return null;
  416. }
  417. /**
  418. * Set the policy used to enforce validation of a client's want list.
  419. *
  420. * @param policy
  421. * the policy used to enforce validation of a client's want list.
  422. * By default the policy is
  423. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#ADVERTISED},
  424. * which is the Git default requiring clients to only ask for an
  425. * object that a reference directly points to. This may be
  426. * relaxed to
  427. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT}
  428. * or
  429. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT_TIP}
  430. * when callers have {@link #setBiDirectionalPipe(boolean)} set
  431. * to false. Overrides any policy specified in a
  432. * {@link org.eclipse.jgit.transport.TransferConfig}.
  433. */
  434. public void setRequestPolicy(RequestPolicy policy) {
  435. switch (policy) {
  436. case ADVERTISED:
  437. default:
  438. requestValidator = new AdvertisedRequestValidator();
  439. break;
  440. case REACHABLE_COMMIT:
  441. requestValidator = new ReachableCommitRequestValidator();
  442. break;
  443. case TIP:
  444. requestValidator = new TipRequestValidator();
  445. break;
  446. case REACHABLE_COMMIT_TIP:
  447. requestValidator = new ReachableCommitTipRequestValidator();
  448. break;
  449. case ANY:
  450. requestValidator = new AnyRequestValidator();
  451. break;
  452. }
  453. }
  454. /**
  455. * Set custom validator for client want list.
  456. *
  457. * @param validator
  458. * custom validator for client want list.
  459. * @since 3.1
  460. */
  461. public void setRequestValidator(@Nullable RequestValidator validator) {
  462. requestValidator = validator != null ? validator
  463. : new AdvertisedRequestValidator();
  464. }
  465. /**
  466. * Get the hook used while advertising the refs to the client.
  467. *
  468. * @return the hook used while advertising the refs to the client.
  469. */
  470. public AdvertiseRefsHook getAdvertiseRefsHook() {
  471. return advertiseRefsHook;
  472. }
  473. /**
  474. * Get the filter used while advertising the refs to the client.
  475. *
  476. * @return the filter used while advertising the refs to the client.
  477. */
  478. public RefFilter getRefFilter() {
  479. return refFilter;
  480. }
  481. /**
  482. * Set the hook used while advertising the refs to the client.
  483. * <p>
  484. * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
  485. * call {@link #setAdvertisedRefs(Map)}, only refs set by this hook
  486. * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
  487. * will be shown to the client.
  488. *
  489. * @param advertiseRefsHook
  490. * the hook; may be null to show all refs.
  491. */
  492. public void setAdvertiseRefsHook(
  493. @Nullable AdvertiseRefsHook advertiseRefsHook) {
  494. this.advertiseRefsHook = advertiseRefsHook != null ? advertiseRefsHook
  495. : AdvertiseRefsHook.DEFAULT;
  496. }
  497. /**
  498. * Set the protocol V2 hook.
  499. *
  500. * @param hook
  501. * the hook; if null no special actions are taken.
  502. * @since 5.1
  503. */
  504. public void setProtocolV2Hook(@Nullable ProtocolV2Hook hook) {
  505. this.protocolV2Hook = hook != null ? hook : ProtocolV2Hook.DEFAULT;
  506. }
  507. /**
  508. * Get the currently installed protocol v2 hook.
  509. *
  510. * @return the hook or a default implementation if none installed.
  511. *
  512. * @since 5.5
  513. */
  514. public ProtocolV2Hook getProtocolV2Hook() {
  515. return this.protocolV2Hook != null ? this.protocolV2Hook
  516. : ProtocolV2Hook.DEFAULT;
  517. }
  518. /**
  519. * Set the filter used while advertising the refs to the client.
  520. * <p>
  521. * Only refs allowed by this filter will be sent to the client. The filter
  522. * is run against the refs specified by the
  523. * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). If
  524. * null or not set, uses the filter implied by the
  525. * {@link org.eclipse.jgit.transport.TransferConfig}.
  526. *
  527. * @param refFilter
  528. * the filter; may be null to show all refs.
  529. */
  530. public void setRefFilter(@Nullable RefFilter refFilter) {
  531. this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  532. }
  533. /**
  534. * Get the configured pre upload hook.
  535. *
  536. * @return the configured pre upload hook.
  537. */
  538. public PreUploadHook getPreUploadHook() {
  539. return preUploadHook;
  540. }
  541. /**
  542. * Set the hook that controls how this instance will behave.
  543. *
  544. * @param hook
  545. * the hook; if null no special actions are taken.
  546. */
  547. public void setPreUploadHook(@Nullable PreUploadHook hook) {
  548. preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  549. }
  550. /**
  551. * Get the configured post upload hook.
  552. *
  553. * @return the configured post upload hook.
  554. * @since 4.1
  555. */
  556. public PostUploadHook getPostUploadHook() {
  557. return postUploadHook;
  558. }
  559. /**
  560. * Set the hook for post upload actions (logging, repacking).
  561. *
  562. * @param hook
  563. * the hook; if null no special actions are taken.
  564. * @since 4.1
  565. */
  566. public void setPostUploadHook(@Nullable PostUploadHook hook) {
  567. postUploadHook = hook != null ? hook : PostUploadHook.NULL;
  568. }
  569. /**
  570. * Set the configuration used by the pack generator.
  571. *
  572. * @param pc
  573. * configuration controlling packing parameters. If null the
  574. * source repository's settings will be used.
  575. */
  576. public void setPackConfig(@Nullable PackConfig pc) {
  577. this.packConfig = pc;
  578. }
  579. /**
  580. * Set configuration controlling transfer options.
  581. *
  582. * @param tc
  583. * configuration controlling transfer options. If null the source
  584. * repository's settings will be used.
  585. * @since 3.1
  586. */
  587. public void setTransferConfig(@Nullable TransferConfig tc) {
  588. this.transferConfig = tc != null ? tc : new TransferConfig(db);
  589. if (transferConfig.isAllowTipSha1InWant()) {
  590. setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  591. ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
  592. } else {
  593. setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  594. ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
  595. }
  596. }
  597. /**
  598. * Check whether the client expects a side-band stream.
  599. *
  600. * @return true if the client has advertised a side-band capability, false
  601. * otherwise.
  602. * @throws org.eclipse.jgit.transport.RequestNotYetReadException
  603. * if the client's request has not yet been read from the wire, so
  604. * we do not know if they expect side-band. Note that the client
  605. * may have already written the request, it just has not been
  606. * read.
  607. */
  608. public boolean isSideBand() throws RequestNotYetReadException {
  609. if (currentRequest == null) {
  610. throw new RequestNotYetReadException();
  611. }
  612. Set<String> caps = currentRequest.getClientCapabilities();
  613. return caps.contains(OPTION_SIDE_BAND)
  614. || caps.contains(OPTION_SIDE_BAND_64K);
  615. }
  616. /**
  617. * Set the Extra Parameters provided by the client.
  618. *
  619. * <p>These are parameters passed by the client through a side channel
  620. * such as the Git-Protocol HTTP header, to allow a client to request
  621. * a newer response format while remaining compatible with older servers
  622. * that do not understand different request formats.
  623. *
  624. * @param params
  625. * parameters supplied by the client, split at colons or NUL
  626. * bytes.
  627. * @since 5.0
  628. */
  629. public void setExtraParameters(Collection<String> params) {
  630. this.clientRequestedV2 = params.contains(VERSION_2_REQUEST);
  631. }
  632. /**
  633. * @param p provider of URIs corresponding to cached packs (to support
  634. * the packfile URIs feature)
  635. * @since 5.5
  636. */
  637. public void setCachedPackUriProvider(@Nullable CachedPackUriProvider p) {
  638. cachedPackUriProvider = p;
  639. }
  640. private boolean useProtocolV2() {
  641. return (transferConfig.protocolVersion == null
  642. || ProtocolVersion.V2.equals(transferConfig.protocolVersion))
  643. && clientRequestedV2;
  644. }
  645. /**
  646. * Execute the upload task on the socket.
  647. *
  648. * <p>
  649. * Same as {@link #uploadWithExceptionPropagation} except that the thrown
  650. * exceptions are handled in the method, and the error messages are sent to
  651. * the clients.
  652. *
  653. * <p>
  654. * Call this method if the caller does not have an error handling mechanism.
  655. * Call {@link #uploadWithExceptionPropagation} if the caller wants to have
  656. * its own error handling mechanism.
  657. *
  658. * @param input
  659. * @param output
  660. * @param messages
  661. * @throws java.io.IOException
  662. */
  663. public void upload(InputStream input, OutputStream output,
  664. @Nullable OutputStream messages) throws IOException {
  665. try {
  666. uploadWithExceptionPropagation(input, output, messages);
  667. } catch (ServiceMayNotContinueException err) {
  668. if (!err.isOutput() && err.getMessage() != null) {
  669. try {
  670. errOut.writeError(err.getMessage());
  671. } catch (IOException e) {
  672. err.addSuppressed(e);
  673. throw err;
  674. }
  675. err.setOutput();
  676. }
  677. throw err;
  678. } catch (IOException | RuntimeException | Error err) {
  679. if (rawOut != null) {
  680. String msg = err instanceof PackProtocolException
  681. ? err.getMessage()
  682. : JGitText.get().internalServerError;
  683. try {
  684. errOut.writeError(msg);
  685. } catch (IOException e) {
  686. err.addSuppressed(e);
  687. throw err;
  688. }
  689. throw new UploadPackInternalServerErrorException(err);
  690. }
  691. throw err;
  692. }
  693. }
  694. /**
  695. * Execute the upload task on the socket.
  696. *
  697. * <p>
  698. * If the client passed extra parameters (e.g., "version=2") through a side
  699. * channel, the caller must call setExtraParameters first to supply them.
  700. *
  701. * @param input
  702. * raw input to read client commands from. Caller must ensure the
  703. * input is buffered, otherwise read performance may suffer.
  704. * @param output
  705. * response back to the Git network client, to write the pack
  706. * data onto. Caller must ensure the output is buffered,
  707. * otherwise write performance may suffer.
  708. * @param messages
  709. * secondary "notice" channel to send additional messages out
  710. * through. When run over SSH this should be tied back to the
  711. * standard error channel of the command execution. For most
  712. * other network connections this should be null.
  713. * @throws ServiceMayNotContinueException
  714. * thrown if one of the hooks throws this.
  715. * @throws IOException
  716. * thrown if the server or the client I/O fails, or there's an
  717. * internal server error.
  718. * @since 5.6
  719. */
  720. public void uploadWithExceptionPropagation(InputStream input,
  721. OutputStream output, @Nullable OutputStream messages)
  722. throws ServiceMayNotContinueException, IOException {
  723. try {
  724. rawIn = input;
  725. if (messages != null) {
  726. msgOut = messages;
  727. }
  728. if (timeout > 0) {
  729. final Thread caller = Thread.currentThread();
  730. timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  731. TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  732. @SuppressWarnings("resource")
  733. TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
  734. i.setTimeout(timeout * 1000);
  735. o.setTimeout(timeout * 1000);
  736. rawIn = i;
  737. output = o;
  738. }
  739. rawOut = new ResponseBufferedOutputStream(output);
  740. if (biDirectionalPipe) {
  741. rawOut.stopBuffering();
  742. }
  743. pckIn = new PacketLineIn(rawIn);
  744. PacketLineOut pckOut = new PacketLineOut(rawOut);
  745. if (useProtocolV2()) {
  746. serviceV2(pckOut);
  747. } else {
  748. service(pckOut);
  749. }
  750. } finally {
  751. msgOut = NullOutputStream.INSTANCE;
  752. walk.close();
  753. if (timer != null) {
  754. try {
  755. timer.terminate();
  756. } finally {
  757. timer = null;
  758. }
  759. }
  760. }
  761. }
  762. /**
  763. * Get the PackWriter's statistics if a pack was sent to the client.
  764. *
  765. * @return statistics about pack output, if a pack was sent. Null if no pack
  766. * was sent, such as during the negotiation phase of a smart HTTP
  767. * connection, or if the client was already up-to-date.
  768. * @since 4.1
  769. */
  770. public PackStatistics getStatistics() {
  771. return statistics;
  772. }
  773. private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
  774. if (refs != null) {
  775. return refs;
  776. }
  777. if (!advertiseRefsHookCalled) {
  778. advertiseRefsHook.advertiseRefs(this);
  779. advertiseRefsHookCalled = true;
  780. }
  781. if (refs == null) {
  782. // Fall back to all refs.
  783. setAdvertisedRefs(
  784. db.getRefDatabase().getRefs().stream()
  785. .collect(toRefMap((a, b) -> b)));
  786. }
  787. return refs;
  788. }
  789. private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
  790. throws IOException {
  791. if (refPrefixes.isEmpty()) {
  792. return getAdvertisedOrDefaultRefs();
  793. }
  794. if (refs == null && !advertiseRefsHookCalled) {
  795. advertiseRefsHook.advertiseRefs(this);
  796. advertiseRefsHookCalled = true;
  797. }
  798. if (refs == null) {
  799. // Fast path: the advertised refs hook did not set advertised refs.
  800. String[] prefixes = refPrefixes.toArray(new String[0]);
  801. Map<String, Ref> rs =
  802. db.getRefDatabase().getRefsByPrefix(prefixes).stream()
  803. .collect(toRefMap((a, b) -> b));
  804. if (refFilter != RefFilter.DEFAULT) {
  805. return refFilter.filter(rs);
  806. }
  807. return transferConfig.getRefFilter().filter(rs);
  808. }
  809. // Slow path: filter the refs provided by the advertised refs hook.
  810. // refFilter has already been applied to refs.
  811. return refs.values().stream()
  812. .filter(ref -> refPrefixes.stream()
  813. .anyMatch(ref.getName()::startsWith))
  814. .collect(toRefMap((a, b) -> b));
  815. }
  816. /**
  817. * Returns the specified references.
  818. * <p>
  819. * This produces an immutable map containing whatever subset of the
  820. * refs named by the caller are present in the supplied {@code refs}
  821. * map.
  822. *
  823. * @param refs
  824. * Map to search for refs to return.
  825. * @param names
  826. * which refs to search for in {@code refs}.
  827. * @return the requested Refs, omitting any that are null or missing.
  828. */
  829. @NonNull
  830. private static Map<String, Ref> mapRefs(
  831. Map<String, Ref> refs, List<String> names) {
  832. return unmodifiableMap(
  833. names.stream()
  834. .map(refs::get)
  835. .filter(Objects::nonNull)
  836. .collect(toRefMap((a, b) -> b)));
  837. }
  838. /**
  839. * Read refs on behalf of the client.
  840. * <p>
  841. * This checks whether the refs are present in the ref advertisement
  842. * since otherwise the client might not be supposed to be able to
  843. * read them.
  844. *
  845. * @param names
  846. * unabbreviated names of references.
  847. * @return the requested Refs, omitting any that are not visible or
  848. * do not exist.
  849. * @throws java.io.IOException
  850. * on failure to read a ref or check it for visibility.
  851. */
  852. @NonNull
  853. private Map<String, Ref> exactRefs(List<String> names) throws IOException {
  854. if (refs != null) {
  855. return mapRefs(refs, names);
  856. }
  857. if (!advertiseRefsHookCalled) {
  858. advertiseRefsHook.advertiseRefs(this);
  859. advertiseRefsHookCalled = true;
  860. }
  861. if (refs == null &&
  862. refFilter == RefFilter.DEFAULT &&
  863. transferConfig.hasDefaultRefFilter()) {
  864. // Fast path: no ref filtering is needed.
  865. String[] ns = names.toArray(new String[0]);
  866. return unmodifiableMap(db.getRefDatabase().exactRef(ns));
  867. }
  868. return mapRefs(getAdvertisedOrDefaultRefs(), names);
  869. }
  870. /**
  871. * Find a ref in the usual search path on behalf of the client.
  872. * <p>
  873. * This checks that the ref is present in the ref advertisement since
  874. * otherwise the client might not be supposed to be able to read it.
  875. *
  876. * @param name
  877. * short name of the ref to find, e.g. "master" to find
  878. * "refs/heads/master".
  879. * @return the requested Ref, or {@code null} if it is not visible or
  880. * does not exist.
  881. * @throws java.io.IOException
  882. * on failure to read the ref or check it for visibility.
  883. */
  884. @Nullable
  885. private Ref findRef(String name) throws IOException {
  886. if (refs != null) {
  887. return RefDatabase.findRef(refs, name);
  888. }
  889. if (!advertiseRefsHookCalled) {
  890. advertiseRefsHook.advertiseRefs(this);
  891. advertiseRefsHookCalled = true;
  892. }
  893. if (refs == null &&
  894. refFilter == RefFilter.DEFAULT &&
  895. transferConfig.hasDefaultRefFilter()) {
  896. // Fast path: no ref filtering is needed.
  897. return db.getRefDatabase().findRef(name);
  898. }
  899. return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
  900. }
  901. private void service(PacketLineOut pckOut) throws IOException {
  902. boolean sendPack = false;
  903. // If it's a non-bidi request, we need to read the entire request before
  904. // writing a response. Buffer the response until then.
  905. PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  906. List<ObjectId> unshallowCommits = new ArrayList<>();
  907. FetchRequest req;
  908. try {
  909. if (biDirectionalPipe)
  910. sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  911. else if (requestValidator instanceof AnyRequestValidator)
  912. advertised = Collections.emptySet();
  913. else
  914. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  915. Instant negotiateStart = Instant.now();
  916. accumulator.advertised = advertised.size();
  917. ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
  918. req = parser.recvWants(pckIn);
  919. currentRequest = req;
  920. wantIds = req.getWantIds();
  921. if (req.getWantIds().isEmpty()) {
  922. preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0);
  923. preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0,
  924. false);
  925. return;
  926. }
  927. accumulator.wants = req.getWantIds().size();
  928. if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) {
  929. multiAck = MultiAck.DETAILED;
  930. noDone = req.getClientCapabilities().contains(OPTION_NO_DONE);
  931. } else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK))
  932. multiAck = MultiAck.CONTINUE;
  933. else
  934. multiAck = MultiAck.OFF;
  935. if (!req.getClientShallowCommits().isEmpty()) {
  936. verifyClientShallow(req.getClientShallowCommits());
  937. }
  938. if (req.getDepth() != 0 || req.getDeepenSince() != 0) {
  939. computeShallowsAndUnshallows(req, shallow -> {
  940. pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
  941. }, unshallow -> {
  942. pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
  943. unshallowCommits.add(unshallow);
  944. }, Collections.emptyList());
  945. pckOut.end();
  946. }
  947. if (!req.getClientShallowCommits().isEmpty())
  948. walk.assumeShallow(req.getClientShallowCommits());
  949. sendPack = negotiate(req, accumulator, pckOut);
  950. accumulator.timeNegotiating = Duration
  951. .between(negotiateStart, Instant.now()).toMillis();
  952. if (sendPack && !biDirectionalPipe) {
  953. // Ensure the request was fully consumed. Any remaining input must
  954. // be a protocol error. If we aren't at EOF the implementation is broken.
  955. int eof = rawIn.read();
  956. if (0 <= eof) {
  957. sendPack = false;
  958. throw new CorruptObjectException(MessageFormat.format(
  959. JGitText.get().expectedEOFReceived,
  960. "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  961. }
  962. }
  963. } finally {
  964. if (!sendPack && !biDirectionalPipe) {
  965. while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  966. // Discard until EOF.
  967. }
  968. }
  969. rawOut.stopBuffering();
  970. }
  971. if (sendPack) {
  972. sendPack(accumulator, req, refs == null ? null : refs.values(),
  973. unshallowCommits, Collections.emptyList(), pckOut);
  974. }
  975. }
  976. private void lsRefsV2(PacketLineOut pckOut) throws IOException {
  977. ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  978. LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
  979. protocolV2Hook.onLsRefs(req);
  980. rawOut.stopBuffering();
  981. PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
  982. adv.setUseProtocolV2(true);
  983. if (req.getPeel()) {
  984. adv.setDerefTags(true);
  985. }
  986. Map<String, Ref> refsToSend = getFilteredRefs(req.getRefPrefixes());
  987. if (req.getSymrefs()) {
  988. findSymrefs(adv, refsToSend);
  989. }
  990. adv.send(refsToSend.values());
  991. adv.end();
  992. }
  993. // Resolves ref names from the request's want-ref lines to
  994. // object ids, throwing PackProtocolException if any are missing.
  995. private Map<String, ObjectId> wantedRefs(FetchV2Request req)
  996. throws IOException {
  997. Map<String, ObjectId> result = new TreeMap<>();
  998. List<String> wanted = req.getWantedRefs();
  999. Map<String, Ref> resolved = exactRefs(wanted);
  1000. for (String refName : wanted) {
  1001. Ref ref = resolved.get(refName);
  1002. if (ref == null) {
  1003. throw new PackProtocolException(MessageFormat
  1004. .format(JGitText.get().invalidRefName, refName));
  1005. }
  1006. ObjectId oid = ref.getObjectId();
  1007. if (oid == null) {
  1008. throw new PackProtocolException(MessageFormat
  1009. .format(JGitText.get().invalidRefName, refName));
  1010. }
  1011. result.put(refName, oid);
  1012. }
  1013. return result;
  1014. }
  1015. private void fetchV2(PacketLineOut pckOut) throws IOException {
  1016. // Depending on the requestValidator, #processHaveLines may
  1017. // require that advertised be set. Set it only in the required
  1018. // circumstances (to avoid a full ref lookup in the case that
  1019. // we don't need it).
  1020. if (requestValidator instanceof TipRequestValidator ||
  1021. requestValidator instanceof ReachableCommitTipRequestValidator ||
  1022. requestValidator instanceof AnyRequestValidator) {
  1023. advertised = Collections.emptySet();
  1024. } else {
  1025. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  1026. }
  1027. PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  1028. Instant negotiateStart = Instant.now();
  1029. ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  1030. FetchV2Request req = parser.parseFetchRequest(pckIn);
  1031. currentRequest = req;
  1032. rawOut.stopBuffering();
  1033. protocolV2Hook.onFetch(req);
  1034. if (req.getSidebandAll()) {
  1035. pckOut.setUsingSideband(true);
  1036. }
  1037. // TODO(ifrade): Refactor to pass around the Request object, instead of
  1038. // copying data back to class fields
  1039. List<ObjectId> deepenNots = new ArrayList<>();
  1040. for (String s : req.getDeepenNotRefs()) {
  1041. Ref ref = findRef(s);
  1042. if (ref == null) {
  1043. throw new PackProtocolException(MessageFormat
  1044. .format(JGitText.get().invalidRefName, s));
  1045. }
  1046. deepenNots.add(ref.getObjectId());
  1047. }
  1048. Map<String, ObjectId> wantedRefs = wantedRefs(req);
  1049. // TODO(ifrade): Avoid mutating the parsed request.
  1050. req.getWantIds().addAll(wantedRefs.values());
  1051. wantIds = req.getWantIds();
  1052. boolean sectionSent = false;
  1053. boolean mayHaveShallow = req.getDepth() != 0
  1054. || req.getDeepenSince() != 0
  1055. || !req.getDeepenNotRefs().isEmpty();
  1056. List<ObjectId> shallowCommits = new ArrayList<>();
  1057. List<ObjectId> unshallowCommits = new ArrayList<>();
  1058. if (!req.getClientShallowCommits().isEmpty()) {
  1059. verifyClientShallow(req.getClientShallowCommits());
  1060. }
  1061. if (mayHaveShallow) {
  1062. computeShallowsAndUnshallows(req,
  1063. shallowCommit -> shallowCommits.add(shallowCommit),
  1064. unshallowCommit -> unshallowCommits.add(unshallowCommit),
  1065. deepenNots);
  1066. }
  1067. if (!req.getClientShallowCommits().isEmpty())
  1068. walk.assumeShallow(req.getClientShallowCommits());
  1069. if (req.wasDoneReceived()) {
  1070. processHaveLines(
  1071. req.getPeerHas(), ObjectId.zeroId(),
  1072. new PacketLineOut(NullOutputStream.INSTANCE, false),
  1073. accumulator, req.wasWaitForDoneReceived() ? Option.WAIT_FOR_DONE : Option.NONE);
  1074. } else {
  1075. pckOut.writeString(
  1076. GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
  1077. for (ObjectId id : req.getPeerHas()) {
  1078. if (walk.getObjectReader().has(id)) {
  1079. pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1080. }
  1081. }
  1082. processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  1083. new PacketLineOut(NullOutputStream.INSTANCE, false),
  1084. accumulator, Option.NONE);
  1085. if (!req.wasWaitForDoneReceived() && okToGiveUp()) {
  1086. pckOut.writeString("ready\n"); //$NON-NLS-1$
  1087. } else if (commonBase.isEmpty()) {
  1088. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1089. }
  1090. sectionSent = true;
  1091. }
  1092. if (req.wasDoneReceived() || (!req.wasWaitForDoneReceived() && okToGiveUp())) {
  1093. if (mayHaveShallow) {
  1094. if (sectionSent)
  1095. pckOut.writeDelim();
  1096. pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
  1097. for (ObjectId o : shallowCommits) {
  1098. pckOut.writeString("shallow " + o.getName() + '\n'); //$NON-NLS-1$
  1099. }
  1100. for (ObjectId o : unshallowCommits) {
  1101. pckOut.writeString("unshallow " + o.getName() + '\n'); //$NON-NLS-1$
  1102. }
  1103. sectionSent = true;
  1104. }
  1105. if (!wantedRefs.isEmpty()) {
  1106. if (sectionSent) {
  1107. pckOut.writeDelim();
  1108. }
  1109. pckOut.writeString("wanted-refs\n"); //$NON-NLS-1$
  1110. for (Map.Entry<String, ObjectId> entry :
  1111. wantedRefs.entrySet()) {
  1112. pckOut.writeString(entry.getValue().getName() + ' ' +
  1113. entry.getKey() + '\n');
  1114. }
  1115. sectionSent = true;
  1116. }
  1117. if (sectionSent)
  1118. pckOut.writeDelim();
  1119. if (!pckOut.isUsingSideband()) {
  1120. // sendPack will write "packfile\n" for us if sideband-all is used.
  1121. // But sideband-all is not used, so we have to write it ourselves.
  1122. pckOut.writeString(
  1123. GitProtocolConstants.SECTION_PACKFILE + '\n');
  1124. }
  1125. accumulator.timeNegotiating = Duration
  1126. .between(negotiateStart, Instant.now()).toMillis();
  1127. sendPack(accumulator,
  1128. req,
  1129. req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  1130. ? db.getRefDatabase().getRefsByPrefix(R_TAGS)
  1131. : null,
  1132. unshallowCommits, deepenNots, pckOut);
  1133. // sendPack invokes pckOut.end() for us, so we do not
  1134. // need to invoke it here.
  1135. } else {
  1136. // Invoke pckOut.end() by ourselves.
  1137. pckOut.end();
  1138. }
  1139. }
  1140. /*
  1141. * Returns true if this is the last command and we should tear down the
  1142. * connection.
  1143. */
  1144. private boolean serveOneCommandV2(PacketLineOut pckOut) throws IOException {
  1145. String command;
  1146. try {
  1147. command = pckIn.readString();
  1148. } catch (EOFException eof) {
  1149. /* EOF when awaiting command is fine */
  1150. return true;
  1151. }
  1152. if (PacketLineIn.isEnd(command)) {
  1153. // A blank request is valid according
  1154. // to the protocol; do nothing in this
  1155. // case.
  1156. return true;
  1157. }
  1158. if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$
  1159. lsRefsV2(pckOut);
  1160. return false;
  1161. }
  1162. if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$
  1163. fetchV2(pckOut);
  1164. return false;
  1165. }
  1166. throw new PackProtocolException(MessageFormat
  1167. .format(JGitText.get().unknownTransportCommand, command));
  1168. }
  1169. @SuppressWarnings("nls")
  1170. private List<String> getV2CapabilityAdvertisement() {
  1171. ArrayList<String> caps = new ArrayList<>();
  1172. caps.add("version 2");
  1173. caps.add(COMMAND_LS_REFS);
  1174. boolean advertiseRefInWant = transferConfig.isAllowRefInWant()
  1175. && db.getConfig().getBoolean("uploadpack", null,
  1176. "advertiserefinwant", true);
  1177. caps.add(COMMAND_FETCH + '='
  1178. + (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "")
  1179. + (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "")
  1180. + (transferConfig.isAdvertiseSidebandAll()
  1181. ? OPTION_SIDEBAND_ALL + ' '
  1182. : "")
  1183. + (cachedPackUriProvider != null ? "packfile-uris " : "")
  1184. + (transferConfig.isAdvertiseWaitForDone()
  1185. ? OPTION_WAIT_FOR_DONE + ' '
  1186. : "")
  1187. + OPTION_SHALLOW);
  1188. caps.add(CAPABILITY_SERVER_OPTION);
  1189. return caps;
  1190. }
  1191. private void serviceV2(PacketLineOut pckOut) throws IOException {
  1192. if (biDirectionalPipe) {
  1193. // Just like in service(), the capability advertisement
  1194. // is sent only if this is a bidirectional pipe. (If
  1195. // not, the client is expected to call
  1196. // sendAdvertisedRefs() on its own.)
  1197. protocolV2Hook
  1198. .onCapabilities(CapabilitiesV2Request.builder().build());
  1199. for (String s : getV2CapabilityAdvertisement()) {
  1200. pckOut.writeString(s + "\n"); //$NON-NLS-1$
  1201. }
  1202. pckOut.end();
  1203. while (!serveOneCommandV2(pckOut)) {
  1204. // Repeat until an empty command or EOF.
  1205. }
  1206. return;
  1207. }
  1208. try {
  1209. serveOneCommandV2(pckOut);
  1210. } finally {
  1211. while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  1212. // Discard until EOF.
  1213. }
  1214. rawOut.stopBuffering();
  1215. }
  1216. }
  1217. private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  1218. Set<ObjectId> ids = new HashSet<>(refs.size());
  1219. for (Ref ref : refs) {
  1220. ObjectId id = ref.getObjectId();
  1221. if (id != null) {
  1222. ids.add(id);
  1223. }
  1224. id = ref.getPeeledObjectId();
  1225. if (id != null) {
  1226. ids.add(id);
  1227. }
  1228. }
  1229. return ids;
  1230. }
  1231. /*
  1232. * Determines what object ids must be marked as shallow or unshallow for the
  1233. * client.
  1234. */
  1235. private void computeShallowsAndUnshallows(FetchRequest req,
  1236. IOConsumer<ObjectId> shallowFunc,
  1237. IOConsumer<ObjectId> unshallowFunc,
  1238. List<ObjectId> deepenNots)
  1239. throws IOException {
  1240. if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) {
  1241. // TODO(jonathantanmy): Implement deepen-relative
  1242. throw new UnsupportedOperationException();
  1243. }
  1244. int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  1245. : req.getDepth() - 1;
  1246. try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
  1247. walk.getObjectReader(), walkDepth)) {
  1248. depthWalk.setDeepenSince(req.getDeepenSince());
  1249. // Find all the commits which will be shallow
  1250. for (ObjectId o : req.getWantIds()) {
  1251. try {
  1252. depthWalk.markRoot(depthWalk.parseCommit(o));
  1253. } catch (IncorrectObjectTypeException notCommit) {
  1254. // Ignore non-commits in this loop.
  1255. }
  1256. }
  1257. depthWalk.setDeepenNots(deepenNots);
  1258. RevCommit o;
  1259. boolean atLeastOne = false;
  1260. while ((o = depthWalk.next()) != null) {
  1261. DepthWalk.Commit c = (DepthWalk.Commit) o;
  1262. atLeastOne = true;
  1263. boolean isBoundary = (c.getDepth() == walkDepth) || c.isBoundary();
  1264. // Commits at the boundary which aren't already shallow in
  1265. // the client need to be marked as such
  1266. if (isBoundary && !req.getClientShallowCommits().contains(c)) {
  1267. shallowFunc.accept(c.copy());
  1268. }
  1269. // Commits not on the boundary which are shallow in the client
  1270. // need to become unshallowed
  1271. if (!isBoundary && req.getClientShallowCommits().remove(c)) {
  1272. unshallowFunc.accept(c.copy());
  1273. }
  1274. }
  1275. if (!atLeastOne) {
  1276. throw new PackProtocolException(
  1277. JGitText.get().noCommitsSelectedForShallow);
  1278. }
  1279. }
  1280. }
  1281. /*
  1282. * Verify all shallow lines refer to commits
  1283. *
  1284. * It can mutate the input set (removing missing object ids from it)
  1285. */
  1286. private void verifyClientShallow(Set<ObjectId> shallowCommits)
  1287. throws IOException, PackProtocolException {
  1288. AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
  1289. try {
  1290. for (;;) {
  1291. try {
  1292. // Shallow objects named by the client must be commits.
  1293. RevObject o = q.next();
  1294. if (o == null) {
  1295. break;
  1296. }
  1297. if (!(o instanceof RevCommit)) {
  1298. throw new PackProtocolException(
  1299. MessageFormat.format(
  1300. JGitText.get().invalidShallowObject,
  1301. o.name()));
  1302. }
  1303. } catch (MissingObjectException notCommit) {
  1304. // shallow objects not known at the server are ignored
  1305. // by git-core upload-pack, match that behavior.
  1306. shallowCommits.remove(notCommit.getObjectId());
  1307. continue;
  1308. }
  1309. }
  1310. } finally {
  1311. q.release();
  1312. }
  1313. }
  1314. /**
  1315. * Generate an advertisement of available refs and capabilities.
  1316. *
  1317. * @param adv
  1318. * the advertisement formatter.
  1319. * @throws java.io.IOException
  1320. * the formatter failed to write an advertisement.
  1321. * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1322. * the hook denied advertisement.
  1323. */
  1324. public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
  1325. ServiceMayNotContinueException {
  1326. sendAdvertisedRefs(adv, null);
  1327. }
  1328. /**
  1329. * Generate an advertisement of available refs and capabilities.
  1330. *
  1331. * @param adv
  1332. * the advertisement formatter.
  1333. * @param serviceName
  1334. * if not null, also output "# service=serviceName" followed by a
  1335. * flush packet before the advertisement. This is required
  1336. * in v0 of the HTTP protocol, described in Git's
  1337. * Documentation/technical/http-protocol.txt.
  1338. * @throws java.io.IOException
  1339. * the formatter failed to write an advertisement.
  1340. * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1341. * the hook denied advertisement.
  1342. * @since 5.0
  1343. */
  1344. public void sendAdvertisedRefs(RefAdvertiser adv,
  1345. @Nullable String serviceName) throws IOException,
  1346. ServiceMayNotContinueException {
  1347. if (useProtocolV2()) {
  1348. // The equivalent in v2 is only the capabilities
  1349. // advertisement.
  1350. protocolV2Hook
  1351. .onCapabilities(CapabilitiesV2Request.builder().build());
  1352. for (String s : getV2CapabilityAdvertisement()) {
  1353. adv.writeOne(s);
  1354. }
  1355. adv.end();
  1356. return;
  1357. }
  1358. Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();
  1359. if (serviceName != null) {
  1360. adv.writeOne("# service=" + serviceName + '\n'); //$NON-NLS-1$
  1361. adv.end();
  1362. }
  1363. adv.init(db);
  1364. adv.advertiseCapability(OPTION_INCLUDE_TAG);
  1365. adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  1366. adv.advertiseCapability(OPTION_MULTI_ACK);
  1367. adv.advertiseCapability(OPTION_OFS_DELTA);
  1368. adv.advertiseCapability(OPTION_SIDE_BAND);
  1369. adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  1370. adv.advertiseCapability(OPTION_THIN_PACK);
  1371. adv.advertiseCapability(OPTION_NO_PROGRESS);
  1372. adv.advertiseCapability(OPTION_SHALLOW);
  1373. if (!biDirectionalPipe)
  1374. adv.advertiseCapability(OPTION_NO_DONE);
  1375. RequestPolicy policy = getRequestPolicy();
  1376. if (policy == RequestPolicy.TIP
  1377. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1378. || policy == null)
  1379. adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  1380. if (policy == RequestPolicy.REACHABLE_COMMIT
  1381. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1382. || policy == null)
  1383. adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
  1384. adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
  1385. if (transferConfig.isAllowFilter()) {
  1386. adv.advertiseCapability(OPTION_FILTER);
  1387. }
  1388. adv.setDerefTags(true);
  1389. findSymrefs(adv, advertisedOrDefaultRefs);
  1390. advertised = adv.send(advertisedOrDefaultRefs.values());
  1391. if (adv.isEmpty())
  1392. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  1393. adv.end();
  1394. }
  1395. /**
  1396. * Send a message to the client, if it supports receiving them.
  1397. * <p>
  1398. * If the client doesn't support receiving messages, the message will be
  1399. * discarded, with no other indication to the caller or to the client.
  1400. *
  1401. * @param what
  1402. * string describing the problem identified by the hook. The
  1403. * string must not end with an LF, and must not contain an LF.
  1404. * @since 3.1
  1405. */
  1406. public void sendMessage(String what) {
  1407. try {
  1408. msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  1409. } catch (IOException e) {
  1410. // Ignore write failures.
  1411. }
  1412. }
  1413. /**
  1414. * Get an underlying stream for sending messages to the client
  1415. *
  1416. * @return an underlying stream for sending messages to the client, or null.
  1417. * @since 3.1
  1418. */
  1419. public OutputStream getMessageOutputStream() {
  1420. return msgOut;
  1421. }
  1422. /**
  1423. * Returns the clone/fetch depth. Valid only after calling recvWants(). A
  1424. * depth of 1 means return only the wants.
  1425. *
  1426. * @return the depth requested by the client, or 0 if unbounded.
  1427. * @since 4.0
  1428. */
  1429. public int getDepth() {
  1430. if (currentRequest == null)
  1431. throw new RequestNotYetReadException();
  1432. return currentRequest.getDepth();
  1433. }
  1434. /**
  1435. * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
  1436. *
  1437. * @return filter blob limit requested by the client, or -1 if no limit
  1438. * @since 5.3
  1439. * @deprecated Use {@link #getFilterSpec()} instead
  1440. */
  1441. @Deprecated
  1442. public final long getFilterBlobLimit() {
  1443. return getFilterSpec().getBlobLimit();
  1444. }
  1445. /**
  1446. * Returns the filter spec for the current request. Valid only after
  1447. * calling recvWants(). This may be a no-op filter spec, but it won't be
  1448. * null.
  1449. *
  1450. * @return filter requested by the client
  1451. * @since 5.4
  1452. */
  1453. public final FilterSpec getFilterSpec() {
  1454. if (currentRequest == null) {
  1455. throw new RequestNotYetReadException();
  1456. }
  1457. return currentRequest.getFilterSpec();
  1458. }
  1459. /**
  1460. * Get the user agent of the client.
  1461. * <p>
  1462. * If the client is new enough to use {@code agent=} capability that value
  1463. * will be returned. Older HTTP clients may also supply their version using
  1464. * the HTTP {@code User-Agent} header. The capability overrides the HTTP
  1465. * header if both are available.
  1466. * <p>
  1467. * When an HTTP request has been received this method returns the HTTP
  1468. * {@code User-Agent} header value until capabilities have been parsed.
  1469. *
  1470. * @return user agent supplied by the client. Available only if the client
  1471. * is new enough to advertise its user agent.
  1472. * @since 4.0
  1473. */
  1474. public String getPeerUserAgent() {
  1475. if (currentRequest != null && currentRequest.getAgent() != null) {
  1476. return currentRequest.getAgent();
  1477. }
  1478. return userAgent;
  1479. }
  1480. private boolean negotiate(FetchRequest req,
  1481. PackStatistics.Accumulator accumulator,
  1482. PacketLineOut pckOut)
  1483. throws IOException {
  1484. okToGiveUp = Boolean.FALSE;
  1485. ObjectId last = ObjectId.zeroId();
  1486. List<ObjectId> peerHas = new ArrayList<>(64);
  1487. for (;;) {
  1488. String line;
  1489. try {
  1490. line = pckIn.readString();
  1491. } catch (EOFException eof) {
  1492. // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  1493. // means the client asked for the updated shallow/unshallow data,
  1494. // disconnected, and will try another request with actual want/have.
  1495. // Don't report the EOF here, its a bug in the protocol that the client
  1496. // just disconnects without sending an END.
  1497. if (!biDirectionalPipe && req.getDepth() > 0)
  1498. return false;
  1499. throw eof;
  1500. }
  1501. if (PacketLineIn.isEnd(line)) {
  1502. last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
  1503. if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  1504. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1505. if (noDone && sentReady) {
  1506. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1507. return true;
  1508. }
  1509. if (!biDirectionalPipe)
  1510. return false;
  1511. pckOut.flush();
  1512. } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  1513. peerHas.add(ObjectId.fromString(line.substring(5)));
  1514. accumulator.haves++;
  1515. } else if (line.equals("done")) { //$NON-NLS-1$
  1516. last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
  1517. if (commonBase.isEmpty())
  1518. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1519. else if (multiAck != MultiAck.OFF)
  1520. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1521. return true;
  1522. } else {
  1523. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  1524. }
  1525. }
  1526. }
  1527. private enum Option {
  1528. WAIT_FOR_DONE,
  1529. NONE;
  1530. }
  1531. private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
  1532. PacketLineOut out, PackStatistics.Accumulator accumulator,
  1533. Option option)
  1534. throws IOException {
  1535. preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  1536. if (wantAll.isEmpty() && !wantIds.isEmpty())
  1537. parseWants(accumulator);
  1538. if (peerHas.isEmpty())
  1539. return last;
  1540. sentReady = false;
  1541. int haveCnt = 0;
  1542. walk.getObjectReader().setAvoidUnreachableObjects(true);
  1543. AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  1544. try {
  1545. for (;;) {
  1546. RevObject obj;
  1547. try {
  1548. obj = q.next();
  1549. } catch (MissingObjectException notFound) {
  1550. continue;
  1551. }
  1552. if (obj == null)
  1553. break;
  1554. last = obj;
  1555. haveCnt++;
  1556. if (obj instanceof RevCommit) {
  1557. RevCommit c = (RevCommit) obj;
  1558. if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  1559. oldestTime = c.getCommitTime();
  1560. }
  1561. if (obj.has(PEER_HAS))
  1562. continue;
  1563. obj.add(PEER_HAS);
  1564. if (obj instanceof RevCommit)
  1565. ((RevCommit) obj).carry(PEER_HAS);
  1566. addCommonBase(obj);
  1567. // If both sides have the same object; let the client know.
  1568. //
  1569. switch (multiAck) {
  1570. case OFF:
  1571. if (commonBase.size() == 1)
  1572. out.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1573. break;
  1574. case CONTINUE:
  1575. out.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1576. break;
  1577. case DETAILED:
  1578. out.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1579. break;
  1580. }
  1581. }
  1582. } finally {
  1583. q.release();
  1584. walk.getObjectReader().setAvoidUnreachableObjects(false);
  1585. }
  1586. int missCnt = peerHas.size() - haveCnt;
  1587. // If we don't have one of the objects but we're also willing to
  1588. // create a pack at this point, let the client know so it stops
  1589. // telling us about its history.
  1590. //
  1591. if (option != Option.WAIT_FOR_DONE) {
  1592. sentReady = shouldGiveUp(peerHas, out, missCnt);
  1593. }
  1594. preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  1595. peerHas.clear();
  1596. return last;
  1597. }
  1598. private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int missCnt)
  1599. throws IOException {
  1600. boolean sentReady = false;
  1601. boolean didOkToGiveUp = false;
  1602. if (0 < missCnt) {
  1603. for (int i = peerHas.size() - 1; i >= 0; i--) {
  1604. ObjectId id = peerHas.get(i);
  1605. if (walk.lookupOrNull(id) == null) {
  1606. didOkToGiveUp = true;
  1607. if (okToGiveUp()) {
  1608. switch (multiAck) {
  1609. case OFF:
  1610. break;
  1611. case CONTINUE:
  1612. out.writeString(
  1613. "ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1614. break;
  1615. case DETAILED:
  1616. out.writeString(
  1617. "ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1618. sentReady = true;
  1619. break;
  1620. }
  1621. }
  1622. break;
  1623. }
  1624. }
  1625. }
  1626. if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
  1627. && okToGiveUp()) {
  1628. ObjectId id = peerHas.get(peerHas.size() - 1);
  1629. out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1630. sentReady = true;
  1631. }
  1632. return sentReady;
  1633. }
  1634. private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {
  1635. List<ObjectId> notAdvertisedWants = null;
  1636. for (ObjectId obj : wantIds) {
  1637. if (!advertised.contains(obj)) {
  1638. if (notAdvertisedWants == null)
  1639. notAdvertisedWants = new ArrayList<>();
  1640. notAdvertisedWants.add(obj);
  1641. }
  1642. }
  1643. if (notAdvertisedWants != null) {
  1644. accumulator.notAdvertisedWants = notAdvertisedWants.size();
  1645. Instant startReachabilityChecking = Instant.now();
  1646. requestValidator.checkWants(this, notAdvertisedWants);
  1647. accumulator.reachabilityCheckDuration = Duration
  1648. .between(startReachabilityChecking, Instant.now())
  1649. .toMillis();
  1650. }
  1651. AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  1652. try {
  1653. RevObject obj;
  1654. while ((obj = q.next()) != null) {
  1655. want(obj);
  1656. if (!(obj instanceof RevCommit))
  1657. obj.add(SATISFIED);
  1658. if (obj instanceof RevTag) {
  1659. obj = walk.peel(obj);
  1660. if (obj instanceof RevCommit)
  1661. want(obj);
  1662. }
  1663. }
  1664. wantIds.clear();
  1665. } catch (MissingObjectException notFound) {
  1666. throw new WantNotValidException(notFound.getObjectId(), notFound);
  1667. } finally {
  1668. q.release();
  1669. }
  1670. }
  1671. private void want(RevObject obj) {
  1672. if (!obj.has(WANT)) {
  1673. obj.add(WANT);
  1674. wantAll.add(obj);
  1675. }
  1676. }
  1677. /**
  1678. * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  1679. *
  1680. * @since 3.1
  1681. */
  1682. public static final class AdvertisedRequestValidator
  1683. implements RequestValidator {
  1684. @Override
  1685. public void checkWants(UploadPack up, List<ObjectId> wants)
  1686. throws PackProtocolException, IOException {
  1687. if (!up.isBiDirectionalPipe())
  1688. new ReachableCommitRequestValidator().checkWants(up, wants);
  1689. else if (!wants.isEmpty())
  1690. throw new WantNotValidException(wants.iterator().next());
  1691. }
  1692. }
  1693. /**
  1694. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  1695. *
  1696. * @since 3.1
  1697. */
  1698. public static final class ReachableCommitRequestValidator
  1699. implements RequestValidator {
  1700. @Override
  1701. public void checkWants(UploadPack up, List<ObjectId> wants)
  1702. throws PackProtocolException, IOException {
  1703. checkNotAdvertisedWants(up, wants, up.getAdvertisedRefs().values());
  1704. }
  1705. }
  1706. /**
  1707. * Validator corresponding to {@link RequestPolicy#TIP}.
  1708. *
  1709. * @since 3.1
  1710. */
  1711. public static final class TipRequestValidator implements RequestValidator {
  1712. @Override
  1713. public void checkWants(UploadPack up, List<ObjectId> wants)
  1714. throws PackProtocolException, IOException {
  1715. if (!up.isBiDirectionalPipe())
  1716. new ReachableCommitTipRequestValidator().checkWants(up, wants);
  1717. else if (!wants.isEmpty()) {
  1718. Set<ObjectId> refIds =
  1719. refIdSet(up.getRepository().getRefDatabase().getRefs());
  1720. for (ObjectId obj : wants) {
  1721. if (!refIds.contains(obj))
  1722. throw new WantNotValidException(obj);
  1723. }
  1724. }
  1725. }
  1726. }
  1727. /**
  1728. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1729. *
  1730. * @since 3.1
  1731. */
  1732. public static final class ReachableCommitTipRequestValidator
  1733. implements RequestValidator {
  1734. @Override
  1735. public void checkWants(UploadPack up, List<ObjectId> wants)
  1736. throws PackProtocolException, IOException {
  1737. checkNotAdvertisedWants(up, wants,
  1738. up.getRepository().getRefDatabase().getRefs());
  1739. }
  1740. }
  1741. /**
  1742. * Validator corresponding to {@link RequestPolicy#ANY}.
  1743. *
  1744. * @since 3.1
  1745. */
  1746. public static final class AnyRequestValidator implements RequestValidator {
  1747. @Override
  1748. public void checkWants(UploadPack up, List<ObjectId> wants)
  1749. throws PackProtocolException, IOException {
  1750. // All requests are valid.
  1751. }
  1752. }
  1753. private static void checkNotAdvertisedWants(UploadPack up,
  1754. List<ObjectId> notAdvertisedWants, Collection<Ref> visibleRefs)
  1755. throws IOException {
  1756. ObjectReader reader = up.getRevWalk().getObjectReader();
  1757. try (RevWalk walk = new RevWalk(reader)) {
  1758. walk.setRetainBody(false);
  1759. // Missing "wants" throw exception here
  1760. List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
  1761. notAdvertisedWants);
  1762. List<RevCommit> wantsAsCommits = wantsAsObjs.stream()
  1763. .filter(obj -> obj instanceof RevCommit)
  1764. .map(obj -> (RevCommit) obj)
  1765. .collect(Collectors.toList());
  1766. boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits
  1767. .size();
  1768. boolean repoHasBitmaps = reader.getBitmapIndex() != null;
  1769. if (!allWantsAreCommits) {
  1770. if (!repoHasBitmaps && !up.transferConfig.isAllowFilter()) {
  1771. // Checking unadvertised non-commits without bitmaps
  1772. // requires an expensive manual walk. Use allowFilter as an
  1773. // indication that the server operator is willing to pay
  1774. // this cost. Reject the request otherwise.
  1775. RevObject nonCommit = wantsAsObjs
  1776. .stream()
  1777. .filter(obj -> !(obj instanceof RevCommit))
  1778. .limit(1)
  1779. .collect(Collectors.toList()).get(0);
  1780. throw new WantNotValidException(nonCommit);
  1781. }
  1782. try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
  1783. Stream<RevObject> startersAsObjs = importantRefsFirst(visibleRefs)
  1784. .map(UploadPack::refToObjectId)
  1785. .map(objId -> objectIdToRevObject(objWalk, objId))
  1786. .filter(Objects::nonNull); // Ignore missing tips
  1787. ObjectReachabilityChecker reachabilityChecker = reader
  1788. .createObjectReachabilityChecker(objWalk);
  1789. Optional<RevObject> unreachable = reachabilityChecker
  1790. .areAllReachable(wantsAsObjs, startersAsObjs);
  1791. if (unreachable.isPresent()) {
  1792. throw new WantNotValidException(unreachable.get());
  1793. }
  1794. }
  1795. return;
  1796. }
  1797. // All wants are commits, we can use ReachabilityChecker
  1798. ReachabilityChecker reachabilityChecker = reader
  1799. .createReachabilityChecker(walk);
  1800. Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
  1801. .map(UploadPack::refToObjectId)
  1802. .map(objId -> objectIdToRevCommit(walk, objId))
  1803. .filter(Objects::nonNull); // Ignore missing tips
  1804. Optional<RevCommit> unreachable = reachabilityChecker
  1805. .areAllReachable(wantsAsCommits, reachableCommits);
  1806. if (unreachable.isPresent()) {
  1807. throw new WantNotValidException(unreachable.get());
  1808. }
  1809. } catch (MissingObjectException notFound) {
  1810. throw new WantNotValidException(notFound.getObjectId(), notFound);
  1811. }
  1812. }
  1813. static Stream<Ref> importantRefsFirst(
  1814. Collection<Ref> visibleRefs) {
  1815. Predicate<Ref> startsWithRefsHeads = ref -> ref.getName()
  1816. .startsWith(Constants.R_HEADS);
  1817. Predicate<Ref> startsWithRefsTags = ref -> ref.getName()
  1818. .startsWith(Constants.R_TAGS);
  1819. Predicate<Ref> allOther = ref -> !startsWithRefsHeads.test(ref)
  1820. && !startsWithRefsTags.test(ref);
  1821. return Stream.concat(
  1822. visibleRefs.stream().filter(startsWithRefsHeads),
  1823. Stream.concat(
  1824. visibleRefs.stream().filter(startsWithRefsTags),
  1825. visibleRefs.stream().filter(allOther)));
  1826. }
  1827. private static ObjectId refToObjectId(Ref ref) {
  1828. return ref.getObjectId() != null ? ref.getObjectId()
  1829. : ref.getPeeledObjectId();
  1830. }
  1831. /**
  1832. * Translate an object id to a RevCommit.
  1833. *
  1834. * @param walk
  1835. * walk on the relevant object storae
  1836. * @param objectId
  1837. * Object Id
  1838. * @return RevCommit instance or null if the object is missing
  1839. */
  1840. @Nullable
  1841. private static RevCommit objectIdToRevCommit(RevWalk walk,
  1842. ObjectId objectId) {
  1843. if (objectId == null) {
  1844. return null;
  1845. }
  1846. try {
  1847. return walk.parseCommit(objectId);
  1848. } catch (IOException e) {
  1849. return null;
  1850. }
  1851. }
  1852. /**
  1853. * Translate an object id to a RevObject.
  1854. *
  1855. * @param walk
  1856. * walk on the relevant object storage
  1857. * @param objectId
  1858. * Object Id
  1859. * @return RevObject instance or null if the object is missing
  1860. */
  1861. @Nullable
  1862. private static RevObject objectIdToRevObject(RevWalk walk,
  1863. ObjectId objectId) {
  1864. if (objectId == null) {
  1865. return null;
  1866. }
  1867. try {
  1868. return walk.parseAny(objectId);
  1869. } catch (IOException e) {
  1870. return null;
  1871. }
  1872. }
  1873. // Resolve the ObjectIds into RevObjects. Any missing object raises an
  1874. // exception
  1875. private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
  1876. Iterable<ObjectId> objectIds)
  1877. throws MissingObjectException, IOException {
  1878. List<RevObject> result = new ArrayList<>();
  1879. for (ObjectId objectId : objectIds) {
  1880. result.add(walk.parseAny(objectId));
  1881. }
  1882. return result;
  1883. }
  1884. private void addCommonBase(RevObject o) {
  1885. if (!o.has(COMMON)) {
  1886. o.add(COMMON);
  1887. commonBase.add(o);
  1888. okToGiveUp = null;
  1889. }
  1890. }
  1891. private boolean okToGiveUp() throws PackProtocolException {
  1892. if (okToGiveUp == null)
  1893. okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1894. return okToGiveUp.booleanValue();
  1895. }
  1896. private boolean okToGiveUpImp() throws PackProtocolException {
  1897. if (commonBase.isEmpty())
  1898. return false;
  1899. try {
  1900. for (RevObject obj : wantAll) {
  1901. if (!wantSatisfied(obj))
  1902. return false;
  1903. }
  1904. return true;
  1905. } catch (IOException e) {
  1906. throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1907. }
  1908. }
  1909. private boolean wantSatisfied(RevObject want) throws IOException {
  1910. if (want.has(SATISFIED))
  1911. return true;
  1912. walk.resetRetain(SAVE);
  1913. walk.markStart((RevCommit) want);
  1914. if (oldestTime != 0)
  1915. walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1916. for (;;) {
  1917. final RevCommit c = walk.next();
  1918. if (c == null)
  1919. break;
  1920. if (c.has(PEER_HAS)) {
  1921. addCommonBase(c);
  1922. want.add(SATISFIED);
  1923. return true;
  1924. }
  1925. }
  1926. return false;
  1927. }
  1928. /**
  1929. * Send the requested objects to the client.
  1930. *
  1931. * @param accumulator
  1932. * where to write statistics about the content of the pack.
  1933. * @param req
  1934. * request in process
  1935. * @param allTags
  1936. * refs to search for annotated tags to include in the pack if
  1937. * the {@link #OPTION_INCLUDE_TAG} capability was requested.
  1938. * @param unshallowCommits
  1939. * shallow commits on the client that are now becoming unshallow
  1940. * @param deepenNots
  1941. * objects that the client specified using --shallow-exclude
  1942. * @param pckOut
  1943. * output writer
  1944. * @throws IOException
  1945. * if an error occurred while generating or writing the pack.
  1946. */
  1947. private void sendPack(PackStatistics.Accumulator accumulator,
  1948. FetchRequest req,
  1949. @Nullable Collection<Ref> allTags,
  1950. List<ObjectId> unshallowCommits,
  1951. List<ObjectId> deepenNots,
  1952. PacketLineOut pckOut) throws IOException {
  1953. Set<String> caps = req.getClientCapabilities();
  1954. boolean sideband = caps.contains(OPTION_SIDE_BAND)
  1955. || caps.contains(OPTION_SIDE_BAND_64K);
  1956. if (sideband) {
  1957. errOut = new SideBandErrorWriter();
  1958. int bufsz = SideBandOutputStream.SMALL_BUF;
  1959. if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) {
  1960. bufsz = SideBandOutputStream.MAX_BUF;
  1961. }
  1962. OutputStream packOut = new SideBandOutputStream(
  1963. SideBandOutputStream.CH_DATA, bufsz, rawOut);
  1964. ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1965. if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
  1966. msgOut = new SideBandOutputStream(
  1967. SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1968. pm = new SideBandProgressMonitor(msgOut);
  1969. }
  1970. sendPack(pm, pckOut, packOut, req, accumulator, allTags,
  1971. unshallowCommits, deepenNots);
  1972. pckOut.end();
  1973. } else {
  1974. sendPack(NullProgressMonitor.INSTANCE, pckOut, rawOut, req,
  1975. accumulator, allTags, unshallowCommits, deepenNots);
  1976. }
  1977. }
  1978. /**
  1979. * Send the requested objects to the client.
  1980. *
  1981. * @param pm
  1982. * progress monitor
  1983. * @param pckOut
  1984. * PacketLineOut that shares the output with packOut
  1985. * @param packOut
  1986. * packfile output
  1987. * @param req
  1988. * request being processed
  1989. * @param accumulator
  1990. * where to write statistics about the content of the pack.
  1991. * @param allTags
  1992. * refs to search for annotated tags to include in the pack if
  1993. * the {@link #OPTION_INCLUDE_TAG} capability was requested.
  1994. * @param unshallowCommits
  1995. * shallow commits on the client that are now becoming unshallow
  1996. * @param deepenNots
  1997. * objects that the client specified using --shallow-exclude
  1998. * @throws IOException
  1999. * if an error occurred while generating or writing the pack.
  2000. */
  2001. private void sendPack(ProgressMonitor pm, PacketLineOut pckOut,
  2002. OutputStream packOut, FetchRequest req,
  2003. PackStatistics.Accumulator accumulator,
  2004. @Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits,
  2005. List<ObjectId> deepenNots) throws IOException {
  2006. if (wantAll.isEmpty()) {
  2007. preUploadHook.onSendPack(this, wantIds, commonBase);
  2008. } else {
  2009. preUploadHook.onSendPack(this, wantAll, commonBase);
  2010. }
  2011. msgOut.flush();
  2012. // Advertised objects and refs are not used from here on and can be
  2013. // cleared.
  2014. advertised = null;
  2015. refs = null;
  2016. PackConfig cfg = packConfig;
  2017. if (cfg == null)
  2018. cfg = new PackConfig(db);
  2019. @SuppressWarnings("resource") // PackWriter is referenced in the finally
  2020. // block, and is closed there
  2021. final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
  2022. accumulator);
  2023. try {
  2024. pw.setIndexDisabled(true);
  2025. if (req.getFilterSpec().isNoOp()) {
  2026. pw.setUseCachedPacks(true);
  2027. } else {
  2028. pw.setFilterSpec(req.getFilterSpec());
  2029. pw.setUseCachedPacks(false);
  2030. }
  2031. pw.setUseBitmaps(
  2032. req.getDepth() == 0
  2033. && req.getClientShallowCommits().isEmpty()
  2034. && req.getFilterSpec().getTreeDepthLimit() == -1);
  2035. pw.setClientShallowCommits(req.getClientShallowCommits());
  2036. pw.setReuseDeltaCommits(true);
  2037. pw.setDeltaBaseAsOffset(
  2038. req.getClientCapabilities().contains(OPTION_OFS_DELTA));
  2039. pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK));
  2040. pw.setReuseValidatingObjects(false);
  2041. // Objects named directly by references go at the beginning
  2042. // of the pack.
  2043. if (commonBase.isEmpty() && refs != null) {
  2044. Set<ObjectId> tagTargets = new HashSet<>();
  2045. for (Ref ref : refs.values()) {
  2046. if (ref.getPeeledObjectId() != null)
  2047. tagTargets.add(ref.getPeeledObjectId());
  2048. else if (ref.getObjectId() == null)
  2049. continue;
  2050. else if (ref.getName().startsWith(Constants.R_HEADS))
  2051. tagTargets.add(ref.getObjectId());
  2052. }
  2053. pw.setTagTargets(tagTargets);
  2054. }
  2055. RevWalk rw = walk;
  2056. if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) {
  2057. int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  2058. : req.getDepth() - 1;
  2059. pw.setShallowPack(req.getDepth(), unshallowCommits);
  2060. @SuppressWarnings("resource") // Ownership is transferred below
  2061. DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
  2062. walk.getObjectReader(), walkDepth);
  2063. dw.setDeepenSince(req.getDeepenSince());
  2064. dw.setDeepenNots(deepenNots);
  2065. dw.assumeShallow(req.getClientShallowCommits());
  2066. rw = dw;
  2067. }
  2068. if (wantAll.isEmpty()) {
  2069. pw.preparePack(pm, wantIds, commonBase,
  2070. req.getClientShallowCommits());
  2071. } else {
  2072. walk.reset();
  2073. ObjectWalk ow = rw.toObjectWalkWithSameObjects();
  2074. pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
  2075. rw = ow;
  2076. }
  2077. if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  2078. && allTags != null) {
  2079. for (Ref ref : allTags) {
  2080. ObjectId objectId = ref.getObjectId();
  2081. if (objectId == null) {
  2082. // skip unborn branch
  2083. continue;
  2084. }
  2085. // If the object was already requested, skip it.
  2086. if (wantAll.isEmpty()) {
  2087. if (wantIds.contains(objectId))
  2088. continue;
  2089. } else {
  2090. RevObject obj = rw.lookupOrNull(objectId);
  2091. if (obj != null && obj.has(WANT))
  2092. continue;
  2093. }
  2094. if (!ref.isPeeled())
  2095. ref = db.getRefDatabase().peel(ref);
  2096. ObjectId peeledId = ref.getPeeledObjectId();
  2097. objectId = ref.getObjectId();
  2098. if (peeledId == null || objectId == null)
  2099. continue;
  2100. objectId = ref.getObjectId();
  2101. if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
  2102. RevObject o = rw.parseAny(objectId);
  2103. addTagChain(o, pw);
  2104. pw.addObject(o);
  2105. }
  2106. }
  2107. }
  2108. if (pckOut.isUsingSideband()) {
  2109. if (req instanceof FetchV2Request &&
  2110. cachedPackUriProvider != null &&
  2111. !((FetchV2Request) req).getPackfileUriProtocols().isEmpty()) {
  2112. FetchV2Request reqV2 = (FetchV2Request) req;
  2113. pw.setPackfileUriConfig(new PackWriter.PackfileUriConfig(
  2114. pckOut,
  2115. reqV2.getPackfileUriProtocols(),
  2116. cachedPackUriProvider));
  2117. } else {
  2118. // PackWriter will write "packfile-uris\n" and "packfile\n"
  2119. // for us if provided a PackfileUriConfig. In this case, we
  2120. // are not providing a PackfileUriConfig, so we have to
  2121. // write this line ourselves.
  2122. pckOut.writeString(
  2123. GitProtocolConstants.SECTION_PACKFILE + '\n');
  2124. }
  2125. }
  2126. pw.enableSearchForReuseTimeout();
  2127. pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
  2128. if (msgOut != NullOutputStream.INSTANCE) {
  2129. String msg = pw.getStatistics().getMessage() + '\n';
  2130. msgOut.write(Constants.encode(msg));
  2131. msgOut.flush();
  2132. }
  2133. } finally {
  2134. statistics = pw.getStatistics();
  2135. if (statistics != null) {
  2136. postUploadHook.onPostUpload(statistics);
  2137. }
  2138. pw.close();
  2139. }
  2140. }
  2141. private static void findSymrefs(
  2142. final RefAdvertiser adv, final Map<String, Ref> refs) {
  2143. Ref head = refs.get(Constants.HEAD);
  2144. if (head != null && head.isSymbolic()) {
  2145. adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  2146. }
  2147. }
  2148. private void addTagChain(
  2149. RevObject o, PackWriter pw) throws IOException {
  2150. while (Constants.OBJ_TAG == o.getType()) {
  2151. RevTag t = (RevTag) o;
  2152. o = t.getObject();
  2153. if (o.getType() == Constants.OBJ_TAG && !pw.willInclude(o.getId())) {
  2154. walk.parseBody(o);
  2155. pw.addObject(o);
  2156. }
  2157. }
  2158. }
  2159. private static class ResponseBufferedOutputStream extends OutputStream {
  2160. private final OutputStream rawOut;
  2161. private OutputStream out;
  2162. ResponseBufferedOutputStream(OutputStream rawOut) {
  2163. this.rawOut = rawOut;
  2164. this.out = new ByteArrayOutputStream();
  2165. }
  2166. @Override
  2167. public void write(int b) throws IOException {
  2168. out.write(b);
  2169. }
  2170. @Override
  2171. public void write(byte[] b) throws IOException {
  2172. out.write(b);
  2173. }
  2174. @Override
  2175. public void write(byte[] b, int off, int len) throws IOException {
  2176. out.write(b, off, len);
  2177. }
  2178. @Override
  2179. public void flush() throws IOException {
  2180. out.flush();
  2181. }
  2182. @Override
  2183. public void close() throws IOException {
  2184. out.close();
  2185. }
  2186. void stopBuffering() throws IOException {
  2187. if (out != rawOut) {
  2188. ((ByteArrayOutputStream) out).writeTo(rawOut);
  2189. out = rawOut;
  2190. }
  2191. }
  2192. }
  2193. private interface ErrorWriter {
  2194. void writeError(String message) throws IOException;
  2195. }
  2196. private class SideBandErrorWriter implements ErrorWriter {
  2197. @Override
  2198. public void writeError(String message) throws IOException {
  2199. @SuppressWarnings("resource" /* java 7 */)
  2200. SideBandOutputStream err = new SideBandOutputStream(
  2201. SideBandOutputStream.CH_ERROR,
  2202. SideBandOutputStream.SMALL_BUF, requireNonNull(rawOut));
  2203. err.write(Constants.encode(message));
  2204. err.flush();
  2205. }
  2206. }
  2207. private class PackProtocolErrorWriter implements ErrorWriter {
  2208. @Override
  2209. public void writeError(String message) throws IOException {
  2210. new PacketLineOut(requireNonNull(rawOut))
  2211. .writeString("ERR " + message + '\n'); //$NON-NLS-1$
  2212. }
  2213. }
  2214. }