{"thread":{"id":"57969","subject":"[PATCH] remote-curl: send Accept-Language header to server","startedAt":"2022-06-08T09:29:15Z","lastAt":"2022-07-11T05:59:02Z","messageCount":20,"participants":["Li Linchao via GitGitGadget","Junio C Hamano","Ævar Arnfjörð Bjarmason","lilinchao@oschina.cn"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"456860","messageId":"pull.1251.git.1654678407365.gitgitgadget@gmail.com","threadId":"57969","inReplyTo":null,"subject":"[PATCH] remote-curl: send Accept-Language header to server","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-06-08T08:53:27Z","receivedAt":"2022-06-08T09:29:15Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: Cactusinhand <lilinchao@oschina.cn>\n\nGit server end's ability to accept Accept-Language header was introduced\nin f18604bbf2(http: add Accept-Language header if possible), but this is\nonly used by very early phase of the transfer, that's HTTP GET request to\ndiscover references. For other phases, like POST request in the smart HTTP\nthe server side don't know what language client speak.\n\nThis patch teaches git client to learn end-user's preferred language and\nthrow accept-language header to server side. Once server get this header\nit have ability to talk to end-user with language they understand, this\nwould be very helpful for many non-English speakers.\n\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    remote-curl: send Accept-Language header to server\n    \n    Teach git client to learn end-user's preferred language and throw\n    accept-language header to server side. As git is developmented and\n    maintained by English, many developer may ignore Non-English speaker\n    experience. This patch give git server the ability to speak to client\n    end with their preferred language, which can be very helpfuly and\n    friendly to understand the exact meaning of some prompt messages sent by\n    git.\n    \n    TODO: For SSH tranport, give it an environment variable to understand\n    locale language.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1251%2FCactusinhand%2Fllc%2Fsend-Accept-Language-header-to-HTTP-server-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1251/Cactusinhand/llc/send-Accept-Language-header-to-HTTP-server-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1251\n\n http.c                      |  2 +-\n http.h                      |  3 +++\n remote-curl.c               | 18 +++++++++++++++++-\n t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n t/t5550-http-fetch-dumb.sh  |  2 +-\n t/t5551-http-fetch-smart.sh | 10 ++++++++--\n 6 files changed, 49 insertions(+), 5 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 11c6f69facd..0654e111d1d 100644\n--- a/http.c\n+++ b/http.c\n@@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)\n  *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n  *   LANGUAGE= LANG=C -> \"\"\n  */\n-static const char *get_accept_language(void)\n+const char *get_accept_language(void)\n {\n \tif (!cached_accept_language) {\n \t\tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/http.h b/http.h\nindex ba303cfb372..c5039a0208e 100644\n--- a/http.h\n+++ b/http.h\n@@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n int http_get_info_packs(const char *base_url,\n \t\t\tstruct packed_git **packs_head);\n \n+/* Helper for getting Accept-Language header */\n+const char *get_accept_language(void);\n+\n struct http_pack_request {\n \tchar *url;\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex 67f178b1120..8acf506705c 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -580,6 +580,7 @@ struct rpc_state {\n \tchar *service_url;\n \tchar *hdr_content_type;\n \tchar *hdr_accept;\n+\tchar *hdr_accept_language;\n \tchar *protocol_header;\n \tchar *buf;\n \tsize_t alloc;\n@@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n \theaders = curl_slist_append(headers, needs_100_continue ?\n \t\t\"Expect: 100-continue\" : \"Expect:\");\n \n+\t/* Add Accept-Language header */\n+\tif (rpc->hdr_accept_language)\n+\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n+\n \t/* Add the extra Git-Protocol header */\n \tif (rpc->protocol_header)\n \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n@@ -1058,6 +1063,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \t\t       struct strbuf *rpc_result)\n {\n \tconst char *svc = rpc->service_name;\n+\tconst char *accept_language;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct child_process client = CHILD_PROCESS_INIT;\n \tint err = 0;\n@@ -1080,6 +1086,12 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n \trpc->service_url = strbuf_detach(&buf, NULL);\n \n+\taccept_language = get_accept_language();\n+\tif (accept_language) {\n+\t\tstrbuf_addstr(&buf, accept_language);\n+\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n+\t}\n+\n \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n \n@@ -1400,7 +1412,7 @@ static int stateless_connect(const char *service_name)\n \tstruct discovery *discover;\n \tstruct rpc_state rpc;\n \tstruct strbuf buf = STRBUF_INIT;\n-\n+\tconst char *accept_language;\n \t/*\n \t * Run the info/refs request and see if the server supports protocol\n \t * v2.  If and only if the server supports v2 can we successfully\n@@ -1418,6 +1430,10 @@ static int stateless_connect(const char *service_name)\n \t\tprintf(\"\\n\");\n \t\tfflush(stdout);\n \t}\n+\taccept_language = get_accept_language();\n+\tif (accept_language) {\n+\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n+\t}\n \n \trpc.service_name = service_name;\n \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 2f09ff4fac6..4288a279e9e 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n \t test $HEAD = $(git rev-parse --verify HEAD))\n '\n \n+test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n+\tcat >exp <<-\\EOF &&\n+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n+\tEOF\n+\n+\tcd \"$ROOT_PATH\"/test_repo_clone &&\n+\t: >path_lang &&\n+\tgit add path_lang &&\n+\ttest_tick &&\n+\tgit commit -m path_lang &&\n+\tHEAD=$(git rev-parse --verify HEAD) &&\n+\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n+\t! grep \"Expect: 100-continue\" err &&\n+\n+\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n+\ttest_cmp exp err.language\n+'\n+\n test_expect_success 'push already up-to-date' '\n \tgit push\n '\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex f0d9cd584d3..bc308519af5 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -369,7 +369,7 @@ ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n '\n \n-test_expect_success 'git client does not send an empty Accept-Language' '\n+test_expect_success 'git client send an empty Accept-Language' '\n \tGIT_TRACE_CURL=true LANGUAGE= git ls-remote \"$HTTPD_URL/dumb/repo.git\" 2>stderr &&\n \t! grep \"^=> Send header: Accept-Language:\" stderr\n '\ndiff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\nindex b9351a732f6..6f65131a4e4 100755\n--- a/t/t5551-http-fetch-smart.sh\n+++ b/t/t5551-http-fetch-smart.sh\n@@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '\n \t> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1\n \t> Accept: */*\n \t> Accept-Encoding: ENCODINGS\n+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n \t> Pragma: no-cache\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n@@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '\n \t> Accept-Encoding: ENCODINGS\n \t> Content-Type: application/x-git-upload-pack-request\n \t> Accept: application/x-git-upload-pack-result\n+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n \t> Content-Length: xxx\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n \t< Cache-Control: no-cache, max-age=0, must-revalidate\n \t< Content-Type: application/x-git-upload-pack-result\n \tEOF\n-\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \\\n+\n+\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"zh_CN:en\" \\\n \t\tgit clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&\n \ttest_cmp file clone/file &&\n \ttr '\\''\\015'\\'' Q <err |\n@@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '\n \t\ttest_cmp exp actual.smudged &&\n \n \t\tgrep \"Accept-Encoding:.*gzip\" actual >actual.gzip &&\n-\t\ttest_line_count = 2 actual.gzip\n+\t\ttest_line_count = 2 actual.gzip &&\n+\n+\t\tgrep \"Accept-Language: zh-CN, en\" actual >actual.language &&\n+\t\ttest_line_count = 2 actual.language\n \tfi\n '\n \n\nbase-commit: ab336e8f1c8009c8b1aab8deb592148e69217085\n-- \ngitgitgadget\n"},{"id":"456909","messageId":"xmqqa6amohop.fsf@gitster.g","threadId":"57969","inReplyTo":"pull.1251.git.1654678407365.gitgitgadget@gmail.com","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-08T23:32:06Z","receivedAt":"2022-06-08T23:32:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Cactusinhand <lilinchao@oschina.cn>\n\nHuh? \n\n> Git server end's ability to accept Accept-Language header was introduced\n> in f18604bbf2(http: add Accept-Language header if possible), but this is\n> only used by very early phase of the transfer, that's HTTP GET request to\n> discover references. For other phases, like POST request in the smart HTTP\n> the server side don't know what language client speak.\n\n\"client speak\" -> \"the client speaks\".\n\n> This patch teaches git client to learn end-user's preferred language and\n\n\"This patch teaches\" -> \"Teach\"\n\n> throw accept-language header to server side. Once server get this header\n\n\"server side\" -> \"the server side\".\n\"server get\" -> \"the server gets\"\n\"header\" -> \"header,\"\n\n> it have ability to talk to end-user with language they understand, this\n\n\"it have ability\" -> \"it has the ability\"\n\n\", this\" -> \". This\"\n\n> would be very helpful for many non-English speakers.\n>\n> Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n\n> diff --git a/http.c b/http.c\n> index 11c6f69facd..0654e111d1d 100644\n> --- a/http.c\n> +++ b/http.c\n> @@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)\n>   *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n>   *   LANGUAGE= LANG=C -> \"\"\n>   */\n> -static const char *get_accept_language(void)\n> +const char *get_accept_language(void)\n\nIt was an understandable name for a file-scope static function, but\nis this name suitable to be a global without making it more narrow\nand specific to \"HTTP\" and \"Header\"?\n\n> diff --git a/remote-curl.c b/remote-curl.c\n> index 67f178b1120..8acf506705c 100644\n> --- a/remote-curl.c\n> +++ b/remote-curl.c\n> @@ -1400,7 +1412,7 @@ static int stateless_connect(const char *service_name)\n>  \tstruct discovery *discover;\n>  \tstruct rpc_state rpc;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> -\n> +\tconst char *accept_language;\n\nDo not lose the blank line after the block with variable\ndeclarations.  IOW, the patch around this line should read like this\ninstead:\n\n>  \tstruct discovery *discover;\n>  \tstruct rpc_state rpc;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> +\tconst char *accept_language;\n>\n>  \t/*\n>  \t * Run the info/refs request and see if the server supports protocol\n\n\n> @@ -1418,6 +1430,10 @@ static int stateless_connect(const char *service_name)\n>  \t\tprintf(\"\\n\");\n>  \t\tfflush(stdout);\n>  \t}\n> +\taccept_language = get_accept_language();\n> +\tif (accept_language) {\n> +\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n> +\t}\n\nDrop {} around a single-statement block.\n\n> +\tcat >exp <<-\\EOF &&\n> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n> +\tEOF\n> +\n> +\tcd \"$ROOT_PATH\"/test_repo_clone &&\n> +\t: >path_lang &&\n> +\tgit add path_lang &&\n> +\ttest_tick &&\n> +\tgit commit -m path_lang &&\n> +\tHEAD=$(git rev-parse --verify HEAD) &&\n> +\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n\nA few comments.\n\n * In all gettext/locale tests we seem to set both LANGUAGE and\n   LC_ALL environment variables.  Shouldn't we do the same for\n   consistency?\n\n * In existing tests, we seem to use ko_KR, en_US, ja_JP, and random\n   assortment of languages (e.g. t5550).  Can we safely add any new\n   languages to the mix without any downside to the tester?  We\n   should reuse what we already use, especially if this ends up\n   forcing users and testers to install yet another \"language pack\"\n   for zh_CN.\n\nThanks.\n"},{"id":"456921","messageId":"pull.1251.v2.git.1654756523475.gitgitgadget@gmail.com","threadId":"57969","inReplyTo":"pull.1251.git.1654678407365.gitgitgadget@gmail.com","subject":"[PATCH v2] remote-curl: send Accept-Language header to server","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-06-09T06:35:23Z","receivedAt":"2022-06-09T06:35:34Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: Li Linchao <lilinchao@oschina.cn>\n\nGit server end's ability to accept Accept-Language header was introduced\nin f18604bbf2(http: add Accept-Language header if possible), but this is\nonly used by very early phase of the transfer, that's HTTP GET request to\ndiscover references. For other phases, like POST request in the smart HTTP\nthe server side don't know what language the client speaks.\n\nTeach git client to learn end-user's preferred language and throw\naccept-language header to the server side. Once the server gets this header,\nit has the ability to talk to end-user with language they understand.\nThis would be very helpful for many non-English speakers.\n\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    remote-curl: send Accept-Language header to server\n    \n    Changes since v1:\n    \n     * change get_accept_language() to http_get_accept_language_header()\n     * reuse test case in t5550\n     * reword commit message\n    \n    TODO: For SSH tranport, give it an environment variable to understand\n    locale language.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1251%2FCactusinhand%2Fllc%2Fsend-Accept-Language-header-to-HTTP-server-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1251/Cactusinhand/llc/send-Accept-Language-header-to-HTTP-server-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1251\n\nRange-diff vs v1:\n\n 1:  b09f10b6c06 ! 1:  a2dd9d4070e remote-curl: send Accept-Language header to server\n     @@\n       ## Metadata ##\n     -Author: Cactusinhand <lilinchao@oschina.cn>\n     +Author: Li Linchao <lilinchao@oschina.cn>\n      \n       ## Commit message ##\n          remote-curl: send Accept-Language header to server\n     @@ Commit message\n          in f18604bbf2(http: add Accept-Language header if possible), but this is\n          only used by very early phase of the transfer, that's HTTP GET request to\n          discover references. For other phases, like POST request in the smart HTTP\n     -    the server side don't know what language client speak.\n     +    the server side don't know what language the client speaks.\n      \n     -    This patch teaches git client to learn end-user's preferred language and\n     -    throw accept-language header to server side. Once server get this header\n     -    it have ability to talk to end-user with language they understand, this\n     -    would be very helpful for many non-English speakers.\n     +    Teach git client to learn end-user's preferred language and throw\n     +    accept-language header to the server side. Once the server gets this header,\n     +    it has the ability to talk to end-user with language they understand.\n     +    This would be very helpful for many non-English speakers.\n      \n          Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n      \n     @@ http.c: static void write_accept_language(struct strbuf *buf)\n        *   LANGUAGE= LANG=C -> \"\"\n        */\n      -static const char *get_accept_language(void)\n     -+const char *get_accept_language(void)\n     ++const char *http_get_accept_language_header(void)\n       {\n       \tif (!cached_accept_language) {\n       \t\tstruct strbuf buf = STRBUF_INIT;\n     +@@ http.c: static int http_request(const char *url,\n     + \t\t\t\t\t fwrite_buffer);\n     + \t}\n     + \n     +-\taccept_language = get_accept_language();\n     ++\taccept_language = http_get_accept_language_header();\n     + \n     + \tif (accept_language)\n     + \t\theaders = curl_slist_append(headers, accept_language);\n      \n       ## http.h ##\n      @@ http.h: int http_fetch_ref(const char *base, struct ref *ref);\n     @@ http.h: int http_fetch_ref(const char *base, struct ref *ref);\n       \t\t\tstruct packed_git **packs_head);\n       \n      +/* Helper for getting Accept-Language header */\n     -+const char *get_accept_language(void);\n     ++const char *http_get_accept_language_header(void);\n      +\n       struct http_pack_request {\n       \tchar *url;\n     @@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *h\n       \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n       \trpc->service_url = strbuf_detach(&buf, NULL);\n       \n     -+\taccept_language = get_accept_language();\n     ++\taccept_language = http_get_accept_language_header();\n      +\tif (accept_language) {\n      +\t\tstrbuf_addstr(&buf, accept_language);\n      +\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n     @@ remote-curl.c: static int stateless_connect(const char *service_name)\n       \tstruct discovery *discover;\n       \tstruct rpc_state rpc;\n       \tstruct strbuf buf = STRBUF_INIT;\n     --\n      +\tconst char *accept_language;\n     + \n       \t/*\n       \t * Run the info/refs request and see if the server supports protocol\n     - \t * v2.  If and only if the server supports v2 can we successfully\n      @@ remote-curl.c: static int stateless_connect(const char *service_name)\n       \t\tprintf(\"\\n\");\n       \t\tfflush(stdout);\n       \t}\n     -+\taccept_language = get_accept_language();\n     -+\tif (accept_language) {\n     ++\taccept_language = http_get_accept_language_header();\n     ++\tif (accept_language)\n      +\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n     -+\t}\n       \n       \trpc.service_name = service_name;\n       \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n     @@ t/t5541-http-push-smart.sh: test_expect_success 'push to remote repository (stan\n       '\n      \n       ## t/t5550-http-fetch-dumb.sh ##\n     -@@ t/t5550-http-fetch-dumb.sh: ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n     +@@ t/t5550-http-fetch-dumb.sh: test_expect_success 'git client sends Accept-Language correctly with unordinary\n     + \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR::en_US\" &&\n     + \tcheck_language \"ko-KR, *;q=0.9\" \":::ko_KR\" &&\n     + \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR!!:en_US\" &&\n     +-\tcheck_language \"ko-KR, ja-JP;q=0.9, *;q=0.8\" \"ko_KR en_US:ja_JP\"'\n     ++\tcheck_language \"ko-KR, ja-JP;q=0.9, zh-CN;q=0.8, *;q=0.7\" \"ko_KR en_US:ja_JP:zh_CN\"'\n     + \n     + test_expect_success 'git client sends Accept-Language with many preferred languages' '\n     +-\tcheck_language \"ko-KR, en-US;q=0.9, fr-CA;q=0.8, de;q=0.7, sr;q=0.6, \\\n     +-ja;q=0.5, zh;q=0.4, sv;q=0.3, pt;q=0.2, *;q=0.1\" \\\n     +-\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt &&\n     ++\tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n     ++ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, zh-CN;q=0.91, *;q=0.90\" \\\n     ++\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:zh_CN &&\n     + \tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n     + ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n       \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n       '\n       \n\n\n http.c                      |  4 ++--\n http.h                      |  3 +++\n remote-curl.c               | 16 ++++++++++++++++\n t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n t/t5550-http-fetch-dumb.sh  | 10 +++++-----\n t/t5551-http-fetch-smart.sh | 10 ++++++++--\n 6 files changed, 53 insertions(+), 9 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 11c6f69facd..33301d8d5d5 100644\n--- a/http.c\n+++ b/http.c\n@@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)\n  *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n  *   LANGUAGE= LANG=C -> \"\"\n  */\n-static const char *get_accept_language(void)\n+const char *http_get_accept_language_header(void)\n {\n \tif (!cached_accept_language) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -1829,7 +1829,7 @@ static int http_request(const char *url,\n \t\t\t\t\t fwrite_buffer);\n \t}\n \n-\taccept_language = get_accept_language();\n+\taccept_language = http_get_accept_language_header();\n \n \tif (accept_language)\n \t\theaders = curl_slist_append(headers, accept_language);\ndiff --git a/http.h b/http.h\nindex ba303cfb372..3c94c479100 100644\n--- a/http.h\n+++ b/http.h\n@@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n int http_get_info_packs(const char *base_url,\n \t\t\tstruct packed_git **packs_head);\n \n+/* Helper for getting Accept-Language header */\n+const char *http_get_accept_language_header(void);\n+\n struct http_pack_request {\n \tchar *url;\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex 67f178b1120..504bbdedbda 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -580,6 +580,7 @@ struct rpc_state {\n \tchar *service_url;\n \tchar *hdr_content_type;\n \tchar *hdr_accept;\n+\tchar *hdr_accept_language;\n \tchar *protocol_header;\n \tchar *buf;\n \tsize_t alloc;\n@@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n \theaders = curl_slist_append(headers, needs_100_continue ?\n \t\t\"Expect: 100-continue\" : \"Expect:\");\n \n+\t/* Add Accept-Language header */\n+\tif (rpc->hdr_accept_language)\n+\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n+\n \t/* Add the extra Git-Protocol header */\n \tif (rpc->protocol_header)\n \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n@@ -1058,6 +1063,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \t\t       struct strbuf *rpc_result)\n {\n \tconst char *svc = rpc->service_name;\n+\tconst char *accept_language;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct child_process client = CHILD_PROCESS_INIT;\n \tint err = 0;\n@@ -1080,6 +1086,12 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n \trpc->service_url = strbuf_detach(&buf, NULL);\n \n+\taccept_language = http_get_accept_language_header();\n+\tif (accept_language) {\n+\t\tstrbuf_addstr(&buf, accept_language);\n+\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n+\t}\n+\n \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n \n@@ -1400,6 +1412,7 @@ static int stateless_connect(const char *service_name)\n \tstruct discovery *discover;\n \tstruct rpc_state rpc;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *accept_language;\n \n \t/*\n \t * Run the info/refs request and see if the server supports protocol\n@@ -1418,6 +1431,9 @@ static int stateless_connect(const char *service_name)\n \t\tprintf(\"\\n\");\n \t\tfflush(stdout);\n \t}\n+\taccept_language = http_get_accept_language_header();\n+\tif (accept_language)\n+\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n \n \trpc.service_name = service_name;\n \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 2f09ff4fac6..4288a279e9e 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n \t test $HEAD = $(git rev-parse --verify HEAD))\n '\n \n+test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n+\tcat >exp <<-\\EOF &&\n+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n+\tEOF\n+\n+\tcd \"$ROOT_PATH\"/test_repo_clone &&\n+\t: >path_lang &&\n+\tgit add path_lang &&\n+\ttest_tick &&\n+\tgit commit -m path_lang &&\n+\tHEAD=$(git rev-parse --verify HEAD) &&\n+\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n+\t! grep \"Expect: 100-continue\" err &&\n+\n+\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n+\ttest_cmp exp err.language\n+'\n+\n test_expect_success 'push already up-to-date' '\n \tgit push\n '\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex f0d9cd584d3..42dd9fe2af7 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -358,18 +358,18 @@ test_expect_success 'git client sends Accept-Language correctly with unordinary\n \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR::en_US\" &&\n \tcheck_language \"ko-KR, *;q=0.9\" \":::ko_KR\" &&\n \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR!!:en_US\" &&\n-\tcheck_language \"ko-KR, ja-JP;q=0.9, *;q=0.8\" \"ko_KR en_US:ja_JP\"'\n+\tcheck_language \"ko-KR, ja-JP;q=0.9, zh-CN;q=0.8, *;q=0.7\" \"ko_KR en_US:ja_JP:zh_CN\"'\n \n test_expect_success 'git client sends Accept-Language with many preferred languages' '\n-\tcheck_language \"ko-KR, en-US;q=0.9, fr-CA;q=0.8, de;q=0.7, sr;q=0.6, \\\n-ja;q=0.5, zh;q=0.4, sv;q=0.3, pt;q=0.2, *;q=0.1\" \\\n-\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt &&\n+\tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n+ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, zh-CN;q=0.91, *;q=0.90\" \\\n+\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:zh_CN &&\n \tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n '\n \n-test_expect_success 'git client does not send an empty Accept-Language' '\n+test_expect_success 'git client send an empty Accept-Language' '\n \tGIT_TRACE_CURL=true LANGUAGE= git ls-remote \"$HTTPD_URL/dumb/repo.git\" 2>stderr &&\n \t! grep \"^=> Send header: Accept-Language:\" stderr\n '\ndiff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\nindex b9351a732f6..6f65131a4e4 100755\n--- a/t/t5551-http-fetch-smart.sh\n+++ b/t/t5551-http-fetch-smart.sh\n@@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '\n \t> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1\n \t> Accept: */*\n \t> Accept-Encoding: ENCODINGS\n+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n \t> Pragma: no-cache\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n@@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '\n \t> Accept-Encoding: ENCODINGS\n \t> Content-Type: application/x-git-upload-pack-request\n \t> Accept: application/x-git-upload-pack-result\n+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n \t> Content-Length: xxx\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n \t< Cache-Control: no-cache, max-age=0, must-revalidate\n \t< Content-Type: application/x-git-upload-pack-result\n \tEOF\n-\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \\\n+\n+\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"zh_CN:en\" \\\n \t\tgit clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&\n \ttest_cmp file clone/file &&\n \ttr '\\''\\015'\\'' Q <err |\n@@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '\n \t\ttest_cmp exp actual.smudged &&\n \n \t\tgrep \"Accept-Encoding:.*gzip\" actual >actual.gzip &&\n-\t\ttest_line_count = 2 actual.gzip\n+\t\ttest_line_count = 2 actual.gzip &&\n+\n+\t\tgrep \"Accept-Language: zh-CN, en\" actual >actual.language &&\n+\t\ttest_line_count = 2 actual.language\n \tfi\n '\n \n\nbase-commit: ab336e8f1c8009c8b1aab8deb592148e69217085\n-- \ngitgitgadget\n"},{"id":"456922","messageId":"220609.86leu6thon.gmgdl@evledraar.gmail.com","threadId":"57969","inReplyTo":"pull.1251.git.1654678407365.gitgitgadget@gmail.com","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-09T07:30:35Z","receivedAt":"2022-06-09T07:33:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jun 08 2022, Li Linchao via GitGitGadget wrote:\n\n> From: Cactusinhand <lilinchao@oschina.cn>\n>\n> Git server end's ability to accept Accept-Language header was introduced\n> in f18604bbf2(http: add Accept-Language header if possible), but this is\n> only used by very early phase of the transfer, that's HTTP GET request to\n> discover references. For other phases, like POST request in the smart HTTP\n> the server side don't know what language client speak.\n>\n> This patch teaches git client to learn end-user's preferred language and\n> throw accept-language header to server side. Once server get this header\n> it have ability to talk to end-user with language they understand, this\n> would be very helpful for many non-English speakers.\n\nI may be missing something, but this is just the \"Accept-Language\" part\nof this change, i.e. there is no \"round-tripping\" here of actually doing\nthe work on the server of doing setlocale(), no?\n\nI think the end-goal of having the \"remote: \" messages translated, if\npossible, is very worthwhile, but I'd always imagined we'd do that with\na protocol extension, because even if we do this with HTTP headers we\nwon't get the same over ssh/git transports.\n\nBut then again we don't have protocol v2 push yet :(\n\nSo perfect certainly shouldn't be the enemy of the good here, I just\nwonder what the end-goal is and if there's a plan to get there.\n"},{"id":"456953","messageId":"xmqq1qvxn3kz.fsf@gitster.g","threadId":"57969","inReplyTo":"220609.86leu6thon.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-09T17:34:20Z","receivedAt":"2022-06-09T17:34:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> I may be missing something, but this is just the \"Accept-Language\" part\n> of this change, i.e. there is no \"round-tripping\" here of actually doing\n> the work on the server of doing setlocale(), no?\n\nI think the wish is that if Accept-Language is interpreted by the\nHTTP(s) server and it gets turned into LC_something=<locale> when\nthe HTTP server spawns us, we'd know what language to issue our\nerror messages.\n\n\n"},{"id":"456969","messageId":"xmqqilp9gznd.fsf@gitster.g","threadId":"57969","inReplyTo":"pull.1251.v2.git.1654756523475.gitgitgadget@gmail.com","subject":"Re: [PATCH v2] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-09T23:55:50Z","receivedAt":"2022-06-09T23:56:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Li Linchao <lilinchao@oschina.cn>\n>\n> Git server end's ability to accept Accept-Language header was introduced\n> in f18604bbf2(http: add Accept-Language header if possible), but this is\n\nPleaes refer to the commit like so:\n\n    f18604bb (http: add Accept-Language header if possible, 2015-01-28)\n\n(cf. Documentation/SubmittingPatches::commit-reference)\n\n\"git show -s --pretty=reference f18604bb\" is one way to format a\ncommit name in that format.\n\n> only used by very early phase of the transfer, that's HTTP GET request to\n\n\"that's\" -> \"which is\", probably.\n\n> discover references. For other phases, like POST request in the smart HTTP\n> the server side don't know what language the client speaks.\n\n\"HTTP the server side don't\" -> \"HTTP, the server does not\" \n\n>  http.c                      |  4 ++--\n>  http.h                      |  3 +++\n>  remote-curl.c               | 16 ++++++++++++++++\n>  t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n>  t/t5550-http-fetch-dumb.sh  | 10 +++++-----\n>  t/t5551-http-fetch-smart.sh | 10 ++++++++--\n>  6 files changed, 53 insertions(+), 9 deletions(-)\n\nWhat is curious is that without any of changes to the *.[ch] files,\nupdated test 5550 and 5551 pass already.\n\nIn other words, these updated tests in 5550 and 5551 probably are\nnot testing the behaviour the updated code intends to show.  Of\ncourse, if we revert the code that taught the Accept-Language to the\nGET requests in f18604bb, these tests will fail.  There is no reason\nto touch these two tests to \"prove\" that the code change in this\npatch does not break existing support, either.\n\n> diff --git a/http.h b/http.h\n> index ba303cfb372..3c94c479100 100644\n> --- a/http.h\n> +++ b/http.h\n> @@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n>  int http_get_info_packs(const char *base_url,\n>  \t\t\tstruct packed_git **packs_head);\n>  \n> +/* Helper for getting Accept-Language header */\n> +const char *http_get_accept_language_header(void);\n\nOK.\n\n> @@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n>  \theaders = curl_slist_append(headers, needs_100_continue ?\n>  \t\t\"Expect: 100-continue\" : \"Expect:\");\n>  \n> +\t/* Add Accept-Language header */\n> +\tif (rpc->hdr_accept_language)\n> +\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n\ncurl_slist_append() makes a copy of .hdr_accept_language, so rpc\nstruct is still responsible to release the resource used for the\nmember when it goes out of scope.\n\n> +\taccept_language = http_get_accept_language_header();\n> +\tif (accept_language) {\n> +\t\tstrbuf_addstr(&buf, accept_language);\n> +\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n\nThat looks like a roundabout way to say xstrdup().  The whole thing\ncan be done like so:\n\n\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n\nAnd by doing so we kill another bug.  \"struct rpc\" is allocated on\nthe stack without any initialization, so the new code leaves the\nhdr_accept_language member uninitialized.  Rather, we want to\nexplicitly set NULL to the member when the new header is not in use.\n\n> +\t}\n> +\n\nThe memory ownership model for this new .hdr_accept_language member\nin the RPC struct seems to be that the struct owns the resource of\nthe member.\n\n>  \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n>  \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n>  \n> @@ -1400,6 +1412,7 @@ static int stateless_connect(const char *service_name)\n>  \tstruct discovery *discover;\n>  \tstruct rpc_state rpc;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> +\tconst char *accept_language;\n>  \n>  \t/*\n>  \t * Run the info/refs request and see if the server supports protocol\n> @@ -1418,6 +1431,9 @@ static int stateless_connect(const char *service_name)\n>  \t\tprintf(\"\\n\");\n>  \t\tfflush(stdout);\n>  \t}\n> +\taccept_language = http_get_accept_language_header();\n> +\tif (accept_language)\n> +\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n\nAnd this is in line with that memory ownership model.\n\n>  \trpc.service_name = service_name;\n>  \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n\nI however do not see anybody that actually freeing when rpc is\ndone.\n\nAre we adding a new memory leak?  Shouldn't we be releasing the\nresources held in rpc.hdr_accept_language when rpc goes out of\nscope?\n\n> diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\n> index 2f09ff4fac6..4288a279e9e 100755\n> --- a/t/t5541-http-push-smart.sh\n> +++ b/t/t5541-http-push-smart.sh\n> @@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n>  \t test $HEAD = $(git rev-parse --verify HEAD))\n>  '\n>  \n> +test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n> +\tcat >exp <<-\\EOF &&\n> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n> +\tEOF\n\nAs I already asked, do we need to use a language code that has never\nbeen used in our existing test to test this new codepath, or is it\nsufficient to reuse what we already know that will not cause problems\nin developers' testing environment, like those used in other\nexisting tests, like ko_KR, en_US, etc.  If the latter, I strongly\ndo not want to see a new language added to the test.  We are *not*\nin the business of testing the system locale support on the user's\nplatform.\n\n> +\tcd \"$ROOT_PATH\"/test_repo_clone &&\n> +\t: >path_lang &&\n> +\tgit add path_lang &&\n> +\ttest_tick &&\n> +\tgit commit -m path_lang &&\n> +\tHEAD=$(git rev-parse --verify HEAD) &&\n> +\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n\nIf this test, or existing tests in other scripts, do not actually\nrequire the LANGUAGE specified in the environment variable to be\n\"installed\" on the user's platform, then it might be an acceptable\nalternative to use a locale (like \"tlh_AQ\") that is implausible to\nexist on the user's system, but using what we already use in other\ntests would be the safest thing to do.\n\nUse ko_KR.UTF8 (and nothing else) like 5550 does with its first use\nof check_language helper.  Or using en_US is also fine, as that is\nalso used over there.\n\n> +\t! grep \"Expect: 100-continue\" err &&\n> +\n> +\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n> +\ttest_cmp exp err.language\n> +'\n> +\n>  test_expect_success 'push already up-to-date' '\n>  \tgit push\n>  '\n\nAs I already said, I do not think changes to the following two tests\nare warranted.\n\n> diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\n> diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\n\n\nThanks.\n"},{"id":"457007","messageId":"2022061010332322021051@oschina.cn","threadId":"57969","inReplyTo":"220609.86leu6thon.gmgdl@evledraar.gmail.com","subject":"Re: Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2022-06-10T02:38:38Z","receivedAt":"2022-06-10T02:38:43Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"Sorry, I mistakenly sent the reply email to you yesterday, I should click \"Reply all\" button :(\n\n>\n>On Wed, Jun 08 2022, Li Linchao via GitGitGadget wrote:\n>\n>> From: Cactusinhand <lilinchao@oschina.cn>\n>>\n>> Git server end's ability to accept Accept-Language header was introduced\n>> in f18604bbf2(http: add Accept-Language header if possible), but this is\n>> only used by very early phase of the transfer, that's HTTP GET request to\n>> discover references. For other phases, like POST request in the smart HTTP\n>> the server side don't know what language client speak.\n>>\n>> This patch teaches git client to learn end-user's preferred language and\n>> throw accept-language header to server side. Once server get this header\n>> it have ability to talk to end-user with language they understand, this\n>> would be very helpful for many non-English speakers.\n>\n>I may be missing something, but this is just the \"Accept-Language\" part\n>of this change, i.e. there is no \"round-tripping\" here of actually doing\n>the work on the server of doing setlocale(), no?\nYes,  here Git just holds this header message, and the actual work depends\non the git service providers, like Github, Gitlab, or Gitee. \n>\n>I think the end-goal of having the \"remote: \" messages translated, if\n>possible, is very worthwhile, but I'd always imagined we'd do that with\n>a protocol extension, because even if we do this with HTTP headers we\n>won't get the same over ssh/git transports.\nAs for ssh transport, can we use ssh environment to reach our goal?\n>\n>But then again we don't have protocol v2 push yet :(\n>\n>So perfect certainly shouldn't be the enemy of the good here, I just\n>wonder what the end-goal is and if there's a plan to get there.\n\nThanks."},{"id":"457009","messageId":"2022061011484327929877@oschina.cn","threadId":"57969","inReplyTo":"xmqqilp9gznd.fsf@gitster.g","subject":"Re: Re: [PATCH v2] remote-curl: send Accept-Language header to server","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2022-06-10T03:49:44Z","receivedAt":"2022-06-10T03:49:51Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":">\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> From: Li Linchao <lilinchao@oschina.cn>\n>>\n>> Git server end's ability to accept Accept-Language header was introduced\n>> in f18604bbf2(http: add Accept-Language header if possible), but this is\n>\n>Pleaes refer to the commit like so:\n>\n>    f18604bb (http: add Accept-Language header if possible, 2015-01-28)\n>\n>(cf. Documentation/SubmittingPatches::commit-reference)\n>\n>\"git show -s --pretty=reference f18604bb\" is one way to format a\n>commit name in that format.\n> \nOK, thanks for reminding.\n>> only used by very early phase of the transfer, that's HTTP GET request to\n>\n>\"that's\" -> \"which is\", probably. \nOK.\n>\n>> discover references. For other phases, like POST request in the smart HTTP\n>> the server side don't know what language the client speaks.\n>\n>\"HTTP the server side don't\" -> \"HTTP, the server does not\"\n>\n>>  http.c                      |  4 ++--\n>>  http.h                      |  3 +++\n>>  remote-curl.c               | 16 ++++++++++++++++\n>>  t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n>>  t/t5550-http-fetch-dumb.sh  | 10 +++++-----\n>>  t/t5551-http-fetch-smart.sh | 10 ++++++++--\n>>  6 files changed, 53 insertions(+), 9 deletions(-)\n>\n>What is curious is that without any of changes to the *.[ch] files,\n>updated test 5550 and 5551 pass already.\n>\n>In other words, these updated tests in 5550 and 5551 probably are\n>not testing the behaviour the updated code intends to show.  Of\n>course, if we revert the code that taught the Accept-Language to the\n>GET requests in f18604bb, these tests will fail.  There is no reason\n>to touch these two tests to \"prove\" that the code change in this\n>patch does not break existing support, either. \nMy bad, the updated test in t5550 can not test the updated code, but test \nthe original code in f18604bb.\n>\n>> diff --git a/http.h b/http.h\n>> index ba303cfb372..3c94c479100 100644\n>> --- a/http.h\n>> +++ b/http.h\n>> @@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n>>  int http_get_info_packs(const char *base_url,\n>>  struct packed_git **packs_head);\n>> \n>> +/* Helper for getting Accept-Language header */\n>> +const char *http_get_accept_language_header(void);\n>\n>OK.\n>\n>> @@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n>>  headers = curl_slist_append(headers, needs_100_continue ?\n>>  \"Expect: 100-continue\" : \"Expect:\");\n>> \n>> +\t/* Add Accept-Language header */\n>> +\tif (rpc->hdr_accept_language)\n>> +\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n>\n>curl_slist_append() makes a copy of .hdr_accept_language, so rpc\n>struct is still responsible to release the resource used for the\n>member when it goes out of scope.\n>\n>> +\taccept_language = http_get_accept_language_header();\n>> +\tif (accept_language) {\n>> +\tstrbuf_addstr(&buf, accept_language);\n>> +\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n>\n>That looks like a roundabout way to say xstrdup().  The whole thing\n>can be done like so:\n>\n>\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n>\n>And by doing so we kill another bug.  \"struct rpc\" is allocated on\n>the stack without any initialization, so the new code leaves the\n>hdr_accept_language member uninitialized.  Rather, we want to\n>explicitly set NULL to the member when the new header is not in use.\n>\n>> +\t}\n>> +\n>\n>The memory ownership model for this new .hdr_accept_language member\n>in the RPC struct seems to be that the struct owns the resource of\n>the member.\n>\n>>  strbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n>>  rpc->hdr_content_type = strbuf_detach(&buf, NULL);\n>> \n>> @@ -1400,6 +1412,7 @@ static int stateless_connect(const char *service_name)\n>>  struct discovery *discover;\n>>  struct rpc_state rpc;\n>>  struct strbuf buf = STRBUF_INIT;\n>> +\tconst char *accept_language;\n>> \n>>  /*\n>>  * Run the info/refs request and see if the server supports protocol\n>> @@ -1418,6 +1431,9 @@ static int stateless_connect(const char *service_name)\n>>  printf(\"\\n\");\n>>  fflush(stdout);\n>>  }\n>> +\taccept_language = http_get_accept_language_header();\n>> +\tif (accept_language)\n>> +\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n>\n>And this is in line with that memory ownership model.\n>\n>>  rpc.service_name = service_name;\n>>  rpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n>\n>I however do not see anybody that actually freeing when rpc is\n>done.\n>\n>Are we adding a new memory leak?  Shouldn't we be releasing the\n>resources held in rpc.hdr_accept_language when rpc goes out of\n>scope? \nRight. we should free it in the end of the method, like so:\n        free(rpc.service_url);\n        free(rpc.hdr_content_type);\n        free(rpc.hdr_accept);\n+      free(rpc.hdr_accept_language);\n        free(rpc.protocol_header);\n        free(rpc.buf);\n        strbuf_release(&buf);\n\n>\n>> diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\n>> index 2f09ff4fac6..4288a279e9e 100755\n>> --- a/t/t5541-http-push-smart.sh\n>> +++ b/t/t5541-http-push-smart.sh\n>> @@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n>>  test $HEAD = $(git rev-parse --verify HEAD))\n>>  '\n>> \n>> +test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n>> +\tcat >exp <<-\\EOF &&\n>> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n>> +\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n>> +\tEOF\n>\n>As I already asked, do we need to use a language code that has never\n>been used in our existing test to test this new codepath, or is it\n>sufficient to reuse what we already know that will not cause problems\n>in developers' testing environment, like those used in other\n>existing tests, like ko_KR, en_US, etc.  If the latter, I strongly\n>do not want to see a new language added to the test.  We are *not*\n>in the business of testing the system locale support on the user's\n>platform. \nOK. \n>\n>> +\tcd \"$ROOT_PATH\"/test_repo_clone &&\n>> +\t: >path_lang &&\n>> +\tgit add path_lang &&\n>> +\ttest_tick &&\n>> +\tgit commit -m path_lang &&\n>> +\tHEAD=$(git rev-parse --verify HEAD) &&\n>> +\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n>\n>If this test, or existing tests in other scripts, do not actually\n>require the LANGUAGE specified in the environment variable to be\n>\"installed\" on the user's platform, then it might be an acceptable\n>alternative to use a locale (like \"tlh_AQ\") that is implausible to\n>exist on the user's system, but using what we already use in other\n>tests would be the safest thing to do.\n>\n>Use ko_KR.UTF8 (and nothing else) like 5550 does with its first use\n>of check_language helper.  Or using en_US is also fine, as that is\n>also used over there. \nOK.\n>\n>> +\t! grep \"Expect: 100-continue\" err &&\n>> +\n>> +\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n>> +\ttest_cmp exp err.language\n>> +'\n>> +\n>>  test_expect_success 'push already up-to-date' '\n>>  git push\n>>  '\n>\n>As I already said, I do not think changes to the following two tests\n>are warranted.\n>\n>> diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\n>> diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh \nWell, after I made some tests, the reason t5551 fail to test what we want is\n\"if test \"$GIT_TEST_PROTOCOL_VERSION\" = 2\" this statement block the real\ntest.\n>\n>\n>Thanks."},{"id":"457010","messageId":"2022061012214725558779@oschina.cn","threadId":"57969","inReplyTo":"2022061011484327929877@oschina.cn","subject":"Re: Re: [PATCH v2] remote-curl: send Accept-Language header to server","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2022-06-10T04:22:48Z","receivedAt":"2022-06-10T04:23:04Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n>>As I already said, I do not think changes to the following two tests\n>>are warranted.\n>>\n>>> diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\n>>> diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\n>Well, after I made some tests, the reason t5551 fail to test what we want is\n>\"if test \"$GIT_TEST_PROTOCOL_VERSION\" = 2\" this statement block the real\n>test. \nFix: '\"$GIT_TEST_PROTOCOL_VERSION\" = 0'\n>>\n>>\n>>Thanks."},{"id":"457090","messageId":"pull.1251.v3.git.1655054421697.gitgitgadget@gmail.com","threadId":"57969","inReplyTo":"pull.1251.v2.git.1654756523475.gitgitgadget@gmail.com","subject":"[PATCH v3] remote-curl: send Accept-Language header to server","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-06-12T17:20:21Z","receivedAt":"2022-06-12T17:20:28Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: Li Linchao <lilinchao@oschina.cn>\n\nGit server end's ability to accept Accept-Language header was introduced\nin f18604bbf2 (http: add Accept-Language header if possible, 2015-01-28),\nbut this is only used by very early phase of the transfer, which is HTTP\nGET request to discover references. For other phases, like POST request\nin the smart HTTP, the server does not know what language the client\nspeaks.\n\nTeach git client to learn end-user's preferred language and throw\naccept-language header to the server side. Once the server gets this header,\nit has the ability to talk to end-user with language they understand.\nThis would be very helpful for many non-English speakers.\n\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    remote-curl: send Accept-Language header to server\n    \n    Change since v2:\n    \n     * free rpc.hdr_accept_language to avoid memory leak\n     * fix test to reuse language to avoid install new language pack\n     * reword commit message\n    \n    Changes since v1:\n    \n     * change get_accept_language() to http_get_accept_language_header()\n     * reuse test case in t5550\n     * reword commit message\n    \n    TODO: For SSH tranport, give it an environment variable to understand\n    locale language.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1251%2FCactusinhand%2Fllc%2Fsend-Accept-Language-header-to-HTTP-server-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1251/Cactusinhand/llc/send-Accept-Language-header-to-HTTP-server-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1251\n\nRange-diff vs v2:\n\n 1:  a2dd9d4070e ! 1:  99a4e23ceb1 remote-curl: send Accept-Language header to server\n     @@ Commit message\n          remote-curl: send Accept-Language header to server\n      \n          Git server end's ability to accept Accept-Language header was introduced\n     -    in f18604bbf2(http: add Accept-Language header if possible), but this is\n     -    only used by very early phase of the transfer, that's HTTP GET request to\n     -    discover references. For other phases, like POST request in the smart HTTP\n     -    the server side don't know what language the client speaks.\n     +    in f18604bbf2 (http: add Accept-Language header if possible, 2015-01-28),\n     +    but this is only used by very early phase of the transfer, which is HTTP\n     +    GET request to discover references. For other phases, like POST request\n     +    in the smart HTTP, the server does not know what language the client\n     +    speaks.\n      \n          Teach git client to learn end-user's preferred language and throw\n          accept-language header to the server side. Once the server gets this header,\n     @@ remote-curl.c: static int post_rpc(struct rpc_state *rpc, int stateless_connect,\n       \t/* Add the extra Git-Protocol header */\n       \tif (rpc->protocol_header)\n       \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n     -@@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n     - \t\t       struct strbuf *rpc_result)\n     - {\n     - \tconst char *svc = rpc->service_name;\n     -+\tconst char *accept_language;\n     - \tstruct strbuf buf = STRBUF_INIT;\n     - \tstruct child_process client = CHILD_PROCESS_INIT;\n     - \tint err = 0;\n      @@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n       \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n       \trpc->service_url = strbuf_detach(&buf, NULL);\n       \n     -+\taccept_language = http_get_accept_language_header();\n     -+\tif (accept_language) {\n     -+\t\tstrbuf_addstr(&buf, accept_language);\n     -+\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n     -+\t}\n     ++\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n      +\n       \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n       \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n       \n     +@@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n     + \tfree(rpc->service_url);\n     + \tfree(rpc->hdr_content_type);\n     + \tfree(rpc->hdr_accept);\n     ++\tfree(rpc->hdr_accept_language);\n     + \tfree(rpc->protocol_header);\n     + \tfree(rpc->buf);\n     + \tstrbuf_release(&buf);\n      @@ remote-curl.c: static int stateless_connect(const char *service_name)\n       \tstruct discovery *discover;\n       \tstruct rpc_state rpc;\n     @@ remote-curl.c: static int stateless_connect(const char *service_name)\n       \n       \trpc.service_name = service_name;\n       \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n     +@@ remote-curl.c: static int stateless_connect(const char *service_name)\n     + \tfree(rpc.service_url);\n     + \tfree(rpc.hdr_content_type);\n     + \tfree(rpc.hdr_accept);\n     ++\tfree(rpc.hdr_accept_language);\n     + \tfree(rpc.protocol_header);\n     + \tfree(rpc.buf);\n     + \tstrbuf_release(&buf);\n      \n       ## t/t5541-http-push-smart.sh ##\n      @@ t/t5541-http-push-smart.sh: test_expect_success 'push to remote repository (standard)' '\n     @@ t/t5541-http-push-smart.sh: test_expect_success 'push to remote repository (stan\n       \n      +test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n      +\tcat >exp <<-\\EOF &&\n     -+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n     -+\t=> Send header: Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n     ++\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n     ++\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n      +\tEOF\n      +\n      +\tcd \"$ROOT_PATH\"/test_repo_clone &&\n     @@ t/t5541-http-push-smart.sh: test_expect_success 'push to remote repository (stan\n      +\ttest_tick &&\n      +\tgit commit -m path_lang &&\n      +\tHEAD=$(git rev-parse --verify HEAD) &&\n     -+\tGIT_TRACE_CURL=true LANGUAGE=\"zh_CN:en\" git push -v -v 2>err &&\n     ++\tGIT_TRACE_CURL=true LANGUAGE=\"ko_KR.UTF-8\" git push -v -v 2>err &&\n      +\t! grep \"Expect: 100-continue\" err &&\n      +\n      +\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n     @@ t/t5541-http-push-smart.sh: test_expect_success 'push to remote repository (stan\n       '\n      \n       ## t/t5550-http-fetch-dumb.sh ##\n     -@@ t/t5550-http-fetch-dumb.sh: test_expect_success 'git client sends Accept-Language correctly with unordinary\n     - \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR::en_US\" &&\n     - \tcheck_language \"ko-KR, *;q=0.9\" \":::ko_KR\" &&\n     - \tcheck_language \"ko-KR, en-US;q=0.9, *;q=0.8\" \"ko_KR!!:en_US\" &&\n     --\tcheck_language \"ko-KR, ja-JP;q=0.9, *;q=0.8\" \"ko_KR en_US:ja_JP\"'\n     -+\tcheck_language \"ko-KR, ja-JP;q=0.9, zh-CN;q=0.8, *;q=0.7\" \"ko_KR en_US:ja_JP:zh_CN\"'\n     - \n     - test_expect_success 'git client sends Accept-Language with many preferred languages' '\n     --\tcheck_language \"ko-KR, en-US;q=0.9, fr-CA;q=0.8, de;q=0.7, sr;q=0.6, \\\n     --ja;q=0.5, zh;q=0.4, sv;q=0.3, pt;q=0.2, *;q=0.1\" \\\n     --\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt &&\n     -+\tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n     -+ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, zh-CN;q=0.91, *;q=0.90\" \\\n     -+\t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:zh_CN &&\n     - \tcheck_language \"ko-KR, en-US;q=0.99, fr-CA;q=0.98, de;q=0.97, sr;q=0.96, \\\n     - ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n     +@@ t/t5550-http-fetch-dumb.sh: ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n       \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n       '\n       \n     @@ t/t5551-http-fetch-smart.sh: test_expect_success 'clone http repository' '\n       \t> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1\n       \t> Accept: */*\n       \t> Accept-Encoding: ENCODINGS\n     -+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n     ++\t> Accept-Language: ko-KR, *;q=0.9\n       \t> Pragma: no-cache\n       \t< HTTP/1.1 200 OK\n       \t< Pragma: no-cache\n     @@ t/t5551-http-fetch-smart.sh: test_expect_success 'clone http repository' '\n       \t> Accept-Encoding: ENCODINGS\n       \t> Content-Type: application/x-git-upload-pack-request\n       \t> Accept: application/x-git-upload-pack-result\n     -+\t> Accept-Language: zh-CN, en;q=0.9, *;q=0.8\n     ++\t> Accept-Language: ko-KR, *;q=0.9\n       \t> Content-Length: xxx\n       \t< HTTP/1.1 200 OK\n       \t< Pragma: no-cache\n     @@ t/t5551-http-fetch-smart.sh: test_expect_success 'clone http repository' '\n       \tEOF\n      -\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \\\n      +\n     -+\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"zh_CN:en\" \\\n     ++\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"ko_KR.UTF-8\" \\\n       \t\tgit clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&\n       \ttest_cmp file clone/file &&\n       \ttr '\\''\\015'\\'' Q <err |\n     @@ t/t5551-http-fetch-smart.sh: test_expect_success 'clone http repository' '\n      -\t\ttest_line_count = 2 actual.gzip\n      +\t\ttest_line_count = 2 actual.gzip &&\n      +\n     -+\t\tgrep \"Accept-Language: zh-CN, en\" actual >actual.language &&\n     ++\t\tgrep \"Accept-Language: ko-KR, *\" actual >actual.language &&\n      +\t\ttest_line_count = 2 actual.language\n       \tfi\n       '\n\n\n http.c                      |  4 ++--\n http.h                      |  3 +++\n remote-curl.c               | 13 +++++++++++++\n t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n t/t5550-http-fetch-dumb.sh  |  2 +-\n t/t5551-http-fetch-smart.sh | 10 ++++++++--\n 6 files changed, 46 insertions(+), 5 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 11c6f69facd..33301d8d5d5 100644\n--- a/http.c\n+++ b/http.c\n@@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)\n  *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n  *   LANGUAGE= LANG=C -> \"\"\n  */\n-static const char *get_accept_language(void)\n+const char *http_get_accept_language_header(void)\n {\n \tif (!cached_accept_language) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -1829,7 +1829,7 @@ static int http_request(const char *url,\n \t\t\t\t\t fwrite_buffer);\n \t}\n \n-\taccept_language = get_accept_language();\n+\taccept_language = http_get_accept_language_header();\n \n \tif (accept_language)\n \t\theaders = curl_slist_append(headers, accept_language);\ndiff --git a/http.h b/http.h\nindex ba303cfb372..3c94c479100 100644\n--- a/http.h\n+++ b/http.h\n@@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n int http_get_info_packs(const char *base_url,\n \t\t\tstruct packed_git **packs_head);\n \n+/* Helper for getting Accept-Language header */\n+const char *http_get_accept_language_header(void);\n+\n struct http_pack_request {\n \tchar *url;\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex 67f178b1120..251d4ee64f6 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -580,6 +580,7 @@ struct rpc_state {\n \tchar *service_url;\n \tchar *hdr_content_type;\n \tchar *hdr_accept;\n+\tchar *hdr_accept_language;\n \tchar *protocol_header;\n \tchar *buf;\n \tsize_t alloc;\n@@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n \theaders = curl_slist_append(headers, needs_100_continue ?\n \t\t\"Expect: 100-continue\" : \"Expect:\");\n \n+\t/* Add Accept-Language header */\n+\tif (rpc->hdr_accept_language)\n+\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n+\n \t/* Add the extra Git-Protocol header */\n \tif (rpc->protocol_header)\n \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n@@ -1080,6 +1085,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n \trpc->service_url = strbuf_detach(&buf, NULL);\n \n+\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n+\n \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n \n@@ -1118,6 +1125,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tfree(rpc->service_url);\n \tfree(rpc->hdr_content_type);\n \tfree(rpc->hdr_accept);\n+\tfree(rpc->hdr_accept_language);\n \tfree(rpc->protocol_header);\n \tfree(rpc->buf);\n \tstrbuf_release(&buf);\n@@ -1400,6 +1408,7 @@ static int stateless_connect(const char *service_name)\n \tstruct discovery *discover;\n \tstruct rpc_state rpc;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *accept_language;\n \n \t/*\n \t * Run the info/refs request and see if the server supports protocol\n@@ -1418,6 +1427,9 @@ static int stateless_connect(const char *service_name)\n \t\tprintf(\"\\n\");\n \t\tfflush(stdout);\n \t}\n+\taccept_language = http_get_accept_language_header();\n+\tif (accept_language)\n+\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n \n \trpc.service_name = service_name;\n \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n@@ -1467,6 +1479,7 @@ static int stateless_connect(const char *service_name)\n \tfree(rpc.service_url);\n \tfree(rpc.hdr_content_type);\n \tfree(rpc.hdr_accept);\n+\tfree(rpc.hdr_accept_language);\n \tfree(rpc.protocol_header);\n \tfree(rpc.buf);\n \tstrbuf_release(&buf);\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 2f09ff4fac6..fbad2d5ff5e 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n \t test $HEAD = $(git rev-parse --verify HEAD))\n '\n \n+test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n+\tcat >exp <<-\\EOF &&\n+\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n+\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n+\tEOF\n+\n+\tcd \"$ROOT_PATH\"/test_repo_clone &&\n+\t: >path_lang &&\n+\tgit add path_lang &&\n+\ttest_tick &&\n+\tgit commit -m path_lang &&\n+\tHEAD=$(git rev-parse --verify HEAD) &&\n+\tGIT_TRACE_CURL=true LANGUAGE=\"ko_KR.UTF-8\" git push -v -v 2>err &&\n+\t! grep \"Expect: 100-continue\" err &&\n+\n+\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n+\ttest_cmp exp err.language\n+'\n+\n test_expect_success 'push already up-to-date' '\n \tgit push\n '\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex f0d9cd584d3..bc308519af5 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -369,7 +369,7 @@ ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n '\n \n-test_expect_success 'git client does not send an empty Accept-Language' '\n+test_expect_success 'git client send an empty Accept-Language' '\n \tGIT_TRACE_CURL=true LANGUAGE= git ls-remote \"$HTTPD_URL/dumb/repo.git\" 2>stderr &&\n \t! grep \"^=> Send header: Accept-Language:\" stderr\n '\ndiff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\nindex b9351a732f6..245532df881 100755\n--- a/t/t5551-http-fetch-smart.sh\n+++ b/t/t5551-http-fetch-smart.sh\n@@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '\n \t> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1\n \t> Accept: */*\n \t> Accept-Encoding: ENCODINGS\n+\t> Accept-Language: ko-KR, *;q=0.9\n \t> Pragma: no-cache\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n@@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '\n \t> Accept-Encoding: ENCODINGS\n \t> Content-Type: application/x-git-upload-pack-request\n \t> Accept: application/x-git-upload-pack-result\n+\t> Accept-Language: ko-KR, *;q=0.9\n \t> Content-Length: xxx\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n \t< Cache-Control: no-cache, max-age=0, must-revalidate\n \t< Content-Type: application/x-git-upload-pack-result\n \tEOF\n-\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \\\n+\n+\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"ko_KR.UTF-8\" \\\n \t\tgit clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&\n \ttest_cmp file clone/file &&\n \ttr '\\''\\015'\\'' Q <err |\n@@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '\n \t\ttest_cmp exp actual.smudged &&\n \n \t\tgrep \"Accept-Encoding:.*gzip\" actual >actual.gzip &&\n-\t\ttest_line_count = 2 actual.gzip\n+\t\ttest_line_count = 2 actual.gzip &&\n+\n+\t\tgrep \"Accept-Language: ko-KR, *\" actual >actual.language &&\n+\t\ttest_line_count = 2 actual.language\n \tfi\n '\n \n\nbase-commit: ab336e8f1c8009c8b1aab8deb592148e69217085\n-- \ngitgitgadget\n"},{"id":"457112","messageId":"xmqqk09k77m3.fsf@gitster.g","threadId":"57969","inReplyTo":"pull.1251.v3.git.1655054421697.gitgitgadget@gmail.com","subject":"Re: [PATCH v3] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-13T18:15:16Z","receivedAt":"2022-06-13T19:44:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Range-diff vs v2:\n>\n>  1:  a2dd9d4070e ! 1:  99a4e23ceb1 remote-curl: send Accept-Language header to server\n>      @@ Commit message\n>           remote-curl: send Accept-Language header to server\n>       \n>           Git server end's ability to accept Accept-Language header was introduced\n>      -    in f18604bbf2(http: add Accept-Language header if possible), but this is\n>      -    only used by very early phase of the transfer, that's HTTP GET request to\n>      -    discover references. For other phases, like POST request in the smart HTTP\n>      -    the server side don't know what language the client speaks.\n>      +    in f18604bbf2 (http: add Accept-Language header if possible, 2015-01-28),\n>      +    but this is only used by very early phase of the transfer, which is HTTP\n>      +    GET request to discover references. For other phases, like POST request\n>      +    in the smart HTTP, the server does not know what language the client\n>      +    speaks.\n\n\nOK.\n\n>      -+\taccept_language = http_get_accept_language_header();\n>      -+\tif (accept_language) {\n>      -+\t\tstrbuf_addstr(&buf, accept_language);\n>      -+\t\trpc->hdr_accept_language = strbuf_detach(&buf, NULL);\n>      -+\t}\n>      ++\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n\nNice.\n\n>      +@@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n>      + \tfree(rpc->service_url);\n>      + \tfree(rpc->hdr_content_type);\n>      + \tfree(rpc->hdr_accept);\n>      ++\tfree(rpc->hdr_accept_language);\n>      + \tfree(rpc->protocol_header);\n>      + \tfree(rpc->buf);\n>      + \tstrbuf_release(&buf);\n\nOK.\n\n>      +@@ remote-curl.c: static int stateless_connect(const char *service_name)\n>      + \tfree(rpc.service_url);\n>      + \tfree(rpc.hdr_content_type);\n>      + \tfree(rpc.hdr_accept);\n>      ++\tfree(rpc.hdr_accept_language);\n>      + \tfree(rpc.protocol_header);\n>      + \tfree(rpc.buf);\n>      + \tstrbuf_release(&buf);\n\nOK.\n\nThanks.  Will queue.\n"},{"id":"457128","messageId":"xmqq1qvs5jx4.fsf@gitster.g","threadId":"57969","inReplyTo":"pull.1251.v3.git.1655054421697.gitgitgadget@gmail.com","subject":"Re: [PATCH v3] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-13T21:32:23Z","receivedAt":"2022-06-13T21:33:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> @@ -932,6 +933,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n>  \theaders = curl_slist_append(headers, needs_100_continue ?\n>  \t\t\"Expect: 100-continue\" : \"Expect:\");\n>  \n> +\t/* Add Accept-Language header */\n> +\tif (rpc->hdr_accept_language)\n> +\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n> +\n>  \t/* Add the extra Git-Protocol header */\n>  \tif (rpc->protocol_header)\n>  \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n> @@ -1080,6 +1085,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n>  \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n>  \trpc->service_url = strbuf_detach(&buf, NULL);\n>  \n> +\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n> +\n>  \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n>  \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n>  \n> @@ -1118,6 +1125,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n>  \tfree(rpc->service_url);\n>  \tfree(rpc->hdr_content_type);\n>  \tfree(rpc->hdr_accept);\n> +\tfree(rpc->hdr_accept_language);\n>  \tfree(rpc->protocol_header);\n>  \tfree(rpc->buf);\n>  \tstrbuf_release(&buf);\n> @@ -1400,6 +1408,7 @@ static int stateless_connect(const char *service_name)\n>  \tstruct discovery *discover;\n>  \tstruct rpc_state rpc;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> +\tconst char *accept_language;\n>  \n>  \t/*\n>  \t * Run the info/refs request and see if the server supports protocol\n> @@ -1418,6 +1427,9 @@ static int stateless_connect(const char *service_name)\n>  \t\tprintf(\"\\n\");\n>  \t\tfflush(stdout);\n>  \t}\n> +\taccept_language = http_get_accept_language_header();\n> +\tif (accept_language)\n> +\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n\nIsn't rpc.hdr_accept_language left uninitialized garbage if\naccept_language is NULL?  It is the same bug I pointed out earlier,\nwhose fix may have to be different.\n\nHas this been tested?  I got immediate segfault with this patch in\n'seen'.\n\n"},{"id":"457132","messageId":"xmqqedzs43o0.fsf@gitster.g","threadId":"57969","inReplyTo":"xmqq1qvs5jx4.fsf@gitster.g","subject":"Re: [PATCH v3] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-13T22:08:47Z","receivedAt":"2022-06-13T22:08:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +\taccept_language = http_get_accept_language_header();\n>> +\tif (accept_language)\n>> +\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n>\n> Isn't rpc.hdr_accept_language left uninitialized garbage if\n> accept_language is NULL?  It is the same bug I pointed out earlier,\n> whose fix may have to be different.\n>\n> Has this been tested?  I got immediate segfault with this patch in\n> 'seen'.\n\nHaving said all that, I wonder if we want to use something like this\nto make it hard to use an uninitialized data.\n\nThe smart-http is quite outside of my area of expertise, and I do\nnot know what Shawn was thinking when de1a2fdd (Smart push over\nHTTP: client side, 2009-10-30) was written (it could be that filling\nall members explicitly was the more prevalent stype back then?).\nI'd appreciate input from folks who regularly deal with smart-http\non the approach.\n\nThanks.\n\n remote-curl.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git i/remote-curl.c w/remote-curl.c\nindex 251d4ee64f..ba6f76a0c1 100644\n--- i/remote-curl.c\n+++ w/remote-curl.c\n@@ -608,6 +608,8 @@ struct rpc_state {\n \tunsigned flush_read_but_not_sent : 1;\n };\n \n+#define RPC_STATE_INIT { 0, }\n+\n /*\n  * Appends the result of reading from rpc->out to the string represented by\n  * rpc->buf and rpc->len if there is enough space. Returns 1 if there was\n@@ -1161,7 +1163,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)\n static int fetch_git(struct discovery *heads,\n \tint nr_heads, struct ref **to_fetch)\n {\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tstruct strbuf preamble = STRBUF_INIT;\n \tint i, err;\n \tstruct strvec args = STRVEC_INIT;\n@@ -1307,7 +1309,7 @@ static int push_dav(int nr_spec, const char **specs)\n \n static int push_git(struct discovery *heads, int nr_spec, const char **specs)\n {\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tint i, err;\n \tstruct strvec args;\n \tstruct string_list_item *cas_option;\n@@ -1406,7 +1408,7 @@ static void parse_push(struct strbuf *buf)\n static int stateless_connect(const char *service_name)\n {\n \tstruct discovery *discover;\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *accept_language;\n \n"},{"id":"457133","messageId":"xmqq7d5k43db.fsf@gitster.g","threadId":"57969","inReplyTo":"xmqqedzs43o0.fsf@gitster.g","subject":"Re: [PATCH v3] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-13T22:15:12Z","receivedAt":"2022-06-13T22:15:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> +#define RPC_STATE_INIT { 0, }\n\nMake it\n\n\t#define RPC_STATE_INIT { 0 }\n\nCuriously, the former form with trailing comma makes sparse unhappy,\nwhile it seems that the latter is taken as a special form \"idiom\".\n"},{"id":"458430","messageId":"xmqq1qv3m2rf.fsf@gitster.g","threadId":"57969","inReplyTo":"2022061010332322021051@oschina.cn","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-03T00:57:40Z","receivedAt":"2022-07-03T00:57:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n\n>>I think the end-goal of having the \"remote: \" messages translated, if\n>>possible, is very worthwhile, but I'd always imagined we'd do that with\n>>a protocol extension, because even if we do this with HTTP headers we\n>>won't get the same over ssh/git transports.\n>\n> As for ssh transport, can we use ssh environment to reach our goal?\n\nNot really.  Before forcing us to invent completely separate\nmechanisms for different transports, it is a very good idea to\nconsider if we can use a single mechanism that can apply to all\ntransports.  Adding something at the protocol level would be a\nstep in the right direction.\n"},{"id":"458474","messageId":"202207051804341356418@oschina.cn","threadId":"57969","inReplyTo":"xmqq1qv3m2rf.fsf@gitster.g","subject":"Re: Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2022-07-05T10:06:25Z","receivedAt":"2022-07-05T10:06:41Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":">\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n>\n>>>I think the end-goal of having the \"remote: \" messages translated, if\n>>>possible, is very worthwhile, but I'd always imagined we'd do that with\n>>>a protocol extension, because even if we do this with HTTP headers we\n>>>won't get the same over ssh/git transports.\n>>\n>> As for ssh transport, can we use ssh environment to reach our goal?\n>\n>Not really.  Before forcing us to invent completely separate\n>mechanisms for different transports, it is a very good idea to\n>consider if we can use a single mechanism that can apply to all\n>transports.  Adding something at the protocol level would be a\n>step in the right direction.\nI wonder if we can use a new protocol-capability like local-lang or \nsomething else, then Git client and server can tell each other's language\nability in the negotiation stage."},{"id":"458475","messageId":"220705.86o7y3am2m.gmgdl@evledraar.gmail.com","threadId":"57969","inReplyTo":"202207051804341356418@oschina.cn","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-05T10:15:44Z","receivedAt":"2022-07-05T10:31:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jul 05 2022, lilinchao@oschina.cn wrote:\n\n>>\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n>>\n>>>>I think the end-goal of having the \"remote: \" messages translated, if\n>>>>possible, is very worthwhile, but I'd always imagined we'd do that with\n>>>>a protocol extension, because even if we do this with HTTP headers we\n>>>>won't get the same over ssh/git transports.\n>>>\n>>> As for ssh transport, can we use ssh environment to reach our goal?\n>>\n>>Not really.  Before forcing us to invent completely separate\n>>mechanisms for different transports, it is a very good idea to\n>>consider if we can use a single mechanism that can apply to all\n>>transports.  Adding something at the protocol level would be a\n>>step in the right direction.\n> I wonder if we can use a new protocol-capability like local-lang or \n> something else, then Git client and server can tell each other's language\n> ability in the negotiation stage.\n\nIt would make sense to call this protocol verb \"setenv\", and just give\nit support for setting arbitrary remote environment, which we'd then\nhave a whitelist configuration variable for, similar to how sshd(1) does\nit.\n\nOr maybe we can just add this as a \"capability\", which seems like a more\nnatural fit, we could even stick it into \"agent\" I guess...\n\nThen it becomes a very thin layer for emulating what we already get with\nthe ssh and http(s) transport(s).\n\nAnyway, while it definitely would be an improvement to pass this along,\na much better way to go IMO (but also harder) is to extend the protocol\nso that we don't a emita human language at all, but emit defined error\nstates for our various known errors.\n\nI have some WIP incomplete patches in that direction somewhere (that I\nhaven't dug up now), and part of it is currently stalled on there being\nno \"push\" support in protocol v2.\n\nBut if we can get to that point it would make for much better UX, even\nif you get e.g. \"git push\" errors in your language now your editor/IDE\nis probably needing to spew terminal \"git push\" output at you as-is, it\nwould make for better UX if it could just render it as it prefers to.\n\nThis would also mean you'd get translations even if the server doesn't\nhave the needed *.po files.\n\nIt *isn't* a full replacement, e.g. if you have a custom hook having the\nlocale is still useful, but for anything that's git native...\n\nFWIW I think the WIP patches I'm referencing were only to upload-pack.c,\ni.e. to make various parts where it calls die() send over ERR packet(s)\ninstead, and either pick that up magically on the client-side, or add a\nnew ERR_CODE packet or whatever (I can't remember...).\n"},{"id":"458488","messageId":"xmqqy1x7jviq.fsf@gitster.g","threadId":"57969","inReplyTo":"202207051804341356418@oschina.cn","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-05T17:53:49Z","receivedAt":"2022-07-05T17:53:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n\n> I wonder if we can use a new protocol-capability like local-lang\n> or something else, then Git client and server can tell each\n> other's language ability in the negotiation stage.\n\nThat is how I read Ævar's message you were responding to.\n\nHaving said that, a mechanism that applies only to HTTP transport\nwas already there, and the patch in question recognises that the\ncoverage of the mechanism is incomplete even within the HTTP\ncodepath and attempts to make it more complete.  Unless we are\nplanning to deprecate the HTTP specific mechanism and replace it\nwith protocol-capability to cover other protocols in the same way,\nI do not think it is a bad idea to have it.\n\nThanks.\n"},{"id":"458489","messageId":"xmqqr12zjuyf.fsf@gitster.g","threadId":"57969","inReplyTo":"220705.86o7y3am2m.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH] remote-curl: send Accept-Language header to server","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-05T18:06:00Z","receivedAt":"2022-07-05T18:06:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> It would make sense to call this protocol verb \"setenv\", and just give\n> it support for setting arbitrary remote environment, which we'd then\n> have a whitelist configuration variable for, similar to how sshd(1) does\n> it.\n\nSounds like a security hole in the making, with dubious risk-benefit\ntradeoff.  I cannot honestly answer favourably to this question if\nsomebody asked me as the project read: Are you solving a real-world\nproblem, or creating one?\n\n> Or maybe we can just add this as a \"capability\", which seems like a more\n> natural fit,\n\nI took your suggestion upthread to be hinting this.\n\n> we could even stick it into \"agent\" I guess...\n\nBut not this.\n\n> Anyway, while it definitely would be an improvement to pass this along,\n> a much better way to go IMO (but also harder) is to extend the protocol\n> so that we don't a emita human language at all, but emit defined error\n> states for our various known errors.\n\nI would not go there.\n\nThe end that sends errors in status code may be running a newer\nversion of the software and the particular status code it sent is so\nnew that the receiving end does not know how to translate it into\nhuman language.\n\nDoing the Accept-Language at the HTTP level, or its equivalent at\nthe protocol-capability level, has the opposite problem that the\nremote end may not know the requested language at all, but at least\nthe side that sends unlocalized messages is aware of it doing so.\n\nAlso the error message sideband carries the same messages that are\nmeant to be read by humans in Git subcommands that are run by the\nprotocol software as well as human users.  We could introduce such a\n\"error status code\" language as an artificial locale, translate\n_(\"...\") messages into such \"status code language\" on the end that\nsends errors, and then re-translate them into human language locale,\nbut it is of a dubious value.  Such an approach would not work well\nat the gettext layer anyway, as we need to deal with placeholders,\nso it would be a lot more involved than just \"lets have catalog of\nprintf formatting templates and translate them\".\n"},{"id":"458752","messageId":"pull.1251.v4.git.1657519134336.gitgitgadget@gmail.com","threadId":"57969","inReplyTo":"pull.1251.v3.git.1655054421697.gitgitgadget@gmail.com","subject":"[PATCH v4] remote-curl: send Accept-Language header to server","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-07-11T05:58:54Z","receivedAt":"2022-07-11T05:59:02Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: Li Linchao <lilinchao@oschina.cn>\n\nGit server end's ability to accept Accept-Language header was introduced\nin f18604bbf2 (http: add Accept-Language header if possible, 2015-01-28),\nbut this is only used by very early phase of the transfer, which is HTTP\nGET request to discover references. For other phases, like POST request\nin the smart HTTP, the server does not know what language the client\nspeaks.\n\nTeach git client to learn end-user's preferred language and throw\naccept-language header to the server side. Once the server gets this header,\nit has the ability to talk to end-user with language they understand.\nThis would be very helpful for many non-English speakers.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    remote-curl: send Accept-Language header to server\n    \n    Changes sin v3:\n    \n     * fix rpc_state initialization issue\n    \n    Changes since v2:\n    \n     * free rpc.hdr_accept_language to avoid memory leak\n     * fix test to reuse language to avoid install new language pack\n     * reword commit message\n    \n    Changes since v1:\n    \n     * change get_accept_language() to http_get_accept_language_header()\n     * reuse test case in t5550\n     * reword commit message\n    \n    TODO: For SSH tranport, give it an environment variable to understand\n    locale language.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1251%2FCactusinhand%2Fllc%2Fsend-Accept-Language-header-to-HTTP-server-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1251/Cactusinhand/llc/send-Accept-Language-header-to-HTTP-server-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1251\n\nRange-diff vs v3:\n\n 1:  99a4e23ceb1 ! 1:  3bec1e85e26 remote-curl: send Accept-Language header to server\n     @@ Commit message\n          it has the ability to talk to end-user with language they understand.\n          This would be very helpful for many non-English speakers.\n      \n     +    Helped-by: Junio C Hamano <gitster@pobox.com>\n          Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n      \n       ## http.c ##\n     @@ remote-curl.c: struct rpc_state {\n       \tchar *protocol_header;\n       \tchar *buf;\n       \tsize_t alloc;\n     +@@ remote-curl.c: struct rpc_state {\n     + \tunsigned flush_read_but_not_sent : 1;\n     + };\n     + \n     ++#define RPC_STATE_INIT { 0 }\n     ++\n     + /*\n     +  * Appends the result of reading from rpc->out to the string represented by\n     +  * rpc->buf and rpc->len if there is enough space. Returns 1 if there was\n      @@ remote-curl.c: static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n       \theaders = curl_slist_append(headers, needs_100_continue ?\n       \t\t\"Expect: 100-continue\" : \"Expect:\");\n     @@ remote-curl.c: static int rpc_service(struct rpc_state *rpc, struct discovery *h\n       \tfree(rpc->protocol_header);\n       \tfree(rpc->buf);\n       \tstrbuf_release(&buf);\n     -@@ remote-curl.c: static int stateless_connect(const char *service_name)\n     +@@ remote-curl.c: static int fetch_dumb(int nr_heads, struct ref **to_fetch)\n     + static int fetch_git(struct discovery *heads,\n     + \tint nr_heads, struct ref **to_fetch)\n     + {\n     +-\tstruct rpc_state rpc;\n     ++\tstruct rpc_state rpc = RPC_STATE_INIT;\n     + \tstruct strbuf preamble = STRBUF_INIT;\n     + \tint i, err;\n     + \tstruct strvec args = STRVEC_INIT;\n     +@@ remote-curl.c: static int push_dav(int nr_spec, const char **specs)\n     + \n     + static int push_git(struct discovery *heads, int nr_spec, const char **specs)\n     + {\n     +-\tstruct rpc_state rpc;\n     ++\tstruct rpc_state rpc = RPC_STATE_INIT;\n     + \tint i, err;\n     + \tstruct strvec args;\n     + \tstruct string_list_item *cas_option;\n     +@@ remote-curl.c: free_specs:\n     + static int stateless_connect(const char *service_name)\n     + {\n       \tstruct discovery *discover;\n     - \tstruct rpc_state rpc;\n     +-\tstruct rpc_state rpc;\n     ++\tstruct rpc_state rpc = RPC_STATE_INIT;\n       \tstruct strbuf buf = STRBUF_INIT;\n      +\tconst char *accept_language;\n       \n\n\n http.c                      |  4 ++--\n http.h                      |  3 +++\n remote-curl.c               | 21 ++++++++++++++++++---\n t/t5541-http-push-smart.sh  | 19 +++++++++++++++++++\n t/t5550-http-fetch-dumb.sh  |  2 +-\n t/t5551-http-fetch-smart.sh | 10 ++++++++--\n 6 files changed, 51 insertions(+), 8 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex 168ca30c558..5d0502f51fd 100644\n--- a/http.c\n+++ b/http.c\n@@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)\n  *   LANGUAGE= LANG=en_US.UTF-8 -> \"Accept-Language: en-US, *; q=0.1\"\n  *   LANGUAGE= LANG=C -> \"\"\n  */\n-static const char *get_accept_language(void)\n+const char *http_get_accept_language_header(void)\n {\n \tif (!cached_accept_language) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -1829,7 +1829,7 @@ static int http_request(const char *url,\n \t\t\t\t\t fwrite_buffer);\n \t}\n \n-\taccept_language = get_accept_language();\n+\taccept_language = http_get_accept_language_header();\n \n \tif (accept_language)\n \t\theaders = curl_slist_append(headers, accept_language);\ndiff --git a/http.h b/http.h\nindex ba303cfb372..3c94c479100 100644\n--- a/http.h\n+++ b/http.h\n@@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);\n int http_get_info_packs(const char *base_url,\n \t\t\tstruct packed_git **packs_head);\n \n+/* Helper for getting Accept-Language header */\n+const char *http_get_accept_language_header(void);\n+\n struct http_pack_request {\n \tchar *url;\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex 67f178b1120..b8758757ece 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -580,6 +580,7 @@ struct rpc_state {\n \tchar *service_url;\n \tchar *hdr_content_type;\n \tchar *hdr_accept;\n+\tchar *hdr_accept_language;\n \tchar *protocol_header;\n \tchar *buf;\n \tsize_t alloc;\n@@ -607,6 +608,8 @@ struct rpc_state {\n \tunsigned flush_read_but_not_sent : 1;\n };\n \n+#define RPC_STATE_INIT { 0 }\n+\n /*\n  * Appends the result of reading from rpc->out to the string represented by\n  * rpc->buf and rpc->len if there is enough space. Returns 1 if there was\n@@ -932,6 +935,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece\n \theaders = curl_slist_append(headers, needs_100_continue ?\n \t\t\"Expect: 100-continue\" : \"Expect:\");\n \n+\t/* Add Accept-Language header */\n+\tif (rpc->hdr_accept_language)\n+\t\theaders = curl_slist_append(headers, rpc->hdr_accept_language);\n+\n \t/* Add the extra Git-Protocol header */\n \tif (rpc->protocol_header)\n \t\theaders = curl_slist_append(headers, rpc->protocol_header);\n@@ -1080,6 +1087,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tstrbuf_addf(&buf, \"%s%s\", url.buf, svc);\n \trpc->service_url = strbuf_detach(&buf, NULL);\n \n+\trpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());\n+\n \tstrbuf_addf(&buf, \"Content-Type: application/x-%s-request\", svc);\n \trpc->hdr_content_type = strbuf_detach(&buf, NULL);\n \n@@ -1118,6 +1127,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,\n \tfree(rpc->service_url);\n \tfree(rpc->hdr_content_type);\n \tfree(rpc->hdr_accept);\n+\tfree(rpc->hdr_accept_language);\n \tfree(rpc->protocol_header);\n \tfree(rpc->buf);\n \tstrbuf_release(&buf);\n@@ -1153,7 +1163,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)\n static int fetch_git(struct discovery *heads,\n \tint nr_heads, struct ref **to_fetch)\n {\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tstruct strbuf preamble = STRBUF_INIT;\n \tint i, err;\n \tstruct strvec args = STRVEC_INIT;\n@@ -1299,7 +1309,7 @@ static int push_dav(int nr_spec, const char **specs)\n \n static int push_git(struct discovery *heads, int nr_spec, const char **specs)\n {\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tint i, err;\n \tstruct strvec args;\n \tstruct string_list_item *cas_option;\n@@ -1398,8 +1408,9 @@ free_specs:\n static int stateless_connect(const char *service_name)\n {\n \tstruct discovery *discover;\n-\tstruct rpc_state rpc;\n+\tstruct rpc_state rpc = RPC_STATE_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *accept_language;\n \n \t/*\n \t * Run the info/refs request and see if the server supports protocol\n@@ -1418,6 +1429,9 @@ static int stateless_connect(const char *service_name)\n \t\tprintf(\"\\n\");\n \t\tfflush(stdout);\n \t}\n+\taccept_language = http_get_accept_language_header();\n+\tif (accept_language)\n+\t\trpc.hdr_accept_language = xstrfmt(\"%s\", accept_language);\n \n \trpc.service_name = service_name;\n \trpc.service_url = xstrfmt(\"%s%s\", url.buf, rpc.service_name);\n@@ -1467,6 +1481,7 @@ static int stateless_connect(const char *service_name)\n \tfree(rpc.service_url);\n \tfree(rpc.hdr_content_type);\n \tfree(rpc.hdr_accept);\n+\tfree(rpc.hdr_accept_language);\n \tfree(rpc.protocol_header);\n \tfree(rpc.buf);\n \tstrbuf_release(&buf);\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 2f09ff4fac6..fbad2d5ff5e 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '\n \t test $HEAD = $(git rev-parse --verify HEAD))\n '\n \n+test_expect_success 'push to remote repository (standard) with sending Accept-Language' '\n+\tcat >exp <<-\\EOF &&\n+\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n+\t=> Send header: Accept-Language: ko-KR, *;q=0.9\n+\tEOF\n+\n+\tcd \"$ROOT_PATH\"/test_repo_clone &&\n+\t: >path_lang &&\n+\tgit add path_lang &&\n+\ttest_tick &&\n+\tgit commit -m path_lang &&\n+\tHEAD=$(git rev-parse --verify HEAD) &&\n+\tGIT_TRACE_CURL=true LANGUAGE=\"ko_KR.UTF-8\" git push -v -v 2>err &&\n+\t! grep \"Expect: 100-continue\" err &&\n+\n+\tgrep \"=> Send header: Accept-Language:\" err >err.language &&\n+\ttest_cmp exp err.language\n+'\n+\n test_expect_success 'push already up-to-date' '\n \tgit push\n '\ndiff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh\nindex f0d9cd584d3..bc308519af5 100755\n--- a/t/t5550-http-fetch-dumb.sh\n+++ b/t/t5550-http-fetch-dumb.sh\n@@ -369,7 +369,7 @@ ja;q=0.95, zh;q=0.94, sv;q=0.93, pt;q=0.92, nb;q=0.91, *;q=0.90\" \\\n \t\tko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb\n '\n \n-test_expect_success 'git client does not send an empty Accept-Language' '\n+test_expect_success 'git client send an empty Accept-Language' '\n \tGIT_TRACE_CURL=true LANGUAGE= git ls-remote \"$HTTPD_URL/dumb/repo.git\" 2>stderr &&\n \t! grep \"^=> Send header: Accept-Language:\" stderr\n '\ndiff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh\nindex b9351a732f6..245532df881 100755\n--- a/t/t5551-http-fetch-smart.sh\n+++ b/t/t5551-http-fetch-smart.sh\n@@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '\n \t> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1\n \t> Accept: */*\n \t> Accept-Encoding: ENCODINGS\n+\t> Accept-Language: ko-KR, *;q=0.9\n \t> Pragma: no-cache\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n@@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '\n \t> Accept-Encoding: ENCODINGS\n \t> Content-Type: application/x-git-upload-pack-request\n \t> Accept: application/x-git-upload-pack-result\n+\t> Accept-Language: ko-KR, *;q=0.9\n \t> Content-Length: xxx\n \t< HTTP/1.1 200 OK\n \t< Pragma: no-cache\n \t< Cache-Control: no-cache, max-age=0, must-revalidate\n \t< Content-Type: application/x-git-upload-pack-result\n \tEOF\n-\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \\\n+\n+\tGIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE=\"ko_KR.UTF-8\" \\\n \t\tgit clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&\n \ttest_cmp file clone/file &&\n \ttr '\\''\\015'\\'' Q <err |\n@@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '\n \t\ttest_cmp exp actual.smudged &&\n \n \t\tgrep \"Accept-Encoding:.*gzip\" actual >actual.gzip &&\n-\t\ttest_line_count = 2 actual.gzip\n+\t\ttest_line_count = 2 actual.gzip &&\n+\n+\t\tgrep \"Accept-Language: ko-KR, *\" actual >actual.language &&\n+\t\ttest_line_count = 2 actual.language\n \tfi\n '\n \n\nbase-commit: 30cc8d0f147546d4dd77bf497f4dec51e7265bd8\n-- \ngitgitgadget\n"}]}