git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v5 07/35] connect: convert get_remote_heads to use struct packet_reader

From
BWBrandon Williams <bmwill@google.com>
Date
Mar 14, 2018, 18:31 UTC
Message-ID
<20180314183213.223440-8-bmwill@google.com>
In-Reply-To
<20180314183213.223440-1-bmwill@google.com>

In order to allow for better control flow when protocol_v2 is introduced convert 'get_remote_heads()' to use 'struct packet_reader' to read packet lines. This enables a client to be able to peek the first line of a server's response (without consuming it) in order to determine the protocol version its speaking and then passing control to the appropriate handler.

This is needed because the initial response from a server speaking protocol_v0 includes the first ref, while subsequent protocol versions respond with a version line. We want to be able to read this first line without consuming the first ref sent in the protocol_v0 case so that the protocol version the server is speaking can be determined outside of 'get_remote_heads()' in a future patch.

Signed-off-by: Brandon Williams <bmwill@google.com>
---
 connect.c | 173 ++++++++++++++++++++++++++++++------------------------
 1 file changed, 95 insertions(+), 78 deletions(-)
diff --git a/connect.c b/connect.c
index c3a014c5ba..c82c90b7c3 100644
--- a/connect.c
+++ b/connect.c
@@ -48,6 +48,12 @@ int check_ref_type(const struct ref *ref, int flags)
 
 static void die_initial_contact(int unexpected)
 {
+	/*
+	 * A hang-up after seeing some response from the other end
+	 * means that it is unexpected, as we know the other end is
+	 * willing to talk to us.  A hang-up before seeing any
+	 * response does not necessarily mean an ACL problem, though.
+	 */
 	if (unexpected)
 		die(_("The remote end hung up upon initial contact"));
 	else
@@ -56,6 +62,40 @@ static void die_initial_contact(int unexpected)
 		      "and the repository exists."));
 }
 
+static enum protocol_version discover_version(struct packet_reader *reader)
+{
+	enum protocol_version version = protocol_unknown_version;
+
+	/*
+	 * Peek the first line of the server's response to
+	 * determine the protocol version the server is speaking.
+	 */
+	switch (packet_reader_peek(reader)) {
+	case PACKET_READ_EOF:
+		die_initial_contact(0);
+	case PACKET_READ_FLUSH:
+	case PACKET_READ_DELIM:
+		version = protocol_v0;
+		break;
+	case PACKET_READ_NORMAL:
+		version = determine_protocol_version_client(reader->line);
+		break;
+	}
+
+	switch (version) {
+	case protocol_v1:
+		/* Read the peeked version line */
+		packet_reader_read(reader);
+		break;
+	case protocol_v0:
+		break;
+	case protocol_unknown_version:
+		BUG("unknown protocol version");
+	}
+
+	return version;
+}
+
 static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
 {
 	char *sym, *target;
@@ -109,60 +149,21 @@ static void annotate_refs_with_symref_info(struct ref *ref)
 	string_list_clear(&symref, 0);
 }
 
-/*
- * Read one line of a server's ref advertisement into packet_buffer.
- */
-static int read_remote_ref(int in, char **src_buf, size_t *src_len,
-			   int *responded)
+static void process_capabilities(const char *line, int *len)
 {
-	int len = packet_read(in, src_buf, src_len,
-			      packet_buffer, sizeof(packet_buffer),
-			      PACKET_READ_GENTLE_ON_EOF |
-			      PACKET_READ_CHOMP_NEWLINE);
-	const char *arg;
-	if (len < 0)
-		die_initial_contact(*responded);
-	if (len > 4 && skip_prefix(packet_buffer, "ERR ", &arg))
-		die("remote error: %s", arg);
-
-	*responded = 1;
-
-	return len;
-}
-
-#define EXPECTING_PROTOCOL_VERSION 0
-#define EXPECTING_FIRST_REF 1
-#define EXPECTING_REF 2
-#define EXPECTING_SHALLOW 3
-
-/* Returns 1 if packet_buffer is a protocol version pkt-line, 0 otherwise. */
-static int process_protocol_version(void)
-{
-	switch (determine_protocol_version_client(packet_buffer)) {
-	case protocol_v1:
-		return 1;
-	case protocol_v0:
-		return 0;
-	default:
-		die("server is speaking an unknown protocol");
-	}
-}
-
-static void process_capabilities(int *len)
-{
-	int nul_location = strlen(packet_buffer);
+	int nul_location = strlen(line);
 	if (nul_location == *len)
 		return;
-	server_capabilities = xstrdup(packet_buffer + nul_location + 1);
+	server_capabilities = xstrdup(line + nul_location + 1);
 	*len = nul_location;
 }
 
-static int process_dummy_ref(void)
+static int process_dummy_ref(const char *line)
 {
 	struct object_id oid;
 	const char *name;
 
-	if (parse_oid_hex(packet_buffer, &oid, &name))
+	if (parse_oid_hex(line, &oid, &name))
 		return 0;
 	if (*name != ' ')
 		return 0;
@@ -171,20 +172,20 @@ static int process_dummy_ref(void)
 	return !oidcmp(&null_oid, &oid) && !strcmp(name, "capabilities^{}");
 }
 
-static void check_no_capabilities(int len)
+static void check_no_capabilities(const char *line, int len)
 {
-	if (strlen(packet_buffer) != len)
+	if (strlen(line) != len)
 		warning("Ignoring capabilities after first line '%s'",
-			packet_buffer + strlen(packet_buffer));
+			line + strlen(line));
 }
 
-static int process_ref(int len, struct ref ***list, unsigned int flags,
-		       struct oid_array *extra_have)
+static int process_ref(const char *line, int len, struct ref ***list,
+		       unsigned int flags, struct oid_array *extra_have)
 {
 	struct object_id old_oid;
 	const char *name;
 
-	if (parse_oid_hex(packet_buffer, &old_oid, &name))
+	if (parse_oid_hex(line, &old_oid, &name))
 		return 0;
 	if (*name != ' ')
 		return 0;
@@ -200,16 +201,17 @@ static int process_ref(int len, struct ref ***list, unsigned int flags,
 		**list = ref;
 		*list = &ref->next;
 	}
-	check_no_capabilities(len);
+	check_no_capabilities(line, len);
 	return 1;
 }
 
-static int process_shallow(int len, struct oid_array *shallow_points)
+static int process_shallow(const char *line, int len,
+			   struct oid_array *shallow_points)
 {
 	const char *arg;
 	struct object_id old_oid;
 
-	if (!skip_prefix(packet_buffer, "shallow ", &arg))
+	if (!skip_prefix(line, "shallow ", &arg))
 		return 0;
 
 	if (get_oid_hex(arg, &old_oid))
@@ -217,10 +219,17 @@ static int process_shallow(int len, struct oid_array *shallow_points)
 	if (!shallow_points)
 		die("repository on the other end cannot be shallow");
 	oid_array_append(shallow_points, &old_oid);
-	check_no_capabilities(len);
+	check_no_capabilities(line, len);
 	return 1;
 }
 
+enum get_remote_heads_state {
+	EXPECTING_FIRST_REF = 0,
+	EXPECTING_REF,
+	EXPECTING_SHALLOW,
+	EXPECTING_DONE,
+};
+
 /*
  * Read all the refs from the other end
  */
@@ -230,47 +239,55 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 			      struct oid_array *shallow_points)
 {
 	struct ref **orig_list = list;
+	int len = 0;
+	enum get_remote_heads_state state = EXPECTING_FIRST_REF;
+	struct packet_reader reader;
+	const char *arg;
 
-	/*
-	 * A hang-up after seeing some response from the other end
-	 * means that it is unexpected, as we know the other end is
-	 * willing to talk to us.  A hang-up before seeing any
-	 * response does not necessarily mean an ACL problem, though.
-	 */
-	int responded = 0;
-	int len;
-	int state = EXPECTING_PROTOCOL_VERSION;
+	packet_reader_init(&reader, in, src_buf, src_len,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_GENTLE_ON_EOF);
+
+	discover_version(&reader);
 
 	*list = NULL;
 
-	while ((len = read_remote_ref(in, &src_buf, &src_len, &responded))) {
+	while (state != EXPECTING_DONE) {
+		switch (packet_reader_read(&reader)) {
+		case PACKET_READ_EOF:
+			die_initial_contact(1);
+		case PACKET_READ_NORMAL:
+			len = reader.pktlen;
+			if (len > 4 && skip_prefix(reader.line, "ERR ", &arg))
+				die("remote error: %s", arg);
+			break;
+		case PACKET_READ_FLUSH:
+			state = EXPECTING_DONE;
+			break;
+		case PACKET_READ_DELIM:
+			die("invalid packet");
+		}
+
 		switch (state) {
-		case EXPECTING_PROTOCOL_VERSION:
-			if (process_protocol_version()) {
-				state = EXPECTING_FIRST_REF;
-				break;
-			}
-			state = EXPECTING_FIRST_REF;
-			/* fallthrough */
 		case EXPECTING_FIRST_REF:
-			process_capabilities(&len);
-			if (process_dummy_ref()) {
+			process_capabilities(reader.line, &len);
+			if (process_dummy_ref(reader.line)) {
 				state = EXPECTING_SHALLOW;
 				break;
 			}
 			state = EXPECTING_REF;
 			/* fallthrough */
 		case EXPECTING_REF:
-			if (process_ref(len, &list, flags, extra_have))
+			if (process_ref(reader.line, len, &list, flags, extra_have))
 				break;
 			state = EXPECTING_SHALLOW;
 			/* fallthrough */
 		case EXPECTING_SHALLOW:
-			if (process_shallow(len, shallow_points))
+			if (process_shallow(reader.line, len, shallow_points))
 				break;
-			die("protocol error: unexpected '%s'", packet_buffer);
-		default:
-			die("unexpected state %d", state);
+			die("protocol error: unexpected '%s'", reader.line);
+		case EXPECTING_DONE:
+			break;
 		}
 	}
 
-- 
2.16.2.804.g6dcf76e118-goog
Previous: Brandon WilliamsNext: Brandon Williams
Message 9 of 85 in “protocol version 2”
  1. 00/35 protocol version 2Brandon Williams, Mar 14, 2018
  2. 01/35 pkt-line: introduce packet_read_with_statusBrandon Williams, Mar 14, 2018
  3. Junio C HamanoMar 14, 2018
  4. Brandon WilliamsMar 15, 2018
  5. Junio C HamanoMar 15, 2018
  6. 02/35 pkt-line: allow peeking a packet line without consuming itBrandon Williams, Mar 14, 2018
  7. 03/35 pkt-line: add delim packet supportBrandon Williams, Mar 14, 2018
  8. 04/35 upload-pack: convert to a builtinBrandon Williams, Mar 14, 2018
  9. 07/35 connect: convert get_remote_heads to use struct packet_readerBrandon Williams, Mar 14, 2018
  10. 10/35 protocol: introduce enum protocol_version value protocol_v2Brandon Williams, Mar 14, 2018
  11. 11/35 test-pkt-line: introduce a packet-line test helperBrandon Williams, Mar 14, 2018
  12. 12/35 serve: introduce git-serveBrandon Williams, Mar 14, 2018
  13. Junio C HamanoMar 14, 2018
  14. Brandon WilliamsMar 15, 2018
  15. 14/35 connect: request remote refs using v2Brandon Williams, Mar 14, 2018
  16. 13/35 ls-refs: introduce ls-refs server commandBrandon Williams, Mar 14, 2018
  17. 18/35 fetch: pass ref prefixes when fetchingBrandon Williams, Mar 14, 2018
  18. 17/35 ls-remote: pass ref prefixes when requesting a remote's refsBrandon Williams, Mar 14, 2018
  19. 19/35 push: pass ref prefixes when pushingBrandon Williams, Mar 14, 2018
  20. 21/35 fetch-pack: perform a fetch using v2Brandon Williams, Mar 14, 2018
  21. 22/35 fetch-pack: support shallow requestsBrandon Williams, Mar 14, 2018
  22. 26/35 transport-helper: refactor process_connect_serviceBrandon Williams, Mar 14, 2018
  23. 27/35 transport-helper: introduce stateless-connectBrandon Williams, Mar 14, 2018
  24. 28/35 pkt-line: add packet_buf_write_len functionBrandon Williams, Mar 14, 2018
  25. 31/35 http: allow providing extra headers for http requestsBrandon Williams, Mar 14, 2018
  26. 29/35 remote-curl: create copy of the service nameBrandon Williams, Mar 14, 2018
  27. 32/35 http: don't always add Git-Protocol headerBrandon Williams, Mar 14, 2018
  28. 33/35 http: eliminate "# service" line when using protocol v2Brandon Williams, Mar 14, 2018
  29. 35/35 remote-curl: don't request v2 when pushingBrandon Williams, Mar 14, 2018
  30. 34/35 remote-curl: implement stateless-connect commandBrandon Williams, Mar 14, 2018
  31. 23/35 connect: refactor git_connect to only get the protocol version onceBrandon Williams, Mar 14, 2018
  32. 25/35 transport-helper: remove name parameterBrandon Williams, Mar 14, 2018
  33. 30/35 remote-curl: store the protocol version the server responded withBrandon Williams, Mar 14, 2018
  34. 20/35 upload-pack: introduce fetch server commandBrandon Williams, Mar 14, 2018
  35. 24/35 connect: don't request v2 when pushingBrandon Williams, Mar 14, 2018
  36. 15/35 transport: convert get_refs_list to take a list of ref prefixesBrandon Williams, Mar 14, 2018
  37. 16/35 transport: convert transport_get_remote_refs to take a list of ref prefixesBrandon Williams, Mar 14, 2018
  38. 08/35 connect: discover protocol version outside of get_remote_headsBrandon Williams, Mar 14, 2018
  39. 09/35 transport: store protocol versionBrandon Williams, Mar 14, 2018
  40. 05/35 upload-pack: factor out processing linesBrandon Williams, Mar 14, 2018
  41. 06/35 transport: use get_refs_via_connect to get refsBrandon Williams, Mar 14, 2018
  42. 00/35 protocol version 2Brandon Williams, Mar 15, 2018
  43. 01/35 pkt-line: introduce packet_read_with_statusBrandon Williams, Mar 15, 2018
  44. 02/35 pkt-line: allow peeking a packet line without consuming itBrandon Williams, Mar 15, 2018
  45. 03/35 pkt-line: add delim packet supportBrandon Williams, Mar 15, 2018
  46. 06/35 transport: use get_refs_via_connect to get refsBrandon Williams, Mar 15, 2018
  47. 07/35 connect: convert get_remote_heads to use struct packet_readerBrandon Williams, Mar 15, 2018
  48. Duy NguyenMar 27, 2018
  49. Jeff KingMar 27, 2018
  50. Duy NguyenMar 27, 2018
  51. Duy NguyenMar 27, 2018
  52. Brandon WilliamsMar 27, 2018
  53. 08/35 connect: discover protocol version outside of get_remote_headsBrandon Williams, Mar 15, 2018
  54. 09/35 transport: store protocol versionBrandon Williams, Mar 15, 2018
  55. 10/35 protocol: introduce enum protocol_version value protocol_v2Brandon Williams, Mar 15, 2018
  56. 11/35 test-pkt-line: introduce a packet-line test helperBrandon Williams, Mar 15, 2018
  57. 12/35 serve: introduce git-serveBrandon Williams, Mar 15, 2018
  58. 13/35 ls-refs: introduce ls-refs server commandBrandon Williams, Mar 15, 2018
  59. 16/35 transport: convert transport_get_remote_refs to take a list of ref prefixesBrandon Williams, Mar 15, 2018
  60. 17/35 ls-remote: pass ref prefixes when requesting a remote's refsBrandon Williams, Mar 15, 2018
  61. 18/35 fetch: pass ref prefixes when fetchingBrandon Williams, Mar 15, 2018
  62. 20/35 upload-pack: introduce fetch server commandBrandon Williams, Mar 15, 2018
  63. 19/35 push: pass ref prefixes when pushingBrandon Williams, Mar 15, 2018
  64. 22/35 fetch-pack: support shallow requestsBrandon Williams, Mar 15, 2018
  65. 27/35 transport-helper: introduce stateless-connectBrandon Williams, Mar 15, 2018
  66. 26/35 transport-helper: refactor process_connect_serviceBrandon Williams, Mar 15, 2018
  67. 29/35 remote-curl: create copy of the service nameBrandon Williams, Mar 15, 2018
  68. 30/35 remote-curl: store the protocol version the server responded withBrandon Williams, Mar 15, 2018
  69. 33/35 http: eliminate "# service" line when using protocol v2Brandon Williams, Mar 15, 2018
  70. 34/35 remote-curl: implement stateless-connect commandBrandon Williams, Mar 15, 2018
  71. 35/35 remote-curl: don't request v2 when pushingBrandon Williams, Mar 15, 2018
  72. 31/35 http: allow providing extra headers for http requestsBrandon Williams, Mar 15, 2018
  73. 32/35 http: don't always add Git-Protocol headerBrandon Williams, Mar 15, 2018
  74. 28/35 pkt-line: add packet_buf_write_len functionBrandon Williams, Mar 15, 2018
  75. 23/35 connect: refactor git_connect to only get the protocol version onceBrandon Williams, Mar 15, 2018
  76. 25/35 transport-helper: remove name parameterBrandon Williams, Mar 15, 2018
  77. 24/35 connect: don't request v2 when pushingBrandon Williams, Mar 15, 2018
  78. 21/35 fetch-pack: perform a fetch using v2Brandon Williams, Mar 15, 2018
  79. 14/35 connect: request remote refs using v2Brandon Williams, Mar 15, 2018
  80. 15/35 transport: convert get_refs_list to take a list of ref prefixesBrandon Williams, Mar 15, 2018
  81. 04/35 upload-pack: convert to a builtinBrandon Williams, Mar 15, 2018
  82. 05/35 upload-pack: factor out processing linesBrandon Williams, Mar 15, 2018
  83. {fetch,upload}-pack: clearly mark unreachable v2 codeÆvar Arnfjörð Bjarmason, Mar 30, 2018
  84. Brandon WilliamsMar 30, 2018
  85. Stefan BellerMar 30, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.