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 <bmw...@google.com>
---
 connect.c | 125 +++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 70 insertions(+), 55 deletions(-)

diff --git a/connect.c b/connect.c
index 7fbd396b3..f79ea9179 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,41 @@ 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_ERROR:
+               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;
+       }
+
+       /* Maybe process capabilities here, at least for v2 */
+       switch (version) {
+       case protocol_v1:
+               /* Read the peeked version line */
+               packet_reader_read(reader);
+               break;
+       case protocol_v0:
+               break;
+       case protocol_unknown_version:
+               BUG("ERROR");
+       }
+
+       return version;
+}
+
 static void parse_one_symref_info(struct string_list *symref, const char *val, 
int len)
 {
        char *sym, *target;
@@ -109,44 +150,10 @@ 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)
-{
-       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");
-       }
-}
+#define EXPECTING_FIRST_REF 0
+#define EXPECTING_REF 1
+#define EXPECTING_SHALLOW 2
+#define EXPECTING_DONE 3
 
 static void process_capabilities(int *len)
 {
@@ -230,28 +237,34 @@ 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;
+       int 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);
+
+       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_ERROR:
+                       die_initial_contact(1);
+               case PACKET_READ_NORMAL:
+                       len = reader.pktlen;
+                       if (len > 4 && skip_prefix(packet_buffer, "ERR ", &arg))
+                               die("remote error: %s", arg);
+                       break;
+               case PACKET_READ_FLUSH:
+                       state = EXPECTING_DONE;
+                       break;
+               case PACKET_READ_DELIM:
+                       die("invalid packet\n");
+               }
+
                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()) {
@@ -269,6 +282,8 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t 
src_len,
                        if (process_shallow(len, shallow_points))
                                break;
                        die("protocol error: unexpected '%s'", packet_buffer);
+               case EXPECTING_DONE:
+                       break;
                default:
                        die("unexpected state %d", state);
                }
-- 
2.15.1.424.g9478a66081-goog

Reply via email to