On Tue, Nov 13, 2018 at 07:49:15PM -0500, Jeff King wrote: > Yes, the packet_read_line_buf() interface will both advance the buf > pointer and decrement the length. So if we want to "peek", we have to > do so with a copy (there's a peek function if you use the packet_reader > interface, but that might be overkill here). > > You can rewrite it like this, which is a pretty faithful conversion and > passes the tests (but see below). > [...] Here's a version which is less faithful, but I think does sensible things in all cases, and is much easier to follow. I get a little nervous just because it tightens some cases, and one never knows if other implementations might be relying on the looseness. E.g.: - in the current code we'd still drop back to dumb http if the server tells us "application/x-git-upload-pack" but the initial pktline doesn't start with "#" (even though if it _does_ have "#" at position 5 but isn't a valid pktline, we'd complain!) - right now the v2 protocol does not require the server to say "application/x-git-upload-pack" for the content-type This patch tightens both of those (I also made a few stylistic tweaks, and added the ERR condition to show where it would go). I dunno. Part of me sees this as a nice cleanup, but maybe it is better to just leave it alone. A lot of these behaviors are just how it happens to work now, and not part of the spec, but we don't know what might be relying on them. diff --git a/remote-curl.c b/remote-curl.c index 762a55a75f..1adb96311b 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -330,9 +330,61 @@ static int get_protocol_http_header(enum protocol_version version, return 0; } +static void check_smart_http(struct discovery *d, const char *service, + struct strbuf *type) +{ + char *src_buf; + size_t src_len; + char *line; + const char *p; + + if (!skip_prefix(type->buf, "application/x-", &p) || + !skip_prefix(p, service, &p) || + strcmp(p, "-advertisement")) + return; + + /* + * We speculatively try to read a packet, which means we must preserve + * the original buf/len pair in some cases. + */ + src_buf = d->buf; + src_len = d->len; + line = packet_read_line_buf(&src_buf, &src_len, NULL); + if (!line) + die("invalid server response; expected service, got flush packet"); + + if (skip_prefix(line, "# service=", &p) && !strcmp(p, service)) { + /* + * The header can include additional metadata lines, up + * until a packet flush marker. Ignore these now, but + * in the future we might start to scan them. + */ + while (packet_read_line_buf(&src_buf, &src_len, NULL)) + ; + + /* + * v0 smart http; callers expect us to soak up the + * service and header packets + */ + d->buf = src_buf; + d->len = src_len; + d->proto_git = 1; + + } else if (starts_with(line, "version 2")) { /* should be strcmp()? */ + /* + * v2 smart http; do not consume version packet, which will + * be handled elsewhere. + */ + d->proto_git = 1; + } else if (skip_prefix(line, "ERR ", &p)) { + die(_("remote error: %s"), p); + } else { + die("invalid server response; got '%s'", line); + } +} + static struct discovery *discover_refs(const char *service, int for_push) { - struct strbuf exp = STRBUF_INIT; struct strbuf type = STRBUF_INIT; struct strbuf charset = STRBUF_INIT; struct strbuf buffer = STRBUF_INIT; @@ -405,38 +457,8 @@ static struct discovery *discover_refs(const char *service, int for_push) last->buf_alloc = strbuf_detach(&buffer, &last->len); last->buf = last->buf_alloc; - strbuf_addf(&exp, "application/x-%s-advertisement", service); - if (maybe_smart && - (5 <= last->len && last->buf[4] == '#') && - !strbuf_cmp(&exp, &type)) { - char *line; - - /* - * smart HTTP response; validate that the service - * pkt-line matches our request. - */ - line = packet_read_line_buf(&last->buf, &last->len, NULL); - if (!line) - die("invalid server response; expected service, got flush packet"); - - strbuf_reset(&exp); - strbuf_addf(&exp, "# service=%s", service); - if (strcmp(line, exp.buf)) - die("invalid server response; got '%s'", line); - strbuf_release(&exp); - - /* The header can include additional metadata lines, up - * until a packet flush marker. Ignore these now, but - * in the future we might start to scan them. - */ - while (packet_read_line_buf(&last->buf, &last->len, NULL)) - ; - - last->proto_git = 1; - } else if (maybe_smart && - last->len > 5 && starts_with(last->buf + 4, "version 2")) { - last->proto_git = 1; - } + if (maybe_smart) + check_smart_http(last, service, &type); if (last->proto_git) last->refs = parse_git_refs(last, for_push); @@ -444,7 +466,6 @@ static struct discovery *discover_refs(const char *service, int for_push) last->refs = parse_info_refs(last); strbuf_release(&refs_url); - strbuf_release(&exp); strbuf_release(&type); strbuf_release(&charset); strbuf_release(&effective_url);