Commit ced9da5412f38eea6d032586d45eba6ebfd34554

Vicent Martí 2012-04-30T14:38:15

Merge pull request #654 from carlosmn/pkt-err Recognize and report server-side error messages

diff --git a/src/pkt.c b/src/pkt.c
index 6cf4dac..00836bc 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -97,6 +97,25 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
 	return 0;
 }
 
+static int err_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_err *pkt;
+
+	/* Remove "ERR " from the line */
+	line += 4;
+	len -= 4;
+	pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_ERR;
+	memcpy(pkt->error, line, len);
+	pkt->error[len] = '\0';
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
 /*
  * Parse an other-ref line.
  */
@@ -234,6 +253,8 @@ int git_pkt_parse_line(
 		ret = ack_pkt(head, line, len);
 	else if (!git__prefixcmp(line, "NAK"))
 		ret = nak_pkt(head);
+	else if (!git__prefixcmp(line, "ERR "))
+		ret = err_pkt(head, line, len);
 	else if (*line == '#')
 		ret = comment_pkt(head, line, len);
 	else
diff --git a/src/pkt.h b/src/pkt.h
index 7e696f7..75442c8 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -23,6 +23,7 @@ enum git_pkt_type {
 	GIT_PKT_NAK,
 	GIT_PKT_PACK,
 	GIT_PKT_COMMENT,
+	GIT_PKT_ERR,
 };
 
 /* Used for multi-ack */
@@ -64,6 +65,11 @@ typedef struct {
 	char comment[GIT_FLEX_ARRAY];
 } git_pkt_comment;
 
+typedef struct {
+	enum git_pkt_type type;
+	char error[GIT_FLEX_ARRAY];
+} git_pkt_err;
+
 int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
 int git_pkt_buffer_flush(git_buf *buf);
 int git_pkt_send_flush(GIT_SOCKET s);
diff --git a/src/protocol.c b/src/protocol.c
index a7df961..a753541 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -40,6 +40,13 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
 			return p->error = -1;
 
 		git_buf_consume(buf, line_end);
+
+		if (pkt->type == GIT_PKT_ERR) {
+			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
+			git__free(pkt);
+			return -1;
+		}
+
 		if (git_vector_insert(refs, pkt) < 0)
 			return p->error = -1;
 
diff --git a/src/transports/http.c b/src/transports/http.c
index 938076f..3690f3d 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -165,6 +165,12 @@ static int on_headers_complete(http_parser *parser)
 	transport_http *t = (transport_http *) parser->data;
 	git_buf *buf = &t->buf;
 
+	/* The content-type is text/plain for 404, so don't validate */
+	if (parser->status_code == 404) {
+		git_buf_clear(buf);
+		return 0;
+	}
+
 	if (t->content_type == NULL) {
 		t->content_type = git__strdup(git_buf_cstr(buf));
 		if (t->content_type == NULL)
@@ -187,6 +193,10 @@ static int on_body_store_refs(http_parser *parser, const char *str, size_t len)
 {
 	transport_http *t = (transport_http *) parser->data;
 
+	if (parser->status_code == 404) {
+		return git_buf_put(&t->buf, str, len);
+	}
+
 	return git_protocol_store_refs(&t->proto, str, len);
 }
 
@@ -195,6 +205,12 @@ static int on_message_complete(http_parser *parser)
 	transport_http *t = (transport_http *) parser->data;
 
 	t->transfer_finished = 1;
+
+	if (parser->status_code == 404) {
+		giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));
+		t->error = -1;
+	}
+
 	return 0;
 }