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>
This commit is contained in:
Li Linchao 2022-07-11 05:58:54 +00:00 committed by Junio C Hamano
parent 1e59178e3f
commit b0c4adcdd7
6 changed files with 51 additions and 8 deletions

4
http.c
View File

@ -1775,7 +1775,7 @@ static void write_accept_language(struct strbuf *buf)
* LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
* LANGUAGE= LANG=C -> ""
*/
static const char *get_accept_language(void)
const char *http_get_accept_language_header(void)
{
if (!cached_accept_language) {
struct strbuf buf = STRBUF_INIT;
@ -1829,7 +1829,7 @@ static int http_request(const char *url,
fwrite_buffer);
}
accept_language = get_accept_language();
accept_language = http_get_accept_language_header();
if (accept_language)
headers = curl_slist_append(headers, accept_language);

3
http.h
View File

@ -178,6 +178,9 @@ int http_fetch_ref(const char *base, struct ref *ref);
int http_get_info_packs(const char *base_url,
struct packed_git **packs_head);
/* Helper for getting Accept-Language header */
const char *http_get_accept_language_header(void);
struct http_pack_request {
char *url;

View File

@ -580,6 +580,7 @@ struct rpc_state {
char *service_url;
char *hdr_content_type;
char *hdr_accept;
char *hdr_accept_language;
char *protocol_header;
char *buf;
size_t alloc;
@ -607,6 +608,8 @@ struct rpc_state {
unsigned flush_read_but_not_sent : 1;
};
#define RPC_STATE_INIT { 0 }
/*
* Appends the result of reading from rpc->out to the string represented by
* 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
headers = curl_slist_append(headers, needs_100_continue ?
"Expect: 100-continue" : "Expect:");
/* Add Accept-Language header */
if (rpc->hdr_accept_language)
headers = curl_slist_append(headers, rpc->hdr_accept_language);
/* Add the extra Git-Protocol header */
if (rpc->protocol_header)
headers = curl_slist_append(headers, rpc->protocol_header);
@ -1080,6 +1087,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
strbuf_addf(&buf, "%s%s", url.buf, svc);
rpc->service_url = strbuf_detach(&buf, NULL);
rpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());
strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
rpc->hdr_content_type = strbuf_detach(&buf, NULL);
@ -1118,6 +1127,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
free(rpc->service_url);
free(rpc->hdr_content_type);
free(rpc->hdr_accept);
free(rpc->hdr_accept_language);
free(rpc->protocol_header);
free(rpc->buf);
strbuf_release(&buf);
@ -1153,7 +1163,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
static int fetch_git(struct discovery *heads,
int nr_heads, struct ref **to_fetch)
{
struct rpc_state rpc;
struct rpc_state rpc = RPC_STATE_INIT;
struct strbuf preamble = STRBUF_INIT;
int i, err;
struct strvec args = STRVEC_INIT;
@ -1299,7 +1309,7 @@ static int push_dav(int nr_spec, const char **specs)
static int push_git(struct discovery *heads, int nr_spec, const char **specs)
{
struct rpc_state rpc;
struct rpc_state rpc = RPC_STATE_INIT;
int i, err;
struct strvec args;
struct string_list_item *cas_option;
@ -1398,8 +1408,9 @@ free_specs:
static int stateless_connect(const char *service_name)
{
struct discovery *discover;
struct rpc_state rpc;
struct rpc_state rpc = RPC_STATE_INIT;
struct strbuf buf = STRBUF_INIT;
const char *accept_language;
/*
* Run the info/refs request and see if the server supports protocol
@ -1418,6 +1429,9 @@ static int stateless_connect(const char *service_name)
printf("\n");
fflush(stdout);
}
accept_language = http_get_accept_language_header();
if (accept_language)
rpc.hdr_accept_language = xstrfmt("%s", accept_language);
rpc.service_name = service_name;
rpc.service_url = xstrfmt("%s%s", url.buf, rpc.service_name);
@ -1467,6 +1481,7 @@ static int stateless_connect(const char *service_name)
free(rpc.service_url);
free(rpc.hdr_content_type);
free(rpc.hdr_accept);
free(rpc.hdr_accept_language);
free(rpc.protocol_header);
free(rpc.buf);
strbuf_release(&buf);

View File

@ -80,6 +80,25 @@ test_expect_success 'push to remote repository (standard)' '
test $HEAD = $(git rev-parse --verify HEAD))
'
test_expect_success 'push to remote repository (standard) with sending Accept-Language' '
cat >exp <<-\EOF &&
=> Send header: Accept-Language: ko-KR, *;q=0.9
=> Send header: Accept-Language: ko-KR, *;q=0.9
EOF
cd "$ROOT_PATH"/test_repo_clone &&
: >path_lang &&
git add path_lang &&
test_tick &&
git commit -m path_lang &&
HEAD=$(git rev-parse --verify HEAD) &&
GIT_TRACE_CURL=true LANGUAGE="ko_KR.UTF-8" git push -v -v 2>err &&
! grep "Expect: 100-continue" err &&
grep "=> Send header: Accept-Language:" err >err.language &&
test_cmp exp err.language
'
test_expect_success 'push already up-to-date' '
git push
'

View File

@ -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" \
ko_KR.EUC-KR:en_US.UTF-8:fr_CA:de.UTF-8@euro:sr@latin:ja:zh:sv:pt:nb
'
test_expect_success 'git client does not send an empty Accept-Language' '
test_expect_success 'git client send an empty Accept-Language' '
GIT_TRACE_CURL=true LANGUAGE= git ls-remote "$HTTPD_URL/dumb/repo.git" 2>stderr &&
! grep "^=> Send header: Accept-Language:" stderr
'

View File

@ -31,6 +31,7 @@ test_expect_success 'clone http repository' '
> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
> Accept: */*
> Accept-Encoding: ENCODINGS
> Accept-Language: ko-KR, *;q=0.9
> Pragma: no-cache
< HTTP/1.1 200 OK
< Pragma: no-cache
@ -40,13 +41,15 @@ test_expect_success 'clone http repository' '
> Accept-Encoding: ENCODINGS
> Content-Type: application/x-git-upload-pack-request
> Accept: application/x-git-upload-pack-result
> Accept-Language: ko-KR, *;q=0.9
> Content-Length: xxx
< HTTP/1.1 200 OK
< Pragma: no-cache
< Cache-Control: no-cache, max-age=0, must-revalidate
< Content-Type: application/x-git-upload-pack-result
EOF
GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 \
GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION=0 LANGUAGE="ko_KR.UTF-8" \
git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
test_cmp file clone/file &&
tr '\''\015'\'' Q <err |
@ -94,7 +97,10 @@ test_expect_success 'clone http repository' '
test_cmp exp actual.smudged &&
grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
test_line_count = 2 actual.gzip
test_line_count = 2 actual.gzip &&
grep "Accept-Language: ko-KR, *" actual >actual.language &&
test_line_count = 2 actual.language
fi
'