Commit d4763c9861ed517ec8c0e08fe0b474d3554a13ee

Carlos Martín Nieto 2016-03-24T06:56:25

Merge pull request #3574 from chescock/buffer-sideband-pack-data Buffer sideband packet data

diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 6363378..02e1ecf 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -721,18 +721,39 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
 	return 0;
 }
 
-static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
+static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
 {
 	git_pkt *pkt;
-	const char *line = data_pkt->data, *line_end;
-	size_t line_len = data_pkt->len;
+	const char *line, *line_end;
+	size_t line_len;
 	int error;
+	int reading_from_buf = data_pkt_buf->size > 0;
+
+	if (reading_from_buf) {
+		/* We had an existing partial packet, so add the new
+		 * packet to the buffer and parse the whole thing */
+		git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
+		line = data_pkt_buf->ptr;
+		line_len = data_pkt_buf->size;
+	}
+	else {
+		line = data_pkt->data;
+		line_len = data_pkt->len;
+	}
 
 	while (line_len > 0) {
 		error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
 
-		if (error < 0)
-			return error;
+		if (error == GIT_EBUFS) {
+			/* Buffer the data when the inner packet is split
+			 * across multiple sideband packets */
+			if (!reading_from_buf)
+				git_buf_put(data_pkt_buf, line, line_len);
+			error = 0;
+			goto done;
+		}
+		else if (error < 0)
+			goto done;
 
 		/* Advance in the buffer */
 		line_len -= (line_end - line);
@@ -743,10 +764,15 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
 		git_pkt_free(pkt);
 
 		if (error < 0 && error != GIT_ITEROVER)
-			return error;
+			goto done;
 	}
 
-	return 0;
+	error = 0;
+
+done:
+	if (reading_from_buf)
+		git_buf_consume(data_pkt_buf, line_end);
+	return error;
 }
 
 static int parse_report(transport_smart *transport, git_push *push)
@@ -755,6 +781,7 @@ static int parse_report(transport_smart *transport, git_push *push)
 	const char *line_end = NULL;
 	gitno_buffer *buf = &transport->buffer;
 	int error, recvd;
+	git_buf data_pkt_buf = GIT_BUF_INIT;
 
 	for (;;) {
 		if (buf->offset > 0)
@@ -763,16 +790,21 @@ static int parse_report(transport_smart *transport, git_push *push)
 		else
 			error = GIT_EBUFS;
 
-		if (error < 0 && error != GIT_EBUFS)
-			return -1;
+		if (error < 0 && error != GIT_EBUFS) {
+			error = -1;
+			goto done;
+		}
 
 		if (error == GIT_EBUFS) {
-			if ((recvd = gitno_recv(buf)) < 0)
-				return recvd;
+			if ((recvd = gitno_recv(buf)) < 0) {
+				error = recvd;
+				goto done;
+			}
 
 			if (recvd == 0) {
 				giterr_set(GITERR_NET, "early EOF");
-				return GIT_EEOF;
+				error = GIT_EEOF;
+				goto done;
 			}
 			continue;
 		}
@@ -784,7 +816,7 @@ static int parse_report(transport_smart *transport, git_push *push)
 		switch (pkt->type) {
 			case GIT_PKT_DATA:
 				/* This is a sideband packet which contains other packets */
-				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
+				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
 				break;
 			case GIT_PKT_ERR:
 				giterr_set(GITERR_NET, "report-status: Error reported: %s",
@@ -805,12 +837,24 @@ static int parse_report(transport_smart *transport, git_push *push)
 		git_pkt_free(pkt);
 
 		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
-		if (error == GIT_ITEROVER)
-			return 0;
+		if (error == GIT_ITEROVER) {
+			error = 0;
+			if (data_pkt_buf.size > 0) {
+				/* If there was data remaining in the pack data buffer,
+				 * then the server sent a partial pkt-line */
+				giterr_set(GITERR_NET, "Incomplete pack data pkt-line");
+				error = GIT_ERROR;
+			}
+			goto done;
+		}
 
-		if (error < 0)
-			return error;
+		if (error < 0) {
+			goto done;
+		}
 	}
+done:
+	git_buf_free(&data_pkt_buf);
+	return error;
 }
 
 static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)