mirror of https://github.com/http-rs/surf
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:
parent
94d2bc6c08
commit
00e53514eb
|
@ -5,3 +5,4 @@ dist/
|
|||
npm-debug.log*
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
wasm-pack.log
|
||||
|
|
|
@ -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
|
||||
|
|
49
Cargo.toml
49
Cargo.toml
|
@ -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"] }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
use surf;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() {
|
||||
println!("Hello wasm");
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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,
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue