]> git.proxmox.com Git - cargo.git/blob - vendor/git2-0.6.8/src/transport.rs
New upstream version 0.23.0
[cargo.git] / vendor / git2-0.6.8 / src / transport.rs
1 //! Interfaces for adding custom transports to libgit2
2
3 use std::ffi::{CStr, CString};
4 use std::io::prelude::*;
5 use std::io;
6 use std::mem;
7 use std::slice;
8 use std::ptr;
9 use std::str;
10 use libc::{c_int, c_void, c_uint, c_char, size_t};
11
12 use {raw, panic, Error, Remote};
13 use util::Binding;
14
15 /// A transport is a structure which knows how to transfer data to and from a
16 /// remote.
17 ///
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,
23 owned: bool,
24 }
25
26 /// Interfaced used by smart transports.
27 ///
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.
31 ///
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.
38 ///
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>;
44
45 /// Terminates a connection with the remote.
46 ///
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.
50 ///
51 /// 1. UploadPackLs -> UploadPack
52 /// 2. ReceivePackLs -> ReceivePack
53 fn close(&self) -> Result<(), Error>;
54 }
55
56 /// Actions that a smart transport can ask a subtransport to perform
57 #[derive(Copy, Clone)]
58 #[allow(missing_docs)]
59 pub enum Service {
60 UploadPackLs,
61 UploadPack,
62 ReceivePackLs,
63 ReceivePack,
64 }
65
66 /// An instance of a stream over which a smart transport will communicate with a
67 /// remote.
68 ///
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 {}
73
74 impl<T: Read + Write + Send + 'static> SmartSubtransportStream for T {}
75
76 type TransportFactory = Fn(&Remote) -> Result<Transport, Error> + Send + Sync +
77 'static;
78
79 /// Boxed data payload used for registering new transports.
80 ///
81 /// Currently only contains a field which knows how to create transports.
82 struct TransportData {
83 factory: Box<TransportFactory>,
84 }
85
86 /// Instance of a `git_smart_subtransport`, must use `#[repr(C)]` to ensure that
87 /// the C fields come first.
88 #[repr(C)]
89 struct RawSmartSubtransport {
90 raw: raw::git_smart_subtransport,
91 obj: Box<SmartSubtransport>,
92 }
93
94 /// Instance of a `git_smart_subtransport_stream`, must use `#[repr(C)]` to
95 /// ensure that the C fields come first.
96 #[repr(C)]
97 struct RawSmartSubtransportStream {
98 raw: raw::git_smart_subtransport_stream,
99 obj: Box<SmartSubtransportStream>,
100 }
101
102 /// Add a custom transport definition, to be used in addition to the built-in
103 /// set of transports that come with libgit2.
104 ///
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
109 {
110 let mut data = Box::new(TransportData {
111 factory: Box::new(factory),
112 });
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,
116 transport_factory,
117 datap));
118 mem::forget(data);
119 Ok(())
120 }
121
122 impl Transport {
123 /// Creates a new transport which will use the "smart" transport protocol
124 /// for transferring data.
125 ///
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.
130 ///
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,
134 rpc: bool,
135 subtransport: S) -> Result<Transport, Error>
136 where S: SmartSubtransport
137 {
138 let mut ret = ptr::null_mut();
139
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,
145 },
146 obj: Box::new(subtransport),
147 });
148 let mut defn = raw::git_smart_subtransport_definition {
149 callback: smart_factory,
150 rpc: rpc as c_uint,
151 param: &mut *raw as *mut _ as *mut _,
152 };
153
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!).
158 //
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!)
162 unsafe {
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`
166 }
167 return Ok(Transport { raw: ret, owned: true });
168
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 {
172 unsafe {
173 *out = ptr as *mut raw::git_smart_subtransport;
174 0
175 }
176 }
177 }
178 }
179
180 impl Drop for Transport {
181 fn drop(&mut self) {
182 if self.owned {
183 unsafe {
184 ((*self.raw).free)(self.raw)
185 }
186 }
187 }
188 }
189
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> {
196 fn drop(&mut self) {
197 // TODO: maybe a method instead?
198 mem::forget(self.remote.take());
199 }
200 }
201
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;
209 0
210 }
211 Err(e) => e.raw_code() as c_int,
212 }
213 }).unwrap_or(-1)
214 }
215
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,
220 url: *const c_char,
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() {
225 Some(s) => s,
226 None => return -1,
227 };
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),
234 };
235 let transport = &mut *(raw_transport as *mut RawSmartSubtransport);
236 let obj = match transport.obj.action(url, action) {
237 Ok(s) => s,
238 Err(e) => return e.raw_code() as c_int,
239 };
240 *stream = mem::transmute(Box::new(RawSmartSubtransportStream {
241 raw: raw::git_smart_subtransport_stream {
242 subtransport: raw_transport,
243 read: stream_read,
244 write: stream_write,
245 free: stream_free,
246 },
247 obj: obj,
248 }));
249 0
250 }).unwrap_or(-1)
251 }
252
253 // callback used by smart transports to close a `SmartSubtransport` trait
254 // object.
255 extern fn subtransport_close(transport: *mut raw::git_smart_subtransport)
256 -> c_int {
257 let ret = panic::wrap(|| unsafe {
258 let transport = &mut *(transport as *mut RawSmartSubtransport);
259 transport.obj.close()
260 });
261 match ret {
262 Some(Ok(())) => 0,
263 Some(Err(e)) => e.raw_code() as c_int,
264 None => -1,
265 }
266 }
267
268 // callback used by smart transports to free a `SmartSubtransport` trait
269 // object.
270 extern fn subtransport_free(transport: *mut raw::git_smart_subtransport) {
271 let _ = panic::wrap(|| unsafe {
272 mem::transmute::<_, Box<RawSmartSubtransport>>(transport);
273 });
274 }
275
276 // callback used by smart transports to read from a `SmartSubtransportStream`
277 // object.
278 extern fn stream_read(stream: *mut raw::git_smart_subtransport_stream,
279 buffer: *mut c_char,
280 buf_size: size_t,
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,
285 buf_size as usize);
286 match transport.obj.read(buf) {
287 Ok(n) => { *bytes_read = n as size_t; Ok(n) }
288 e => e,
289 }
290 });
291 match ret {
292 Some(Ok(_)) => 0,
293 Some(Err(e)) => unsafe { set_err(&e); -2 },
294 None => -1,
295 }
296 }
297
298 // callback used by smart transports to write to a `SmartSubtransportStream`
299 // object.
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)
307 });
308 match ret {
309 Some(Ok(())) => 0,
310 Some(Err(e)) => unsafe { set_err(&e); -2 },
311 None => -1,
312 }
313 }
314
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())
318 }
319
320 // callback used by smart transports to free a `SmartSubtransportStream`
321 // object.
322 extern fn stream_free(stream: *mut raw::git_smart_subtransport_stream) {
323 let _ = panic::wrap(|| unsafe {
324 mem::transmute::<_, Box<RawSmartSubtransportStream>>(stream);
325 });
326 }