252 lines
4.9 KiB
Go
252 lines
4.9 KiB
Go
package git
|
|
|
|
/*
|
|
#include <git2.h>
|
|
#include <git2/sys/stream.h>
|
|
|
|
typedef struct {
|
|
git_stream parent;
|
|
void *ptr;
|
|
} managed_stream;
|
|
|
|
extern int _go_git_register_tls(void);
|
|
extern void _go_git_setup_stream(managed_stream* s, int encrypted, int proxy_support, void *ptr);
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
// Network stream for libgit2 to use
|
|
type Stream interface {
|
|
Encrypted() bool
|
|
ProxySupport() bool
|
|
Connect() error
|
|
Certificate() (Certificate, error)
|
|
SetProxy(ProxyOptions) error
|
|
io.ReadWriteCloser
|
|
}
|
|
|
|
type ManagedStream struct {
|
|
host string
|
|
port string
|
|
conn *tls.Conn
|
|
}
|
|
|
|
func (self *ManagedStream) Encrypted() bool {
|
|
return true
|
|
}
|
|
|
|
func (self *ManagedStream) ProxySupport() bool {
|
|
return false
|
|
}
|
|
|
|
func (self *ManagedStream) Connect() error {
|
|
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%s", self.host, self.port), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
self.conn = conn
|
|
return nil
|
|
}
|
|
|
|
func (self *ManagedStream) Certificate() (Certificate, error) {
|
|
connState := self.conn.ConnectionState()
|
|
cert := Certificate{
|
|
Kind: CertificateX509,
|
|
X509: connState.PeerCertificates[0],
|
|
}
|
|
|
|
return cert, nil
|
|
}
|
|
|
|
func (self *ManagedStream) SetProxy(opts ProxyOptions) error {
|
|
return errors.New("proxy not supported")
|
|
}
|
|
|
|
func (self *ManagedStream) Read(p []byte) (int, error) {
|
|
return self.conn.Read(p)
|
|
}
|
|
|
|
func (self *ManagedStream) Write(p []byte) (int, error) {
|
|
return self.conn.Write(p)
|
|
}
|
|
|
|
func (self *ManagedStream) Close() error {
|
|
return self.conn.Close()
|
|
}
|
|
|
|
var errNotStream = errors.New("passed object does not implement Stream")
|
|
|
|
// getStreamInterface extracts the Stream interface from the pointers we passed
|
|
// to the C code.
|
|
func getStreamInterface(_s *C.git_stream) (Stream, error) {
|
|
// For type compatibility we accept C.git_stream but we know we pass
|
|
// C.managed_stream so force the casting to that.
|
|
wrapperPtr := (*C.managed_stream)(unsafe.Pointer(_s))
|
|
|
|
// Inside we've stored a handle to the actual type, which must implement
|
|
// Stream.
|
|
stream, ok := pointerHandles.Get(wrapperPtr.ptr).(Stream)
|
|
if !ok {
|
|
return nil, errNotStream
|
|
}
|
|
|
|
return stream, nil
|
|
}
|
|
|
|
//export streamCertificate
|
|
func streamCertificate(out **C.git_cert, _s *C.git_stream) C.int {
|
|
stream, err := getStreamInterface(_s)
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
cert, err := stream.Certificate()
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
ccert, err := cert.toC()
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
*out = ccert
|
|
return 0
|
|
}
|
|
|
|
//export streamSetProxy
|
|
func streamSetProxy(s *C.git_stream, proxy_opts *C.git_proxy_options) C.int {
|
|
setLibgit2Error(errors.New("proxy not supported"))
|
|
return -1
|
|
}
|
|
|
|
//export streamConnect
|
|
func streamConnect(_s *C.git_stream) C.int {
|
|
stream, err := getStreamInterface(_s)
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
err = stream.Connect()
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
//export streamRead
|
|
func streamRead(_s *C.git_stream, data unsafe.Pointer, l C.size_t) C.ssize_t {
|
|
stream, err := getStreamInterface(_s)
|
|
if err != nil {
|
|
setLibgit2Error(err)
|
|
return -1
|
|
}
|
|
|
|
var p []byte
|
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
|
|
header.Cap = int(l)
|
|
header.Len = int(l)
|
|
header.Data = uintptr(data)
|
|
|
|
n, err := stream.Read(p)
|
|
if err != nil {
|
|
setLibgit2Error(err)
|
|
return -1
|
|
}
|
|
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export streamWrite
|
|
func streamWrite(_s *C.git_stream, data unsafe.Pointer, l C.size_t, _f C.int) C.ssize_t {
|
|
stream, err := getStreamInterface(_s)
|
|
if err != nil {
|
|
setLibgit2Error(err)
|
|
return -1
|
|
}
|
|
|
|
var p []byte
|
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
|
|
header.Cap = int(l)
|
|
header.Len = int(l)
|
|
header.Data = uintptr(data)
|
|
|
|
n, err := stream.Write(p)
|
|
if err != nil {
|
|
setLibgit2Error(err)
|
|
return -1
|
|
}
|
|
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export streamClose
|
|
func streamClose(_s *C.git_stream) C.int {
|
|
stream, err := getStreamInterface(_s)
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
err = stream.Close()
|
|
if err != nil {
|
|
return setLibgit2Error(err)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
//export streamFree
|
|
func streamFree(_s *C.git_stream) {
|
|
wrapperPtr := (*C.managed_stream)(unsafe.Pointer(_s))
|
|
pointerHandles.Untrack(wrapperPtr.ptr)
|
|
}
|
|
|
|
func newManagedStream(host, port string) *ManagedStream {
|
|
return &ManagedStream{
|
|
host: host,
|
|
port: port,
|
|
}
|
|
}
|
|
|
|
//export streamCallbackCb
|
|
func streamCallbackCb(out **C.git_stream, chost, cport *C.char) C.int {
|
|
stream := C.calloc(1, C.size_t(unsafe.Sizeof(C.managed_stream{})))
|
|
managed := newManagedStream(C.GoString(chost), C.GoString(cport))
|
|
managedPtr := pointerHandles.Track(managed)
|
|
C._go_git_setup_stream(stream, 1, 0, managedPtr)
|
|
|
|
*out = (*C.git_stream)(stream)
|
|
return 0
|
|
}
|
|
|
|
func setLibgit2Error(err error) C.int {
|
|
cstr := C.CString(err.Error())
|
|
defer C.free(unsafe.Pointer(cstr))
|
|
C.giterr_set_str(C.GITERR_NET, cstr)
|
|
|
|
return -1
|
|
}
|
|
|
|
func RegisterManagedTls() error {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
if err := C._go_git_register_tls(); err != 0 {
|
|
return MakeGitError(err)
|
|
}
|
|
|
|
return nil
|
|
}
|