summaryrefslogtreecommitdiffstats
path: root/extra/git2-curl/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'extra/git2-curl/src/lib.rs')
-rw-r--r--extra/git2-curl/src/lib.rs291
1 files changed, 0 insertions, 291 deletions
diff --git a/extra/git2-curl/src/lib.rs b/extra/git2-curl/src/lib.rs
deleted file mode 100644
index dbdbdc4a6..000000000
--- a/extra/git2-curl/src/lib.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-//! A crate for using libcurl as a backend for HTTP git requests with git2-rs.
-//!
-//! This crate provides one public function, `register`, which will register
-//! a custom HTTP transport with libcurl for any HTTP requests made by libgit2.
-//! At this time the `register` function is unsafe for the same reasons that
-//! `git2::transport::register` is also unsafe.
-//!
-//! It is not recommended to use this crate wherever possible. The current
-//! libcurl backend used, `curl-rust`, only supports executing a request in one
-//! method call implying no streaming support. This consequently means that
-//! when a repository is cloned the entire contents of the repo are downloaded
-//! into memory, and *then* written off to disk by libgit2 afterwards. It
-//! should be possible to alleviate this problem in the future.
-//!
-//! > **NOTE**: At this time this crate likely does not support a `git push`
-//! > operation, only clones.
-
-#![doc(html_root_url = "https://docs.rs/git2-curl/0.19")]
-#![deny(missing_docs)]
-#![warn(rust_2018_idioms)]
-#![cfg_attr(test, deny(warnings))]
-
-use std::error;
-use std::io::prelude::*;
-use std::io::{self, Cursor};
-use std::str;
-use std::sync::{Arc, Mutex, Once};
-
-use curl::easy::{Easy, List};
-use git2::transport::SmartSubtransportStream;
-use git2::transport::{Service, SmartSubtransport, Transport};
-use git2::Error;
-use log::{debug, info};
-use url::Url;
-
-struct CurlTransport {
- handle: Arc<Mutex<Easy>>,
- /// The URL of the remote server, e.g. "https://github.com/user/repo"
- ///
- /// This is an empty string until the first action is performed.
- /// If there is an HTTP redirect, this will be updated with the new URL.
- base_url: Arc<Mutex<String>>,
-}
-
-struct CurlSubtransport {
- handle: Arc<Mutex<Easy>>,
- service: &'static str,
- url_path: &'static str,
- base_url: Arc<Mutex<String>>,
- method: &'static str,
- reader: Option<Cursor<Vec<u8>>>,
- sent_request: bool,
-}
-
-/// Register the libcurl backend for HTTP requests made by libgit2.
-///
-/// This function takes one parameter, a `handle`, which is used to perform all
-/// future HTTP requests. The handle can be previously configured with
-/// information such as proxies, SSL information, etc.
-///
-/// This function is unsafe largely for the same reasons as
-/// `git2::transport::register`:
-///
-/// * The function needs to be synchronized against all other creations of
-/// transport (any API calls to libgit2).
-/// * The function will leak `handle` as once registered it is not currently
-/// possible to unregister the backend.
-///
-/// This function may be called concurrently, but only the first `handle` will
-/// be used. All others will be discarded.
-pub unsafe fn register(handle: Easy) {
- static INIT: Once = Once::new();
-
- let handle = Arc::new(Mutex::new(handle));
- let handle2 = handle.clone();
- INIT.call_once(move || {
- git2::transport::register("http", move |remote| factory(remote, handle.clone())).unwrap();
- git2::transport::register("https", move |remote| factory(remote, handle2.clone())).unwrap();
- });
-}
-
-fn factory(remote: &git2::Remote<'_>, handle: Arc<Mutex<Easy>>) -> Result<Transport, Error> {
- Transport::smart(
- remote,
- true,
- CurlTransport {
- handle: handle,
- base_url: Arc::new(Mutex::new(String::new())),
- },
- )
-}
-
-impl SmartSubtransport for CurlTransport {
- fn action(
- &self,
- url: &str,
- action: Service,
- ) -> Result<Box<dyn SmartSubtransportStream>, Error> {
- let mut base_url = self.base_url.lock().unwrap();
- if base_url.len() == 0 {
- *base_url = url.to_string();
- }
- let (service, path, method) = match action {
- Service::UploadPackLs => ("upload-pack", "/info/refs?service=git-upload-pack", "GET"),
- Service::UploadPack => ("upload-pack", "/git-upload-pack", "POST"),
- Service::ReceivePackLs => {
- ("receive-pack", "/info/refs?service=git-receive-pack", "GET")
- }
- Service::ReceivePack => ("receive-pack", "/git-receive-pack", "POST"),
- };
- info!("action {} {}", service, path);
- Ok(Box::new(CurlSubtransport {
- handle: self.handle.clone(),
- service: service,
- url_path: path,
- base_url: self.base_url.clone(),
- method: method,
- reader: None,
- sent_request: false,
- }))
- }
-
- fn close(&self) -> Result<(), Error> {
- Ok(()) // ...
- }
-}
-
-impl CurlSubtransport {
- fn err<E: Into<Box<dyn error::Error + Send + Sync>>>(&self, err: E) -> io::Error {
- io::Error::new(io::ErrorKind::Other, err)
- }
-
- fn execute(&mut self, data: &[u8]) -> io::Result<()> {
- if self.sent_request {
- return Err(self.err("already sent HTTP request"));
- }
- let agent = format!("git/1.0 (git2-curl {})", env!("CARGO_PKG_VERSION"));
-
- // Parse our input URL to figure out the host
- let url = format!("{}{}", self.base_url.lock().unwrap(), self.url_path);
- let parsed = Url::parse(&url).map_err(|_| self.err("invalid url, failed to parse"))?;
- let host = match parsed.host_str() {
- Some(host) => host,
- None => return Err(self.err("invalid url, did not have a host")),
- };
-
- // Prep the request
- debug!("request to {}", url);
- let mut h = self.handle.lock().unwrap();
- h.url(&url)?;
- h.useragent(&agent)?;
- h.follow_location(true)?;
- match self.method {
- "GET" => h.get(true)?,
- "PUT" => h.put(true)?,
- "POST" => h.post(true)?,
- other => h.custom_request(other)?,
- }
-
- let mut headers = List::new();
- headers.append(&format!("Host: {}", host))?;
- if data.len() > 0 {
- h.post_fields_copy(data)?;
- headers.append(&format!(
- "Accept: application/x-git-{}-result",
- self.service
- ))?;
- headers.append(&format!(
- "Content-Type: \
- application/x-git-{}-request",
- self.service
- ))?;
- } else {
- headers.append("Accept: */*")?;
- }
- headers.append("Expect:")?;
- h.http_headers(headers)?;
-
- let mut content_type = None;
- let mut data = Vec::new();
- {
- let mut h = h.transfer();
-
- // Look for the Content-Type header
- h.header_function(|header| {
- let header = match str::from_utf8(header) {
- Ok(s) => s,
- Err(..) => return true,
- };
- let mut parts = header.splitn(2, ": ");
- let name = parts.next().unwrap();
- let value = match parts.next() {
- Some(value) => value,
- None => return true,
- };
- if name.eq_ignore_ascii_case("Content-Type") {
- content_type = Some(value.trim().to_string());
- }
-
- true
- })?;
-
- // Collect the request's response in-memory
- h.write_function(|buf| {
- data.extend_from_slice(buf);
- Ok(buf.len())
- })?;
-
- // Send the request
- h.perform()?;
- }
-
- let code = h.response_code()?;
- if code != 200 {
- return Err(self.err(
- &format!(
- "failed to receive HTTP 200 response: \
- got {}",
- code
- )[..],
- ));
- }
-
- // Check returned headers
- let expected = match self.method {
- "GET" => format!("application/x-git-{}-advertisement", self.service),
- _ => format!("application/x-git-{}-result", self.service),
- };
- match content_type {
- Some(ref content_type) if *content_type != expected => {
- return Err(self.err(
- &format!(
- "expected a Content-Type header \
- with `{}` but found `{}`",
- expected, content_type
- )[..],
- ))
- }
- Some(..) => {}
- None => {
- return Err(self.err(
- &format!(
- "expected a Content-Type header \
- with `{}` but didn't find one",
- expected
- )[..],
- ))
- }
- }
-
- // Ok, time to read off some data.
- let rdr = Cursor::new(data);
- self.reader = Some(rdr);
-
- // If there was a redirect, update the `CurlTransport` with the new base.
- if let Ok(Some(effective_url)) = h.effective_url() {
- let new_base = if effective_url.ends_with(self.url_path) {
- // Strip the action from the end.
- &effective_url[..effective_url.len() - self.url_path.len()]
- } else {
- // I'm not sure if this code path makes sense, but it's what
- // libgit does.
- effective_url
- };
- *self.base_url.lock().unwrap() = new_base.to_string();
- }
-
- Ok(())
- }
-}
-
-impl Read for CurlSubtransport {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- if self.reader.is_none() {
- self.execute(&[])?;
- }
- self.reader.as_mut().unwrap().read(buf)
- }
-}
-
-impl Write for CurlSubtransport {
- fn write(&mut self, data: &[u8]) -> io::Result<usize> {
- if self.reader.is_none() {
- self.execute(data)?;
- }
- Ok(data.len())
- }
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}