]> granicus.if.org Git - git/commitdiff
Merge branch 'js/protocol-advertise-multi' into pu
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Oct 2019 06:10:17 +0000 (15:10 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 25 Oct 2019 06:10:17 +0000 (15:10 +0900)
The transport layer has been updated so that the protocol version
used can be negotiated between the parties, by the initiator
listing the protocol versions it is willing to talk, and the other
side choosing from one of them.

* js/protocol-advertise-multi:
  protocol: advertise multiple supported versions

18 files changed:
1  2 
builtin/archive.c
builtin/clone.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/ls-remote.c
builtin/pull.c
builtin/push.c
builtin/receive-pack.c
builtin/send-pack.c
builtin/upload-pack.c
connect.c
protocol.c
remote-curl.c
t/t5551-http-fetch-smart.sh
t/t5601-clone.sh
t/t5700-protocol-v1.sh
t/t5702-protocol-v2.sh
transport-helper.c

Simple merge
diff --cc builtin/clone.c
index 4348d962c9c81e4f6681eb53163b4f1278952153,d7826ae8f0836ba355d601001ed07df1198329f3..b43b954f862597b16a02d18c4a3e6e45b4ff5803
@@@ -949,8 -898,13 +949,12 @@@ int cmd_clone(int argc, const char **ar
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
  
 -      struct refspec rs = REFSPEC_INIT_FETCH;
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
  
+       register_allowed_protocol_version(protocol_v2);
+       register_allowed_protocol_version(protocol_v1);
+       register_allowed_protocol_version(protocol_v0);
        fetch_if_missing = 0;
  
        packet_trace_identity("clone");
Simple merge
diff --cc builtin/fetch.c
index 0c345b5dfe4b09e8d626815cb9909ef1f78e6338,07f2d428f937786c96b6cfed029f7d983ce40676..6b6dad68eeb6d028258dfa9d732e59e4a35b945d
  #include "argv-array.h"
  #include "utf8.h"
  #include "packfile.h"
+ #include "protocol.h"
  #include "list-objects-filter-options.h"
  #include "commit-reach.h"
 +#include "branch.h"
 +#include "promisor-remote.h"
 +#include "commit-graph.h"
 +
 +#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
  
  static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
Simple merge
diff --cc builtin/pull.c
Simple merge
diff --cc builtin/push.c
Simple merge
Simple merge
Simple merge
index 6da8fa2607c6b831c69a76c5bb2ec5caec444d54,293dd45b9e42846faea03019a42fb90789eda0b6..6c468661abdecbc2814ee935c3ffd70a21928d70
@@@ -33,7 -33,11 +33,11 @@@ int cmd_upload_pack(int argc, const cha
        packet_trace_identity("upload-pack");
        read_replace_refs = 0;
  
 -      argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
+       register_allowed_protocol_version(protocol_v2);
+       register_allowed_protocol_version(protocol_v1);
+       register_allowed_protocol_version(protocol_v0);
 +      argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
  
        if (argc != 1)
                usage_with_options(upload_pack_usage, options);
diff --cc connect.c
index da7daa2b683f9aed7e1e63656bbefd83a1a4fa56,3080fb1fd17cbc7fbfce18921f11638f35575fee..0e7aa7d91e2e79739efe5521a3ab8d2a0e9e3cd5
+++ b/connect.c
@@@ -1251,8 -1241,8 +1242,9 @@@ struct child_process *git_connect(int f
                printf("Diag: path=%s\n", path ? path : "NULL");
                conn = NULL;
        } else if (protocol == PROTO_GIT) {
-               conn = git_connect_git(fd, hostandport, path, prog, version, flags);
+               conn = git_connect_git(fd, hostandport, path, prog,
+                                      &version_advert, flags);
 +              conn->trace2_child_class = "transport/git";
        } else {
                struct strbuf cmd = STRBUF_INIT;
                const char *const *var;
                                strbuf_release(&cmd);
                                return NULL;
                        }
-                       fill_ssh_args(conn, ssh_host, port, version, flags);
 +                      conn->trace2_child_class = "transport/ssh";
+                       fill_ssh_args(conn, ssh_host, port, &version_advert,
+                                     flags);
                } else {
                        transport_check_allowed("file");
-                       if (version > 0) {
-                               argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
-                                                version);
-                       }
 +                      conn->trace2_child_class = "transport/file";
+                       argv_array_pushf(&conn->env_array,
+                                        GIT_PROTOCOL_ENVIRONMENT "=%s",
+                                        version_advert.buf);
                }
                argv_array_push(&conn->args, cmd.buf);
  
diff --cc protocol.c
index 9741f057505d2041be1e8dbd49b444aaeab4bd82,5664bd7a050a49f78d5268f11ea572572cb3ed25..9539c3338dabe27a73ae4575398afe45dffa6632
@@@ -28,21 -49,91 +53,100 @@@ enum protocol_version get_protocol_vers
                        die("unknown value for config 'protocol.version': %s",
                            value);
  
 -              return version;
 +              retval = version;
 +      }
 +
 +      if (git_test_v && *git_test_v) {
 +              enum protocol_version env = parse_protocol_version(git_test_v);
 +
 +              if (env == protocol_unknown_version)
 +                      die("unknown value for %s: %s", git_test_k, git_test_v);
 +              if (retval < env)
 +                      retval = env;
        }
  
 -      return protocol_v0;
 +      return retval;
  }
  
+ void register_allowed_protocol_version(enum protocol_version version)
+ {
+       if (version_registration_locked)
+               BUG("late attempt to register an allowed protocol version");
+       ALLOC_GROW(allowed_versions, nr_allowed_versions + 1,
+                  alloc_allowed_versions);
+       allowed_versions[nr_allowed_versions++] = version;
+ }
+ void register_allowed_protocol_versions_from_env(void)
+ {
+       const char *git_protocol = getenv(GIT_PROTOCOL_ENVIRONMENT);
+       const char *version_str;
+       struct string_list version_list = STRING_LIST_INIT_DUP;
+       struct string_list_item *version;
+       if (!git_protocol)
+               return;
+       string_list_split(&version_list, git_protocol, ':', -1);
+       for_each_string_list_item(version, &version_list) {
+               if (skip_prefix(version->string, "version=", &version_str))
+                       register_allowed_protocol_version(
+                               parse_protocol_version(version_str));
+       }
+       string_list_clear(&version_list, 0);
+ }
+ static int is_allowed_protocol_version(enum protocol_version version)
+ {
+       int i;
+       version_registration_locked = 1;
+       for (i = 0; i < nr_allowed_versions; i++)
+               if (version == allowed_versions[i])
+                       return 1;
+       return 0;
+ }
+ void get_client_protocol_version_advertisement(struct strbuf *advert)
+ {
+       int i, tmp_nr = nr_allowed_versions;
+       enum protocol_version *tmp_allowed_versions, config_version;
+       strbuf_reset(advert);
+       version_registration_locked = 1;
+       config_version = get_protocol_version_config();
+       if (config_version == protocol_v0) {
+               strbuf_addstr(advert, "version=0");
+               return;
+       }
+       if (tmp_nr > 0) {
+               ALLOC_ARRAY(tmp_allowed_versions, tmp_nr);
+               copy_array(tmp_allowed_versions, allowed_versions, tmp_nr,
+                          sizeof(enum protocol_version));
+       } else {
+               ALLOC_ARRAY(tmp_allowed_versions, 1);
+               tmp_nr = 1;
+               tmp_allowed_versions[0] = config_version;
+       }
+       if (tmp_allowed_versions[0] != config_version)
+               for (i = 1; i < nr_allowed_versions; i++)
+                       if (tmp_allowed_versions[i] == config_version) {
+                               SWAP(tmp_allowed_versions[0],
+                                    tmp_allowed_versions[i]);
+                       }
+       strbuf_addf(advert, "version=%s",
+                   format_protocol_version(tmp_allowed_versions[0]));
+       for (i = 1; i < tmp_nr; i++)
+               strbuf_addf(advert, ":version=%s",
+                           format_protocol_version(tmp_allowed_versions[i]));
+       free(tmp_allowed_versions);
+ }
  enum protocol_version determine_protocol_version_server(void)
  {
        const char *git_protocol = getenv(GIT_PROTOCOL_ENVIRONMENT);
diff --cc remote-curl.c
index 350d92a074ed82a99060cebb1a4a3fb0a98cfd8e,0d8fe19cc717c3363757ab98e4006d7b954a505b..aed26b7a217056a84299d1753f1925f5ecf10a01
@@@ -341,63 -330,22 +341,76 @@@ static int get_protocol_http_header(enu
        return 0;
  }
  
+ static int get_client_protocol_http_header(const struct strbuf *version_advert,
+                                          struct strbuf *header)
+ {
+       if (version_advert->len > 0) {
+               strbuf_addf(header, GIT_PROTOCOL_HEADER ": %s",
+                           version_advert->buf);
+               return 1;
+       }
+       return 0;
+ }
 +static void check_smart_http(struct discovery *d, const char *service,
 +                           struct strbuf *type)
 +{
 +      const char *p;
 +      struct packet_reader reader;
 +
 +      /*
 +       * If we don't see x-$service-advertisement, then it's not smart-http.
 +       * But once we do, we commit to it and assume any other protocol
 +       * violations are hard errors.
 +       */
 +      if (!skip_prefix(type->buf, "application/x-", &p) ||
 +          !skip_prefix(p, service, &p) ||
 +          strcmp(p, "-advertisement"))
 +              return;
 +
 +      packet_reader_init(&reader, -1, d->buf, d->len,
 +                         PACKET_READ_CHOMP_NEWLINE |
 +                         PACKET_READ_DIE_ON_ERR_PACKET);
 +      if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
 +              die(_("invalid server response; expected service, got flush packet"));
 +
 +      if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
 +              /*
 +               * The header can include additional metadata lines, up
 +               * until a packet flush marker.  Ignore these now, but
 +               * in the future we might start to scan them.
 +               */
 +              for (;;) {
 +                      packet_reader_read(&reader);
 +                      if (reader.pktlen <= 0) {
 +                              break;
 +                      }
 +              }
 +
 +              /*
 +               * v0 smart http; callers expect us to soak up the
 +               * service and header packets
 +               */
 +              d->buf = reader.src_buffer;
 +              d->len = reader.src_len;
 +              d->proto_git = 1;
 +
 +      } else if (!strcmp(reader.line, "version 2")) {
 +              /*
 +               * v2 smart http; do not consume version packet, which will
 +               * be handled elsewhere.
 +               */
 +              d->proto_git = 1;
 +
 +      } else {
 +              die(_("invalid server response; got '%s'"), reader.line);
 +      }
 +}
 +
  static struct discovery *discover_refs(const char *service, int for_push)
  {
 -      struct strbuf exp = STRBUF_INIT;
        struct strbuf type = STRBUF_INIT;
        struct strbuf charset = STRBUF_INIT;
        struct strbuf buffer = STRBUF_INIT;
@@@ -1362,9 -1335,11 +1369,11 @@@ int cmd_main(int argc, const char **arg
        struct strbuf buf = STRBUF_INIT;
        int nongit;
  
+       register_allowed_protocol_versions_from_env();
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
 -              error("remote-curl: usage: git remote-curl <remote> [<url>]");
 +              error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
                return 1;
        }
  
Simple merge
index ad8c41176ef76690004c8f73131a7ba464758143,d9caa1891ef48c93f7edb0fd142eec81e3241579..7a6159a44b7429eb8054885563b6e24fc853f83f
@@@ -345,8 -345,8 +345,8 @@@ expect_ssh () 
  }
  
  test_expect_success 'clone myhost:src uses ssh' '
 -      git clone myhost:src ssh-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone myhost:src ssh-clone &&
-       expect_ssh myhost src
+       expect_ssh "-o SendEnv=GIT_PROTOCOL" myhost src
  '
  
  test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
  '
  
  test_expect_success 'bracketed hostnames are still ssh' '
 -      git clone "[myhost:123]:src" ssh-bracket-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone &&
-       expect_ssh "-p 123" myhost src
+       expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
  '
  
  test_expect_success 'OpenSSH variant passes -4' '
 -      git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
-       expect_ssh "-4 -p 123" myhost src
+       expect_ssh "-o SendEnv=GIT_PROTOCOL -4 -p 123" myhost src
  '
  
  test_expect_success 'variant can be overridden' '
@@@ -405,8 -405,8 +405,8 @@@ test_expect_success 'OpenSSH-like uplin
        test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
        GIT_SSH="$TRASH_DIRECTORY/uplink" &&
        test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
 -      git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
-       expect_ssh "-p 123" myhost src
+       expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
  '
  
  test_expect_success 'plink is treated specially (as putty)' '
@@@ -443,16 -444,16 +443,16 @@@ test_expect_success 'single quoted plin
  
  test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
 -      GIT_SSH_VARIANT=ssh \
 +      GIT_TEST_PROTOCOL_VERSION=0 GIT_SSH_VARIANT=ssh \
-               git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
-       expect_ssh "-p 123" myhost src
+       git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
+       expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
  '
  
  test_expect_success 'ssh.variant overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
 -      git -c ssh.variant=ssh \
 +      GIT_TEST_PROTOCOL_VERSION=0 git -c ssh.variant=ssh \
                clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
-       expect_ssh "-p 123" myhost src
+       expect_ssh "-o SendEnv=GIT_PROTOCOL -p 123" myhost src
  '
  
  test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
@@@ -486,8 -487,8 +486,8 @@@ test_clone_url () 
        expect_ssh "$@"
  }
  
 -test_expect_success !MINGW 'clone c:temp is ssl' '
 +test_expect_success !MINGW,!CYGWIN 'clone c:temp is ssl' '
-       test_clone_url c:temp c temp
+       test_clone_url c:temp "-o SendEnv=GIT_PROTOCOL" c temp
  '
  
  test_expect_success MINGW 'clone c:temp is dos drive' '
Simple merge
Simple merge
index a9d690297e1fe33abdafed6126f0b4541ce35af5,9c84e4a67f2995ec425ea29fa253a3b9ecce3286..6b70739840844e23dd9bdc51794ffd6621e7cee7
@@@ -137,8 -128,11 +138,13 @@@ static struct child_process *get_helper
                argv_array_pushf(&helper->env_array, "%s=%s",
                                 GIT_DIR_ENVIRONMENT, get_git_dir());
  
+       get_client_protocol_version_advertisement(&version_advert);
+       if (version_advert.len > 0)
+               argv_array_pushf(&helper->env_array, "%s=%s",
+                                GIT_PROTOCOL_ENVIRONMENT, version_advert.buf);
 +      helper->trace2_child_class = helper->args.argv[0]; /* "remote-<name>" */
 +
        code = start_command(helper);
        if (code < 0 && errno == ENOENT)
                die(_("unable to find remote helper for '%s'"), data->name);