Commit eb07a4d1dd5b6ec2ee60566d0ec2aa705bf37f4c

Carlos Martín Nieto 2011-09-12T15:25:45

http: add more modularity to the code Not every request needs a new connection if we're using a keep-alive connection. Store the HTTP parser, host and port in the transport in order to have it available in later calls. Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/src/transport-http.c b/src/transport-http.c
index f740aa4..cc12939 100644
--- a/src/transport-http.c
+++ b/src/transport-http.c
@@ -32,14 +32,17 @@ typedef struct {
 		ct_found :1,
 		ct_finished :1;
 	enum last_cb last_cb;
+	http_parser parser;
 	char *content_type;
+	char *host;
+	char *port;
 	char *service;
 #ifdef GIT_WIN32
 	WSADATA wsd;
 #endif
 } transport_http;
 
-static int gen_request(git_buf *buf, const char *url, const char *host, const char *service)
+static int gen_request(git_buf *buf, const char *url, const char *host, const char *op, const char *service)
 {
 	const char *path = url;
 
@@ -47,7 +50,7 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
 	if (path == NULL) /* Is 'git fetch http://host.com/' valid? */
 		path = "/";
 
-	git_buf_printf(buf, "GET %s/info/refs?service=git-%s HTTP/1.1\r\n", path, service);
+	git_buf_printf(buf, "%s %s/info/refs?service=git-%s HTTP/1.1\r\n", op, path, service);
 	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
 	git_buf_printf(buf, "Host: %s\r\n", host);
 	git_buf_puts(buf, "Accept: */*\r\n" "Pragma: no-cache\r\n\r\n");
@@ -58,52 +61,21 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
 	return GIT_SUCCESS;
 }
 
-static int do_connect(transport_http *t, const char *service)
+static int do_connect(transport_http *t, const char *host, const char *port)
 {
-	git_buf request = GIT_BUF_INIT;
-	int error;
-	int s;
-	const char *url, *prefix;
-	char *host = NULL, *port = NULL;
-
-	url = t->parent.url;
-	prefix = "http://";
-
-	if (!git__prefixcmp(url, prefix))
-		url += strlen(prefix);
+	GIT_SOCKET s = -1;
 
-	error = gitno_extract_host_and_port(&host, &port, url, "80");
-	if (error < GIT_SUCCESS)
-		goto cleanup;
-
-	t->service = git__strdup(service);
-	if (t->service == NULL) {
-		error = GIT_ENOMEM;
-		goto cleanup;
-	}
+	if (t->parent.connected && http_should_keep_alive(&t->parser))
+		return GIT_SUCCESS;
 
 	s = gitno_connect(host, port);
 	if (s < GIT_SUCCESS) {
-	    error = git__throw(error, "Failed to connect to host");
+	    return git__rethrow(s, "Failed to connect to host");
 	}
 	t->socket = s;
+	t->parent.connected = 1;
 
-	/* Generate and send the HTTP request */
-	error = gen_request(&request, url, host, service);
-	if (error < GIT_SUCCESS) {
-		error = git__throw(error, "Failed to generate request");
-		goto cleanup;
-	}
-	error = gitno_send(s, git_buf_cstr(&request), strlen(git_buf_cstr(&request)), 0);
-	if (error < GIT_SUCCESS)
-		error = git__rethrow(error, "Failed to send the HTTP request");
-
-cleanup:
-	git_buf_free(&request);
-	free(host);
-	free(port);
-
-	return error;
+	return GIT_SUCCESS;
 }
 
 /*
@@ -248,13 +220,12 @@ static int on_message_complete(http_parser *parser)
 static int store_refs(transport_http *t)
 {
 	int error = GIT_SUCCESS;
-	http_parser parser;
 	http_parser_settings settings;
 	char buffer[1024];
 	gitno_buffer buf;
 
-	http_parser_init(&parser, HTTP_RESPONSE);
-	parser.data = t;
+	http_parser_init(&t->parser, HTTP_RESPONSE);
+	t->parser.data = t;
 	memset(&settings, 0x0, sizeof(http_parser_settings));
 	settings.on_header_field = on_header_field;
 	settings.on_header_value = on_header_value;
@@ -271,7 +242,7 @@ static int store_refs(transport_http *t)
 		if (error < GIT_SUCCESS)
 			return git__rethrow(error, "Error receiving data from network");
 
-		parsed = http_parser_execute(&parser, &settings, buf.data, buf.offset);
+		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
 		/* Both should happen at the same time */
 		if (parsed != buf.offset || t->error < GIT_SUCCESS)
 			return git__rethrow(t->error, "Error parsing HTTP data");
@@ -289,6 +260,9 @@ static int http_connect(git_transport *transport, int direction)
 {
 	transport_http *t = (transport_http *) transport;
 	int error;
+	git_buf request = GIT_BUF_INIT;
+	const char *service = "upload-pack";
+	const char *url = t->parent.url, *prefix = "http://";
 
 	if (direction == GIT_DIR_PUSH)
 		return git__throw(GIT_EINVALIDARGS, "Pushing over HTTP is not supported");
@@ -298,17 +272,41 @@ static int http_connect(git_transport *transport, int direction)
 	if (error < GIT_SUCCESS)
 		return git__rethrow(error, "Failed to init refs vector");
 
-	error = do_connect(t, "upload-pack");
+	if (!git__prefixcmp(url, prefix))
+		url += strlen(prefix);
+
+	error = gitno_extract_host_and_port(&t->host, &t->port, url, "80");
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+	t->service = git__strdup(service);
+	if (t->service == NULL) {
+		error = GIT_ENOMEM;
+		goto cleanup;
+	}
+
+	error = do_connect(t, t->host, t->port);
 	if (error < GIT_SUCCESS) {
 		error = git__rethrow(error, "Failed to connect to host");
 		goto cleanup;
 	}
 
+	/* Generate and send the HTTP request */
+	error = gen_request(&request, url, t->host, "GET", service);
+	if (error < GIT_SUCCESS) {
+		error = git__throw(error, "Failed to generate request");
+		goto cleanup;
+	}
+
+	error = gitno_send(t->socket, git_buf_cstr(&request), strlen(git_buf_cstr(&request)), 0);
+	if (error < GIT_SUCCESS)
+		error = git__rethrow(error, "Failed to send the HTTP request");
+
 	error = store_refs(t);
 
 cleanup:
+	git_buf_free(&request);
 	git_buf_clear(&t->buf);
-	git_buf_free(&t->buf);
 
 	return error;
 }
@@ -371,8 +369,11 @@ static void http_free(git_transport *transport)
 		git_pkt_free(p);
 	}
 	git_vector_free(refs);
+	git_buf_free(&t->buf);
 	free(t->heads);
 	free(t->content_type);
+	free(t->host);
+	free(t->port);
 	free(t->service);
 	free(t->parent.url);
 	free(t);