net: recognize and report server-side error messages When e.g. a repository isn't found, the server sends an error saying so. Put that error message in our error buffer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
diff --git a/src/pkt.c b/src/pkt.c
index ae7a408..f25365d 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 4c4a7f7..d1bfc51 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 012e8ff..102401b 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;
}