diff options
author | Philip Kelley <phkelley@hotmail.com> | 2012-11-28 11:42:37 -0500 |
---|---|---|
committer | Philip Kelley <phkelley@hotmail.com> | 2012-11-28 11:42:37 -0500 |
commit | 613d5eb9391d6cc33c91f4dc9cdb5ede1885dc72 (patch) | |
tree | bd94a2a5ea154a3849114575368b7531f0d45024 /src/transports/smart.c | |
parent | 693021262ba0eeac2923bbce1b2262717019c807 (diff) | |
download | libgit2-613d5eb9391d6cc33c91f4dc9cdb5ede1885dc72.tar.gz |
Push! By schu, phkelley, and congyiwu, et al
Diffstat (limited to 'src/transports/smart.c')
-rw-r--r-- | src/transports/smart.c | 209 |
1 files changed, 129 insertions, 80 deletions
diff --git a/src/transports/smart.c b/src/transports/smart.c index e8dbbef5c..00f8832e0 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -29,12 +29,18 @@ static int git_smart__recv_cb(gitno_buffer *buf) return (int)(buf->offset - old_len); } -GIT_INLINE(void) git_smart__reset_stream(transport_smart *t) +GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport) { if (t->current_stream) { t->current_stream->free(t->current_stream); t->current_stream = NULL; } + + if (close_subtransport && + t->wrapped->close(t->wrapped) < 0) + return -1; + + return 0; } static int git_smart__set_callbacks( @@ -63,8 +69,11 @@ static int git_smart__connect( git_smart_subtransport_stream *stream; int error; git_pkt *pkt; + git_pkt_ref *first; + git_smart_service_t service; - git_smart__reset_stream(t); + if (git_smart__reset_stream(t, true) < 0) + return -1; t->url = git__strdup(url); GITERR_CHECK_ALLOC(t->url); @@ -73,55 +82,64 @@ static int git_smart__connect( t->flags = flags; t->cred_acquire_cb = cred_acquire_cb; - if (GIT_DIRECTION_FETCH == direction) - { - if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK_LS)) < 0) - return error; - - /* Save off the current stream (i.e. socket) that we are working with */ - t->current_stream = stream; - - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - - /* 2 flushes for RPC; 1 for stateful */ - if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) - return error; - - /* Strip the comment packet for RPC */ - if (t->rpc) { - pkt = (git_pkt *)git_vector_get(&t->refs, 0); - - if (!pkt || GIT_PKT_COMMENT != pkt->type) { - giterr_set(GITERR_NET, "Invalid response"); - return -1; - } else { - /* Remove the comment pkt from the list */ - git_vector_remove(&t->refs, 0); - git__free(pkt); - } - } + if (GIT_DIRECTION_FETCH == t->direction) + service = GIT_SERVICE_UPLOADPACK_LS; + else if (GIT_DIRECTION_PUSH == t->direction) + service = GIT_SERVICE_RECEIVEPACK_LS; + else { + giterr_set(GITERR_NET, "Invalid direction"); + return -1; + } - /* We now have loaded the refs. */ - t->have_refs = 1; + if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0) + return error; - if (git_smart__detect_caps((git_pkt_ref *)git_vector_get(&t->refs, 0), &t->caps) < 0) - return -1; + /* Save off the current stream (i.e. socket) that we are working with */ + t->current_stream = stream; - if (t->rpc) - git_smart__reset_stream(t); + gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - /* We're now logically connected. */ - t->connected = 1; + /* 2 flushes for RPC; 1 for stateful */ + if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) + return error; + + /* Strip the comment packet for RPC */ + if (t->rpc) { + pkt = (git_pkt *)git_vector_get(&t->refs, 0); - return 0; + if (!pkt || GIT_PKT_COMMENT != pkt->type) { + giterr_set(GITERR_NET, "Invalid response"); + return -1; + } else { + /* Remove the comment pkt from the list */ + git_vector_remove(&t->refs, 0); + git__free(pkt); + } } - else - { - giterr_set(GITERR_NET, "Push not implemented"); + + /* We now have loaded the refs. */ + t->have_refs = 1; + + first = (git_pkt_ref *)git_vector_get(&t->refs, 0); + + /* Detect capabilities */ + if (git_smart__detect_caps(first, &t->caps) < 0) return -1; + + /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */ + if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") && + git_oid_iszero(&first->head.oid)) { + git_vector_clear(&t->refs); + git_pkt_free((git_pkt *)first); } - return -1; + if (t->rpc && git_smart__reset_stream(t, false) < 0) + return -1; + + /* We're now logically connected. */ + t->connected = 1; + + return 0; } static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload) @@ -156,29 +174,55 @@ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len git_smart_subtransport_stream *stream; int error; - if (t->rpc) - git_smart__reset_stream(t); + if (t->rpc && git_smart__reset_stream(t, false) < 0) + return -1; + + if (GIT_DIRECTION_FETCH != t->direction) { + giterr_set(GITERR_NET, "This operation is only valid for fetch"); + return -1; + } + + if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0) + return error; + + /* If this is a stateful implementation, the stream we get back should be the same */ + assert(t->rpc || t->current_stream == stream); - if (GIT_DIRECTION_FETCH == t->direction) { - if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0) - return error; + /* Save off the current stream (i.e. socket) that we are working with */ + t->current_stream = stream; - /* If this is a stateful implementation, the stream we get back should be the same */ - assert(t->rpc || t->current_stream == stream); + if ((error = stream->write(stream, (const char *)data, len)) < 0) + return error; - /* Save off the current stream (i.e. socket) that we are working with */ - t->current_stream = stream; + gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - if ((error = stream->write(stream, (const char *)data, len)) < 0) - return error; + return 0; +} - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); +int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream) +{ + int error; + + if (t->rpc && git_smart__reset_stream(t, false) < 0) + return -1; - return 0; + if (GIT_DIRECTION_PUSH != t->direction) { + giterr_set(GITERR_NET, "This operation is only valid for push"); + return -1; } - giterr_set(GITERR_NET, "Push not implemented"); - return -1; + if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0) + return error; + + /* If this is a stateful implementation, the stream we get back should be the same */ + assert(t->rpc || t->current_stream == *stream); + + /* Save off the current stream (i.e. socket) that we are working with */ + t->current_stream = *stream; + + gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + + return 0; } static void git_smart__cancel(git_transport *transport) @@ -188,13 +232,11 @@ static void git_smart__cancel(git_transport *transport) git_atomic_set(&t->cancelled, 1); } -static int git_smart__is_connected(git_transport *transport, int *connected) +static int git_smart__is_connected(git_transport *transport) { transport_smart *t = (transport_smart *)transport; - *connected = t->connected; - - return 0; + return t->connected; } static int git_smart__read_flags(git_transport *transport, int *flags) @@ -209,21 +251,37 @@ static int git_smart__read_flags(git_transport *transport, int *flags) static int git_smart__close(git_transport *transport) { transport_smart *t = (transport_smart *)transport; - - git_smart__reset_stream(t); + git_vector *refs = &t->refs; + git_vector *common = &t->common; + unsigned int i; + git_pkt *p; + int ret; + + ret = git_smart__reset_stream(t, true); + + git_vector_foreach(refs, i, p) + git_pkt_free(p); + + git_vector_free(refs); + + git_vector_foreach(common, i, p) + git_pkt_free(p); + + git_vector_free(common); + + if (t->url) { + git__free(t->url); + t->url = NULL; + } t->connected = 0; - return 0; + return ret; } static void git_smart__free(git_transport *transport) { transport_smart *t = (transport_smart *)transport; - git_vector *refs = &t->refs; - git_vector *common = &t->common; - unsigned int i; - git_pkt *p; /* Make sure that the current stream is closed, if we have one. */ git_smart__close(transport); @@ -231,21 +289,10 @@ static void git_smart__free(git_transport *transport) /* Free the subtransport */ t->wrapped->free(t->wrapped); - git_vector_foreach(refs, i, p) { - git_pkt_free(p); - } - git_vector_free(refs); - - git_vector_foreach(common, i, p) { - git_pkt_free(p); - } - git_vector_free(common); - - git__free(t->url); git__free(t); } -int git_transport_smart(git_transport **out, void *param) +int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param; @@ -262,11 +309,13 @@ int git_transport_smart(git_transport **out, void *param) t->parent.free = git_smart__free; t->parent.negotiate_fetch = git_smart__negotiate_fetch; t->parent.download_pack = git_smart__download_pack; + t->parent.push = git_smart__push; t->parent.ls = git_smart__ls; t->parent.is_connected = git_smart__is_connected; t->parent.read_flags = git_smart__read_flags; t->parent.cancel = git_smart__cancel; + t->owner = owner; t->rpc = definition->rpc; if (git_vector_init(&t->refs, 16, NULL) < 0) { |