- Notifications
You must be signed in to change notification settings - Fork2.5k
smart: ignore shallow/unshallow packets during ACK processing#6973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Merged
ethomson merged 1 commit intolibgit2:mainfromkempniu:ignore-shallow-packets-during-ack-processingDec 23, 2024
Merged
smart: ignore shallow/unshallow packets during ACK processing#6973
ethomson merged 1 commit intolibgit2:mainfromkempniu:ignore-shallow-packets-during-ack-processingDec 23, 2024
Uh oh!
There was an error while loading.Please reload this page.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
In RPC mode (https), the client sends its list of shallow commits at thebeginning of each request during packfile negotiation, so that theremote endpoint keeps context. This causes the remote to prependappropriate shallow/unshallow packets to each response sent back to theclient.However, the store_common() helper function (used in multi_ack mode)does not cater for this, returning as soon as it encounters any packetdifferent than an ACK packet and therefore leaving the rest of the HTTPbuffer unprocessed. This in turn causes subsequent iterations of thewhile loop processing ACK packets to process data returned by older HTTPrequests instead of the current one, messing up the packfile negotiationprocess. Given that the wait_while_ack() helper function (called afterthe client signals to the remote that it is ready to receive packfiledata) correctly skips over shallow/unshallow packets, packfile contentscan still be received successfully in some cases (depending on messageframing); in some other ones, though (particularly whengit_smart__download_pack() processes an HTTP buffer starting withshallow/unshallow packets), the fetching process fails with an"incomplete pack header" error due to the flush packet terminating a setof shallow/unshallow packets being incorrectly interpreted as the flushpacket indicating the end of the packfile (making the code behave as ifno packfile data was sent by the remote).Fix by ignoring shallow/unshallow packets in the store_common() helperfunction, therefore making the ACK processing logic work on the correctHTTP buffers and ensuring that git_smart__download_pack() is not calleduntil packfile negotiation is actually finished.
Wow.
What an understatement. Thanks for the deep investigation and the fix. |
I had another read through - this makes sense, and I really appreciate the detailed explanation that you gave in the PR. 🙏 |
536f868
intolibgit2:main 19 checks passed
Uh oh!
There was an error while loading.Please reload this page.
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading.Please reload this page.
In RPC mode (https), the client sends its list of shallow commits at the beginning of each request during packfile negotiation, so that the remote endpoint keeps context. This causes the remote to prepend appropriate shallow/unshallow packets to each response sent back to the client.
However, the store_common() helper function (used in multi_ack mode) does not cater for this, returning as soon as it encounters any packet different than an ACK packet and therefore leaving the rest of the HTTP buffer unprocessed. This in turn causes subsequent iterations of the while loop processing ACK packets to process data returned by older HTTP requests instead of the current one, messing up the packfile negotiation process. Given that the wait_while_ack() helper function (called after the client signals to the remote that it is ready to receive packfile data) correctly skips over shallow/unshallow packets, packfile contents can still be received successfully in some cases (depending on message framing); in some other ones, though (particularly when git_smart__download_pack() processes an HTTP buffer starting with shallow/unshallow packets), the fetching process fails with an "incomplete pack header" error due to the flush packet terminating a set of shallow/unshallow packets being incorrectly interpreted as the flush packet indicating the end of the packfile (making the code behave as if no packfile data was sent by the remote).
Fix by ignoring shallow/unshallow packets in the store_common() helper function, therefore making the ACK processing logic work on the correct HTTP buffers and ensuring that git_smart__download_pack() is not called until packfile negotiation is actually finished.
This bug was fairly nasty to pinpoint, so it was also challenging to come up with a test triggering it, particularly given that the test repository used in
tests/libgit2/online/shallow.c
only contains 21 commits, i.e. barely enough to exceed the 20-packethave
line split point. In fact, the test included in this PR only demonstrates a case in which the code is able to (mostly accidentally) "resync" the packfile negotiation process and fetch packfile data after all:To make this test trigger the
incomplete pack header
error using the test repository, an extra modification lowering the maximum count ofhave
lines per request must be applied (which does not affect the way the ACK processing logic works on a higher level), e.g.:With the above modification in place, the test fails:
Adding HTTP tracing to the test code hopefully further clarifies what happens behind the scenes, e.g.:
With these two modifications applied (
i % 5
and tracing), the test triggers the following behaviorwithout the fix proposed in this PR:Note in particular how the code does not read new data from the wire between some of the later POST requests - that's the "out of sync" HTTP buffer processing in action.
With the fix proposed in this PR applied, the above becomes:
Looking at the code, I believe a similar issue affects non-multi_ack packfile negotiation (I would expect the
unexpected pkt type
error to be raised), but I did not have the heart to test and/or fix against a server thatdoes support shallow clones whilenot supporting multi_ack mode...