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

Re: [PATCH 1/3] remote-curl: refactor smart-http discovery

From
Josh Steadmon <steadmon@google.com>
Date
Nov 16, 2018, 20:27 UTC
Message-ID
<20181116202734.GC9703@google.com>
In-Reply-To
<20181116084725.GA31603@sigill.intra.peff.net>
On 2018.11.16 03:47, Jeff King wrote:
Show 182 quoted lines
> After making initial contact with an http server, we have to decide if
> the server supports smart-http, and if so, which version. Our rules are
> a bit inconsistent:
> 
>   1. For v0, we require that the content-type indicates a smart-http
>      response. We also require the response to look vaguely like a
>      pkt-line starting with "#". If one of those does not match, we fall
>      back to dumb-http.
> 
>      But according to our http protocol spec[1]:
> 
>        Dumb servers MUST NOT return a return type starting with
>        `application/x-git-`.
> 
>      If we see the expected content-type, we should consider it
>      smart-http. At that point we can parse the pkt-line for real, and
>      complain if it is not syntactically valid.
> 
>   2. For v2, we do not actually check the content-type. Our v2 protocol
>      spec says[2]:
> 
>        When using the http:// or https:// transport a client makes a
>        "smart" info/refs request as described in `http-protocol.txt`[...]
> 
>      and the http spec is clear that for a smart-http[3]:
> 
>        The Content-Type MUST be `application/x-$servicename-advertisement`.
> 
>      So it is required according to the spec.
> 
> These inconsistencies were easy to miss because of the way the original
> code was written as an inline conditional. Let's pull it out into its
> own function for readability, and improve a few things:
> 
>  - we now predicate the smart/dumb decision entirely on the presence of
>    the correct content-type
> 
>  - we do a real pkt-line parse before deciding how to proceed (and die
>    if it isn't valid)
> 
>  - use skip_prefix() for comparing service strings, instead of
>    constructing expected output in a strbuf; this avoids dealing with
>    memory cleanup
> 
> Note that this _is_ tightening what the client will allow. It's all
> according to the spec, but it's possible that other implementations
> might violate these. However, violating these particular rules seems
> like an odd choice for a server to make.
> 
> [1] Documentation/technical/http-protocol.txt, l. 166-167
> [2] Documentation/technical/protocol-v2.txt, l. 63-64
> [3] Documentation/technical/http-protocol.txt, l. 247
> 
> Helped-by: Josh Steadmon <steadmon@google.com>
> Signed-off-by: Jeff King <peff@peff.net>
> ---
>  remote-curl.c | 93 ++++++++++++++++++++++++++++++++-------------------
>  1 file changed, 59 insertions(+), 34 deletions(-)
> 
> diff --git a/remote-curl.c b/remote-curl.c
> index 762a55a75f..dd9bc41aa1 100644
> --- a/remote-curl.c
> +++ b/remote-curl.c
> @@ -330,9 +330,65 @@ 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 we don't see x-$service-advertisement, then it's not smart-http.
> +	 * But once we do, we commit to it and assume any other protocol
> +	 * violations are hard errors.
> +	 */
> +	if (!skip_prefix(type->buf, "application/x-", &p) ||
> +	    !skip_prefix(p, service, &p) ||
> +	    strcmp(p, "-advertisement"))
> +		return;
> +
> +	/*
> +	 * "Peek" at the first packet by using a separate buf/len pair; some
> +	 * cases below require us leaving the originals intact.
> +	 */
> +	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")) {
> +		/*
> +		 * v2 smart http; do not consume version packet, which will
> +		 * be handled elsewhere.
> +		 */
> +		d->proto_git = 1;
> +
> +	} 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 +461,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 +470,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);
> -- 
> 2.19.1.1636.gc7a073d580
> 
Looks good to me.
Reviewed-by: Josh Steadmon <steadmon@google.com>
Previous: Jeff KingNext: Junio C Hamano
Message 13 of 41 in “remote-curl: die on server-side errors”
  1. remote-curl: die on server-side errorssteadmon@google.com, Nov 12, 2018
  2. Stefan BellerNov 12, 2018
  3. Junio C HamanoNov 13, 2018
  4. Junio C HamanoNov 13, 2018
  5. Josh SteadmonNov 13, 2018
  6. Jeff KingNov 13, 2018
  7. Josh SteadmonNov 13, 2018
  8. Jeff KingNov 14, 2018
  9. Jeff KingNov 14, 2018
  10. Josh SteadmonNov 15, 2018
  11. 0/3 remote-curl smart-http discovery cleanupJeff King, Nov 16, 2018
  12. 1/3 remote-curl: refactor smart-http discoveryJeff King, Nov 16, 2018
  13. Josh SteadmonNov 16, 2018
  14. Junio C HamanoFeb 5, 2019
  15. Jeff KingFeb 6, 2019
  16. 1/3 remote-curl: refactor smart-http discoveryJeff King, Feb 6, 2019
  17. Josh SteadmonFeb 6, 2019
  18. Junio C HamanoFeb 6, 2019
  19. Jeff KingFeb 6, 2019
  20. 2/3 remote-curl: tighten "version 2" check for smart-httpJeff King, Feb 6, 2019
  21. 3/3 t5551: test server-side ERR packetJeff King, Feb 6, 2019
  22. Junio C HamanoFeb 6, 2019
  23. 2/3 remote-curl: tighten "version 2" check for smart-httpJeff King, Nov 16, 2018
  24. Josh SteadmonNov 16, 2018
  25. 3/3 remote-curl: die on server-side errorsJeff King, Nov 16, 2018
  26. Josh SteadmonNov 16, 2018
  27. 0/4 Unify pkt-line error handling and refactor smart-httpJosh Steadmon, Dec 12, 2018
  28. 1/4 pack-protocol.txt: accept error packets in any contextJosh Steadmon, Dec 12, 2018
  29. Jeff KingDec 12, 2018
  30. Masaya SuzukiDec 13, 2018
  31. Jeff KingDec 13, 2018
  32. Josh SteadmonDec 13, 2018
  33. Jeff KingDec 17, 2018
  34. Josh SteadmonDec 19, 2018
  35. Jeff KingDec 20, 2018
  36. 2/4 remote-curl: refactor smart-http discoveryJosh Steadmon, Dec 12, 2018
  37. 3/4 remote-curl: tighten "version 2" check for smart-httpJosh Steadmon, Dec 12, 2018
  38. 4/4 lib-httpd, t5551: check server-side HTTP errorsJosh Steadmon, Dec 12, 2018
  39. Junio C HamanoDec 12, 2018
  40. Junio C HamanoNov 13, 2018
  41. Josh SteadmonNov 13, 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.