1 //! Interfaces for adding custom transports to libgit2
3 use std
::ffi
::{CStr, CString}
;
4 use std
::io
::prelude
::*;
10 use libc
::{c_int, c_void, c_uint, c_char, size_t}
;
12 use {raw, panic, Error, Remote}
;
15 /// A transport is a structure which knows how to transfer data to and from a
18 /// This transport is a representation of the raw transport underneath it, which
19 /// is similar to a trait object in Rust.
20 #[allow(missing_copy_implementations)]
21 pub struct Transport
{
22 raw
: *mut raw
::git_transport
,
26 /// Interfaced used by smart transports.
28 /// The full-fledged definiton of transports has to deal with lots of
29 /// nitty-gritty details of the git protocol, but "smart transports" largely
30 /// only need to deal with read() and write() of data over a channel.
32 /// A smart subtransport is contained within an instance of a smart transport
33 /// and is delegated to in order to actually conduct network activity to push or
34 /// pull data from a remote.
35 pub trait SmartSubtransport
: Send
+ '
static {
36 /// Indicates that this subtransport will be performing the specified action
37 /// on the specified URL.
39 /// This function is responsible for making any network connections and
40 /// returns a stream which can be read and written from in order to
41 /// negotiate the git protocol.
42 fn action(&self, url
: &str, action
: Service
)
43 -> Result
<Box
<SmartSubtransportStream
>, Error
>;
45 /// Terminates a connection with the remote.
47 /// Each subtransport is guaranteed a call to close() between calls to
48 /// action(), except for the following tow natural progressions of actions
49 /// against a constant URL.
51 /// 1. UploadPackLs -> UploadPack
52 /// 2. ReceivePackLs -> ReceivePack
53 fn close(&self) -> Result
<(), Error
>;
56 /// Actions that a smart transport can ask a subtransport to perform
57 #[derive(Copy, Clone)]
58 #[allow(missing_docs)]
66 /// An instance of a stream over which a smart transport will communicate with a
69 /// Currently this only requires the standard `Read` and `Write` traits. This
70 /// trait also does not need to be implemented manually as long as the `Read`
71 /// and `Write` traits are implemented.
72 pub trait SmartSubtransportStream
: Read
+ Write
+ Send
+ '
static {}
74 impl<T
: Read
+ Write
+ Send
+ '
static> SmartSubtransportStream
for T {}
76 type TransportFactory
= Fn(&Remote
) -> Result
<Transport
, Error
> + Send
+ Sync
+
79 /// Boxed data payload used for registering new transports.
81 /// Currently only contains a field which knows how to create transports.
82 struct TransportData
{
83 factory
: Box
<TransportFactory
>,
86 /// Instance of a `git_smart_subtransport`, must use `#[repr(C)]` to ensure that
87 /// the C fields come first.
89 struct RawSmartSubtransport
{
90 raw
: raw
::git_smart_subtransport
,
91 obj
: Box
<SmartSubtransport
>,
94 /// Instance of a `git_smart_subtransport_stream`, must use `#[repr(C)]` to
95 /// ensure that the C fields come first.
97 struct RawSmartSubtransportStream
{
98 raw
: raw
::git_smart_subtransport_stream
,
99 obj
: Box
<SmartSubtransportStream
>,
102 /// Add a custom transport definition, to be used in addition to the built-in
103 /// set of transports that come with libgit2.
105 /// This function is unsafe as it needs to be externally synchronized with calls
106 /// to creation of other transports.
107 pub unsafe fn register
<F
>(prefix
: &str, factory
: F
) -> Result
<(), Error
>
108 where F
: Fn(&Remote
) -> Result
<Transport
, Error
> + Send
+ Sync
+ '
static
110 let mut data
= Box
::new(TransportData
{
111 factory
: Box
::new(factory
),
113 let prefix
= try
!(CString
::new(prefix
));
114 let datap
= (&mut *data
) as *mut TransportData
as *mut c_void
;
115 try_call
!(raw
::git_transport_register(prefix
,
123 /// Creates a new transport which will use the "smart" transport protocol
124 /// for transferring data.
126 /// A smart transport requires a *subtransport* over which data is actually
127 /// communicated, but this subtransport largely just needs to be able to
128 /// read() and write(). The subtransport provided will be used to make
129 /// connections which can then be read/written from.
131 /// The `rpc` argument is `true` if the protocol is stateless, false
132 /// otherwise. For example `http://` is stateless but `git://` is not.
133 pub fn smart
<S
>(remote
: &Remote
,
135 subtransport
: S
) -> Result
<Transport
, Error
>
136 where S
: SmartSubtransport
138 let mut ret
= ptr
::null_mut();
140 let mut raw
= Box
::new(RawSmartSubtransport
{
141 raw
: raw
::git_smart_subtransport
{
142 action
: subtransport_action
,
143 close
: subtransport_close
,
144 free
: subtransport_free
,
146 obj
: Box
::new(subtransport
),
148 let mut defn
= raw
::git_smart_subtransport_definition
{
149 callback
: smart_factory
,
151 param
: &mut *raw
as *mut _
as *mut _
,
154 // Currently there's no way to pass a paload via the
155 // git_smart_subtransport_definition structure, but it's only used as a
156 // configuration for the initial creation of the smart transport (verified
157 // by reading the current code, hopefully it doesn't change!).
159 // We, however, need some state (gotta pass in our
160 // `RawSmartSubtransport`). This also means that this block must be
161 // entirely synchronized with a lock (boo!)
163 try_call
!(raw
::git_transport_smart(&mut ret
, remote
.raw(),
164 &mut defn
as *mut _
as *mut _
));
165 mem
::forget(raw
); // ownership transport to `ret`
167 return Ok(Transport { raw: ret, owned: true }
);
169 extern fn smart_factory(out
: *mut *mut raw
::git_smart_subtransport
,
170 _owner
: *mut raw
::git_transport
,
171 ptr
: *mut c_void
) -> c_int
{
173 *out
= ptr
as *mut raw
::git_smart_subtransport
;
180 impl Drop
for Transport
{
184 ((*self.raw
).free
)(self.raw
)
190 // callback used by register() to create new transports
191 extern fn transport_factory(out
: *mut *mut raw
::git_transport
,
192 owner
: *mut raw
::git_remote
,
193 param
: *mut c_void
) -> c_int
{
194 struct Bomb
<'a
> { remote: Option<Remote<'a>> }
195 impl<'a
> Drop
for Bomb
<'a
> {
197 // TODO: maybe a method instead?
198 mem
::forget(self.remote
.take());
202 panic
::wrap(|| unsafe {
203 let remote
= Bomb { remote: Some(Binding::from_raw(owner)) }
;
204 let data
= &mut *(param
as *mut TransportData
);
205 match (data
.factory
)(remote
.remote
.as_ref().unwrap()) {
206 Ok(mut transport
) => {
207 *out
= transport
.raw
;
208 transport
.owned
= false;
211 Err(e
) => e
.raw_code() as c_int
,
216 // callback used by smart transports to delegate an action to a
217 // `SmartSubtransport` trait object.
218 extern fn subtransport_action(stream
: *mut *mut raw
::git_smart_subtransport_stream
,
219 raw_transport
: *mut raw
::git_smart_subtransport
,
221 action
: raw
::git_smart_service_t
) -> c_int
{
222 panic
::wrap(|| unsafe {
223 let url
= CStr
::from_ptr(url
).to_bytes();
224 let url
= match str::from_utf8(url
).ok() {
228 let action
= match action
{
229 raw
::GIT_SERVICE_UPLOADPACK_LS
=> Service
::UploadPackLs
,
230 raw
::GIT_SERVICE_UPLOADPACK
=> Service
::UploadPack
,
231 raw
::GIT_SERVICE_RECEIVEPACK_LS
=> Service
::ReceivePackLs
,
232 raw
::GIT_SERVICE_RECEIVEPACK
=> Service
::ReceivePack
,
233 n
=> panic
!("unknown action: {}", n
),
235 let transport
= &mut *(raw_transport
as *mut RawSmartSubtransport
);
236 let obj
= match transport
.obj
.action(url
, action
) {
238 Err(e
) => return e
.raw_code() as c_int
,
240 *stream
= mem
::transmute(Box
::new(RawSmartSubtransportStream
{
241 raw
: raw
::git_smart_subtransport_stream
{
242 subtransport
: raw_transport
,
253 // callback used by smart transports to close a `SmartSubtransport` trait
255 extern fn subtransport_close(transport
: *mut raw
::git_smart_subtransport
)
257 let ret
= panic
::wrap(|| unsafe {
258 let transport
= &mut *(transport
as *mut RawSmartSubtransport
);
259 transport
.obj
.close()
263 Some(Err(e
)) => e
.raw_code() as c_int
,
268 // callback used by smart transports to free a `SmartSubtransport` trait
270 extern fn subtransport_free(transport
: *mut raw
::git_smart_subtransport
) {
271 let _
= panic
::wrap(|| unsafe {
272 mem
::transmute
::<_
, Box
<RawSmartSubtransport
>>(transport
);
276 // callback used by smart transports to read from a `SmartSubtransportStream`
278 extern fn stream_read(stream
: *mut raw
::git_smart_subtransport_stream
,
281 bytes_read
: *mut size_t
) -> c_int
{
282 let ret
= panic
::wrap(|| unsafe {
283 let transport
= &mut *(stream
as *mut RawSmartSubtransportStream
);
284 let buf
= slice
::from_raw_parts_mut(buffer
as *mut u8,
286 match transport
.obj
.read(buf
) {
287 Ok(n
) => { *bytes_read = n as size_t; Ok(n) }
293 Some(Err(e
)) => unsafe { set_err(&e); -2 }
,
298 // callback used by smart transports to write to a `SmartSubtransportStream`
300 extern fn stream_write(stream
: *mut raw
::git_smart_subtransport_stream
,
301 buffer
: *const c_char
,
302 len
: size_t
) -> c_int
{
303 let ret
= panic
::wrap(|| unsafe {
304 let transport
= &mut *(stream
as *mut RawSmartSubtransportStream
);
305 let buf
= slice
::from_raw_parts(buffer
as *const u8, len
as usize);
306 transport
.obj
.write_all(buf
)
310 Some(Err(e
)) => unsafe { set_err(&e); -2 }
,
315 unsafe fn set_err(e
: &io
::Error
) {
316 let s
= CString
::new(e
.to_string()).unwrap();
317 raw
::giterr_set_str(raw
::GITERR_NET
as c_int
, s
.as_ptr())
320 // callback used by smart transports to free a `SmartSubtransportStream`
322 extern fn stream_free(stream
: *mut raw
::git_smart_subtransport_stream
) {
323 let _
= panic
::wrap(|| unsafe {
324 mem
::transmute
::<_
, Box
<RawSmartSubtransportStream
>>(stream
);