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

[RFC PATCH 1/8] Pass unknown protocols to external protocol handlers

From
ILIlari Liusvaara <ilari.liusvaara@elisanet.fi>
Date
Dec 1, 2009, 13:57 UTC
Message-ID
<1259675838-14692-2-git-send-email-ilari.liusvaara@elisanet.fi>
In-Reply-To
<1259675838-14692-1-git-send-email-ilari.liusvaara@elisanet.fi>

Change URL handling to allow external protocol handlers to provde new protocols without the '::' syntax if helper name does not conflict with any builtin protocol.

foo:// now invokes git-remote-foo with foo:// URL.
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
 transport-helper.c |   57 ++++++++++++++++++++++++++++++++----
 transport.c        |   82 +++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 122 insertions(+), 17 deletions(-)
diff --git a/transport-helper.c b/transport-helper.c
index 6182413..a499751 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -22,6 +22,26 @@ struct helper_data
 	int refspec_nr;
 };
 
+const char* remove_ext_force(const char* url)
+{
+	const char* url2 = url;
+	const char* first_colon = NULL;
+
+	if(!url)
+		return NULL;
+
+	while(*url2 && !first_colon)
+		if(*url2 == ':')
+			first_colon = url2;
+		else
+			url2++;
+
+	if(first_colon && first_colon[1] == ':')
+		return first_colon + 2;
+	else
+		return url;
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
 	struct helper_data *data = transport->data;
@@ -30,6 +50,8 @@ static struct child_process *get_helper(struct transport *transport)
 	const char **refspecs = NULL;
 	int refspec_nr = 0;
 	int refspec_alloc = 0;
+	int duped;
+	int seen_line = 0;
 
 	if (data->helper)
 		return data->helper;
@@ -39,21 +61,43 @@ static struct child_process *get_helper(struct transport *transport)
 	helper->out = -1;
 	helper->err = 0;
 	helper->argv = xcalloc(4, sizeof(*helper->argv));
-	strbuf_addf(&buf, "remote-%s", data->name);
+	/* We use the dashed form because git <unknown helper>
+	   would run and print totally inapporiate error message. */
+	strbuf_addf(&buf, "git-remote-%s", data->name);
 	helper->argv[0] = strbuf_detach(&buf, NULL);
 	helper->argv[1] = transport->remote->name;
-	helper->argv[2] = transport->url;
-	helper->git_cmd = 1;
+	helper->argv[2] = remove_ext_force(transport->url);
+	helper->git_cmd = 0;
 	if (start_command(helper))
-		die("Unable to run helper: git %s", helper->argv[0]);
+		die("Unable to run helper: %s", helper->argv[0]);
 	data->helper = helper;
 
+	/* Open the output as FILE* so strbuf_getline() can be used.
+	   Do this with duped fd because fclose() will close the fd,
+	   and stuff like disowning will require the fd to remain.
+
+	   Set the stream to unbuffered because some reads are critical
+	   in sense that any overreading will cause deadlocks.
+	*/
+	if((duped = dup(helper->out)) < 0)
+		die_errno("Can't dup helper output fd");
+	data->out = xfdopen(duped, "r");
+	setvbuf(data->out, NULL, _IONBF, 0);
+
 	write_str_in_full(helper->in, "capabilities\n");
 
-	data->out = xfdopen(helper->out, "r");
 	while (1) {
-		if (strbuf_getline(&buf, data->out, '\n') == EOF)
+		if (strbuf_getline(&buf, data->out, '\n') == EOF) {
+			/* If we haven't seen line yet, try to finish the
+			   command so we get error message about failed
+			   execution. */
+			if(!seen_line)
+				finish_command(helper);
+
 			exit(128); /* child died, message supplied already */
+		}
+
+		seen_line = 1;
 
 		if (!*buf.buf)
 			break;
@@ -91,6 +135,7 @@ static int disconnect_helper(struct transport *transport)
 	if (data->helper) {
 		write_str_in_full(data->helper->in, "\n");
 		close(data->helper->in);
+		close(data->helper->out);
 		fclose(data->out);
 		finish_command(data->helper);
 		free((char *)data->helper->argv[0]);
diff --git a/transport.c b/transport.c
index 3eea836..148b9e1 100644
--- a/transport.c
+++ b/transport.c
@@ -780,6 +780,55 @@ static int is_file(const char *url)
 	return S_ISREG(buf.st_mode);
 }
 
+static const char* strchrc(const char* str, int c)
+{
+	while(*str)
+		if(*str == c)
+			return str;
+		else
+			str++;
+	return NULL;
+}
+
+static int is_url(const char* url)
+{
+	if(!url)
+		return 0;
+
+	const char* url2 = url;
+	const char* first_slash = strchrc(url, '/');
+
+	/* Input with no slash at all or slash first can't be URL. */
+	if(!first_slash || first_slash == url)
+		return 0;
+	/* Character before must be : and next must be /. */
+	if(first_slash[-1] != ':' || first_slash[1] != '/')
+		return 0;
+	/* There must be something before the :// */
+	if(first_slash == url + 1)
+		return 0;
+	/* Check all characters up to first slash. Only alpha, num and
+	   : are allowed. : must be followed by : or / */
+	url2 = url;
+	while(url2 < first_slash) {
+		if(*url2 != ':' && !isalnum((unsigned char)*url2))
+			return 0;
+		if(*url2 == ':' && url2[1] != ':' && url2[1] != '/')
+			return 0;
+		if(*url2 == ':')
+			url2++;		/* Skip second : */
+		url2++;
+	}
+
+	/* Valid enough. */
+	return 1;
+}
+
+static int external_specification_len(const char* url)
+{
+	return strchrc(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
 	struct transport *ret = xcalloc(1, sizeof(*ret));
@@ -812,23 +861,19 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		ret->get_refs_list = get_refs_via_rsync;
 		ret->fetch = fetch_objs_via_rsync;
 		ret->push = rsync_transport_push;
-
-	} else if (!prefixcmp(url, "http://")
-	        || !prefixcmp(url, "https://")
-	        || !prefixcmp(url, "ftp://")) {
-		transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-		error("git was compiled without libcurl support.");
-#endif
-
 	} else if (is_local(url) && is_file(url)) {
 		struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->get_refs_list = get_refs_from_bundle;
 		ret->fetch = fetch_refs_from_bundle;
 		ret->disconnect = close_bundle;
-
-	} else {
+	} else if(!is_url(url)
+		|| !prefixcmp(url, "file://")
+		|| !prefixcmp(url, "git://")
+		|| !prefixcmp(url, "ssh://")
+		|| !prefixcmp(url, "git+ssh://")
+		|| !prefixcmp(url, "ssh+git://")) {
+		/* These are builtin smart transports. */
 		struct git_transport_data *data = xcalloc(1, sizeof(*data));
 		ret->data = data;
 		ret->set_option = set_git_option;
@@ -845,6 +890,21 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		data->receivepack = "git-receive-pack";
 		if (remote->receivepack)
 			data->receivepack = remote->receivepack;
+	} else if (!prefixcmp(url, "http://")
+		|| !prefixcmp(url, "https://")
+		|| !prefixcmp(url, "ftp://")) {
+		/* These three are just plain special. */
+		transport_helper_init(ret, "curl");
+#ifdef NO_CURL
+		error("git was compiled without libcurl support.");
+#else
+	} else {
+		/* Unknown protocol in URL. Pass to external handler. */
+		int len = external_specification_len(url);
+		char* handler = xmalloc(len + 1);
+		handler[len] = 0;
+		strncpy(handler, url, len);
+		transport_helper_init(ret, handler);
 	}
 
 	return ret;
-- 
1.6.6.rc0.64.g5593e
Previous: Ilari LiusvaaraNext: Ilari Liusvaara
Message 2 of 42 in “Git remote helpers to implement smart transports.”
  1. 0/8 Git remote helpers to implement smart transports.Ilari Liusvaara, Dec 1, 2009
  2. 1/8 Pass unknown protocols to external protocol handlersIlari Liusvaara, Dec 1, 2009
  3. 2/8 Refactor git transport options parsingIlari Liusvaara, Dec 1, 2009
  4. 3/8 Support taking over transportsIlari Liusvaara, Dec 1, 2009
  5. 4/8 Support remote helpers implementing smart transportsIlari Liusvaara, Dec 1, 2009
  6. Shawn O. PearceDec 1, 2009
  7. Ilari LiusvaaraDec 2, 2009
  8. Shawn O. PearceDec 2, 2009
  9. Ilari LiusvaaraDec 2, 2009
  10. Shawn O. PearceDec 3, 2009
  11. Shawn O. PearceDec 2, 2009
  12. 5/8 Support remote archive from external protocol helpersIlari Liusvaara, Dec 1, 2009
  13. 6/8 Remove special casing of http, https and ftpIlari Liusvaara, Dec 1, 2009
  14. Shawn O. PearceDec 1, 2009
  15. Ilari LiusvaaraDec 1, 2009
  16. Daniel BarkalowDec 1, 2009
  17. Ilari LiusvaaraDec 2, 2009
  18. 7/8 Add remote helper debug modeIlari Liusvaara, Dec 1, 2009
  19. 8/8 Support mandatory capabilitiesIlari Liusvaara, Dec 1, 2009
  20. Sverre RabbelierDec 1, 2009
  21. Shawn O. PearceDec 1, 2009
  22. Ilari LiusvaaraDec 1, 2009
  23. Shawn O. PearceDec 1, 2009
  24. Junio C HamanoDec 1, 2009
  25. Shawn O. PearceDec 1, 2009
  26. Ilari LiusvaaraDec 2, 2009
  27. Junio C HamanoDec 2, 2009
  28. Ilari LiusvaaraDec 2, 2009
  29. Junio C HamanoDec 2, 2009
  30. Johannes SchindelinDec 2, 2009
  31. Sverre RabbelierDec 2, 2009
  32. Junio C HamanoDec 2, 2009
  33. Sverre RabbelierDec 2, 2009
  34. Junio C HamanoDec 2, 2009
  35. Sverre RabbelierDec 2, 2009
  36. Junio C HamanoDec 2, 2009
  37. Jeff KingDec 2, 2009
  38. Ilari LiusvaaraDec 2, 2009
  39. Junio C HamanoDec 2, 2009
  40. Ilari LiusvaaraDec 2, 2009
  41. Ilari LiusvaaraDec 2, 2009
  42. Ilari LiusvaaraDec 2, 2009

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.