remote-curl: send Accept-Language header to server

Git server end's ability to accept Accept-Language header was introduced in f18604bbf2 (http: add Accept-Language header if possible, 2015-01-28), but this is only used by very early phase of the transfer, which is HTTP GET request to discover references. For other phases, like POST request in the smart HTTP, the server does not know what language the client speaks. Teach git client to learn end-user's preferred language and throw accept-language header to the server side. Once the server gets this header, it has the ability to talk to end-user with language they understand. This would be very helpful for many non-English speakers. Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Li Linchao <lilinchao@oschina.cn> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Li Linchao committed Jul 11, 2022 at 05:58 UTC b0c4adcdd7e339110bea8da94d7880d413e49330
6 files changed +51 -8
http.c
+2 -2
@@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)
1775 * LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
1776 * LANGUAGE= LANG=C -> ""
1777 */
1778 -static const char *get_accept_language(void)
1778 +const char *http_get_accept_language_header(void)
1779 {
1780 if (!cached_accept_language) {
1781 struct strbuf buf = STRBUF_INIT;
@@ -1829,7 +1829,7 @@ static int http_request(const char *url,
1829 fwrite_buffer);
1830 }
1831
1832 - accept_language = get_accept_language();
1832 + accept_language = http_get_accept_language_header();
1833
1834 if (accept_language)
1835 headers = curl_slist_append(headers, accept_language);
http.h
+3
@@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);
178 int http_get_info_packs(const char *base_url,
179 struct packed_git **packs_head);
180
181 +/* Helper for getting Accept-Language header */
182 +const char *http_get_accept_language_header(void);
183 +
184 struct http_pack_request {
185 char *url;
186
remote-curl.c
+18 -3
@@ -580,6 +580,7 @@ struct rpc_state {
580 char *service_url;
581 char *hdr_content_type;
582 char *hdr_accept;
583 + char *hdr_accept_language;
584 char *protocol_header;
585 char *buf;
586 size_t alloc;
@@ -607,6 +608,8 @@ struct rpc_state {
608 unsigned flush_read_but_not_sent : 1;
609 };
610
611 +#define RPC_STATE_INIT { 0 }
612 +
613 /*
614 * Appends the result of reading from rpc->out to the string represented by
615 * rpc->buf and rpc->len if there is enough space. Returns 1 if there was
@@ -932,6 +935,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
935 headers = curl_slist_append(headers, needs_100_continue ?
936 "Expect: 100-continue" : "Expect:");
937
938 + /* Add Accept-Language header */
939 + if (rpc->hdr_accept_language)
940 + headers = curl_slist_append(headers, rpc->hdr_accept_language);
941 +
942 /* Add the extra Git-Protocol header */
943 if (rpc->protocol_header)
944 headers = curl_slist_append(headers, rpc->protocol_header);
@@ -1080,6 +1087,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
1087 strbuf_addf(&buf, "%s%s", url.buf, svc);
1088 rpc->service_url = strbuf_detach(&buf, NULL);
1089
1090 + rpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());
1091 +
1092 strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
1093 rpc->hdr_content_type = strbuf_detach(&buf, NULL);
1094
@@ -1118,6 +1127,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
1127 free(rpc->service_url);
1128 free(rpc->hdr_content_type);
1129 free(rpc->hdr_accept);
1130 + free(rpc->hdr_accept_language);
1131 free(rpc->protocol_header);
1132 free(rpc->buf);
1133 strbuf_release(&buf);
@@ -1153,7 +1163,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
1163 static int fetch_git(struct discovery *heads,
1164 int nr_heads, struct ref **to_fetch)
1165 {
1156 - struct rpc_state rpc;
1166 + struct rpc_state rpc = RPC_STATE_INIT;
1167 struct strbuf preamble = STRBUF_INIT;
1168 int i, err;
1169 struct strvec args = STRVEC_INIT;
@@ -1299,7 +1309,7 @@ static int push_dav(int nr_spec, const char **specs)
1309
1310 static int push_git(struct discovery *heads, int nr_spec, const char **specs)
1311 {
1302 - struct rpc_state rpc;
1312 + struct rpc_state rpc = RPC_STATE_INIT;
1313 int i, err;
1314 struct strvec args;
1315 struct string_list_item *cas_option;
@@ -1398,8 +1408,9 @@ free_specs:
1408 static int stateless_connect(const char *service_name)
1409 {
1410 struct discovery *discover;
1401 - struct rpc_state rpc;
1411 + struct rpc_state rpc = RPC_STATE_INIT;
1412 struct strbuf buf = STRBUF_INIT;
1413 + const char *accept_language;
1414
1415 /*
1416 * Run the info/refs request and see if the server supports protocol
@@ -1418,6 +1429,9 @@ static int stateless_connect(const char *service_name)
1429 printf("\n");
1430 fflush(stdout);
1431 }
1432 + accept_language = http_get_accept_language_header();
1433 + if (accept_language)
1434 + rpc.hdr_accept_language = xstrfmt("%s", accept_language);
1435
1436 rpc.service_name = service_name;
1437 rpc.service_url = xstrfmt("%s%s", url.buf, rpc.service_name);
@@ -1467,6 +1481,7 @@ static int stateless_connect(const char *service_name)
1481 free(rpc.service_url);
1482 free(rpc.hdr_content_type);
1483 free(rpc.hdr_accept);
1484 + free(rpc.hdr_accept_language);
1485 free(rpc.protocol_header);
1486 free(rpc.buf);
1487 strbuf_release(&buf);
t/t5541-http-push-smart.sh
+19
@@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '
80 test $HEAD = $(git rev-parse --verify HEAD))
81 '
82
83 +test_expect_success 'push to remote repository (standard) with sending Accept-Language' '
84 + cat >exp <<-\EOF &&
85 + => Send header: Accept-Language: ko-KR, *;q=0.9
86 + => Send header: Accept-Language: ko-KR, *;q=0.9
87 + EOF
88 +
89 + cd "$ROOT_PATH"/test_repo_clone &&
90 + : >path_lang &&
91 + git add path_lang &&
92 + test_tick &&
93 + git commit -m path_lang &&
94 + HEAD=$(git rev-parse --verify HEAD) &&
95 + GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" git push -v -v 2>err &&
96 + ! grep "Expect: 100-continue" err &&
97 +
98 + grep "=> Send header: Accept-Language:" err >err.language &&
99 + test_cmp exp err.language
100 +'
101 +
102 test_expect_success 'push already up-to-date' '
103 git push
104 '
t/t5550-http-fetch-dumb.sh
+1 -1
@@ -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" \
369 ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb
370 '
371
372 -test_expect_success 'git client does not send an empty Accept-Language' '
372 +test_expect_success 'git client send an empty Accept-Language' '
373 GIT_TRACE_CURL=true LANGUAGE= git ls-remote "$HTTPD_URL/dumb/repo.git" 2>stderr &&
374 ! grep "^=> Send header: Accept-Language:" stderr
375 '
t/t5551-http-fetch-smart.sh
+8 -2
@@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '
31 > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
32 > Accept: */*
33 > Accept-Encoding: ENCODINGS
34 + > Accept-Language: ko-KR, *;q=0.9
35 > Pragma: no-cache
36 < HTTP/1.1 200 OK
37 < Pragma: no-cache
@@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '
41 > Accept-Encoding: ENCODINGS
42 > Content-Type: application/x-git-upload-pack-request
43 > Accept: application/x-git-upload-pack-result
44 + > Accept-Language: ko-KR, *;q=0.9
45 > Content-Length: xxx
46 < HTTP/1.1 200 OK
47 < Pragma: no-cache
48 < Cache-Control: no-cache, max-age=0, must-revalidate
49 < Content-Type: application/x-git-upload-pack-result
50 EOF
49 - GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \
51 +
52 + GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE="ko_KR.UTF-8" \
53 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
54 test_cmp file clone/file &&
55 tr '\''\015'\'' Q <err |
@@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '
97 test_cmp exp actual.smudged &&
98
99 grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
97 - test_line_count = 2 actual.gzip
100 + test_line_count = 2 actual.gzip &&
101 +
102 + grep "Accept-Language: ko-KR, *" actual >actual.language &&
103 + test_line_count = 2 actual.language
104 fi
105 '
106