receive-pack: gently write messages to proc-receive

Johannes found a flaky hang in `t5411/test-0013-bad-protocol.sh` in the
osx-clang job of the CI/PR builds, and ran into an issue when using
the `--stress` option with the following error messages:

    fatal: unable to write flush packet: Broken pipe
    send-pack: unexpected disconnect while reading sideband packet
    fatal: the remote end hung up unexpectedly

In this test case, the "proc-receive" hook sends an error message and
dies earlier. While "receive-pack" on the other side of the pipe
should forward the error message of the "proc-receive" hook to the
client side, but it fails to do so. This is because "receive-pack"
uses `packet_write_fmt()` and `packet_flush()` to write pkt-line
message to "proc-receive" hook, and these functions die immediately
when pipe is broken. Using "gently" forms for these functions will get
more predicable output.

Add more "--die-*" options to test helper to test different stages of
the protocol between "receive-pack" and "proc-receive" hook.

Reported-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jiang Xin
2020-11-11 19:32:01 +08:00
committed by Junio C Hamano
parent cf3d868f35
commit f65003b4c4
4 changed files with 394 additions and 50 deletions

View File

@ -977,15 +977,25 @@ static int read_proc_receive_report(struct packet_reader *reader,
int new_report = 0;
int code = 0;
int once = 0;
int response = 0;
for (;;) {
struct object_id old_oid, new_oid;
const char *head;
const char *refname;
char *p;
enum packet_read_status status;
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
status = packet_reader_read(reader);
if (status != PACKET_READ_NORMAL) {
/* Check whether proc-receive exited abnormally */
if (status == PACKET_READ_EOF && !response) {
strbuf_addstr(errmsg, "proc-receive exited abnormally");
return -1;
}
break;
}
response++;
head = reader->line;
p = strchr(head, ' ');
@ -1145,28 +1155,41 @@ static int run_proc_receive_hook(struct command *commands,
if (use_push_options)
strbuf_addstr(&cap, " push-options");
if (cap.len) {
packet_write_fmt(proc.in, "version=1%c%s\n", '\0', cap.buf + 1);
code = packet_write_fmt_gently(proc.in, "version=1%c%s\n", '\0', cap.buf + 1);
strbuf_release(&cap);
} else {
packet_write_fmt(proc.in, "version=1\n");
code = packet_write_fmt_gently(proc.in, "version=1\n");
}
packet_flush(proc.in);
if (!code)
code = packet_flush_gently(proc.in);
for (;;) {
int linelen;
if (!code)
for (;;) {
int linelen;
enum packet_read_status status;
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
break;
status = packet_reader_read(&reader);
if (status != PACKET_READ_NORMAL) {
/* Check whether proc-receive exited abnormally */
if (status == PACKET_READ_EOF)
code = -1;
break;
}
if (reader.pktlen > 8 && starts_with(reader.line, "version=")) {
version = atoi(reader.line + 8);
linelen = strlen(reader.line);
if (linelen < reader.pktlen) {
const char *feature_list = reader.line + linelen + 1;
if (parse_feature_request(feature_list, "push-options"))
hook_use_push_options = 1;
if (reader.pktlen > 8 && starts_with(reader.line, "version=")) {
version = atoi(reader.line + 8);
linelen = strlen(reader.line);
if (linelen < reader.pktlen) {
const char *feature_list = reader.line + linelen + 1;
if (parse_feature_request(feature_list, "push-options"))
hook_use_push_options = 1;
}
}
}
if (code) {
strbuf_addstr(&errmsg, "fail to negotiate version with proc-receive hook");
goto cleanup;
}
if (version != 1) {
@ -1180,20 +1203,36 @@ static int run_proc_receive_hook(struct command *commands,
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->run_proc_receive || cmd->skip_update || cmd->error_string)
continue;
packet_write_fmt(proc.in, "%s %s %s",
oid_to_hex(&cmd->old_oid),
oid_to_hex(&cmd->new_oid),
cmd->ref_name);
code = packet_write_fmt_gently(proc.in, "%s %s %s",
oid_to_hex(&cmd->old_oid),
oid_to_hex(&cmd->new_oid),
cmd->ref_name);
if (code)
break;
}
if (!code)
code = packet_flush_gently(proc.in);
if (code) {
strbuf_addstr(&errmsg, "fail to write commands to proc-receive hook");
goto cleanup;
}
packet_flush(proc.in);
/* Send push options */
if (hook_use_push_options) {
struct string_list_item *item;
for_each_string_list_item(item, push_options)
packet_write_fmt(proc.in, "%s", item->string);
packet_flush(proc.in);
for_each_string_list_item(item, push_options) {
code = packet_write_fmt_gently(proc.in, "%s", item->string);
if (code)
break;
}
if (!code)
code = packet_flush_gently(proc.in);
if (code) {
strbuf_addstr(&errmsg,
"fail to write push-options to proc-receive hook");
goto cleanup;
}
}
/* Read result from proc-receive */