summaryrefslogtreecommitdiff
path: root/src/transports/smart.c
diff options
context:
space:
mode:
authorPhilip Kelley <phkelley@hotmail.com>2012-11-28 11:42:37 -0500
committerPhilip Kelley <phkelley@hotmail.com>2012-11-28 11:42:37 -0500
commit613d5eb9391d6cc33c91f4dc9cdb5ede1885dc72 (patch)
treebd94a2a5ea154a3849114575368b7531f0d45024 /src/transports/smart.c
parent693021262ba0eeac2923bbce1b2262717019c807 (diff)
downloadlibgit2-613d5eb9391d6cc33c91f4dc9cdb5ede1885dc72.tar.gz
Push! By schu, phkelley, and congyiwu, et al
Diffstat (limited to 'src/transports/smart.c')
-rw-r--r--src/transports/smart.c209
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) {