extern crate hyper;
extern crate unix_socket;
extern crate url;
extern crate rustc_serialize;
use std::borrow::Cow;
use hyper::client::IntoUrl;
use hyper::net::{NetworkConnector, NetworkStream, NetworkListener};
use hyper::Server;
use std::io::{self, Read, Write};
use std::path::Path;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::time::Duration;
use unix_socket::{UnixListener, UnixStream};
use url::Url;
use url::ParseError as UrlError;
use rustc_serialize::hex::{ToHex, FromHex};
const UNIX_SCHEME: &'static str = "unix";
pub struct UnixSocketConnector;
pub struct UnixSocketStream(pub UnixStream);
impl Clone for UnixSocketStream {
#[inline]
fn clone(&self) -> UnixSocketStream {
UnixSocketStream(self.0.try_clone().unwrap())
}
}
impl NetworkConnector for UnixSocketConnector {
type Stream = UnixSocketStream;
fn connect(&self, host: &str, _: u16, scheme: &str) -> hyper::Result<UnixSocketStream> {
Ok(try!(match scheme {
unix if unix == UNIX_SCHEME => {
let host_str = try!(DomainUrl::resolve(host));
Ok(UnixSocketStream(try!(UnixStream::connect(host_str))))
},
_ => {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"Invalid scheme for unix"))
}
}))
}
}
impl NetworkStream for UnixSocketStream {
#[inline]
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
self.0.peer_addr().map(|_|{
SocketAddr::V4(
SocketAddrV4::new(
Ipv4Addr::new(0, 0, 0, 0),
0
)
)
})
}
#[inline]
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.0.set_read_timeout(dur)
}
#[inline]
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.0.set_write_timeout(dur)
}
}
impl Read for UnixSocketStream {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0.read(buf)
}
}
impl Write for UnixSocketStream {
#[inline]
fn write(&mut self, msg: &[u8]) -> std::io::Result<usize> {
self.0.write(msg)
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
self.0.flush()
}
}
#[derive(Debug)]
pub struct DomainUrl<'a> {
url: Cow<'a, str>
}
impl<'a> DomainUrl<'a> {
pub fn new(socket: &'a str, path: &'a str) -> DomainUrl<'a> {
let host = socket.as_bytes().to_hex();
let host_str = format!("unix://{}:0{}", host, path);
DomainUrl {
url: Cow::Owned(host_str)
}
}
fn resolve<'b>(host: &'b str) -> hyper::Result<String> {
let host_bytes = try!(host.from_hex().map_err(|_| UrlError::InvalidDomainCharacter));
let host_str = try!(std::str::from_utf8(host_bytes.as_ref()));
Ok(host_str.to_owned())
}
}
impl<'a> IntoUrl for DomainUrl<'a> {
fn into_url(self) -> Result<Url, UrlError> {
Url::parse(&self.url)
}
}
#[derive(Debug)]
pub struct UnixSocketListener(pub UnixListener);
impl Clone for UnixSocketListener {
#[inline]
fn clone(&self) -> UnixSocketListener {
UnixSocketListener(self.0.try_clone().unwrap())
}
}
impl UnixSocketListener {
pub fn new<P: AsRef<Path>>(addr: P) -> hyper::Result<UnixSocketListener> {
Ok(UnixSocketListener(try!(UnixListener::bind(addr))))
}
}
impl NetworkListener for UnixSocketListener {
type Stream = UnixSocketStream;
#[inline]
fn accept(&mut self) -> hyper::Result<UnixSocketStream> {
Ok(UnixSocketStream(try!(self.0.accept()).0))
}
#[inline]
fn local_addr(&mut self) -> io::Result<SocketAddr> {
self.0.local_addr().map(|_| {
SocketAddr::V4(
SocketAddrV4::new(
Ipv4Addr::new(0, 0, 0, 0), 0
)
)
})
}
}
pub struct UnixSocketServer;
impl UnixSocketServer {
pub fn new<P: AsRef<Path>>(p: P) -> hyper::Result<Server<UnixSocketListener>> {
UnixSocketListener::new(p).map(Server::new)
}
}
#[cfg(test)]
mod tests {
use super::DomainUrl;
#[test]
fn domain_url_test() {
let url = DomainUrl::new("/var/run/tube.sock", "/");
assert_eq!(url.url, "unix://2f7661722f72756e2f747562652e736f636b:0/");
}
#[test]
fn domain_url_resolve() {
assert_eq!(DomainUrl::resolve("2f7661722f72756e2f747562652e736f636b").unwrap(), "/var/run/tube.sock")
}
}