WASM backend (#25)

* init wasm client

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wasm32 cfg flags

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* sort deps alphabetically

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* init cross platform by default

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* finish compile

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wasm ci

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* move over cfg statements

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* make fetch

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wasmify logging middleware

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wip

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* add wasm kv logging

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wasm compiles

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* update safety notice

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* requests work!

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* wasm req method

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* split out fetch code to separate block

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* fix all warnings

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>

* finalize header extraction

Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
This commit is contained in:
Yoshua Wuyts 2019-08-07 13:53:47 +02:00 committed by GitHub
parent 94d2bc6c08
commit 00e53514eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 459 additions and 79 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ dist/
npm-debug.log*
Cargo.lock
.DS_Store
wasm-pack.log

View File

@ -4,10 +4,12 @@ rust:
before_script: |
rustup component add rustfmt-preview &&
rustup component add clippy-preview
rustup component add clippy-preview &&
rustup target add wasm32-unknown-unknown
script: |
cargo fmt -- --check &&
cargo clippy -- -D clippy &&
cargo build --verbose &&
cargo check --target wasm32-unknown-unknown &&
cargo test --verbose
cache: cargo

View File

@ -12,34 +12,61 @@ readme = "README.md"
edition = "2018"
[features]
default = ["chttp-client", "middleware-logger"]
default = ["native-client", "middleware-logger"]
native-client = ["chttp-client", "wasm-client"]
hyper-client = ["hyper", "runtime", "runtime-raw", "runtime-tokio" ]
chttp-client = ["chttp"]
wasm-client = ["js-sys", "web-sys", "wasm-bindgen", "wasm-bindgen-futures"]
middleware-logger = []
[dependencies]
http = "0.1.17"
futures-preview = { version = "0.3.0-alpha.17", features = ["compat", "io-compat"] }
http = "0.1.17"
log = { version = "0.4.7", features = ["kv_unstable"] }
mime = "0.3.13"
mime_guess = "2.0.0-alpha.6"
serde = "1.0.97"
serde_json = "1.0.40"
log = { version = "0.4.7", features = ["kv_unstable"] }
serde_urlencoded = "0.6.1"
url = "2.0.0"
mime_guess = "2.0.0-alpha.6"
mime = "0.3.13"
# Chttp
# chttp-client
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
chttp = { version = "0.5.3", optional = true }
# Hyper deps
# hyper-client
hyper = { version = "0.12.32", optional = true, default-features = false }
hyper-tls = { version = "0.3.2", optional = true }
runtime = { version = "0.3.0-alpha.6", optional = true }
native-tls = { version = "0.2.2", optional = true }
runtime = { version = "0.3.0-alpha.6", optional = true }
runtime-raw = { version = "0.3.0-alpha.4", optional = true }
runtime-tokio = { version = "0.3.0-alpha.5", optional = true }
serde_urlencoded = "0.6.1"
# wasm-client
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.25", optional = true }
wasm-bindgen = { version = "0.2.48", optional = true }
wasm-bindgen-futures = { version = "0.3.25", features = ["futures_0_3"], optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
version = "0.3.25"
optional = true
features = [
"AbortSignal",
"Headers",
"ObserverCallback",
"ReferrerPolicy",
"Request",
"RequestCache",
"RequestCredentials",
"RequestInit",
"RequestMode",
"RequestRedirect",
"Response",
"Window",
]
[dev-dependencies]
serde = { version = "1.0.97", features = ["derive"] }
runtime = "0.3.0-alpha.6"
femme = "1.1.0"
runtime = "0.3.0-alpha.6"
serde = { version = "1.0.97", features = ["derive"] }

View File

@ -124,8 +124,11 @@ $ cargo add surf
```
## Safety
This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in
100% Safe Rust.
This crate makes use of a single instance of `unsafe` in order to make the WASM
backend work despite the `Send` bounds. This is safe because WASM targets
currently have no access to threads. Once they do we'll be able to drop this
implementation, and use a parked thread instead and move to full multi-threading
in the process too.
## Contributing
Want to join us? Check out our ["Contributing" guide][contributing] and take a

7
examples/browser.rs Normal file
View File

@ -0,0 +1,7 @@
use surf;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn main() {
println!("Hello wasm");
}

View File

@ -1,8 +1,8 @@
use crate::http_client::HttpClient;
use crate::Request;
#[cfg(feature = "chttp-client")]
use crate::http_client::chttp::ChttpClient;
#[cfg(feature = "native-client")]
use super::http_client::native::NativeClient;
/// An HTTP client, capable of creating new `Request`s.
///
@ -23,8 +23,8 @@ pub struct Client<C: HttpClient> {
client: C,
}
#[cfg(feature = "chttp-client")]
impl Client<ChttpClient> {
#[cfg(feature = "native-client")]
impl Client<NativeClient> {
/// Create a new instance.
///
/// # Examples
@ -37,7 +37,7 @@ impl Client<ChttpClient> {
/// # Ok(()) }
/// ```
pub fn new() -> Self {
Self::with_client(ChttpClient::new())
Self::with_client(NativeClient::new())
}
}

View File

@ -4,10 +4,7 @@ use futures::future::BoxFuture;
use std::sync::Arc;
/// Curl HTTP Client.
///
/// ## Performance
/// Libcurl is not thread safe, which means unfortunatley we cannot reuse connections or multiplex.
/// Curl-based HTTP Client.
#[derive(Debug)]
pub struct ChttpClient {
client: Arc<chttp::HttpClient>,

View File

@ -8,12 +8,18 @@ use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
#[cfg(feature = "hyper-client")]
#[cfg(all(feature = "hyper-client", not(target_arch = "wasm32")))]
pub(crate) mod hyper;
#[cfg(feature = "chttp-client")]
#[cfg(all(feature = "chttp-client", not(target_arch = "wasm32")))]
pub(crate) mod chttp;
#[cfg(all(feature = "wasm-client", target_arch = "wasm32"))]
pub(crate) mod wasm;
#[cfg(feature = "native-client")]
pub(crate) mod native;
/// An HTTP Request type with a streaming body.
pub type Request = http::Request<Body>;

View File

@ -0,0 +1,8 @@
#[cfg(all(feature = "chttp-client", not(target_arch = "wasm32")))]
pub(crate) use super::chttp::ChttpClient as NativeClient;
#[cfg(all(
feature = "wasm-client",
target_arch = "wasm32"
))]
pub(crate) use super::wasm::WasmClient as NativeClient;

201
src/http_client/wasm.rs Normal file
View File

@ -0,0 +1,201 @@
use super::{Body, HttpClient, Request, Response};
use futures::future::BoxFuture;
use futures::prelude::*;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::io;
/// WebAssembly HTTP Client.
#[derive(Debug)]
pub struct WasmClient {
_priv: (),
}
impl WasmClient {
/// Create a new instance.
pub fn new() -> Self {
Self { _priv: () }
}
}
impl Clone for WasmClient {
fn clone(&self) -> Self {
Self { _priv: () }
}
}
impl HttpClient for WasmClient {
type Error = std::io::Error;
fn send(&self, req: Request) -> BoxFuture<'static, Result<Response, Self::Error>> {
let fut = Box::pin(async move {
let url = format!("{}", req.uri());
let req = fetch::new(req.method().as_str(), &url);
let mut res = req.send().await?;
let body = res.body_bytes();
let mut response = Response::new(Body::from(body));
*response.status_mut() = http::StatusCode::from_u16(res.status()).unwrap();
for (name, value) in res.headers() {
let name: http::header::HeaderName = name.parse().unwrap();
response.headers_mut().insert(name, value.parse().unwrap());
}
Ok(response)
});
Box::pin(InnerFuture { fut })
}
}
// This type e
struct InnerFuture {
fut: Pin<Box<dyn Future<Output = Result<Response, io::Error>> + 'static>>,
}
// This is safe because WASM doesn't have threads yet. Once WASM supports threads we should use a
// thread to park the blocking implementation until it's been completed.
unsafe impl Send for InnerFuture {}
impl Future for InnerFuture {
type Output = Result<Response, io::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// This is safe because we're only using this future as a pass-through for the inner
// future, in order to implement `Send`. If it's safe to poll the inner future, it's safe
// to proxy it too.
unsafe { Pin::new_unchecked(&mut self.fut).poll(cx) }
}
}
mod fetch {
use js_sys::{Array, ArrayBuffer, Uint8Array, Reflect};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::futures_0_3::JsFuture;
use web_sys::window;
use web_sys::RequestInit;
use std::iter::{Iterator, IntoIterator};
use std::io;
/// Create a new fetch request.
pub(crate) fn new(method: impl AsRef<str>, url: impl AsRef<str>) -> Request {
Request::new(method, url)
}
/// An HTTP Fetch Request.
pub(crate) struct Request {
init: RequestInit,
url: String,
}
impl Request {
/// Create a new instance.
pub(crate) fn new(method: impl AsRef<str>, url: impl AsRef<str>) -> Self {
let mut init = web_sys::RequestInit::new();
init.method(method.as_ref());
Self {
init,
url: url.as_ref().to_owned(),
}
}
/// Submit a request
// TODO(yoshuawuyts): turn this into a `Future` impl on `Request` instead.
pub(crate) async fn send(self) -> Result<Response, io::Error> {
// Send the request.
let window = window().expect("A global window object could not be found");
let request = web_sys::Request::new_with_str_and_init(&self.url, &self.init).unwrap();
let promise = window.fetch_with_request(&request);
let resp = JsFuture::from(promise).await.unwrap();
debug_assert!(resp.is_instance_of::<web_sys::Response>());
let res: web_sys::Response = resp.dyn_into().unwrap();
// Get the request body.
let promise = res.array_buffer().unwrap();
let resp = JsFuture::from(promise).await.unwrap();
debug_assert!(resp.is_instance_of::<js_sys::ArrayBuffer>());
let buf: ArrayBuffer = resp.dyn_into().unwrap();
let slice = Uint8Array::new(&buf);
let mut body: Vec<u8> = vec![0; slice.length() as usize];
slice.copy_to(&mut body);
Ok(Response::new(res, body))
}
}
/// An HTTP Fetch Response.
pub(crate) struct Response {
res: web_sys::Response,
body: Option<Vec<u8>>,
}
impl Response {
fn new(res: web_sys::Response, body: Vec<u8>) -> Self {
Self {
res,
body: Some(body),
}
}
/// Access the HTTP headers.
pub(crate) fn headers(&self) -> Headers {
Headers {
headers: self.res.headers()
}
}
/// Get the request body as a byte vector.
///
/// Returns an empty vector if the body has already been consumed.
pub(crate) fn body_bytes(&mut self) -> Vec<u8> {
self.body.take().unwrap_or_else(|| vec![])
}
/// Get the HTTP return status code.
pub(crate) fn status(&self) -> u16 {
self.res.status()
}
}
/// HTTP Headers.
pub(crate) struct Headers {
headers: web_sys::Headers,
}
impl IntoIterator for Headers {
type Item = (String, String);
type IntoIter = HeadersIter;
fn into_iter(self) -> Self::IntoIter {
HeadersIter {
iter: js_sys::try_iter(&self.headers).unwrap().unwrap(),
}
}
}
/// HTTP Headers Iterator.
pub(crate) struct HeadersIter {
iter: js_sys::IntoIter,
}
impl Iterator for HeadersIter {
type Item = (String, String);
fn next(&mut self) -> Option<Self::Item> {
let pair = self.iter.next()?;
let array: Array = pair.unwrap().into();
let vals = array.values();
let prop = String::from("value").into();
let key = Reflect::get(&vals.next().unwrap(), &prop).unwrap();
let value = Reflect::get(&vals.next().unwrap(), &prop).unwrap();
Some((key.as_string().to_owned().unwrap(), value.as_string().to_owned().unwrap()))
}
}
}

View File

@ -70,7 +70,7 @@
//! - __`hyper-client`:__ use `hyper` as the HTTP backend.
//! - __`middleware-logger` (default):__ enables logging requests and responses using a middleware.
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
#![forbid(future_incompatible, rust_2018_idioms)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
#![cfg_attr(test, deny(warnings))]
@ -91,9 +91,9 @@ pub use client::Client;
pub use request::Request;
pub use response::Response;
#[cfg(feature = "chttp-client")]
#[cfg(feature = "native-client")]
mod one_off;
#[cfg(feature = "chttp-client")]
#[cfg(feature = "native-client")]
pub use one_off::{connect, delete, get, head, options, patch, post, put, trace};
/// A generic error type.

View File

@ -0,0 +1,44 @@
//! Logging middleware.
//!
//! # Examples
//!
//! ```
//! # #![feature(async_await)]
//! # #[runtime::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//! let mut res = surf::get("https://google.com")
//! .middleware(surf::middleware::logger::new())
//! .await?;
//! dbg!(res.body_string().await?);
//! # Ok(()) }
//! ```
#[cfg(target_arch = "wasm32")]
mod wasm;
#[cfg(target_arch = "wasm32")]
use wasm::Logger;
#[cfg(not(target_arch = "wasm32"))]
mod native;
#[cfg(not(target_arch = "wasm32"))]
use native::Logger;
/// Create a new instance.
///
/// # Examples
///
/// ```
/// # #![feature(async_await)]
/// # #[runtime::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
/// let mut res = surf::get("https://google.com")
/// .middleware(surf::middleware::logger::new())
/// .await?;
/// dbg!(res.body_string().await?);
/// # Ok(()) }
/// ```
pub fn new() -> Logger {
Logger::new()
}

View File

@ -1,18 +1,3 @@
//! Logging middleware.
//!
//! # Examples
//!
//! ```
//! # #![feature(async_await)]
//! # #[runtime::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
//! let mut res = surf::get("https://google.com")
//! .middleware(surf::middleware::logger::new())
//! .await?;
//! dbg!(res.body_string().await?);
//! # Ok(()) }
//! ```
use crate::http_client::HttpClient;
use crate::middleware::{Middleware, Next, Request, Response};
@ -29,6 +14,13 @@ pub struct Logger {
_priv: (),
}
impl Logger {
/// Create a new instance.
pub fn new() -> Self{
Logger {_priv: ()}
}
}
impl<C: HttpClient> Middleware<C> for Logger {
#[allow(missing_doc_code_examples)]
fn handle<'a>(
@ -79,24 +71,6 @@ impl<C: HttpClient> Middleware<C> for Logger {
}
}
/// Create a new instance.
///
/// # Examples
///
/// ```
/// # #![feature(async_await)]
/// # #[runtime::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
/// let mut res = surf::get("https://google.com")
/// .middleware(surf::middleware::logger::new())
/// .await?;
/// dbg!(res.body_string().await?);
/// # Ok(()) }
/// ```
pub fn new() -> Logger {
Logger { _priv: () }
}
struct RequestPairs<'a> {
id: usize,
method: &'a str,

View File

@ -0,0 +1,108 @@
use crate::http_client::HttpClient;
use crate::middleware::{Middleware, Next, Request, Response};
use futures::future::BoxFuture;
use std::fmt::Arguments;
/// Log each request's duration.
#[derive(Debug)]
pub struct Logger {
_priv: (),
}
impl Logger {
/// Create a new instance.
pub fn new() -> Self{
Logger {_priv: ()}
}
}
impl<C: HttpClient> Middleware<C> for Logger {
#[allow(missing_doc_code_examples)]
fn handle<'a>(
&'a self,
req: Request,
client: C,
next: Next<'a, C>,
) -> BoxFuture<'a, Result<Response, crate::Exception>> {
Box::pin(async move {
let uri = format!("{}", req.uri());
let method = format!("{}", req.method());
print(
log::Level::Info,
format_args!("sending request"),
RequestPairs {
uri: &uri,
method: &method,
},
);
let res = next.run(req, client).await?;
let status = res.status();
let level = if status.is_server_error() {
log::Level::Error
} else if status.is_client_error() {
log::Level::Warn
} else {
log::Level::Info
};
print(
level,
format_args!("request completed"),
ResponsePairs {
status: status.as_u16(),
},
);
Ok(res)
})
}
}
struct RequestPairs<'a> {
method: &'a str,
uri: &'a str,
}
impl<'a> log::kv::Source for RequestPairs<'a> {
fn visit<'kvs>(
&'kvs self,
visitor: &mut dyn log::kv::Visitor<'kvs>,
) -> Result<(), log::kv::Error> {
visitor.visit_pair("req.method".into(), self.method.into())?;
visitor.visit_pair("req.uri".into(), self.uri.into())?;
Ok(())
}
}
struct ResponsePairs {
status: u16,
}
impl log::kv::Source for ResponsePairs {
fn visit<'kvs>(
&'kvs self,
visitor: &mut dyn log::kv::Visitor<'kvs>,
) -> Result<(), log::kv::Error> {
visitor.visit_pair("req.status".into(), self.status.into())?;
Ok(())
}
}
fn print(level: log::Level, msg: Arguments<'_>, key_values: impl log::kv::Source) {
if level <= log::STATIC_MAX_LEVEL && level <= log::max_level() {
log::logger().log(
&log::Record::builder()
.args(msg)
.key_values(&key_values)
.level(level)
.target(module_path!())
.module_path(Some(module_path!()))
.file(Some(file!()))
.line(Some(line!()))
.build(),
);
}
}

View File

@ -1,4 +1,6 @@
use super::http_client::chttp::ChttpClient;
#[cfg(feature = "native-client")]
use super::http_client::native::NativeClient;
use super::Request;
/// Perform a one-off `GET` request.
@ -29,7 +31,7 @@ use super::Request;
/// let string = surf::get("https://httpbin.org/get").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn get(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn get(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::GET, uri)
}
@ -71,7 +73,7 @@ pub fn get(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::head("https://httpbin.org/head").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn head(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn head(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::HEAD, uri)
}
@ -130,7 +132,7 @@ pub fn head(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::post("https://httpbin.org/post").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn post(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn post(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::POST, uri)
}
@ -167,7 +169,7 @@ pub fn post(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::put("https://httpbin.org/put").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn put(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn put(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::PUT, uri)
}
@ -199,7 +201,7 @@ pub fn put(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::delete("https://httpbin.org/delete").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn delete(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn delete(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::DELETE, uri)
}
@ -240,7 +242,7 @@ pub fn delete(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::connect("https://httpbin.org/connect").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn connect(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn connect(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::CONNECT, uri)
}
@ -274,7 +276,7 @@ pub fn connect(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::options("https://httpbin.org/options").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn options(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn options(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::OPTIONS, uri)
}
@ -312,7 +314,7 @@ pub fn options(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::trace("https://httpbin.org/trace").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn trace(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn trace(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::TRACE, uri)
}
@ -356,7 +358,7 @@ pub fn trace(uri: impl AsRef<str>) -> Request<ChttpClient> {
/// let string = surf::patch("https://httpbin.org/patch").recv_string().await?;
/// # Ok(()) }
/// ```
pub fn patch(uri: impl AsRef<str>) -> Request<ChttpClient> {
pub fn patch(uri: impl AsRef<str>) -> Request<NativeClient> {
let uri = uri.as_ref().to_owned().parse().unwrap();
Request::new(http::Method::PATCH, uri)
}

View File

@ -20,9 +20,9 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
#[cfg(feature = "chttp-client")]
use super::http_client::chttp::ChttpClient;
#[cfg(feature = "chttp-client")]
#[cfg(feature = "native-client")]
use super::http_client::native::NativeClient;
#[cfg(feature = "native-client")]
use std::convert::TryFrom;
/// An HTTP request, returns a `Response`.
@ -39,8 +39,8 @@ pub struct Request<C: HttpClient + Debug + Unpin + Send + Sync> {
url: Url,
}
#[cfg(feature = "chttp-client")]
impl Request<ChttpClient> {
#[cfg(feature = "native-client")]
impl Request<NativeClient> {
/// Create a new instance.
///
/// This method is particularly useful when input URLs might be passed by third parties, and
@ -61,7 +61,7 @@ impl Request<ChttpClient> {
/// # Ok(()) }
/// ```
pub fn new(method: http::Method, url: Url) -> Self {
Self::with_client(method, url, ChttpClient::new())
Self::with_client(method, url, NativeClient::new())
}
}
@ -563,9 +563,9 @@ impl<C: HttpClient> Future for Request<C> {
}
}
#[cfg(feature = "chttp-client")]
#[cfg(feature = "native-client")]
impl<R: AsyncRead + Unpin + Send + 'static> TryFrom<http::Request<Box<R>>>
for Request<ChttpClient>
for Request<NativeClient>
{
type Error = io::Error;