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

[PATCH 04/11] Resumable clone: add prime-clone to remote-curl

From
KWKevin Wern <kevin.m.wern@gmail.com>
Date
Sep 16, 2016, 00:12 UTC
Message-ID
<1473984742-12516-5-git-send-email-kevin.m.wern@gmail.com>
In-Reply-To
<1473984742-12516-1-git-send-email-kevin.m.wern@gmail.com>

Add function and interface to handle prime-clone input, extracting and using duplicate functionality from discover_refs as function request_service.

Because part of our goal is for prime_clone to recover from errors, HTTP errors are only optionally printed to screen and never cause death in this case.

Signed-off-by: Kevin Wern <kevin.m.wern@gmail.com>
---
 remote-curl.c | 165 ++++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 121 insertions(+), 44 deletions(-)
diff --git a/remote-curl.c b/remote-curl.c
index 15e48e2..8ebb587 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -13,6 +13,8 @@
 #include "sha1-array.h"
 #include "send-pack.h"
 
+#define HTTP_ERROR_GENTLE (1u << 0)
+
 static struct remote *remote;
 /* always ends with a trailing slash */
 static struct strbuf url = STRBUF_INIT;
@@ -244,7 +246,31 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,
 	return 0;
 }
 
-static struct discovery *discover_refs(const char *service, int for_push)
+static char *http_handle_result(int http_return)
+{
+	struct strbuf error = STRBUF_INIT;
+
+	switch (http_return) {
+	case HTTP_OK:
+		return NULL;
+	case HTTP_MISSING_TARGET:
+		strbuf_addf(&error, "repository '%s' not found", url.buf);
+		break;
+	case HTTP_NOAUTH:
+		strbuf_addf(&error, "Authentication failed for '%s'",
+			    url.buf);
+		break;
+	default:
+		strbuf_addf(&error, "unable to access '%s': %s", url.buf,
+			    curl_errorstr);
+		break;
+	}
+
+	return strbuf_detach(&error, NULL);
+}
+
+static int request_service(char const *const service, char **buffer_full,
+			    char **buffer_msg, size_t *buffer_len, int flags)
 {
 	struct strbuf exp = STRBUF_INIT;
 	struct strbuf type = STRBUF_INIT;
@@ -252,13 +278,9 @@ static struct discovery *discover_refs(const char *service, int for_push)
 	struct strbuf buffer = STRBUF_INIT;
 	struct strbuf refs_url = STRBUF_INIT;
 	struct strbuf effective_url = STRBUF_INIT;
-	struct discovery *last = last_discovery;
-	int http_ret, maybe_smart = 0;
-	struct http_get_options options;
-
-	if (last && !strcmp(service, last->service))
-		return last;
-	free_discovery(last);
+	int http_ret, maybe_smart = 0, ran_smart = 0;
+	struct http_get_options get_options;
+	const char *error_string;
 
 	strbuf_addf(&refs_url, "%sinfo/refs", url.buf);
 	if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) &&
@@ -271,45 +293,41 @@ static struct discovery *discover_refs(const char *service, int for_push)
 		strbuf_addf(&refs_url, "service=%s", service);
 	}
 
-	memset(&options, 0, sizeof(options));
-	options.content_type = &type;
-	options.charset = &charset;
-	options.effective_url = &effective_url;
-	options.base_url = &url;
-	options.no_cache = 1;
-	options.keep_error = 1;
-
-	http_ret = http_get_strbuf(refs_url.buf, &buffer, &options);
-	switch (http_ret) {
-	case HTTP_OK:
-		break;
-	case HTTP_MISSING_TARGET:
-		show_http_message(&type, &charset, &buffer);
-		die("repository '%s' not found", url.buf);
-	case HTTP_NOAUTH:
-		show_http_message(&type, &charset, &buffer);
-		die("Authentication failed for '%s'", url.buf);
-	default:
-		show_http_message(&type, &charset, &buffer);
-		die("unable to access '%s': %s", url.buf, curl_errorstr);
+	memset(&get_options, 0, sizeof(get_options));
+	get_options.content_type = &type;
+	get_options.charset = &charset;
+	get_options.effective_url = &effective_url;
+	get_options.base_url = &url;
+	get_options.no_cache = 1;
+	get_options.keep_error = 1;
+
+	http_ret = http_get_strbuf(refs_url.buf, &buffer, &get_options);
+	error_string = http_handle_result(http_ret);
+	if (error_string) {
+		if (!(flags & HTTP_ERROR_GENTLE)) {
+			show_http_message(&type, &charset, &buffer);
+			die("%s", error_string);
+		}
+		else if (options.verbosity > 1) {
+			show_http_message(&type, &charset, &buffer);
+			fprintf(stderr, "%s\n", error_string);
+		}
 	}
 
-	last= xcalloc(1, sizeof(*last_discovery));
-	last->service = service;
-	last->buf_alloc = strbuf_detach(&buffer, &last->len);
-	last->buf = last->buf_alloc;
+	*buffer_full = strbuf_detach(&buffer, buffer_len);
+	*buffer_msg = *buffer_full;
 
 	strbuf_addf(&exp, "application/x-%s-advertisement", service);
 	if (maybe_smart &&
-	    (5 <= last->len && last->buf[4] == '#') &&
-	    !strbuf_cmp(&exp, &type)) {
+	    (5 <= *buffer_len && (*buffer_msg)[4] == '#') &&
+	    !strbuf_cmp(&exp, &type) && http_ret == HTTP_OK) {
 		char *line;
 
 		/*
 		 * smart HTTP response; validate that the service
 		 * pkt-line matches our request.
 		 */
-		line = packet_read_line_buf(&last->buf, &last->len, NULL);
+		line = packet_read_line_buf(buffer_msg, buffer_len, NULL);
 
 		strbuf_reset(&exp);
 		strbuf_addf(&exp, "# service=%s", service);
@@ -321,23 +339,80 @@ static struct discovery *discover_refs(const char *service, int for_push)
 		 * 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))
+		while (packet_read_line_buf(buffer_msg, buffer_len, NULL))
 			;
 
-		last->proto_git = 1;
+		ran_smart = 1;
 	}
 
-	if (last->proto_git)
-		last->refs = parse_git_refs(last, for_push);
-	else
-		last->refs = parse_info_refs(last);
-
 	strbuf_release(&refs_url);
 	strbuf_release(&exp);
 	strbuf_release(&type);
 	strbuf_release(&charset);
 	strbuf_release(&effective_url);
 	strbuf_release(&buffer);
+
+	return ran_smart;
+}
+
+static void prime_clone(void)
+{
+	char *result, *result_full, *line;
+	size_t result_len;
+	int err = 0, one_successful = 0;
+
+	if (request_service("git-prime-clone", &result_full, &result,
+			&result_len, HTTP_ERROR_GENTLE)) {
+		while (line = packet_read_line_buf_gentle(&result, &result_len,
+							  NULL)) {
+			char *space = strchr(line ,' ');
+
+			// We will eventually support multiple resources, so
+			// always parse the whole message
+			if (err)
+				continue;
+			if (!space || strchr(space + 1, ' ')) {
+				if (options.verbosity > 1)
+					fprintf(stderr, "prime clone "
+						"protocol error: got '%s'\n",
+						line);
+				printf("error\n");
+				err = 1;
+				continue;
+			}
+
+			one_successful = 1;
+			printf("%s\n", line);
+		}
+		if (!one_successful && options.verbosity > 1)
+			fprintf(stderr, "did not get required components for "
+				"alternate resource\n");
+	}
+
+	printf("\n");
+	fflush(stdout);
+	free(result_full);
+}
+
+
+static struct discovery *discover_refs(const char *service, int for_push)
+{
+	struct discovery *last = last_discovery;
+
+	if (last && !strcmp(service, last->service))
+		return last;
+	free_discovery(last);
+
+	last= xcalloc(1, sizeof(*last_discovery));
+	last->service = service;
+	last->proto_git = request_service(service, &last->buf_alloc,
+					  &last->buf, &last->len, 0);
+
+	if (last->proto_git)
+		last->refs = parse_git_refs(last, for_push);
+	else
+		last->refs = parse_info_refs(last);
+
 	last_discovery = last;
 	return last;
 }
@@ -1030,7 +1105,8 @@ int main(int argc, const char **argv)
 		} else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
 			int for_push = !!strstr(buf.buf + 4, "for-push");
 			output_refs(get_refs(for_push));
-
+		} else if (!strcmp(buf.buf, "prime-clone")) {
+			prime_clone();
 		} else if (starts_with(buf.buf, "push ")) {
 			parse_push(&buf);
 
@@ -1056,6 +1132,7 @@ int main(int argc, const char **argv)
 			printf("fetch\n");
 			printf("option\n");
 			printf("push\n");
+			printf("prime-clone\n");
 			printf("check-connectivity\n");
 			printf("\n");
 			fflush(stdout);
-- 
2.7.4
Previous: Kevin WernNext: Duy Nguyen
Message 11 of 39 in “Resumable clone”
  1. 00/11 Resumable cloneKevin Wern, Sep 16, 2016
  2. 01/11 Resumable clone: create service git-prime-cloneKevin Wern, Sep 16, 2016
  3. Junio C HamanoSep 16, 2016
  4. Kevin WernSep 28, 2016
  5. 02/11 Resumable clone: add prime-clone endpointsKevin Wern, Sep 16, 2016
  6. Duy NguyenSep 19, 2016
  7. Kevin WernSep 28, 2016
  8. 03/11 pkt-line: create gentle packet_read_line functionsKevin Wern, Sep 16, 2016
  9. Junio C HamanoSep 16, 2016
  10. Kevin WernSep 28, 2016
  11. 04/11 Resumable clone: add prime-clone to remote-curlKevin Wern, Sep 16, 2016
  12. Duy NguyenSep 19, 2016
  13. Kevin WernSep 28, 2016
  14. 05/11 Resumable clone: add output parsing to connect.cKevin Wern, Sep 16, 2016
  15. 06/11 Resumable clone: implement transport_prime_cloneKevin Wern, Sep 16, 2016
  16. 07/11 Resumable clone: add resumable download to http/curlKevin Wern, Sep 16, 2016
  17. Junio C HamanoSep 16, 2016
  18. Kevin WernSep 28, 2016
  19. 08/11 Resumable clone: create transport_download_primerKevin Wern, Sep 16, 2016
  20. 10/11 run command: add RUN_COMMAND_NO_STDOUTKevin Wern, Sep 16, 2016
  21. Junio C HamanoSep 16, 2016
  22. Johannes SchindelinSep 18, 2016
  23. Kevin WernSep 28, 2016
  24. Junio C HamanoSep 28, 2016
  25. Kevin WernSep 28, 2016
  26. 09/11 path: add resumable markerKevin Wern, Sep 16, 2016
  27. Duy NguyenSep 19, 2016
  28. 11/11 Resumable clone: implement primer logic in git-cloneKevin Wern, Sep 16, 2016
  29. Junio C HamanoSep 16, 2016
  30. Kevin WernSep 28, 2016
  31. Duy NguyenSep 19, 2016
  32. Junio C HamanoSep 19, 2016
  33. Kevin WernSep 28, 2016
  34. Junio C HamanoSep 16, 2016
  35. Eric WongSep 27, 2016
  36. Junio C HamanoSep 27, 2016
  37. Junio C HamanoSep 28, 2016
  38. Junio C HamanoSep 28, 2016
  39. Eric WongSep 28, 2016

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.