diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 000000000..1f39f105b --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "protocol.h" +#include "pkt.h" +#include "buffer.h" + +int git_protocol_store_refs(git_protocol *p, const char *data, size_t len) +{ + git_buf *buf = &p->buf; + git_vector *refs = p->refs; + int error; + const char *line_end, *ptr; + + if (len == 0) { /* EOF */ + if (buf->size != 0) + return p->error = git__throw(GIT_ERROR, "EOF and unprocessed data"); + else + return 0; + } + + git_buf_put(buf, data, len); + ptr = buf->ptr; + while (1) { + git_pkt *pkt; + + if (buf->size == 0) + return 0; + + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size); + if (error == GIT_ESHORTBUFFER) + return 0; /* Ask for more */ + if (error < GIT_SUCCESS) + return p->error = git__rethrow(error, "Failed to parse pkt-line"); + + git_buf_consume(buf, line_end); + error = git_vector_insert(refs, pkt); + if (error < GIT_SUCCESS) + return p->error = git__rethrow(error, "Failed to add pkt to list"); + + if (pkt->type == GIT_PKT_FLUSH) + p->flush = 1; + } + + return error; +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 000000000..e3315738a --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_protocol_h__ +#define INCLUDE_protocol_h__ + +#include "transport.h" +#include "buffer.h" + +typedef struct { + git_transport *transport; + git_vector *refs; + git_buf buf; + int error; + unsigned int flush :1; +} git_protocol; + +int git_protocol_store_refs(git_protocol *p, const char *data, size_t len); + +#endif diff --git a/src/transports/git.c b/src/transports/git.c index c2014529b..2ee2e4831 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -20,9 +20,11 @@ #include "filebuf.h" #include "repository.h" #include "fetch.h" +#include "protocol.h" typedef struct { git_transport parent; + git_protocol proto; GIT_SOCKET socket; git_vector refs; git_remote_head **heads; @@ -126,11 +128,7 @@ static int do_connect(transport_git *t, const char *url) static int store_refs(transport_git *t) { gitno_buffer *buf = &t->buf; - git_vector *refs = &t->refs; int error = GIT_SUCCESS; - const char *line_end, *ptr; - git_pkt *pkt; - while (1) { error = gitno_recv(buf); @@ -139,34 +137,20 @@ static int store_refs(transport_git *t) if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */ return GIT_SUCCESS; - ptr = buf->data; - while (1) { - if (buf->offset == 0) - break; - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); - /* - * If the error is GIT_ESHORTBUFFER, it means the buffer - * isn't long enough to satisfy the request. Break out and - * wait for more input. - * On any other error, fail. - */ - if (error == GIT_ESHORTBUFFER) { - break; - } - if (error < GIT_SUCCESS) { - return error; - } + error = git_protocol_store_refs(&t->proto, buf->data, buf->offset); + if (error == GIT_ESHORTBUFFER) { + gitno_consume_n(buf, buf->len); + continue; + } - /* Get rid of the part we've used already */ - gitno_consume(buf, line_end); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to store refs"); - error = git_vector_insert(refs, pkt); - if (error < GIT_SUCCESS) - return error; - - if (pkt->type == GIT_PKT_FLUSH) - return GIT_SUCCESS; + gitno_consume_n(buf, buf->offset); + if (t->proto.flush) { /* No more refs */ + t->proto.flush = 0; + return GIT_SUCCESS; } } @@ -476,6 +460,7 @@ static void git_free(git_transport *transport) git_vector_free(refs); git__free(t->heads); + git_buf_free(&t->proto.buf); git__free(t->parent.url); git__free(t); } @@ -501,6 +486,8 @@ int git_transport_git(git_transport **out) t->parent.download_pack = git_download_pack; t->parent.close = git_close; t->parent.free = git_free; + t->proto.refs = &t->refs; + t->proto.transport = (git_transport *) t; *out = (git_transport *) t; diff --git a/src/transports/http.c b/src/transports/http.c index 66b6f252c..ae0c56a73 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -19,6 +19,7 @@ #include "fetch.h" #include "filebuf.h" #include "repository.h" +#include "protocol.h" enum last_cb { NONE, @@ -28,6 +29,7 @@ enum last_cb { typedef struct { git_transport parent; + git_protocol proto; git_vector refs; git_vector common; int socket; @@ -186,47 +188,8 @@ static int on_headers_complete(http_parser *parser) static int on_body_store_refs(http_parser *parser, const char *str, size_t len) { transport_http *t = (transport_http *) parser->data; - git_buf *buf = &t->buf; - git_vector *refs = &t->refs; - int error; - const char *line_end, *ptr; - static int first_pkt = 1; - if (len == 0) { /* EOF */ - if (buf->size != 0) - return t->error = git__throw(GIT_ERROR, "EOF and unprocessed data"); - else - return 0; - } - - git_buf_put(buf, str, len); - ptr = buf->ptr; - while (1) { - git_pkt *pkt; - - if (buf->size == 0) - return 0; - - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size); - if (error == GIT_ESHORTBUFFER) - return 0; /* Ask for more */ - if (error < GIT_SUCCESS) - return t->error = git__rethrow(error, "Failed to parse pkt-line"); - - git_buf_consume(buf, line_end); - - if (first_pkt) { - first_pkt = 0; - if (pkt->type != GIT_PKT_COMMENT) - return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response"); - } - - error = git_vector_insert(refs, pkt); - if (error < GIT_SUCCESS) - return t->error = git__rethrow(error, "Failed to add pkt to list"); - } - - return error; + return git_protocol_store_refs(&t->proto, str, len); } static int on_message_complete(http_parser *parser) @@ -243,6 +206,7 @@ static int store_refs(transport_http *t) http_parser_settings settings; char buffer[1024]; gitno_buffer buf; + git_pkt *pkt; http_parser_init(&t->parser, HTTP_RESPONSE); t->parser.data = t; @@ -273,6 +237,12 @@ static int store_refs(transport_http *t) return GIT_SUCCESS; } + pkt = git_vector_get(&t->refs, 0); + if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) + return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response"); + else + git_vector_remove(&t->refs, 0); + return error; } @@ -750,6 +720,7 @@ static void http_free(git_transport *transport) } git_vector_free(common); git_buf_free(&t->buf); + git_buf_free(&t->proto.buf); git__free(t->heads); git__free(t->content_type); git__free(t->host); @@ -775,6 +746,8 @@ int git_transport_http(git_transport **out) t->parent.download_pack = http_download_pack; t->parent.close = http_close; t->parent.free = http_free; + t->proto.refs = &t->refs; + t->proto.transport = (git_transport *) t; #ifdef GIT_WIN32 /* on win32, the WSA context needs to be initialized