Skip to content

Commit 2d103c3

Browse files
draftcodegitster
authored andcommitted
pack-protocol.txt: accept error packets in any context
In the Git pack protocol definition, an error packet may appear only in a certain context. However, servers can face a runtime error (e.g. I/O error) at an arbitrary timing. This patch changes the protocol to allow an error packet to be sent instead of any packet. Without this protocol spec change, when a server cannot process a request, there's no way to tell that to a client. Since the server cannot produce a valid response, it would be forced to cut a connection without telling why. With this protocol spec change, the server can be more gentle in this situation. An old client may see these error packets as an unexpected packet, but this is not worse than having an unexpected EOF. Following this protocol spec change, the error packet handling code is moved to pkt-line.c. Implementation wise, this implementation uses pkt-line to communicate with a subprocess. Since this is not a part of Git protocol, it's possible that a packet that is not supposed to be an error packet is mistakenly parsed as an error packet. This error packet handling is enabled only for the Git pack protocol parsing code considering this. Signed-off-by: Masaya Suzuki <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 01f9ec6 commit 2d103c3

15 files changed

+54
-34
lines changed

Documentation/technical/pack-protocol.txt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ protocol-common.txt. When the grammar indicate `PKT-LINE(...)`, unless
2222
otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
2323
include a LF, but the receiver MUST NOT complain if it is not present.
2424

25+
An error packet is a special pkt-line that contains an error string.
26+
27+
----
28+
error-line = PKT-LINE("ERR" SP explanation-text)
29+
----
30+
31+
Throughout the protocol, where `PKT-LINE(...)` is expected, an error packet MAY
32+
be sent. Once this packet is sent by a client or a server, the data transfer
33+
process defined in this protocol is terminated.
34+
2535
Transports
2636
----------
2737
There are three transports over which the packfile protocol is
@@ -89,13 +99,6 @@ process on the server side over the Git protocol is this:
8999
"0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
90100
nc -v example.com 9418
91101

92-
If the server refuses the request for some reasons, it could abort
93-
gracefully with an error message.
94-
95-
----
96-
error-line = PKT-LINE("ERR" SP explanation-text)
97-
----
98-
99102

100103
SSH Transport
101104
-------------
@@ -398,12 +401,11 @@ from the client).
398401
Then the server will start sending its packfile data.
399402

400403
----
401-
server-response = *ack_multi ack / nak / error-line
404+
server-response = *ack_multi ack / nak
402405
ack_multi = PKT-LINE("ACK" SP obj-id ack_status)
403406
ack_status = "continue" / "common" / "ready"
404407
ack = PKT-LINE("ACK" SP obj-id)
405408
nak = PKT-LINE("NAK")
406-
error-line = PKT-LINE("ERR" SP explanation-text)
407409
----
408410

409411
A simple clone may look like this (with no 'have' lines):

builtin/archive.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ static int run_remote_archiver(int argc, const char **argv,
5353
packet_write_fmt(fd[1], "argument %s\n", argv[i]);
5454
packet_flush(fd[1]);
5555

56-
packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE);
56+
packet_reader_init(&reader, fd[0], NULL, 0,
57+
PACKET_READ_CHOMP_NEWLINE |
58+
PACKET_READ_DIE_ON_ERR_PACKET);
5759

5860
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
5961
die(_("git archive: expected ACK/NAK, got a flush packet"));
6062
if (strcmp(reader.line, "ACK")) {
6163
if (starts_with(reader.line, "NACK "))
6264
die(_("git archive: NACK %s"), reader.line + 5);
63-
if (starts_with(reader.line, "ERR "))
64-
die(_("remote error: %s"), reader.line + 4);
6565
die(_("git archive: protocol error"));
6666
}
6767

builtin/fetch-pack.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
217217

218218
packet_reader_init(&reader, fd[0], NULL, 0,
219219
PACKET_READ_CHOMP_NEWLINE |
220-
PACKET_READ_GENTLE_ON_EOF);
220+
PACKET_READ_GENTLE_ON_EOF |
221+
PACKET_READ_DIE_ON_ERR_PACKET);
221222

222223
switch (discover_version(&reader)) {
223224
case protocol_v2:

builtin/receive-pack.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
19861986
if (advertise_refs)
19871987
return 0;
19881988

1989-
packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE);
1989+
packet_reader_init(&reader, 0, NULL, 0,
1990+
PACKET_READ_CHOMP_NEWLINE |
1991+
PACKET_READ_DIE_ON_ERR_PACKET);
19901992

19911993
if ((commands = read_head_info(&reader, &shallow)) != NULL) {
19921994
const char *unpack_status = NULL;

builtin/send-pack.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
250250

251251
packet_reader_init(&reader, fd[0], NULL, 0,
252252
PACKET_READ_CHOMP_NEWLINE |
253-
PACKET_READ_GENTLE_ON_EOF);
253+
PACKET_READ_GENTLE_ON_EOF |
254+
PACKET_READ_DIE_ON_ERR_PACKET);
254255

255256
switch (discover_version(&reader)) {
256257
case protocol_v2:

connect.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
296296
struct ref **orig_list = list;
297297
int len = 0;
298298
enum get_remote_heads_state state = EXPECTING_FIRST_REF;
299-
const char *arg;
300299

301300
*list = NULL;
302301

@@ -306,8 +305,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
306305
die_initial_contact(1);
307306
case PACKET_READ_NORMAL:
308307
len = reader->pktlen;
309-
if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
310-
die(_("remote error: %s"), arg);
311308
break;
312309
case PACKET_READ_FLUSH:
313310
state = EXPECTING_DONE;

fetch-pack.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,6 @@ static enum ack_type get_ack(struct packet_reader *reader,
182182
return ACK;
183183
}
184184
}
185-
if (skip_prefix(reader->line, "ERR ", &arg))
186-
die(_("remote error: %s"), arg);
187185
die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
188186
}
189187

@@ -258,7 +256,8 @@ static int find_common(struct fetch_negotiator *negotiator,
258256
die(_("--stateless-rpc requires multi_ack_detailed"));
259257

260258
packet_reader_init(&reader, fd[0], NULL, 0,
261-
PACKET_READ_CHOMP_NEWLINE);
259+
PACKET_READ_CHOMP_NEWLINE |
260+
PACKET_READ_DIE_ON_ERR_PACKET);
262261

263262
if (!args->no_dependents) {
264263
mark_tips(negotiator, args->negotiation_tips);
@@ -1358,7 +1357,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
13581357
struct fetch_negotiator negotiator;
13591358
fetch_negotiator_init(&negotiator, negotiation_algorithm);
13601359
packet_reader_init(&reader, fd[0], NULL, 0,
1361-
PACKET_READ_CHOMP_NEWLINE);
1360+
PACKET_READ_CHOMP_NEWLINE |
1361+
PACKET_READ_DIE_ON_ERR_PACKET);
13621362

13631363
while (state != FETCH_DONE) {
13641364
switch (state) {

pkt-line.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,10 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
346346
return PACKET_READ_EOF;
347347
}
348348

349+
if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
350+
starts_with(buffer, "ERR "))
351+
die(_("remote error: %s"), buffer + 4);
352+
349353
if ((options & PACKET_READ_CHOMP_NEWLINE) &&
350354
len && buffer[len-1] == '\n')
351355
len--;

pkt-line.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,13 @@ int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
6262
*
6363
* If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
6464
* present) is removed from the buffer before returning.
65+
*
66+
* If options contains PACKET_READ_DIE_ON_ERR_PACKET, it dies when it sees an
67+
* ERR packet.
6568
*/
66-
#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
67-
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
69+
#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
70+
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
71+
#define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2)
6872
int packet_read(int fd, char **src_buffer, size_t *src_len, char
6973
*buffer, unsigned size, int options);
7074

remote-curl.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
204204

205205
packet_reader_init(&reader, -1, heads->buf, heads->len,
206206
PACKET_READ_CHOMP_NEWLINE |
207-
PACKET_READ_GENTLE_ON_EOF);
207+
PACKET_READ_GENTLE_ON_EOF |
208+
PACKET_READ_DIE_ON_ERR_PACKET);
208209

209210
heads->version = discover_version(&reader);
210211
switch (heads->version) {
@@ -411,7 +412,8 @@ static struct discovery *discover_refs(const char *service, int for_push)
411412
!strbuf_cmp(&exp, &type)) {
412413
struct packet_reader reader;
413414
packet_reader_init(&reader, -1, last->buf, last->len,
414-
PACKET_READ_CHOMP_NEWLINE);
415+
PACKET_READ_CHOMP_NEWLINE |
416+
PACKET_READ_DIE_ON_ERR_PACKET);
415417

416418
/*
417419
* smart HTTP response; validate that the service
@@ -1182,7 +1184,8 @@ static void proxy_state_init(struct proxy_state *p, const char *service_name,
11821184
p->headers = curl_slist_append(p->headers, buf.buf);
11831185

11841186
packet_reader_init(&p->reader, p->in, NULL, 0,
1185-
PACKET_READ_GENTLE_ON_EOF);
1187+
PACKET_READ_GENTLE_ON_EOF |
1188+
PACKET_READ_DIE_ON_ERR_PACKET);
11861189

11871190
strbuf_release(&buf);
11881191
}

0 commit comments

Comments
 (0)