Add support for custom smart transports (#806) #809

Merged
lhchavez merged 1 commits from cherry-pick-806-release-0.27 into release-0.27 2021-09-05 18:03:45 -05:00
8 changed files with 742 additions and 6 deletions

View File

@ -85,6 +85,7 @@ func remoteCreateCallback(
// clear finalizer as the calling C function will // clear finalizer as the calling C function will
// free the remote itself // free the remote itself
runtime.SetFinalizer(remote, nil) runtime.SetFinalizer(remote, nil)
remote.repo.Remotes.untrackRemote(remote)
return C.int(ErrorCodeOK) return C.int(ErrorCodeOK)
} }

6
git.go
View File

@ -124,6 +124,7 @@ var (
type doNotCompare [0]func() type doNotCompare [0]func()
var pointerHandles *HandleList var pointerHandles *HandleList
var remotePointers *remotePointerList
func init() { func init() {
initLibGit2() initLibGit2()
@ -131,6 +132,7 @@ func init() {
func initLibGit2() { func initLibGit2() {
pointerHandles = NewHandleList() pointerHandles = NewHandleList()
remotePointers = newRemotePointerList()
C.git_libgit2_init() C.git_libgit2_init()
@ -156,7 +158,11 @@ func initLibGit2() {
// After this is called, invoking any function from this library will result in // After this is called, invoking any function from this library will result in
// undefined behavior, so make sure this is called carefully. // undefined behavior, so make sure this is called carefully.
func Shutdown() { func Shutdown() {
if err := unregisterManagedTransports(); err != nil {
panic(err)
}
pointerHandles.Clear() pointerHandles.Clear()
remotePointers.clear()
C.git_libgit2_shutdown() C.git_libgit2_shutdown()
} }

View File

@ -13,6 +13,10 @@ import (
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
ret := m.Run() ret := m.Run()
if err := unregisterManagedTransports(); err != nil {
panic(err)
}
// Ensure that we are not leaking any pointer handles. // Ensure that we are not leaking any pointer handles.
pointerHandles.Lock() pointerHandles.Lock()
if len(pointerHandles.handles) > 0 { if len(pointerHandles.handles) > 0 {
@ -23,6 +27,16 @@ func TestMain(m *testing.M) {
} }
pointerHandles.Unlock() pointerHandles.Unlock()
// Or remote pointers.
remotePointers.Lock()
if len(remotePointers.pointers) > 0 {
for ptr, remote := range remotePointers.pointers {
fmt.Printf("%016p: %+v\n", ptr, remote)
}
panic("remote pointer list not empty")
}
remotePointers.Unlock()
Shutdown() Shutdown()
os.Exit(ret) os.Exit(ret)

110
remote.go
View File

@ -14,6 +14,7 @@ import (
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
"sync"
"unsafe" "unsafe"
) )
@ -156,6 +157,64 @@ type Remote struct {
repo *Repository repo *Repository
} }
type remotePointerList struct {
sync.RWMutex
// stores the Go pointers
pointers map[*C.git_remote]*Remote
}
func newRemotePointerList() *remotePointerList {
return &remotePointerList{
pointers: make(map[*C.git_remote]*Remote),
}
}
// track adds the given pointer to the list of pointers to track and
// returns a pointer value which can be passed to C as an opaque
// pointer.
func (v *remotePointerList) track(remote *Remote) {
v.Lock()
v.pointers[remote.ptr] = remote
v.Unlock()
runtime.SetFinalizer(remote, (*Remote).Free)
}
// untrack stops tracking the git_remote pointer.
func (v *remotePointerList) untrack(remote *Remote) {
v.Lock()
delete(v.pointers, remote.ptr)
v.Unlock()
}
// clear stops tracking all the git_remote pointers.
func (v *remotePointerList) clear() {
v.Lock()
var remotes []*Remote
for remotePtr, remote := range v.pointers {
remotes = append(remotes, remote)
delete(v.pointers, remotePtr)
}
v.Unlock()
for _, remote := range remotes {
remote.free()
}
}
// get retrieves the pointer from the given *git_remote.
func (v *remotePointerList) get(ptr *C.git_remote) (*Remote, bool) {
v.RLock()
defer v.RUnlock()
r, ok := v.pointers[ptr]
if !ok {
return nil, false
}
return r, true
}
type CertificateKind uint type CertificateKind uint
const ( const (
@ -490,17 +549,42 @@ func RemoteIsValidName(name string) bool {
return C.git_remote_is_valid_name(cname) == 1 return C.git_remote_is_valid_name(cname) == 1
} }
// Free releases the resources of the Remote. // free releases the resources of the Remote.
func (r *Remote) Free() { func (r *Remote) free() {
runtime.SetFinalizer(r, nil) runtime.SetFinalizer(r, nil)
C.git_remote_free(r.ptr) C.git_remote_free(r.ptr)
r.ptr = nil r.ptr = nil
r.repo = nil r.repo = nil
} }
// Free releases the resources of the Remote.
func (r *Remote) Free() {
r.repo.Remotes.untrackRemote(r)
r.free()
}
type RemoteCollection struct { type RemoteCollection struct {
doNotCompare doNotCompare
repo *Repository repo *Repository
sync.RWMutex
remotes map[*C.git_remote]*Remote
}
func (c *RemoteCollection) trackRemote(r *Remote) {
c.Lock()
c.remotes[r.ptr] = r
c.Unlock()
remotePointers.track(r)
}
func (c *RemoteCollection) untrackRemote(r *Remote) {
c.Lock()
delete(c.remotes, r.ptr)
c.Unlock()
remotePointers.untrack(r)
} }
func (c *RemoteCollection) List() ([]string, error) { func (c *RemoteCollection) List() ([]string, error) {
@ -535,7 +619,7 @@ func (c *RemoteCollection) Create(name string, url string) (*Remote, error) {
if ret < 0 { if ret < 0 {
return nil, MakeGitError(ret) return nil, MakeGitError(ret)
} }
runtime.SetFinalizer(remote, (*Remote).Free) c.trackRemote(remote)
return remote, nil return remote, nil
} }
@ -571,7 +655,7 @@ func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch st
if ret < 0 { if ret < 0 {
return nil, MakeGitError(ret) return nil, MakeGitError(ret)
} }
runtime.SetFinalizer(remote, (*Remote).Free) c.trackRemote(remote)
return remote, nil return remote, nil
} }
@ -588,7 +672,7 @@ func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) {
if ret < 0 { if ret < 0 {
return nil, MakeGitError(ret) return nil, MakeGitError(ret)
} }
runtime.SetFinalizer(remote, (*Remote).Free) c.trackRemote(remote)
return remote, nil return remote, nil
} }
@ -605,10 +689,24 @@ func (c *RemoteCollection) Lookup(name string) (*Remote, error) {
if ret < 0 { if ret < 0 {
return nil, MakeGitError(ret) return nil, MakeGitError(ret)
} }
runtime.SetFinalizer(remote, (*Remote).Free) c.trackRemote(remote)
return remote, nil return remote, nil
} }
func (c *RemoteCollection) Free() {
var remotes []*Remote
c.Lock()
for remotePtr, remote := range c.remotes {
remotes = append(remotes, remote)
delete(c.remotes, remotePtr)
}
c.Unlock()
for _, remote := range remotes {
remotePointers.untrack(remote)
}
}
func (o *Remote) Name() string { func (o *Remote) Name() string {
s := C.git_remote_name(o.ptr) s := C.git_remote_name(o.ptr)
runtime.KeepAlive(o) runtime.KeepAlive(o)

View File

@ -46,6 +46,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository {
repo := &Repository{ptr: ptr} repo := &Repository{ptr: ptr}
repo.Remotes.repo = repo repo.Remotes.repo = repo
repo.Remotes.remotes = make(map[*C.git_remote]*Remote)
repo.Submodules.repo = repo repo.Submodules.repo = repo
repo.References.repo = repo repo.References.repo = repo
repo.Notes.repo = repo repo.Notes.repo = repo
@ -144,6 +145,7 @@ func (v *Repository) Free() {
ptr := v.ptr ptr := v.ptr
v.ptr = nil v.ptr = nil
runtime.SetFinalizer(v, nil) runtime.SetFinalizer(v, nil)
v.Remotes.Free()
if v.weak { if v.weak {
return return
} }

430
transport.go Normal file
View File

@ -0,0 +1,430 @@
package git
/*
#include <git2.h>
#include <git2/sys/transport.h>
typedef struct {
git_smart_subtransport parent;
void *handle;
} _go_managed_smart_subtransport;
typedef struct {
git_smart_subtransport_stream parent;
void *handle;
} _go_managed_smart_subtransport_stream;
int _go_git_transport_register(const char *prefix, void *handle);
int _go_git_transport_smart(git_transport **out, git_remote *owner, int stateless, _go_managed_smart_subtransport *subtransport_payload);
void _go_git_setup_smart_subtransport_stream(_go_managed_smart_subtransport_stream *stream);
*/
import "C"
import (
"errors"
"fmt"
"io"
"reflect"
"runtime"
"sync"
"unsafe"
)
var (
// globalRegisteredSmartTransports is a mapping of global, git2go-managed
// transports.
globalRegisteredSmartTransports = struct {
sync.Mutex
transports map[string]*RegisteredSmartTransport
}{
transports: make(map[string]*RegisteredSmartTransport),
}
)
// unregisterManagedTransports unregisters all git2go-managed transports.
func unregisterManagedTransports() error {
globalRegisteredSmartTransports.Lock()
originalTransports := globalRegisteredSmartTransports.transports
globalRegisteredSmartTransports.transports = make(map[string]*RegisteredSmartTransport)
globalRegisteredSmartTransports.Unlock()
var err error
for protocol, managed := range originalTransports {
unregisterErr := managed.Free()
if err == nil && unregisterErr != nil {
err = fmt.Errorf("failed to unregister transport for %q: %v", protocol, unregisterErr)
}
}
return err
}
// SmartServiceAction is an action that the smart transport can ask a
// subtransport to perform.
type SmartServiceAction int
const (
// SmartServiceActionUploadpackLs is used upon connecting to a remote, and is
// used to perform reference discovery prior to performing a pull operation.
SmartServiceActionUploadpackLs SmartServiceAction = C.GIT_SERVICE_UPLOADPACK_LS
// SmartServiceActionUploadpack is used when performing a pull operation.
SmartServiceActionUploadpack SmartServiceAction = C.GIT_SERVICE_UPLOADPACK
// SmartServiceActionReceivepackLs is used upon connecting to a remote, and is
// used to perform reference discovery prior to performing a push operation.
SmartServiceActionReceivepackLs SmartServiceAction = C.GIT_SERVICE_RECEIVEPACK_LS
// SmartServiceActionReceivepack is used when performing a push operation.
SmartServiceActionReceivepack SmartServiceAction = C.GIT_SERVICE_RECEIVEPACK
)
// Transport encapsulates a way to communicate with a Remote.
type Transport struct {
doNotCompare
ptr *C.git_transport
}
// SmartCredentials calls the credentials callback for this transport.
func (t *Transport) SmartCredentials(user string, methods CredType) (*Cred, error) {
cred := &Cred{}
var cstr *C.char
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if user != "" {
cstr = C.CString(user)
defer C.free(unsafe.Pointer(cstr))
}
ret := C.git_transport_smart_credentials(&cred.ptr, t.ptr, cstr, C.int(methods))
if ret != 0 {
return nil, MakeGitError(ret)
}
return cred, nil
}
// SmartSubtransport is the interface for custom subtransports which carry data
// for the smart transport.
type SmartSubtransport interface {
// Action creates a SmartSubtransportStream for the provided url and
// requested action.
Action(url string, action SmartServiceAction) (SmartSubtransportStream, error)
// Close closes the SmartSubtransport.
//
// 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
Close() error
// Free releases the resources of the SmartSubtransport.
Free()
}
// SmartSubtransportStream is the interface for streams used by the smart
// transport to read and write data from a subtransport.
type SmartSubtransportStream interface {
io.Reader
io.Writer
// Free releases the resources of the SmartSubtransportStream.
Free()
}
// SmartSubtransportCallback is a function which creates a new subtransport for
// the smart transport.
type SmartSubtransportCallback func(remote *Remote, transport *Transport) (SmartSubtransport, error)
// RegisteredSmartTransport represents a transport that has been registered.
type RegisteredSmartTransport struct {
doNotCompare
name string
stateless bool
callback SmartSubtransportCallback
handle unsafe.Pointer
}
// NewRegisteredSmartTransport adds a custom transport definition, to be used
// in addition to the built-in set of transports that come with libgit2.
func NewRegisteredSmartTransport(
name string,
stateless bool,
callback SmartSubtransportCallback,
) (*RegisteredSmartTransport, error) {
return newRegisteredSmartTransport(name, stateless, callback, false)
}
func newRegisteredSmartTransport(
name string,
stateless bool,
callback SmartSubtransportCallback,
global bool,
) (*RegisteredSmartTransport, error) {
if !global {
// Check if we had already registered a smart transport for this protocol. If
// we had, free it. The user is now responsible for this transport for the
// lifetime of the library.
globalRegisteredSmartTransports.Lock()
if managed, ok := globalRegisteredSmartTransports.transports[name]; ok {
delete(globalRegisteredSmartTransports.transports, name)
globalRegisteredSmartTransports.Unlock()
err := managed.Free()
if err != nil {
return nil, err
}
} else {
globalRegisteredSmartTransports.Unlock()
}
}
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))
runtime.LockOSThread()
defer runtime.UnlockOSThread()
registeredSmartTransport := &RegisteredSmartTransport{
name: name,
stateless: stateless,
callback: callback,
}
registeredSmartTransport.handle = pointerHandles.Track(registeredSmartTransport)
ret := C._go_git_transport_register(cstr, registeredSmartTransport.handle)
if ret != 0 {
pointerHandles.Untrack(registeredSmartTransport.handle)
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(registeredSmartTransport, (*RegisteredSmartTransport).Free)
return registeredSmartTransport, nil
}
// Free releases all resources used by the RegisteredSmartTransport and
// unregisters the custom transport definition referenced by it.
func (t *RegisteredSmartTransport) Free() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
cstr := C.CString(t.name)
defer C.free(unsafe.Pointer(cstr))
if ret := C.git_transport_unregister(cstr); ret < 0 {
return MakeGitError(ret)
}
pointerHandles.Untrack(t.handle)
runtime.SetFinalizer(t, nil)
t.handle = nil
return nil
}
//export smartTransportCallback
func smartTransportCallback(
errorMessage **C.char,
out **C.git_transport,
owner *C.git_remote,
handle unsafe.Pointer,
) C.int {
registeredSmartTransport := pointerHandles.Get(handle).(*RegisteredSmartTransport)
remote, ok := remotePointers.get(owner)
if !ok {
err := errors.New("remote pointer not found")
return setCallbackError(errorMessage, err)
}
managed := &managedSmartSubtransport{
remote: remote,
callback: registeredSmartTransport.callback,
subtransport: (*C._go_managed_smart_subtransport)(C.calloc(1, C.size_t(unsafe.Sizeof(C._go_managed_smart_subtransport{})))),
}
managedHandle := pointerHandles.Track(managed)
managed.handle = managedHandle
managed.subtransport.handle = managedHandle
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C._go_git_transport_smart(out, owner, cbool(registeredSmartTransport.stateless), managed.subtransport)
if ret != 0 {
pointerHandles.Untrack(managedHandle)
}
return ret
}
//export smartTransportSubtransportCallback
func smartTransportSubtransportCallback(
errorMessage **C.char,
wrapperPtr *C._go_managed_smart_subtransport,
owner *C.git_transport,
) C.int {
subtransport := pointerHandles.Get(wrapperPtr.handle).(*managedSmartSubtransport)
underlyingSmartSubtransport, err := subtransport.callback(subtransport.remote, &Transport{ptr: owner})
if err != nil {
return setCallbackError(errorMessage, err)
}
subtransport.underlying = underlyingSmartSubtransport
return C.int(ErrorCodeOK)
}
type managedSmartSubtransport struct {
owner *C.git_transport
callback SmartSubtransportCallback
remote *Remote
subtransport *C._go_managed_smart_subtransport
underlying SmartSubtransport
handle unsafe.Pointer
currentManagedStream *managedSmartSubtransportStream
}
func getSmartSubtransportInterface(subtransport *C.git_smart_subtransport) *managedSmartSubtransport {
wrapperPtr := (*C._go_managed_smart_subtransport)(unsafe.Pointer(subtransport))
return pointerHandles.Get(wrapperPtr.handle).(*managedSmartSubtransport)
}
//export smartSubtransportActionCallback
func smartSubtransportActionCallback(
errorMessage **C.char,
out **C.git_smart_subtransport_stream,
t *C.git_smart_subtransport,
url *C.char,
action C.git_smart_service_t,
) C.int {
subtransport := getSmartSubtransportInterface(t)
underlyingStream, err := subtransport.underlying.Action(C.GoString(url), SmartServiceAction(action))
if err != nil {
return setCallbackError(errorMessage, err)
}
// It's okay to do strict equality here: we expect both to be identical.
if subtransport.currentManagedStream == nil || subtransport.currentManagedStream.underlying != underlyingStream {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
stream := (*C._go_managed_smart_subtransport_stream)(C.calloc(1, C.size_t(unsafe.Sizeof(C._go_managed_smart_subtransport_stream{}))))
managed := &managedSmartSubtransportStream{
underlying: underlyingStream,
streamPtr: stream,
}
managedHandle := pointerHandles.Track(managed)
managed.handle = managedHandle
stream.handle = managedHandle
C._go_git_setup_smart_subtransport_stream(stream)
subtransport.currentManagedStream = managed
}
*out = &subtransport.currentManagedStream.streamPtr.parent
return C.int(ErrorCodeOK)
}
//export smartSubtransportCloseCallback
func smartSubtransportCloseCallback(errorMessage **C.char, t *C.git_smart_subtransport) C.int {
subtransport := getSmartSubtransportInterface(t)
subtransport.currentManagedStream = nil
if subtransport.underlying != nil {
err := subtransport.underlying.Close()
if err != nil {
return setCallbackError(errorMessage, err)
}
}
return C.int(ErrorCodeOK)
}
//export smartSubtransportFreeCallback
func smartSubtransportFreeCallback(t *C.git_smart_subtransport) {
subtransport := getSmartSubtransportInterface(t)
if subtransport.underlying != nil {
subtransport.underlying.Free()
subtransport.underlying = nil
}
pointerHandles.Untrack(subtransport.handle)
C.free(unsafe.Pointer(subtransport.subtransport))
subtransport.handle = nil
subtransport.subtransport = nil
}
type managedSmartSubtransportStream struct {
owner *C.git_smart_subtransport_stream
streamPtr *C._go_managed_smart_subtransport_stream
underlying SmartSubtransportStream
handle unsafe.Pointer
}
func getSmartSubtransportStreamInterface(subtransportStream *C.git_smart_subtransport_stream) *managedSmartSubtransportStream {
managedSubtransportStream := (*C._go_managed_smart_subtransport_stream)(unsafe.Pointer(subtransportStream))
return pointerHandles.Get(managedSubtransportStream.handle).(*managedSmartSubtransportStream)
}
//export smartSubtransportStreamReadCallback
func smartSubtransportStreamReadCallback(
errorMessage **C.char,
s *C.git_smart_subtransport_stream,
buffer *C.char,
bufSize C.size_t,
bytesRead *C.size_t,
) C.int {
stream := getSmartSubtransportStreamInterface(s)
var p []byte
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
header.Cap = int(bufSize)
header.Len = int(bufSize)
header.Data = uintptr(unsafe.Pointer(buffer))
n, err := stream.underlying.Read(p)
*bytesRead = C.size_t(n)
if n == 0 && err != nil {
if err == io.EOF {
return C.int(ErrorCodeOK)
}
return setCallbackError(errorMessage, err)
}
return C.int(ErrorCodeOK)
}
//export smartSubtransportStreamWriteCallback
func smartSubtransportStreamWriteCallback(
errorMessage **C.char,
s *C.git_smart_subtransport_stream,
buffer *C.char,
bufLen C.size_t,
) C.int {
stream := getSmartSubtransportStreamInterface(s)
var p []byte
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
header.Cap = int(bufLen)
header.Len = int(bufLen)
header.Data = uintptr(unsafe.Pointer(buffer))
if _, err := stream.underlying.Write(p); err != nil {
return setCallbackError(errorMessage, err)
}
return C.int(ErrorCodeOK)
}
//export smartSubtransportStreamFreeCallback
func smartSubtransportStreamFreeCallback(s *C.git_smart_subtransport_stream) {
stream := getSmartSubtransportStreamInterface(s)
stream.underlying.Free()
pointerHandles.Untrack(stream.handle)
C.free(unsafe.Pointer(stream.streamPtr))
stream.handle = nil
stream.streamPtr = nil
}

72
transport_test.go Normal file
View File

@ -0,0 +1,72 @@
package git
import (
"io"
"reflect"
"testing"
)
type testSmartSubtransport struct {
}
func (t *testSmartSubtransport) Action(url string, action SmartServiceAction) (SmartSubtransportStream, error) {
return &testSmartSubtransportStream{}, nil
}
func (t *testSmartSubtransport) Close() error {
return nil
}
func (t *testSmartSubtransport) Free() {
}
type testSmartSubtransportStream struct {
}
func (s *testSmartSubtransportStream) Read(buf []byte) (int, error) {
payload := "" +
"001e# service=git-upload-pack\n" +
"0000005d0000000000000000000000000000000000000000 HEAD\x00symref=HEAD:refs/heads/master agent=libgit\n" +
"003f0000000000000000000000000000000000000000 refs/heads/master\n" +
"0000"
return copy(buf, []byte(payload)), io.EOF
}
func (s *testSmartSubtransportStream) Write(buf []byte) (int, error) {
return 0, io.EOF
}
func (s *testSmartSubtransportStream) Free() {
}
func TestTransport(t *testing.T) {
t.Parallel()
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)
callback := func(remote *Remote, transport *Transport) (SmartSubtransport, error) {
return &testSmartSubtransport{}, nil
}
registeredSmartTransport, err := NewRegisteredSmartTransport("foo", true, callback)
checkFatal(t, err)
defer registeredSmartTransport.Free()
remote, err := repo.Remotes.Create("test", "foo://bar")
checkFatal(t, err)
defer remote.Free()
err = remote.ConnectFetch(nil, nil, nil)
checkFatal(t, err)
remoteHeads, err := remote.Ls()
checkFatal(t, err)
expectedRemoteHeads := []RemoteHead{
{&Oid{}, "HEAD"},
{&Oid{}, "refs/heads/master"},
}
if !reflect.DeepEqual(expectedRemoteHeads, remoteHeads) {
t.Errorf("mismatched remote heads. expected %v, got %v", expectedRemoteHeads, remoteHeads)
}
}

113
wrapper.c
View File

@ -467,3 +467,116 @@ int _go_git_indexer_new(
{ {
return git_indexer_new(out, path, mode, odb, transfer_progress_callback, progress_cb_payload); return git_indexer_new(out, path, mode, odb, transfer_progress_callback, progress_cb_payload);
} }
static int smart_transport_callback(
git_transport **out,
git_remote *owner,
void *param)
{
char *error_message = NULL;
const int ret = smartTransportCallback(
&error_message,
out,
owner,
param);
return set_callback_error(error_message, ret);
}
int _go_git_transport_register(const char *prefix, void *param)
{
return git_transport_register(prefix, smart_transport_callback, param);
}
static int smart_subtransport_action_callback(
git_smart_subtransport_stream **out,
git_smart_subtransport *transport,
const char *url,
git_smart_service_t action)
{
char *error_message = NULL;
const int ret = smartSubtransportActionCallback(
&error_message,
out,
transport,
(char *)url,
action);
return set_callback_error(error_message, ret);
}
static int smart_subtransport_close_callback(git_smart_subtransport *transport)
{
char *error_message = NULL;
const int ret = smartSubtransportCloseCallback(
&error_message,
transport);
return set_callback_error(error_message, ret);
}
static int smart_subtransport_callback(
git_smart_subtransport **out,
git_transport *owner,
void *param)
{
_go_managed_smart_subtransport *subtransport = (_go_managed_smart_subtransport *)param;
subtransport->parent.action = smart_subtransport_action_callback;
subtransport->parent.close = smart_subtransport_close_callback;
subtransport->parent.free = smartSubtransportFreeCallback;
*out = &subtransport->parent;
char *error_message = NULL;
const int ret = smartTransportSubtransportCallback(&error_message, subtransport, owner);
return set_callback_error(error_message, ret);
}
int _go_git_transport_smart(
git_transport **out,
git_remote *owner,
int stateless,
_go_managed_smart_subtransport *subtransport_payload)
{
git_smart_subtransport_definition definition = {
smart_subtransport_callback,
stateless,
subtransport_payload,
};
return git_transport_smart(out, owner, &definition);
}
static int smart_subtransport_stream_read_callback(
git_smart_subtransport_stream *stream,
char *buffer,
size_t buf_size,
size_t *bytes_read)
{
char *error_message = NULL;
const int ret = smartSubtransportStreamReadCallback(
&error_message,
stream,
buffer,
buf_size,
bytes_read);
return set_callback_error(error_message, ret);
}
static int smart_subtransport_stream_write_callback(
git_smart_subtransport_stream *stream,
const char *buffer,
size_t len)
{
char *error_message = NULL;
const int ret = smartSubtransportStreamWriteCallback(
&error_message,
stream,
(char *)buffer,
len);
return set_callback_error(error_message, ret);
}
void _go_git_setup_smart_subtransport_stream(_go_managed_smart_subtransport_stream *stream)
{
_go_managed_smart_subtransport_stream *managed_stream = (_go_managed_smart_subtransport_stream *)stream;
managed_stream->parent.read = smart_subtransport_stream_read_callback;
managed_stream->parent.write = smart_subtransport_stream_write_callback;
managed_stream->parent.free = smartSubtransportStreamFreeCallback;
}