blob: 2c75b8084600828e9a912cc33784a077c26f96b9 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![feature(arbitrary_self_types, futures_api)]
use {
fuchsia_async::{
EHandle,
net::{TcpConnector, TcpStream}
},
futures::{
compat::Compat,
future::{Future, FutureExt, TryFutureExt},
io::{self, AsyncReadExt},
ready,
task::{LocalWaker, Poll, SpawnExt},
},
hyper::{
Body,
client::{
Client,
connect::{
Connect,
Connected,
Destination,
},
},
},
std::net::ToSocketAddrs,
std::pin::Pin,
};
/// A future that yields a hyper-compatible TCP stream.
pub struct HyperTcpConnector(Result<TcpConnector, Option<io::Error>>);
impl Future for HyperTcpConnector {
type Output = Result<(Compat<TcpStream>, Connected), io::Error>;
fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
let connector = self.0.as_mut().map_err(|x| x.take().unwrap())?;
let stream = ready!(connector.poll_unpin(lw)?);
Poll::Ready(Ok((
stream.compat(),
Connected::new(),
)))
}
}
/// A Fuchsia-compatible implementation of hyper's `Connect` trait which allows
/// creating a TcpStream to a particular destination.
pub struct HyperConnector;
impl Connect for HyperConnector {
type Transport = Compat<TcpStream>;
type Error = io::Error;
type Future = Compat<HyperTcpConnector>;
fn connect(&self, dst: Destination) -> Self::Future {
let res = (|| {
let host = dst.host();
// TODO(cramertj): smarter DNS-- nonblocking, don't just pick first addr
let addr_opt = match dst.port() {
Some(port) => (host, port).to_socket_addrs()?.next(),
None => host.to_socket_addrs()?.next(),
};
let addr = addr_opt.ok_or_else(||
io::Error::new(io::ErrorKind::Other, "destination resolved to no address")
)?;
TcpStream::connect(addr)
})();
HyperTcpConnector(res.map_err(Some)).compat()
}
}
/// Returns a new Fuchsia-compatible hyper client for making HTTP requests.
pub fn new_client() -> Client<HyperConnector, Body> {
Client::builder()
.executor(EHandle::local().compat())
.build(HyperConnector)
}
#[cfg(test)]
mod test {
use super::*;
use fuchsia_async::Executor;
#[test]
fn can_create_client() {
let _exec = Executor::new().unwrap();
let _client = new_client();
}
}