From c180c06586050a33f6e8a6d25cc30b7bcbef702d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 9 Jul 2014 17:58:39 -0400 Subject: [PATCH] Custom transport: minor cleanups * Move the transport registration mechanisms into a new header under 'sys/' because this is advanced stuff. * Remove the 'priority' argument from the registration as it adds unnecessary complexity. (Since transports cannot decline to operate, only the highest priority transport is ever executed.) Users who require per-priority transports can implement that in their custom transport themselves. * Simplify registration further by taking a scheme (eg "http") instead of a prefix (eg "http://"). --- CHANGELOG.md | 6 + include/git2/remote.h | 15 -- include/git2/sys/transport.h | 354 ++++++++++++++++++++++++++++++++++ include/git2/transport.h | 333 -------------------------------- include/git2/types.h | 10 + src/remote.h | 1 + src/transport.c | 148 ++++++++------ src/transports/git.c | 1 + src/transports/smart.h | 1 + tests/clone/transport.c | 1 + tests/structinit/structinit.c | 1 + 11 files changed, 461 insertions(+), 410 deletions(-) create mode 100644 include/git2/sys/transport.h diff --git a/CHANGELOG.md b/CHANGELOG.md index d389b2cb0..93b5f20db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ v0.21 + 1 * LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4) +* The git_transport structure definition has moved into the sys/transport.h + file. + +* The git_transport_register function no longer takes a priority and takes + a URL scheme name (eg "http") instead of a prefix like "http://" + * The git_remote_set_transport function now sets a transport factory function, rather than a pre-existing transport instance. diff --git a/include/git2/remote.h b/include/git2/remote.h index c8b6ac97a..c0717fa31 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -418,21 +418,6 @@ GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo); */ GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check); -/** - * Sets a custom transport factory for the remote. The caller can use this - * function to override the transport used for this remote when performing - * network operations. - * - * @param remote the remote to configure - * @param transport_cb the function to use to create a transport - * @param payload opaque parameter passed to transport_cb - * @return 0 or an error code - */ -GIT_EXTERN(int) git_remote_set_transport( - git_remote *remote, - git_transport_cb transport_cb, - void *payload); - /** * Argument to the completion callback which tells it which operation * finished. diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h new file mode 100644 index 000000000..62ac455d3 --- /dev/null +++ b/include/git2/sys/transport.h @@ -0,0 +1,354 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * 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_sys_git_transport_h +#define INCLUDE_sys_git_transport_h + +#include "git2/net.h" +#include "git2/types.h" + +/** + * @file git2/sys/transport.h + * @brief Git custom transport registration interfaces and functions + * @defgroup git_transport Git custom transport registration + * @ingroup Git + * @{ + */ + +GIT_BEGIN_DECL + +typedef enum { + GIT_TRANSPORTFLAGS_NONE = 0, + /* If the connection is secured with SSL/TLS, the authenticity + * of the server certificate should not be verified. */ + GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1 +} git_transport_flags_t; + +typedef struct git_transport git_transport; + +struct git_transport { + unsigned int version; + /* Set progress and error callbacks */ + int (*set_callbacks)( + git_transport *transport, + git_transport_message_cb progress_cb, + git_transport_message_cb error_cb, + void *payload); + + /* Connect the transport to the remote repository, using the given + * direction. */ + int (*connect)( + git_transport *transport, + const char *url, + git_cred_acquire_cb cred_acquire_cb, + void *cred_acquire_payload, + int direction, + int flags); + + /* This function may be called after a successful call to + * connect(). The array returned is owned by the transport and + * is guranteed until the next call of a transport function. */ + int (*ls)( + const git_remote_head ***out, + size_t *size, + git_transport *transport); + + /* Executes the push whose context is in the git_push object. */ + int (*push)(git_transport *transport, git_push *push); + + /* This function may be called after a successful call to connect(), when + * the direction is FETCH. The function performs a negotiation to calculate + * the wants list for the fetch. */ + int (*negotiate_fetch)( + git_transport *transport, + git_repository *repo, + const git_remote_head * const *refs, + size_t count); + + /* This function may be called after a successful call to negotiate_fetch(), + * when the direction is FETCH. This function retrieves the pack file for + * the fetch from the remote end. */ + int (*download_pack)( + git_transport *transport, + git_repository *repo, + git_transfer_progress *stats, + git_transfer_progress_cb progress_cb, + void *progress_payload); + + /* Checks to see if the transport is connected */ + int (*is_connected)(git_transport *transport); + + /* Reads the flags value previously passed into connect() */ + int (*read_flags)(git_transport *transport, int *flags); + + /* Cancels any outstanding transport operation */ + void (*cancel)(git_transport *transport); + + /* This function is the reverse of connect() -- it terminates the + * connection to the remote end. */ + int (*close)(git_transport *transport); + + /* Frees/destructs the git_transport object. */ + void (*free)(git_transport *transport); +}; + +#define GIT_TRANSPORT_VERSION 1 +#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} + +/** + * Initializes a `git_transport` with default values. Equivalent to + * creating an instance with GIT_TRANSPORT_INIT. + * + * @param opts the `git_transport` struct to initialize + * @param version Version of struct; pass `GIT_TRANSPORT_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_transport_init( + git_transport *opts, + unsigned int version); + +/** + * Function to use to create a transport from a URL. The transport database + * is scanned to find a transport that implements the scheme of the URI (i.e. + * git:// or http://) and a transport object is returned to the caller. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param url The URL to connect to + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); + +/** + * Create an ssh transport with custom git command paths + * + * This is a factory function suitable for setting as the transport + * callback in a remote (or for a clone in the options). + * + * The payload argument must be a strarray pointer with the paths for + * the `git-upload-pack` and `git-receive-pack` at index 0 and 1. + * + * @param out the resulting transport + * @param owner the owning remote + * @param payload a strarray with the paths + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload); + +/* Signature of a function which creates a transport */ +typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); + +/** + * Add a custom transport definition, to be used in addition to the built-in + * set of transports that come with libgit2. + * + * The caller is responsible for synchronizing calls to git_transport_register + * and git_transport_unregister with other calls to the library that + * instantiate transports. + * + * @param prefix The scheme (ending in "://") to match, i.e. "git://" + * @param cb The callback used to create an instance of the transport + * @param param A fixed parameter to pass to cb at creation time + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_register( + const char *prefix, + git_transport_cb cb, + void *param); + +/** + * + * Unregister a custom transport definition which was previously registered + * with git_transport_register. + * + * @param prefix From the previous call to git_transport_register + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_unregister( + const char *prefix); + +/* Transports which come with libgit2 (match git_transport_cb). The expected + * value for "param" is listed in-line below. */ + +/** + * Create an instance of the dummy transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload You must pass NULL for this parameter. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_dummy( + git_transport **out, + git_remote *owner, + /* NULL */ void *payload); + +/** + * Create an instance of the local transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload You must pass NULL for this parameter. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_local( + git_transport **out, + git_remote *owner, + /* NULL */ void *payload); + +/** + * Create an instance of the smart transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload A pointer to a git_smart_subtransport_definition + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_smart( + git_transport **out, + git_remote *owner, + /* (git_smart_subtransport_definition *) */ void *payload); + +/* + *** End of base transport interface *** + *** Begin interface for subtransports for the smart transport *** + */ + +/* The smart transport knows how to speak the git protocol, but it has no + * knowledge of how to establish a connection between it and another endpoint, + * or how to move data back and forth. For this, a subtransport interface is + * declared, and the smart transport delegates this work to the subtransports. + * Three subtransports are implemented: git, http, and winhttp. (The http and + * winhttp transports each implement both http and https.) */ + +/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1 + * (request/response). The smart transport handles the differences in its own + * logic. The git subtransport is RPC = 0, while http and winhttp are both + * RPC = 1. */ + +/* Actions that the smart transport can ask + * a subtransport to perform */ +typedef enum { + GIT_SERVICE_UPLOADPACK_LS = 1, + GIT_SERVICE_UPLOADPACK = 2, + GIT_SERVICE_RECEIVEPACK_LS = 3, + GIT_SERVICE_RECEIVEPACK = 4, +} git_smart_service_t; + +typedef struct git_smart_subtransport git_smart_subtransport; +typedef struct git_smart_subtransport_stream git_smart_subtransport_stream; + +/* A stream used by the smart transport to read and write data + * from a subtransport */ +struct git_smart_subtransport_stream { + /* The owning subtransport */ + git_smart_subtransport *subtransport; + + int (*read)( + git_smart_subtransport_stream *stream, + char *buffer, + size_t buf_size, + size_t *bytes_read); + + int (*write)( + git_smart_subtransport_stream *stream, + const char *buffer, + size_t len); + + void (*free)( + git_smart_subtransport_stream *stream); +}; + +/* An implementation of a subtransport which carries data for the + * smart transport */ +struct git_smart_subtransport { + int (* action)( + git_smart_subtransport_stream **out, + git_smart_subtransport *transport, + const char *url, + git_smart_service_t action); + + /* Subtransports are guaranteed a call to close() between + * calls to action(), except for the following two "natural" progressions + * of actions against a constant URL. + * + * 1. UPLOADPACK_LS -> UPLOADPACK + * 2. RECEIVEPACK_LS -> RECEIVEPACK */ + int (*close)(git_smart_subtransport *transport); + + void (*free)(git_smart_subtransport *transport); +}; + +/* A function which creates a new subtransport for the smart transport */ +typedef int (*git_smart_subtransport_cb)( + git_smart_subtransport **out, + git_transport* owner); + +typedef struct git_smart_subtransport_definition { + /* The function to use to create the git_smart_subtransport */ + git_smart_subtransport_cb callback; + + /* True if the protocol is stateless; false otherwise. For example, + * http:// is stateless, but git:// is not. */ + unsigned rpc; +} git_smart_subtransport_definition; + +/* Smart transport subtransports that come with libgit2 */ + +/** + * Create an instance of the http subtransport. This subtransport + * also supports https. On Win32, this subtransport may be implemented + * using the WinHTTP library. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_http( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Create an instance of the git subtransport. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_git( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Create an instance of the ssh subtransport. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_ssh( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Sets a custom transport factory for the remote. The caller can use this + * function to override the transport used for this remote when performing + * network operations. + * + * @param remote the remote to configure + * @param transport_cb the function to use to create a transport + * @param payload opaque parameter passed to transport_cb + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_set_transport( + git_remote *remote, + git_transport_cb transport_cb, + void *payload); + +/** @} */ +GIT_END_DECL +#endif diff --git a/include/git2/transport.h b/include/git2/transport.h index 67939a747..66341d296 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -20,10 +20,6 @@ */ GIT_BEGIN_DECL -/* - *** Begin interface for credentials acquisition *** - */ - /** Authentication type requested */ typedef enum { /* git_cred_userpass_plaintext */ @@ -227,335 +223,6 @@ typedef int (*git_cred_acquire_cb)( unsigned int allowed_types, void *payload); -/* - *** End interface for credentials acquisition *** - *** Begin base transport interface *** - */ - -typedef enum { - GIT_TRANSPORTFLAGS_NONE = 0, - /* If the connection is secured with SSL/TLS, the authenticity - * of the server certificate should not be verified. */ - GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1 -} git_transport_flags_t; - -typedef int (*git_transport_message_cb)(const char *str, int len, void *data); - -typedef struct git_transport git_transport; - -struct git_transport { - unsigned int version; - /* Set progress and error callbacks */ - int (*set_callbacks)( - git_transport *transport, - git_transport_message_cb progress_cb, - git_transport_message_cb error_cb, - void *payload); - - /* Connect the transport to the remote repository, using the given - * direction. */ - int (*connect)( - git_transport *transport, - const char *url, - git_cred_acquire_cb cred_acquire_cb, - void *cred_acquire_payload, - int direction, - int flags); - - /* This function may be called after a successful call to - * connect(). The array returned is owned by the transport and - * is guranteed until the next call of a transport function. */ - int (*ls)( - const git_remote_head ***out, - size_t *size, - git_transport *transport); - - /* Executes the push whose context is in the git_push object. */ - int (*push)(git_transport *transport, git_push *push); - - /* This function may be called after a successful call to connect(), when - * the direction is FETCH. The function performs a negotiation to calculate - * the wants list for the fetch. */ - int (*negotiate_fetch)( - git_transport *transport, - git_repository *repo, - const git_remote_head * const *refs, - size_t count); - - /* This function may be called after a successful call to negotiate_fetch(), - * when the direction is FETCH. This function retrieves the pack file for - * the fetch from the remote end. */ - int (*download_pack)( - git_transport *transport, - git_repository *repo, - git_transfer_progress *stats, - git_transfer_progress_cb progress_cb, - void *progress_payload); - - /* Checks to see if the transport is connected */ - int (*is_connected)(git_transport *transport); - - /* Reads the flags value previously passed into connect() */ - int (*read_flags)(git_transport *transport, int *flags); - - /* Cancels any outstanding transport operation */ - void (*cancel)(git_transport *transport); - - /* This function is the reverse of connect() -- it terminates the - * connection to the remote end. */ - int (*close)(git_transport *transport); - - /* Frees/destructs the git_transport object. */ - void (*free)(git_transport *transport); -}; - -#define GIT_TRANSPORT_VERSION 1 -#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} - -/** - * Initializes a `git_transport` with default values. Equivalent to - * creating an instance with GIT_TRANSPORT_INIT. - * - * @param opts the `git_transport` struct to initialize - * @param version Version of struct; pass `GIT_TRANSPORT_VERSION` - * @return Zero on success; -1 on failure. - */ -GIT_EXTERN(int) git_transport_init( - git_transport *opts, - unsigned int version); - -/** - * Function to use to create a transport from a URL. The transport database - * is scanned to find a transport that implements the scheme of the URI (i.e. - * git:// or http://) and a transport object is returned to the caller. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param url The URL to connect to - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); - -/** - * Create an ssh transport with custom git command paths - * - * This is a factory function suitable for setting as the transport - * callback in a remote (or for a clone in the options). - * - * The payload argument must be a strarray pointer with the paths for - * the `git-upload-pack` and `git-receive-pack` at index 0 and 1. - * - * @param out the resulting transport - * @param owner the owning remote - * @param payload a strarray with the paths - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload); - -/* Signature of a function which creates a transport */ -typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); - -/** - * Add a custom transport definition, to be used in addition to the built-in - * set of transports that come with libgit2. - * - * The caller is responsible for synchronizing calls to git_transport_register - * and git_transport_unregister with other calls to the library that - * instantiate transports. - * - * @param prefix The scheme (ending in "://") to match, i.e. "git://" - * @param priority The priority of this transport relative to others with - * the same prefix. Built-in transports have a priority of 1. - * @param cb The callback used to create an instance of the transport - * @param param A fixed parameter to pass to cb at creation time - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_register( - const char *prefix, - unsigned priority, - git_transport_cb cb, - void *param); - -/** - * - * Unregister a custom transport definition which was previously registered - * with git_transport_register. - * - * @param prefix From the previous call to git_transport_register - * @param priority From the previous call to git_transport_register - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_unregister( - const char *prefix, - unsigned priority); - -/* Transports which come with libgit2 (match git_transport_cb). The expected - * value for "param" is listed in-line below. */ - -/** - * Create an instance of the dummy transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload You must pass NULL for this parameter. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_dummy( - git_transport **out, - git_remote *owner, - /* NULL */ void *payload); - -/** - * Create an instance of the local transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload You must pass NULL for this parameter. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_local( - git_transport **out, - git_remote *owner, - /* NULL */ void *payload); - -/** - * Create an instance of the smart transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload A pointer to a git_smart_subtransport_definition - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_smart( - git_transport **out, - git_remote *owner, - /* (git_smart_subtransport_definition *) */ void *payload); - -/* - *** End of base transport interface *** - *** Begin interface for subtransports for the smart transport *** - */ - -/* The smart transport knows how to speak the git protocol, but it has no - * knowledge of how to establish a connection between it and another endpoint, - * or how to move data back and forth. For this, a subtransport interface is - * declared, and the smart transport delegates this work to the subtransports. - * Three subtransports are implemented: git, http, and winhttp. (The http and - * winhttp transports each implement both http and https.) */ - -/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1 - * (request/response). The smart transport handles the differences in its own - * logic. The git subtransport is RPC = 0, while http and winhttp are both - * RPC = 1. */ - -/* Actions that the smart transport can ask - * a subtransport to perform */ -typedef enum { - GIT_SERVICE_UPLOADPACK_LS = 1, - GIT_SERVICE_UPLOADPACK = 2, - GIT_SERVICE_RECEIVEPACK_LS = 3, - GIT_SERVICE_RECEIVEPACK = 4, -} git_smart_service_t; - -typedef struct git_smart_subtransport git_smart_subtransport; -typedef struct git_smart_subtransport_stream git_smart_subtransport_stream; - -/* A stream used by the smart transport to read and write data - * from a subtransport */ -struct git_smart_subtransport_stream { - /* The owning subtransport */ - git_smart_subtransport *subtransport; - - int (*read)( - git_smart_subtransport_stream *stream, - char *buffer, - size_t buf_size, - size_t *bytes_read); - - int (*write)( - git_smart_subtransport_stream *stream, - const char *buffer, - size_t len); - - void (*free)( - git_smart_subtransport_stream *stream); -}; - -/* An implementation of a subtransport which carries data for the - * smart transport */ -struct git_smart_subtransport { - int (* action)( - git_smart_subtransport_stream **out, - git_smart_subtransport *transport, - const char *url, - git_smart_service_t action); - - /* Subtransports are guaranteed a call to close() between - * calls to action(), except for the following two "natural" progressions - * of actions against a constant URL. - * - * 1. UPLOADPACK_LS -> UPLOADPACK - * 2. RECEIVEPACK_LS -> RECEIVEPACK */ - int (*close)(git_smart_subtransport *transport); - - void (*free)(git_smart_subtransport *transport); -}; - -/* A function which creates a new subtransport for the smart transport */ -typedef int (*git_smart_subtransport_cb)( - git_smart_subtransport **out, - git_transport* owner); - -typedef struct git_smart_subtransport_definition { - /* The function to use to create the git_smart_subtransport */ - git_smart_subtransport_cb callback; - - /* True if the protocol is stateless; false otherwise. For example, - * http:// is stateless, but git:// is not. */ - unsigned rpc; -} git_smart_subtransport_definition; - -/* Smart transport subtransports that come with libgit2 */ - -/** - * Create an instance of the http subtransport. This subtransport - * also supports https. On Win32, this subtransport may be implemented - * using the WinHTTP library. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_http( - git_smart_subtransport **out, - git_transport* owner); - -/** - * Create an instance of the git subtransport. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_git( - git_smart_subtransport **out, - git_transport* owner); - -/** - * Create an instance of the ssh subtransport. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_ssh( - git_smart_subtransport **out, - git_transport* owner); - -/* - *** End interface for subtransports for the smart transport *** - */ - /** @} */ GIT_END_DECL #endif diff --git a/include/git2/types.h b/include/git2/types.h index 76175b6bd..7ed1bcd4c 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -243,6 +243,16 @@ typedef struct git_transfer_progress { */ typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload); +/** + * Type for messages delivered by the transport. Return a negative value + * to cancel the network operation. + * + * @param str The message from the transport + * @param len The length of the message + * @param payload Payload provided by the caller + */ +typedef int (*git_transport_message_cb)(const char *str, int len, void *payload); + /** * Opaque structure representing a submodule. */ diff --git a/src/remote.h b/src/remote.h index 47c4f7221..c471756b8 100644 --- a/src/remote.h +++ b/src/remote.h @@ -9,6 +9,7 @@ #include "git2/remote.h" #include "git2/transport.h" +#include "git2/sys/transport.h" #include "refspec.h" #include "vector.h" diff --git a/src/transport.c b/src/transport.c index fbcda5a53..c0311fa90 100644 --- a/src/transport.c +++ b/src/transport.c @@ -9,11 +9,11 @@ #include "git2/remote.h" #include "git2/net.h" #include "git2/transport.h" +#include "git2/sys/transport.h" #include "path.h" typedef struct transport_definition { char *prefix; - unsigned priority; git_transport_cb fn; void *param; } transport_definition; @@ -24,51 +24,54 @@ static git_smart_subtransport_definition git_subtransport_definition = { git_sma static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 }; #endif -static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL }; +static transport_definition local_transport_definition = { "file://", git_transport_local, NULL }; #ifdef GIT_SSH -static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition }; +static transport_definition ssh_transport_definition = { "ssh://", git_transport_smart, &ssh_subtransport_definition }; #else -static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL }; +static transport_definition dummy_transport_definition = { NULL, git_transport_dummy, NULL }; #endif static transport_definition transports[] = { - {"git://", 1, git_transport_smart, &git_subtransport_definition}, - {"http://", 1, git_transport_smart, &http_subtransport_definition}, - {"https://", 1, git_transport_smart, &http_subtransport_definition}, - {"file://", 1, git_transport_local, NULL}, + { "git://", git_transport_smart, &git_subtransport_definition }, + { "http://", git_transport_smart, &http_subtransport_definition }, + { "https://", git_transport_smart, &http_subtransport_definition }, + { "file://", git_transport_local, NULL }, #ifdef GIT_SSH - {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, + { "ssh://", git_transport_smart, &ssh_subtransport_definition }, #endif - {NULL, 0, 0} + { NULL, 0, 0 } }; -static git_vector additional_transports = GIT_VECTOR_INIT; +static git_vector custom_transports = GIT_VECTOR_INIT; #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1 -static int transport_find_fn(const char *url, git_transport_cb *callback, void **param) +static int transport_find_fn( + git_transport_cb *out, + const char *url, + void **param) { size_t i = 0; - unsigned priority = 0; - transport_definition *definition = NULL, *definition_iter; + transport_definition *d, *definition = NULL; - // First, check to see if it's an obvious URL, which a URL scheme - for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { - definition_iter = &transports[i]; - - if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix))) - continue; - - if (definition_iter->priority > priority) - definition = definition_iter; + /* Find a user transport who wants to deal with this URI */ + git_vector_foreach(&custom_transports, i, d) { + if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { + definition = d; + break; + } } - git_vector_foreach(&additional_transports, i, definition_iter) { - if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix))) - continue; + /* Find a system transport for this URI */ + if (!definition) { + for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { + d = &transports[i]; - if (definition_iter->priority > priority) - definition = definition_iter; + if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { + definition = d; + break; + } + } } #ifdef GIT_WIN32 @@ -90,7 +93,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void * #ifdef GIT_SSH definition = &ssh_transport_definition; #else - definition = &dummy_transport_definition; + definition = &dummy_transport_definition; #endif #ifndef GIT_WIN32 @@ -100,9 +103,9 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void * #endif if (!definition) - return -1; + return GIT_ENOTFOUND; - *callback = definition->fn; + *out = definition->fn; *param = definition->param; return 0; @@ -128,10 +131,11 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url) void *param; int error; - if (transport_find_fn(url, &fn, ¶m) < 0) { + if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) { giterr_set(GITERR_NET, "Unsupported URL protocol"); return -1; - } + } else if (error < 0) + return error; if ((error = fn(&transport, owner, param)) < 0) return error; @@ -144,59 +148,79 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url) } int git_transport_register( - const char *prefix, - unsigned priority, + const char *scheme, git_transport_cb cb, void *param) { - transport_definition *d; + git_buf prefix = GIT_BUF_INIT; + transport_definition *d, *definition = NULL; + size_t i; + int error = 0; - d = git__calloc(sizeof(transport_definition), 1); - GITERR_CHECK_ALLOC(d); + assert(scheme); + assert(cb); - d->prefix = git__strdup(prefix); - - if (!d->prefix) + if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) goto on_error; - d->priority = priority; - d->fn = cb; - d->param = param; + git_vector_foreach(&custom_transports, i, d) { + if (strcasecmp(d->prefix, prefix.ptr) == 0) { + error = GIT_EEXISTS; + goto on_error; + } + } - if (git_vector_insert(&additional_transports, d) < 0) + definition = git__calloc(1, sizeof(transport_definition)); + GITERR_CHECK_ALLOC(definition); + + definition->prefix = git_buf_detach(&prefix); + definition->fn = cb; + definition->param = param; + + if (git_vector_insert(&custom_transports, definition) < 0) goto on_error; return 0; on_error: - git__free(d->prefix); - git__free(d); - return -1; + git_buf_free(&prefix); + git__free(definition); + return error; } -int git_transport_unregister( - const char *prefix, - unsigned priority) +int git_transport_unregister(const char *scheme) { + git_buf prefix = GIT_BUF_INIT; transport_definition *d; - unsigned i; + size_t i; + int error = 0; - git_vector_foreach(&additional_transports, i, d) { - if (d->priority == priority && !strcasecmp(d->prefix, prefix)) { - if (git_vector_remove(&additional_transports, i) < 0) - return -1; + assert(scheme); + + if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) + goto done; + + git_vector_foreach(&custom_transports, i, d) { + if (strcasecmp(d->prefix, prefix.ptr) == 0) { + if ((error = git_vector_remove(&custom_transports, i)) < 0) + goto done; git__free(d->prefix); git__free(d); - if (!additional_transports.length) - git_vector_free(&additional_transports); + if (!custom_transports.length) + git_vector_free(&custom_transports); - return 0; + error = 0; + goto done; } } - return GIT_ENOTFOUND; + error = GIT_ENOTFOUND; + +done: + git_buf_free(&prefix); + return error; } /* from remote.h */ @@ -205,7 +229,7 @@ int git_remote_valid_url(const char *url) git_transport_cb fn; void *param; - return !transport_find_fn(url, &fn, ¶m); + return !transport_find_fn(&fn, url, ¶m); } int git_remote_supported_url(const char* url) @@ -213,7 +237,7 @@ int git_remote_supported_url(const char* url) git_transport_cb fn; void *param; - if (transport_find_fn(url, &fn, ¶m) < 0) + if (transport_find_fn(&fn, url, ¶m) < 0) return 0; return fn != &git_transport_dummy; diff --git a/src/transports/git.c b/src/transports/git.c index 21507c1c7..e2690fe36 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -8,6 +8,7 @@ #include "git2.h" #include "buffer.h" #include "netops.h" +#include "git2/sys/transport.h" #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) diff --git a/src/transports/smart.h b/src/transports/smart.h index f1fc29520..b29faae44 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -9,6 +9,7 @@ #include "netops.h" #include "buffer.h" #include "push.h" +#include "git2/sys/transport.h" #define GIT_SIDE_BAND_DATA 1 #define GIT_SIDE_BAND_PROGRESS 2 diff --git a/tests/clone/transport.c b/tests/clone/transport.c index 27568f228..46c16a241 100644 --- a/tests/clone/transport.c +++ b/tests/clone/transport.c @@ -2,6 +2,7 @@ #include "git2/clone.h" #include "git2/transport.h" +#include "git2/sys/transport.h" #include "fileops.h" static int custom_transport( diff --git a/tests/structinit/structinit.c b/tests/structinit/structinit.c index 38bedada7..def9fefc0 100644 --- a/tests/structinit/structinit.c +++ b/tests/structinit/structinit.c @@ -2,6 +2,7 @@ #include #include #include +#include #define STRINGIFY(s) #s