-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
connect: tell server that the client understands v1
Teach the connection logic to tell a serve that it understands protocol v1. This is done in 2 different ways for the builtin transports, both of which ultimately set 'GIT_PROTOCOL' to 'version=1' on the server. 1. git:// A normal request to git-daemon is structured as "command path/to/repo\0host=..\0" and due to a bug introduced in 49ba83f (Add virtualization support to git-daemon, 2006-09-19) we aren't able to place any extra arguments (separated by NULs) besides the host otherwise the parsing of those arguments would enter an infinite loop. This bug was fixed in 73bb33a (daemon: Strictly parse the "extra arg" part of the command, 2009-06-04) but a check was put in place to disallow extra arguments so that new clients wouldn't trigger this bug in older servers. In order to get around this limitation git-daemon was taught to recognize additional request arguments hidden behind a second NUL byte. Requests can then be structured like: "command path/to/repo\0host=..\0\0version=1\0key=value\0". git-daemon can then parse out the extra arguments and set 'GIT_PROTOCOL' accordingly. By placing these extra arguments behind a second NUL byte we can skirt around both the infinite loop bug in 49ba83f (Add virtualization support to git-daemon, 2006-09-19) as well as the explicit disallowing of extra arguments introduced in 73bb33a (daemon: Strictly parse the "extra arg" part of the command, 2009-06-04) because both of these versions of git-daemon check for a single NUL byte after the host argument before terminating the argument parsing. 2. ssh://, file:// Set 'GIT_PROTOCOL' environment variable with the desired protocol version. With the file:// transport, 'GIT_PROTOCOL' can be set explicitly in the locally running git-upload-pack or git-receive-pack processes. With the ssh:// transport and OpenSSH compliant ssh programs, 'GIT_PROTOCOL' can be sent across ssh by using '-o SendEnv=GIT_PROTOCOL' and having the server whitelist this environment variable. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
- Loading branch information
Showing
2 changed files
with
255 additions
and
5 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
#!/bin/sh | ||
|
||
test_description='test git wire-protocol transition' | ||
|
||
TEST_NO_CREATE_REPO=1 | ||
|
||
. ./test-lib.sh | ||
|
||
# Test protocol v1 with 'git://' transport | ||
# | ||
. "$TEST_DIRECTORY"/lib-git-daemon.sh | ||
start_git_daemon --export-all --enable=receive-pack | ||
daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent | ||
|
||
test_expect_success 'create repo to be served by git-daemon' ' | ||
git init "$daemon_parent" && | ||
test_commit -C "$daemon_parent" one | ||
' | ||
|
||
test_expect_success 'clone with git:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -c protocol.version=1 \ | ||
clone "$GIT_DAEMON_URL/parent" daemon_child 2>log && | ||
git -C daemon_child log -1 --format=%s >actual && | ||
git -C "$daemon_parent" log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Client requested to use protocol v1 | ||
grep "clone> .*\\\0\\\0version=1\\\0$" log && | ||
# Server responded using protocol v1 | ||
grep "clone< version 1" log | ||
' | ||
|
||
test_expect_success 'fetch with git:// using protocol v1' ' | ||
test_commit -C "$daemon_parent" two && | ||
GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \ | ||
fetch 2>log && | ||
git -C daemon_child log -1 --format=%s origin/master >actual && | ||
git -C "$daemon_parent" log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Client requested to use protocol v1 | ||
grep "fetch> .*\\\0\\\0version=1\\\0$" log && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'pull with git:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \ | ||
pull 2>log && | ||
git -C daemon_child log -1 --format=%s >actual && | ||
git -C "$daemon_parent" log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Client requested to use protocol v1 | ||
grep "fetch> .*\\\0\\\0version=1\\\0$" log && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'push with git:// using protocol v1' ' | ||
test_commit -C daemon_child three && | ||
# Push to another branch, as the target repository has the | ||
# master branch checked out and we cannot push into it. | ||
GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \ | ||
push origin HEAD:client_branch 2>log && | ||
git -C daemon_child log -1 --format=%s >actual && | ||
git -C "$daemon_parent" log -1 --format=%s client_branch >expect && | ||
test_cmp expect actual && | ||
# Client requested to use protocol v1 | ||
grep "push> .*\\\0\\\0version=1\\\0$" log && | ||
# Server responded using protocol v1 | ||
grep "push< version 1" log | ||
' | ||
|
||
stop_git_daemon | ||
|
||
# Test protocol v1 with 'file://' transport | ||
# | ||
test_expect_success 'create repo to be served by file:// transport' ' | ||
git init file_parent && | ||
test_commit -C file_parent one | ||
' | ||
|
||
test_expect_success 'clone with file:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -c protocol.version=1 \ | ||
clone "file://$(pwd)/file_parent" file_child 2>log && | ||
git -C file_child log -1 --format=%s >actual && | ||
git -C file_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "clone< version 1" log | ||
' | ||
|
||
test_expect_success 'fetch with file:// using protocol v1' ' | ||
test_commit -C file_parent two && | ||
GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \ | ||
fetch 2>log && | ||
git -C file_child log -1 --format=%s origin/master >actual && | ||
git -C file_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'pull with file:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \ | ||
pull 2>log && | ||
git -C file_child log -1 --format=%s >actual && | ||
git -C file_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'push with file:// using protocol v1' ' | ||
test_commit -C file_child three && | ||
# Push to another branch, as the target repository has the | ||
# master branch checked out and we cannot push into it. | ||
GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \ | ||
push origin HEAD:client_branch 2>log && | ||
git -C file_child log -1 --format=%s >actual && | ||
git -C file_parent log -1 --format=%s client_branch >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "push< version 1" log | ||
' | ||
|
||
# Test protocol v1 with 'ssh://' transport | ||
# | ||
test_expect_success 'setup ssh wrapper' ' | ||
GIT_SSH="$GIT_BUILD_DIR/t/helper/test-fake-ssh" && | ||
export GIT_SSH && | ||
export TRASH_DIRECTORY && | ||
>"$TRASH_DIRECTORY"/ssh-output | ||
' | ||
|
||
expect_ssh () { | ||
test_when_finished '(cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)' && | ||
echo "ssh: -o SendEnv=GIT_PROTOCOL myhost $1 '$PWD/ssh_parent'" >"$TRASH_DIRECTORY/ssh-expect" && | ||
(cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) | ||
} | ||
|
||
test_expect_success 'create repo to be served by ssh:// transport' ' | ||
git init ssh_parent && | ||
test_commit -C ssh_parent one | ||
' | ||
|
||
test_expect_success 'clone with ssh:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -c protocol.version=1 \ | ||
clone "ssh://myhost:$(pwd)/ssh_parent" ssh_child 2>log && | ||
expect_ssh git-upload-pack && | ||
git -C ssh_child log -1 --format=%s >actual && | ||
git -C ssh_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "clone< version 1" log | ||
' | ||
|
||
test_expect_success 'fetch with ssh:// using protocol v1' ' | ||
test_commit -C ssh_parent two && | ||
GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \ | ||
fetch 2>log && | ||
expect_ssh git-upload-pack && | ||
git -C ssh_child log -1 --format=%s origin/master >actual && | ||
git -C ssh_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'pull with ssh:// using protocol v1' ' | ||
GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \ | ||
pull 2>log && | ||
expect_ssh git-upload-pack && | ||
git -C ssh_child log -1 --format=%s >actual && | ||
git -C ssh_parent log -1 --format=%s >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "fetch< version 1" log | ||
' | ||
|
||
test_expect_success 'push with ssh:// using protocol v1' ' | ||
test_commit -C ssh_child three && | ||
# Push to another branch, as the target repository has the | ||
# master branch checked out and we cannot push into it. | ||
GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \ | ||
push origin HEAD:client_branch 2>log && | ||
expect_ssh git-receive-pack && | ||
git -C ssh_child log -1 --format=%s >actual && | ||
git -C ssh_parent log -1 --format=%s client_branch >expect && | ||
test_cmp expect actual && | ||
# Server responded using protocol v1 | ||
grep "push< version 1" log | ||
' | ||
|
||
test_done |