diff options
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | src/builder.rs | 24 | ||||
-rw-r--r-- | src/errors.rs | 18 | ||||
-rw-r--r-- | src/lib.rs | 458 | ||||
-rw-r--r-- | src/transport.rs | 282 |
5 files changed, 430 insertions, 365 deletions
@@ -13,15 +13,18 @@ license = "MIT" [dependencies] byteorder = "1.0.0" flate2 = "0.2" -hyper = "0.10" -hyper-openssl = "0.2" -hyperlocal = { version = "0.3", optional = true } +http = "0.1" +hyper = "0.12" +hyper-openssl = "0.6" +hyperlocal = { version = "0.6", optional = true } jed = "0.1" log = "0.3" -openssl = "0.9" +mime = "0.3" +openssl = "0.10" rustc-serialize = "0.3" tar = "0.4" -url = "0.5" +tokio = "0.1" +url = "1.7" serde = "1.0" serde_derive = "1.0" diff --git a/src/builder.rs b/src/builder.rs index c69a155..ffc0cf1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -25,7 +25,8 @@ impl PullOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -99,7 +100,8 @@ impl BuildOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -207,7 +209,8 @@ impl ContainerListOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -680,7 +683,8 @@ impl EventsOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -817,7 +821,8 @@ impl LogsOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -886,7 +891,8 @@ impl ImageListOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -963,7 +969,8 @@ impl RmContainerOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } @@ -1006,7 +1013,8 @@ impl NetworkListOptions { if self.params.is_empty() { None } else { - Some(form_urlencoded::serialize(&self.params)) + Some(form_urlencoded::Serializer::new(String::new()) + .extend_pairs(&self.params).finish()) } } } diff --git a/src/errors.rs b/src/errors.rs index 856a010..d4905a1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,7 +1,7 @@ //! Representations of various client errors -use hyper::Error as HttpError; -use hyper::status::StatusCode; +use http; +use hyper::{self, StatusCode}; use rustc_serialize::json::{DecoderError, EncoderError, ParserError}; use std::error::Error as ErrorTrait; use std::fmt; @@ -12,7 +12,8 @@ pub enum Error { Decoding(DecoderError), Encoding(EncoderError), Parse(ParserError), - Http(HttpError), + Hyper(hyper::Error), + Http(http::Error), IO(IoError), Fault { code: StatusCode, message: String }, } @@ -35,8 +36,14 @@ impl From<EncoderError> for Error { } } -impl From<HttpError> for Error { - fn from(error: HttpError) -> Error { +impl From<hyper::Error> for Error { + fn from(error: hyper::Error) -> Error { + Error::Hyper(error) + } +} + +impl From<http::Error> for Error { + fn from(error: http::Error) -> Error { Error::Http(error) } } @@ -55,6 +62,7 @@ impl fmt::Display for Error { &Error::Encoding(ref err) => return err.fmt(f), &Error::Parse(ref err) => return err.fmt(f), &Error::Http(ref err) => return err.fmt(f), + &Error::Hyper(ref err) => return err.fmt(f), &Error::IO(ref err) => return err.fmt(f), &Error::Fault { code, .. } => return write!(f, "{}", code), }; @@ -15,56 +15,63 @@ #[macro_use] extern crate log; +extern crate byteorder; +extern crate flate2; +extern crate http; extern crate hyper; extern crate hyper_openssl; #[cfg(feature = "unix-socket")] extern crate hyperlocal; -extern crate flate2; extern crate jed; +extern crate mime; extern crate openssl; extern crate rustc_serialize; -extern crate url; extern crate tar; -extern crate byteorder; +extern crate url; #[macro_use] extern crate serde_derive; extern crate serde; +extern crate tokio; pub mod builder; +pub mod errors; pub mod rep; pub mod transport; -pub mod errors; pub mod tty; mod tarball; -pub use builder::{BuildOptions, ContainerConnectionOptions, ContainerFilter, - ContainerListOptions, ContainerOptions, EventsOptions, - ExecContainerOptions, ImageFilter, ImageListOptions, - LogsOptions, NetworkCreateOptions, NetworkListOptions, - PullOptions, RmContainerOptions}; +pub use builder::{ + BuildOptions, ContainerConnectionOptions, ContainerFilter, + ContainerListOptions, ContainerOptions, EventsOptions, + ExecContainerOptions, ImageFilter, ImageListOptions, LogsOptions, + NetworkCreateOptions, NetworkListOptions, PullOptions, RmContainerOptions, +}; pub use errors::Error; -use hyper::{Client, Url}; -use hyper::client::Body; -use hyper::header::ContentType; -use hyper::method::Method; -use hyper::net::HttpsConnector; -use hyper_openssl::OpensslClient; -use openssl::ssl::{SslConnectorBuilder, SslMethod}; -use openssl::x509::X509_FILETYPE_PEM; -use rep::{Change, Container as ContainerRep, ContainerCreateInfo, - ContainerDetails, Event, Exit, History, ImageDetails, Info, - SearchResult, Stats, Status, Top, Version}; -use rep::{NetworkCreateInfo, NetworkDetails as NetworkInfo}; +use hyper::client::HttpConnector; +use hyper::Body; +use hyper::{Client, Method, Uri}; +use hyper_openssl::HttpsConnector; +#[cfg(feature = "unix-socket")] +use hyperlocal::UnixConnector; +use mime::Mime; +use openssl::ssl::{SslConnector, SslFiletype, SslMethod}; use rep::Image as ImageRep; +use rep::{ + Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, + Event, Exit, History, ImageDetails, Info, SearchResult, Stats, Status, Top, + Version, +}; +use rep::{NetworkCreateInfo, NetworkDetails as NetworkInfo}; use rustc_serialize::json::{self, Json}; use std::borrow::Cow; -use std::env::{self, VarError}; -use std::io::Read; +use std::cell::RefCell; +use std::env; +use std::io::prelude::*; use std::iter::IntoIterator; use std::path::Path; use std::time::Duration; -use transport::{Transport, tar}; +use transport::{tar, Transport}; use tty::Tty; use url::form_urlencoded; @@ -96,58 +103,49 @@ impl<'a, 'b> Image<'a, 'b> { /// Inspects a named image's details pub fn inspect(&self) -> Result<ImageDetails> { - let raw = self.docker.get(&format!("/images/{}/json", self.name)[..])?; + let raw = self + .docker + .get(&format!("/images/{}/json", self.name)[..])?; Ok(json::decode::<ImageDetails>(&raw)?) } /// Lists the history of the images set of changes pub fn history(&self) -> Result<Vec<History>> { - let raw = self.docker.get( - &format!("/images/{}/history", self.name)[..], - )?; + let raw = self + .docker + .get(&format!("/images/{}/history", self.name)[..])?; Ok(json::decode::<Vec<History>>(&raw)?) } /// Delete's an image pub fn delete(&self) -> Result<Vec<Status>> { let raw = self.docker.delete(&format!("/images/{}", self.name)[..])?; - Ok( - match Json::from_str(&raw)? { - Json::Array(ref xs) => { - xs.iter().map(|j| { - let obj = j.as_object().expect("expected json object"); - obj.get("Untagged") - .map(|sha| { - Status::Untagged( - sha.as_string() - .expect( - "expected Untagged to be a string", - ) - .to_owned(), - ) - }) - .or(obj.get("Deleted").map(|sha| { - Status::Deleted( - sha.as_string() - .expect( - "expected Deleted to be a string", - ) - .to_owned(), - ) - })) - .expect("expected Untagged or Deleted") - }) - } - _ => unreachable!(), - }.collect(), - ) + Ok(match Json::from_str(&raw)? { + Json::Array(ref xs) => xs.iter().map(|j| { + let obj = j.as_object().expect("expected json object"); + obj.get("Untagged") + .map(|sha| { + Status::Untagged( + sha.as_string() + .expect("expected Untagged to be a string") + .to_owned(), + ) + }).or(obj.get("Deleted").map(|sha| { + Status::Deleted( + sha.as_string() + .expect("expected Deleted to be a string") + .to_owned(), + ) + })).expect("expected Untagged or Deleted") + }), + _ => unreachable!(), + }.collect()) } /// Export this image to a tarball pub fn export(&self) -> Result<Box<Read>> { - self.docker.stream_get( - &format!("/images/{}/get", self.name)[..], - ) + self.docker + .stream_get(&format!("/images/{}/get", self.name)[..]) } } @@ -176,12 +174,9 @@ impl<'a> Images<'a> { tarball::dir(&mut bytes, &opts.path[..])?; - let raw = self.docker.stream_post( - &path.join("?"), - Some( - (Body::BufBody(&bytes[..], bytes.len()), tar()), - ), - )?; + let raw = self + .docker + .stream_post(&path.join("?"), Some((Body::from(bytes), tar())))?; let it = jed::Iter::new(raw).into_iter(); Ok(Box::new(it)) } @@ -203,7 +198,9 @@ impl<'a> Images<'a> { /// Search for docker images by term pub fn search(&self, term: &str) -> Result<Vec<SearchResult>> { - let query = form_urlencoded::serialize(vec![("term", term)]); + let query = form_urlencoded::Serializer::new(String::new()) + .append_pair("term", term) + .finish(); let raw = self.docker.get(&format!("/images/search?{}", query)[..])?; Ok(json::decode::<Vec<SearchResult>>(&raw)?) } @@ -217,10 +214,7 @@ impl<'a> Images<'a> { if let Some(query) = opts.serialize() { path.push(query); } - let raw = self.docker.stream_post( - &path.join("?"), - None as Option<(&'a str, ContentType)>, - )?; + let raw = self.docker.stream_post::<Body>(&path.join("?"), None)?; let it = jed::Iter::new(raw).into_iter(); Ok(Box::new(it)) } @@ -228,14 +222,12 @@ impl<'a> Images<'a> { /// exports a collection of named images, /// either by name, name:tag, or image id, into a tarball pub fn export(&self, names: Vec<&str>) -> Result<Box<Read>> { - let params = names - .iter() - .map(|n| ("names", *n)) - .collect::<Vec<(&str, &str)>>(); - let query = form_urlencoded::serialize(params); - self.docker.stream_get( - &format!("/images/get?{}", query)[..], - ) + let params = names.iter().map(|n| ("names", *n)); + let query = form_urlencoded::Serializer::new(String::new()) + .extend_pairs(params) + .finish(); + self.docker + .stream_get(&format!("/images/get?{}", query)[..]) } // pub fn import(self, tarball: Box<Read>) -> Result<()> { @@ -268,9 +260,9 @@ impl<'a, 'b> Container<'a, 'b> { /// Inspects the current docker container instance's details pub fn inspect(&self) -> Result<ContainerDetails> { - let raw = self.docker.get( - &format!("/containers/{}/json", self.id)[..], - )?; + let raw = self + .docker + .get(&format!("/containers/{}/json", self.id)[..])?; Ok(json::decode::<ContainerDetails>(&raw)?) } @@ -278,7 +270,9 @@ impl<'a, 'b> Container<'a, 'b> { pub fn top(&self, psargs: Option<&str>) -> Result<Top> { let mut path = vec![format!("/containers/{}/top", self.id)]; if let Some(ref args) = psargs { - let encoded = form_urlencoded::serialize(vec![("ps_args", args)]); + let encoded = form_urlencoded::Serializer::new(String::new()) + .append_pair("ps_args", args) + .finish(); path.push(encoded) } let raw = self.docker.get(&path.join("?"))?; @@ -297,24 +291,23 @@ impl<'a, 'b> Container<'a, 'b> { /// Returns a set of changes made to the container instance pub fn changes(&self) -> Result<Vec<Change>> { - let raw = self.docker.get( - &format!("/containers/{}/changes", self.id)[..], - )?; + let raw = self + .docker + .get(&format!("/containers/{}/changes", self.id)[..])?; Ok(json::decode::<Vec<Change>>(&raw)?) } /// Exports the current docker container into a tarball pub fn export(&self) -> Result<Box<Read>> { - self.docker.stream_get( - &format!("/containers/{}/export", self.id)[..], - ) + self.docker + .stream_get(&format!("/containers/{}/export", self.id)[..]) } /// Returns a stream of stats specific to this container instance pub fn stats(&self) -> Result<Box<Iterator<Item = Stats>>> { - let raw = self.docker.stream_get( - &format!("/containers/{}/stats", self.id)[..], - )?; + let raw = self + .docker + .stream_get(&format!("/containers/{}/stats", self.id)[..])?; let it = jed::Iter::new(raw).into_iter().map(|j| { // fixme: better error handling debug!("{:?}", j); @@ -327,10 +320,7 @@ impl<'a, 'b> Container<'a, 'b> { /// Start the container instance pub fn start(&'a self) -> Result<()> { self.docker - .post( - &format!("/containers/{}/start", self.id)[..], - None as Option<(&'a str, ContentType)>, - ) + .post::<Body>(&format!("/containers/{}/start", self.id)[..], None) .map(|_| ()) } @@ -338,80 +328,70 @@ impl<'a, 'b> Container<'a, 'b> { pub fn stop(&self, wait: Option<Duration>) -> Result<()> { let mut path = vec![format!("/containers/{}/stop", self.id)]; if let Some(w) = wait { - let encoded = form_urlencoded::serialize( - vec![("t", w.as_secs().to_string())], - ); + let encoded = form_urlencoded::Serializer::new(String::new()) + .append_pair("t", &w.as_secs().to_string()) + .finish(); + path.push(encoded) } - self.docker - .post(&path.join("?"), None as Option<(&'a str, ContentType)>) - .map(|_| ()) + self.docker.post::<Body>(&path.join("?"), None).map(|_| ()) } /// Restart the container instance pub fn restart(&self, wait: Option<Duration>) -> Result<()> { let mut path = vec![format!("/containers/{}/restart", self.id)]; if let Some(w) = wait { - let encoded = form_urlencoded::serialize( - vec![("t", w.as_secs().to_string())], - ); + let encoded = form_urlencoded::Serializer::new(String::new()) + .append_pair("t", &w.as_secs().to_string()) + .finish(); path.push(encoded) } - self.docker - .post(&path.join("?"), None as Option<(&'a str, ContentType)>) - .map(|_| ()) + self.docker.post::<Body>(&path.join("?"), None).map(|_| ()) } /// Kill the container instance pub fn kill(&self, signal: Option<&str>) -> Result<()> { let mut path = vec![format!("/containers/{}/kill", self.id)]; if let Some(sig) = signal { - let encoded = - form_urlencoded::serialize(vec![("signal", sig.to_owned())]); + let encoded = form_urlencoded::Serializer::new(String::new()) + .append_pair("signal", &sig.to_owned()) + .finish(); path.push(encoded) } - self.docker - .post(&path.join("?"), None as Option<(&'a str, ContentType)>) - .map(|_| ()) + self.docker.post::<Body>(&path.join("?"), None).map(|_| ()) } /// Rename the container instance pub fn rename(&self, name: &str) -> Result<()> { - let query = form_urlencoded::serialize(vec![("name", name)]); + let query = form_urlencoded::Serializer::new(String::new()) + .append_pair("name", name) + .finish(); self.docker - .post( + .post::<Body>( &format!("/containers/{}/rename?{}", self.id, query)[..], - None as Option<(&'a str, ContentType)>, - ) - .map(|_| ()) + None, + ).map(|_| ()) } /// Pause the container instance pub fn pause(&self) -> Result<()> { self.docker - .post( - &format!("/containers/{}/pause", self.id)[..], - None as Option<(&'a str, ContentType)>, - ) + .post::<Body>(&format!("/containers/{}/pause", self.id)[..], None) .map(|_| ()) } /// Unpause the container instance pub fn unpause(&self) -> Result<()> { self.docker - .post( - &format!("/containers/{}/unpause", self.id)[..], - None as Option<(&'a str, ContentType)>, - ) + .post::<Body>(&format!("/containers/{}/unpause", self.id)[..], None) .map(|_| ()) } /// Wait until the container stops pub fn wait(&self) -> Result<Exit> { - let raw = self.docker.post( - &format!("/containers/{}/wait", self.id)[..], - None as Option<(&'a str, ContentType)>, - )?; + let raw = self + .docker + .post::<Body>(&format!("/containers/{}/wait", self.id)[..], None)?; Ok(json::decode::<Exit>(&raw)?) } @@ -437,10 +417,10 @@ impl<'a, 'b> Container<'a, 'b> { /// Exec the specified command in the container pub fn exec(&self, opts: &ExecContainerOptions) -> Result<Tty> { let data = opts.serialize()?; - let mut bytes = data.as_bytes(); + let bytes = data.into_bytes(); match self.docker.post( &format!("/containers/{}/exec", self.id)[..], - Some((&mut bytes, ContentType::json())), + Some((bytes, mime::APPLICATION_JSON)), ) { Err(e) => Err(e), Ok(res) => { @@ -456,11 +436,9 @@ impl<'a, 'b> Container<'a, 'b> { .unwrap() .as_string() .unwrap() - ) - [..], - Some((&mut bytes, ContentType::json())), - ) - .map(|stream| Tty::new(stream)) + )[..], + Some((bytes, mime::APPLICATION_JSON)), + ).map(|stream| Tty::new(stream)) } } } @@ -503,17 +481,20 @@ impl<'a> Containers<'a> { opts: &ContainerOptions, ) -> Result<ContainerCreateInfo> { let data = opts.serialize()?; - let mut bytes = data.as_bytes(); + let bytes = data.into_bytes(); let mut path = vec!["/containers/create".to_owned()]; if let Some(ref name) = opts.name { - path.push(form_urlencoded::serialize(vec![("name", name)])); + path.push( + form_urlencoded::Serializer::new(String::new()) + .append_pair("name", name) + .finish(), + ); } - let raw = self.docker.post( - &path.join("?"), - Some((&mut bytes, ContentType::json())), - )?; + let raw = self + .docker + .post(&path.join("?"), Some((bytes, mime::APPLICATION_JSON)))?; Ok(json::decode::<ContainerCreateInfo>(&raw)?) } } @@ -549,13 +530,12 @@ impl<'a> Networks<'a> { opts: &NetworkCreateOptions, ) -> Result<NetworkCreateInfo> { let data = opts.serialize()?; - let mut bytes = data.as_bytes(); + let bytes = data.into_bytes(); let path = vec!["/networks/create".to_owned()]; - let raw = self.docker.post( - &path.join("?"), - Some((&mut bytes, ContentType::json())), - )?; + let raw = self + .docker + .post(&path.join("?"), Some((bytes, mime::APPLICATION_JSON)))?; Ok(json::decode::<NetworkCreateInfo>(&raw)?) } } @@ -612,14 +592,13 @@ impl<'a, 'b> Network<'a, 'b> { opts: &ContainerConnectionOptions, ) -> Result<()> { let data = opts.serialize()?; - let mut bytes = data.as_bytes(); + let bytes = data.into_bytes(); self.docker .post( &format!("/networks/{}/{}", self.id, segment)[..], - Some((&mut bytes, ContentType::json())), - ) - .map(|_| ()) + Some((bytes, mime::APPLICATION_JSON)), + ).map(|_| ()) } } @@ -628,80 +607,106 @@ impl Docker { /// constructs a new Docker instance for a docker host listening at a url specified by an env var `DOCKER_HOST`, /// falling back on unix:///var/run/docker.sock pub fn new() -> Docker { - let fallback: std::result::Result<String, VarError> = - Ok("unix:///var/run/docker.sock".to_owned()); - let host = env::var("DOCKER_HOST") - .or(fallback) - .map(|h| Url::parse(&h).ok().expect("invalid url")) - .ok() - .expect("expected host"); - Docker::host(host) + match env::var("DOCKER_HOST").ok() { + Some(host) => { + let host = host.parse().expect("invalid url"); + Docker::host(host) + } + #[cfg(feature = "unix-socket")] + None => Docker::unix("/var/run/docker.sock"), + #[cfg(not(feature = "unix-socket"))] + None => panic!("Unix socket support is disabled"), + } + } + + /// Creates a new docker instance for a docker host + /// listening on a given Unix socket. + #[cfg(feature = "unix-socket")] + pub fn unix<S>(socket_path: S) -> Docker + where + S: Into<String>, + { + Docker { + transport: Transport::Unix { + client: Client::builder() + .keep_alive(false) + .build(UnixConnector), + runtime: RefCell::new(tokio::runtime::Runtime::new().unwrap()), + path: socket_path.into(), + }, + } } /// constructs a new Docker instance for docker host listening at the given host url - pub fn host(host: Url) -> Docker { - match host.scheme() { + pub fn host(host: Uri) -> Docker { + let tcp_host_str = format!( + "{}://{}:{}", + host.scheme_part().map(|s| s.as_str()).unwrap(), + host.host().unwrap().to_owned(), + host.port().unwrap_or(80) + ); + + match host.scheme_part().map(|s| s.as_str()) { #[cfg(feature = "unix-socket")] - "unix" => { - Docker { - transport: Transport::Unix { - client: Client::with_connector(hyperlocal::UnixSocketConnector), - path: host.path().to_owned(), - }, - } - } + Some("unix") => Docker { + transport: Transport::Unix { + client: Client::builder().build(UnixConnector), + runtime: RefCell::new( + tokio::runtime::Runtime::new().unwrap(), + ), + path: host.path().to_owned(), + }, + }, #[cfg(not(feature = "unix-socket"))] - "unix" => panic!("Unix socket support is disabled"), + Some("unix") => panic!("Unix socket support is disabled"), _ => { - let client = if let Some(ref certs) = env::var( - "DOCKER_CERT_PATH", - ).ok() - { + if let Some(ref certs) = env::var("DOCKER_CERT_PATH").ok() { // fixme: don't unwrap before you know what's in the box // https://github.com/hyperium/hyper/blob/master/src/net.rs#L427-L428 let mut connector = - SslConnectorBuilder::new(SslMethod::tls()).unwrap(); - connector.builder_mut().set_cipher_list("DEFAULT").unwrap(); + SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_cipher_list("DEFAULT").unwrap(); let cert = &format!("{}/cert.pem", certs); let key = &format!("{}/key.pem", certs); connector - .builder_mut() .set_certificate_file( &Path::new(cert), - X509_FILETYPE_PEM, - ) - .unwrap(); + SslFiletype::PEM, + ).unwrap(); connector - .builder_mut() - .set_private_key_file( - &Path::new(key), - X509_FILETYPE_PEM, - ) + .set_private_key_file(&Path::new(key), SslFiletype::PEM) .unwrap(); if let Some(_) = env::var("DOCKER_TLS_VERIFY").ok() { let ca = &format!("{}/ca.pem", certs); - connector - .builder_mut() - .set_ca_file(&Path::new(ca)) + connector.set_ca_file(&Path::new(ca)).unwrap(); + } + + let http = HttpConnector::new(1); + let connector = + HttpsConnector::with_connector(http, connector) .unwrap(); + + Docker { + transport: Transport::EncryptedTcp { + client: Client::builder().build(connector), + runtime: RefCell::new( + tokio::runtime::Runtime::new().unwrap(), + ), + host: tcp_host_str, + }, } - let ssl = OpensslClient::from(connector.build()); - Client::with_connector(HttpsConnector::new(ssl)) } else { - Client::new() - }; - Docker { - transport: Transport::Tcp { - client: client, - host: format!( - "{}://{}:{}", - host.scheme(), - host.host_str().unwrap().to_owned(), - host.port_or_known_default().unwrap() - ), - }, + Docker { + transport: Transport::Tcp { + client: Client::new(), + runtime: RefCell::new( + tokio::runtime::Runtime::new().unwrap(), + ), + host: tcp_host_str, + }, + } } } } @@ -757,49 +762,34 @@ impl Docker { Ok(Box::new(it)) } - fn get<'a>(&self, endpoint: &str) -> Result<String> { - self.transport.request( - Method::Get, - endpoint, - None as Option<(&'a str, ContentType)>, - ) + fn get(&self, endpoint: &str) -> Result<String> { + self.transport.request::<Body>(Method::GET, endpoint, None) } - fn post<'a, B>( - &'a self, - endpoint: &str, - body: Option<(B, ContentType)>, - ) -> Result<String> + fn post<B>(&self, endpoint: &str, body: Option<(B, Mime)>) -> Result<String> where - B: Into<Body<'a>>, + B: Into<Body>, { - self.transport.request(Method::Post, endpoint, body) + self.transport.request(Method::POST, endpoint, body) } - fn delete<'a>(&self, endpoint: &str) -> Result<String> { - self.transport.request( - Method::Delete, - endpoint, - None as Option<(&'a str, ContentType)>, - ) + fn delete(&self, endpoint: &str) -> Result<String> { + self.transport + .request::<Body>(Method::DELETE, endpoint, None) } - fn stream_post<'a, B>( - &'a self, + fn stream_post<B>( + &self, endpoint: &str, - body: Option<(B, ContentType)>, + body: Option<(B, Mime)>, ) -> Result<Box<Read>> where - B: Into<Body<'a>>, + B: Into<Body>, { - self.transport.stream(Method::Post, endpoint, body) + self.transport.stream(Method::POST, endpoint, body) } - fn stream_get<'a>(&self, endpoint: &str) -> Result<Box<Read>> { - self.transport.stream( - Method::Get, - endpoint, - None as Option<(&'a str, ContentType)>, - ) + fn stream_get(&self, endpoint: &str) -> Result<Box<Read>> { + self.transport.stream::<Body>(Method::GET, endpoint, None) } } diff --git a/src/transport.rs b/src/transport.rs index 69c2eab..4cd0a96 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -4,42 +4,60 @@ extern crate hyper; #[cfg(feature = "unix-socket")] extern crate hyperlocal; -use self::hyper::buffer::BufReader; -use self::hyper::header::ContentType; -use self::hyper::status::StatusCode; use self::super::{Error, Result}; -use hyper::Client; -use hyper::client::Body; -use hyper::client::response::Response; +use hyper::client::{Client, HttpConnector}; use hyper::header; -use hyper::method::Method; -use hyper::mime; +use hyper::rt::Stream; +use hyper::Body; +use hyper::{Method, Request, Response, StatusCode}; +use hyper_openssl::HttpsConnector; +#[cfg(feature = "unix-socket")] +use hyperlocal::UnixConnector; +#[cfg(feature = "unix-socket")] +use hyperlocal::Uri as DomainUri; +use mime::Mime; use rustc_serialize::json; +use std::cell::{RefCell, RefMut}; use std::fmt; use std::io::Read; +use std::io::{BufReader, Cursor}; +use tokio::runtime::Runtime; -pub fn tar() -> ContentType { - ContentType(mime::Mime( - mime::TopLevel::Application, - mime::SubLevel::Ext(String::from("tar")), - vec![], - )) +pub fn tar() -> Mime { + "application/tar".parse().unwrap() } /// Transports are types which define the means of communication /// with the docker daemon pub enum Transport { /// A network tcp interface - Tcp { client: Client, host: String }, + Tcp { + client: Client<HttpConnector>, + runtime: RefCell<Runtime>, + host: String, + }, + /// TCP/TLS + EncryptedTcp { + client: Client<HttpsConnector<HttpConnector>>, + runtime: RefCell<Runtime>, + host: String, + }, /// A Unix domain socket #[cfg(feature = "unix-socket")] - Unix { client: Client, path: String }, + Unix { + client: Client<UnixConnector>, + runtime: RefCell<Runtime>, + path: String, + }, } impl fmt::Debug for Transport { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Transport::Tcp { ref host, .. } => write!(f, "Tcp({})", host), + Transport::EncryptedTcp { ref host, .. } => { + write!(f, "EncryptedTcp({})", host) + } #[cfg(feature = "unix-socket")] Transport::Unix { ref path, .. } => write!(f, "Unix({})", path), } @@ -47,14 +65,14 @@ impl fmt::Debug for Transport { } impl Transport { - pub fn request<'a, B>( - &'a self, + pub fn request<B>( + &self, method: Method, endpoint: &str, - body: Option<(B, ContentType)>, + body: Option<(B, Mime)>, ) -> Result<String> where |