Introduce unstable, incomplete WASM support
This commit is contained in:
@@ -47,10 +47,18 @@ matrix:
|
|||||||
#- rust: nightly
|
#- rust: nightly
|
||||||
# env: FEATURES="--features socks"
|
# env: FEATURES="--features socks"
|
||||||
|
|
||||||
|
# trust-dns
|
||||||
#- rust: stable
|
#- rust: stable
|
||||||
#- rust: nightly
|
#- rust: nightly
|
||||||
# env: FEATURES="--features trust-dns"
|
# env: FEATURES="--features trust-dns"
|
||||||
|
|
||||||
|
# wasm
|
||||||
|
- name: "WASM"
|
||||||
|
env: TARGET=wasm32-unknown-unknown
|
||||||
|
rust: nightly
|
||||||
|
install: rustup target add "$TARGET"
|
||||||
|
script: cargo check --target "$TARGET"
|
||||||
|
|
||||||
# android
|
# android
|
||||||
#- rust: stable
|
#- rust: stable
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
|
|||||||
63
Cargo.toml
63
Cargo.toml
@@ -17,13 +17,34 @@ publish = false
|
|||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["default-tls"]
|
||||||
|
|
||||||
|
tls = []
|
||||||
|
|
||||||
|
default-tls = ["hyper-tls", "native-tls", "tls", "tokio-tls"]
|
||||||
|
default-tls-vendored = ["default-tls", "native-tls/vendored"]
|
||||||
|
|
||||||
|
#rustls-tls = ["hyper-rustls", "tokio-rustls", "webpki-roots", "rustls", "tls"]
|
||||||
|
|
||||||
|
blocking = ["futures-channel-preview", "futures-util-preview/io"]
|
||||||
|
|
||||||
|
cookies = ["cookie_crate", "cookie_store"]
|
||||||
|
|
||||||
|
gzip = ["async-compression"]
|
||||||
|
|
||||||
|
#trust-dns = ["trust-dns-resolver"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
http = "0.1.15"
|
||||||
|
url = "2.1"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
base64 = "0.10"
|
base64 = "0.10"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-core-preview = { version = "=0.3.0-alpha.18" }
|
futures-core-preview = { version = "=0.3.0-alpha.18" }
|
||||||
futures-util-preview = { version = "=0.3.0-alpha.18" }
|
futures-util-preview = { version = "=0.3.0-alpha.18" }
|
||||||
http = "0.1.15"
|
|
||||||
http-body = "=0.2.0-alpha.1"
|
http-body = "=0.2.0-alpha.1"
|
||||||
hyper = "=0.13.0-alpha.2"
|
hyper = "=0.13.0-alpha.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -32,13 +53,11 @@ mime_guess = "2.0"
|
|||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
tokio = { version = "=0.2.0-alpha.5", default-features = false, features = ["rt-full", "tcp"] }
|
tokio = { version = "=0.2.0-alpha.5", default-features = false, features = ["rt-full", "tcp"] }
|
||||||
tokio-executor = "=0.2.0-alpha.5"
|
tokio-executor = "=0.2.0-alpha.5"
|
||||||
url = "2.1"
|
|
||||||
uuid = { version = "0.7", features = ["v4"] }
|
uuid = { version = "0.7", features = ["v4"] }
|
||||||
time = "0.1.42"
|
time = "0.1.42"
|
||||||
|
|
||||||
# TODO: candidates for optional features
|
# TODO: candidates for optional features
|
||||||
|
|
||||||
|
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
@@ -72,7 +91,7 @@ async-compression = { version = "0.1.0-alpha.4", default-features = false, featu
|
|||||||
## trust-dns
|
## trust-dns
|
||||||
#trust-dns-resolver = { version = "0.11", optional = true }
|
#trust-dns-resolver = { version = "0.11", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
hyper = { version = "=0.13.0-alpha.2", features = ["unstable-stream"] }
|
hyper = { version = "=0.13.0-alpha.2", features = ["unstable-stream"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
@@ -80,28 +99,26 @@ libflate = "0.1"
|
|||||||
doc-comment = "0.3"
|
doc-comment = "0.3"
|
||||||
tokio-fs = { version = "=0.2.0-alpha.5" }
|
tokio-fs = { version = "=0.2.0-alpha.5" }
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["default-tls"]
|
|
||||||
|
|
||||||
tls = []
|
|
||||||
|
|
||||||
default-tls = ["hyper-tls", "native-tls", "tls", "tokio-tls"]
|
|
||||||
default-tls-vendored = ["default-tls", "native-tls/vendored"]
|
|
||||||
|
|
||||||
# re-enable CI also
|
|
||||||
#rustls-tls = ["hyper-rustls", "tokio-rustls", "webpki-roots", "rustls", "tls"]
|
|
||||||
|
|
||||||
blocking = ["futures-channel-preview", "futures-util-preview/io"]
|
|
||||||
|
|
||||||
cookies = ["cookie_crate", "cookie_store"]
|
|
||||||
|
|
||||||
gzip = ["async-compression"]
|
|
||||||
|
|
||||||
#trust-dns = ["trust-dns-resolver"]
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.6"
|
winreg = "0.6"
|
||||||
|
|
||||||
|
# wasm
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
js-sys = "0.3.25"
|
||||||
|
wasm-bindgen = "0.2.48"
|
||||||
|
wasm-bindgen-futures = { version = "", features = ["futures_0_3"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
|
||||||
|
version = "0.3.25"
|
||||||
|
features = [
|
||||||
|
"Headers",
|
||||||
|
"Request",
|
||||||
|
"RequestInit",
|
||||||
|
"Response",
|
||||||
|
"Window",
|
||||||
|
]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "blocking"
|
name = "blocking"
|
||||||
path = "examples/blocking.rs"
|
path = "examples/blocking.rs"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg_attr(target_arch = "wasm32", allow(unused))]
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -12,7 +13,7 @@ pub struct Error {
|
|||||||
inner: Box<Inner>,
|
inner: Box<Inner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoxError = Box<dyn StdError + Send + Sync>;
|
pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
|
||||||
|
|
||||||
struct Inner {
|
struct Inner {
|
||||||
kind: Kind,
|
kind: Kind,
|
||||||
@@ -213,6 +214,12 @@ pub(crate) fn url_bad_scheme(url: Url) -> Error {
|
|||||||
Error::new(Kind::Builder, Some("URL scheme is not allowed")).with_url(url)
|
Error::new(Kind::Builder, Some("URL scheme is not allowed")).with_url(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if_wasm! {
|
||||||
|
pub(crate) fn wasm(js_val: wasm_bindgen::JsValue) -> BoxError {
|
||||||
|
format!("{:?}", js_val).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// io::Error helpers
|
// io::Error helpers
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|||||||
@@ -37,14 +37,16 @@ impl<'a> PolyfillTryInto for &'a String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expect_uri(url: &Url) -> hyper::Uri {
|
if_hyper! {
|
||||||
url.as_str()
|
pub(crate) fn expect_uri(url: &Url) -> http::Uri {
|
||||||
.parse()
|
url.as_str()
|
||||||
.expect("a parsed Url should always be a valid Uri")
|
.parse()
|
||||||
}
|
.expect("a parsed Url should always be a valid Uri")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn try_uri(url: &Url) -> Option<hyper::Uri> {
|
pub(crate) fn try_uri(url: &Url) -> Option<http::Uri> {
|
||||||
url.as_str().parse().ok()
|
url.as_str().parse().ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
114
src/lib.rs
114
src/lib.rs
@@ -175,60 +175,32 @@
|
|||||||
////! - **trust-dns**: Enables a trust-dns async resolver instead of default
|
////! - **trust-dns**: Enables a trust-dns async resolver instead of default
|
||||||
////! threadpool using `getaddrinfo`.
|
////! threadpool using `getaddrinfo`.
|
||||||
|
|
||||||
#[cfg(test)]
|
macro_rules! if_wasm {
|
||||||
#[macro_use]
|
($($item:item)*) => {$(
|
||||||
extern crate doc_comment;
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
$item
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
macro_rules! if_hyper {
|
||||||
doctest!("../README.md");
|
($($item:item)*) => {$(
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
$item
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
pub use hyper::header;
|
pub use http::header;
|
||||||
pub use hyper::Method;
|
pub use http::Method;
|
||||||
pub use hyper::{StatusCode, Version};
|
pub use http::{StatusCode, Version};
|
||||||
pub use url::ParseError as UrlError;
|
|
||||||
pub use url::Url;
|
pub use url::Url;
|
||||||
|
|
||||||
pub use self::async_impl::{
|
// universal mods
|
||||||
multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response,
|
|
||||||
};
|
|
||||||
//pub use self::body::Body;
|
|
||||||
//pub use self::client::{Client, ClientBuilder};
|
|
||||||
pub use self::error::{Error, Result};
|
|
||||||
pub use self::into_url::IntoUrl;
|
|
||||||
pub use self::proxy::Proxy;
|
|
||||||
pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
|
|
||||||
//pub use self::request::{Request, RequestBuilder};
|
|
||||||
//pub use self::response::Response;
|
|
||||||
#[cfg(feature = "tls")]
|
|
||||||
pub use self::tls::{Certificate, Identity};
|
|
||||||
|
|
||||||
// this module must be first because of the `try_` macro
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
mod async_impl;
|
|
||||||
#[cfg(feature = "blocking")]
|
|
||||||
pub mod blocking;
|
|
||||||
mod connect;
|
|
||||||
#[cfg(feature = "cookies")]
|
|
||||||
pub mod cookie;
|
|
||||||
//#[cfg(feature = "trust-dns")]
|
|
||||||
//mod dns;
|
|
||||||
mod into_url;
|
mod into_url;
|
||||||
mod proxy;
|
|
||||||
mod redirect;
|
|
||||||
#[cfg(feature = "tls")]
|
|
||||||
mod tls;
|
|
||||||
|
|
||||||
//pub mod multipart;
|
pub use self::error::{Error, Result};
|
||||||
|
pub use self::into_url::IntoUrl;
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated(note = "types moved to top of crate")]
|
|
||||||
pub mod r#async {
|
|
||||||
pub use crate::async_impl::{
|
|
||||||
multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shortcut method to quickly make a `GET` request.
|
/// Shortcut method to quickly make a `GET` request.
|
||||||
///
|
///
|
||||||
@@ -274,8 +246,56 @@ fn _assert_impls() {
|
|||||||
assert_send::<Request>();
|
assert_send::<Request>();
|
||||||
assert_send::<RequestBuilder>();
|
assert_send::<RequestBuilder>();
|
||||||
|
|
||||||
assert_send::<Response>();
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
assert_send::<Response>();
|
||||||
|
}
|
||||||
|
|
||||||
assert_send::<Error>();
|
assert_send::<Error>();
|
||||||
assert_sync::<Error>();
|
assert_sync::<Error>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if_hyper! {
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate doc_comment;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
doctest!("../README.md");
|
||||||
|
|
||||||
|
pub use self::async_impl::{
|
||||||
|
multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response,
|
||||||
|
};
|
||||||
|
pub use self::proxy::Proxy;
|
||||||
|
pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
pub use self::tls::{Certificate, Identity};
|
||||||
|
|
||||||
|
|
||||||
|
mod async_impl;
|
||||||
|
#[cfg(feature = "blocking")]
|
||||||
|
pub mod blocking;
|
||||||
|
mod connect;
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub mod cookie;
|
||||||
|
//#[cfg(feature = "trust-dns")]
|
||||||
|
//mod dns;
|
||||||
|
mod proxy;
|
||||||
|
mod redirect;
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
mod tls;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[deprecated(note = "types moved to top of crate")]
|
||||||
|
pub mod r#async {
|
||||||
|
pub use crate::async_impl::{
|
||||||
|
multipart, Body, Client, ClientBuilder, Request, RequestBuilder, Response,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if_wasm! {
|
||||||
|
mod wasm;
|
||||||
|
|
||||||
|
pub use self::wasm::{Body, Client, ClientBuilder, Request, RequestBuilder, Response};
|
||||||
|
}
|
||||||
|
|||||||
3
src/wasm/body.rs
Normal file
3
src/wasm/body.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// dox
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Body(());
|
||||||
162
src/wasm/client.rs
Normal file
162
src/wasm/client.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
use http::Method;
|
||||||
|
use wasm_bindgen::UnwrapThrowExt as _;
|
||||||
|
|
||||||
|
use crate::IntoUrl;
|
||||||
|
use super::{Request, RequestBuilder, Response};
|
||||||
|
|
||||||
|
/// dox
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Client(());
|
||||||
|
|
||||||
|
/// dox
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClientBuilder(());
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// dox
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Client::builder().build().unwrap_throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dox
|
||||||
|
pub fn builder() -> ClientBuilder {
|
||||||
|
ClientBuilder::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `GET` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::GET, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `POST` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::POST, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `PUT` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::PUT, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `PATCH` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::PATCH, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `DELETE` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::DELETE, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to make a `HEAD` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
|
self.request(Method::HEAD, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start building a `Request` with the `Method` and `Url`.
|
||||||
|
///
|
||||||
|
/// Returns a `RequestBuilder`, which will allow setting headers and
|
||||||
|
/// request body before sending.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever supplied `Url` cannot be parsed.
|
||||||
|
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
|
||||||
|
let req = url.into_url().map(move |url| Request::new(method, url));
|
||||||
|
RequestBuilder::new(self.clone(), req)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn execute_request(&self, req: Request) -> impl Future<Output = crate::Result<Response>> {
|
||||||
|
fetch(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch(req: Request) -> crate::Result<Response> {
|
||||||
|
// Build the js Request
|
||||||
|
let mut init = web_sys::RequestInit::new();
|
||||||
|
init.method(req.method().as_str());
|
||||||
|
|
||||||
|
let js_headers = web_sys::Headers::new()
|
||||||
|
.map_err(crate::error::wasm)
|
||||||
|
.map_err(crate::error::builder)?;
|
||||||
|
|
||||||
|
for (name, value) in req.headers() {
|
||||||
|
js_headers
|
||||||
|
.append(name.as_str(), value.to_str().map_err(crate::error::builder)?)
|
||||||
|
.map_err(crate::error::wasm)
|
||||||
|
.map_err(crate::error::builder)?;
|
||||||
|
}
|
||||||
|
init.headers(&js_headers.into());
|
||||||
|
|
||||||
|
let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
|
||||||
|
.map_err(crate::error::wasm)
|
||||||
|
.map_err(crate::error::builder)?;
|
||||||
|
|
||||||
|
// Await the fetch() promise
|
||||||
|
let p = web_sys::window()
|
||||||
|
.expect("window should exist")
|
||||||
|
.fetch_with_request(&js_req);
|
||||||
|
let js_resp = super::promise::<web_sys::Response>(p)
|
||||||
|
.await
|
||||||
|
.map_err(crate::error::request)?;
|
||||||
|
|
||||||
|
// Convert from the js Response
|
||||||
|
let mut resp = http::Response::builder();
|
||||||
|
resp.status(js_resp.status());
|
||||||
|
|
||||||
|
// TODO: translate js_resp.headers()
|
||||||
|
/*
|
||||||
|
let js_headers = js_resp.headers();
|
||||||
|
let js_iter = js_sys::try_iter(&js_headers)
|
||||||
|
.expect_throw("headers try_iter")
|
||||||
|
.expect_throw("headers have an iterator");
|
||||||
|
|
||||||
|
for item in js_iter {
|
||||||
|
let item = item.expect_throw("headers iterator doesn't throw");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
resp.body(js_resp)
|
||||||
|
.map(Response::new)
|
||||||
|
.map_err(crate::error::request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== impl ClientBuilder =====
|
||||||
|
|
||||||
|
impl ClientBuilder {
|
||||||
|
/// dox
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ClientBuilder(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dox
|
||||||
|
pub fn build(self) -> Result<Client, crate::Error> {
|
||||||
|
Ok(Client(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
29
src/wasm/mod.rs
Normal file
29
src/wasm/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
mod body;
|
||||||
|
mod client;
|
||||||
|
mod request;
|
||||||
|
mod response;
|
||||||
|
|
||||||
|
pub use self::body::Body;
|
||||||
|
pub use self::client::{Client, ClientBuilder};
|
||||||
|
pub use self::request::{Request, RequestBuilder};
|
||||||
|
pub use self::response::Response;
|
||||||
|
|
||||||
|
|
||||||
|
async fn promise<T>(promise: js_sys::Promise) -> Result<T, crate::error::BoxError>
|
||||||
|
where
|
||||||
|
T: JsCast,
|
||||||
|
{
|
||||||
|
use wasm_bindgen_futures::futures_0_3::JsFuture;
|
||||||
|
|
||||||
|
let js_val = JsFuture::from(promise)
|
||||||
|
.await
|
||||||
|
.map_err(crate::error::wasm)?;
|
||||||
|
|
||||||
|
js_val
|
||||||
|
.dyn_into::<T>()
|
||||||
|
.map_err(|_js_val| {
|
||||||
|
"promise resolved to unexpected type".into()
|
||||||
|
})
|
||||||
|
}
|
||||||
144
src/wasm/request.rs
Normal file
144
src/wasm/request.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use http::{Method, HeaderMap};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use super::{Body, Client, Response};
|
||||||
|
|
||||||
|
/// A request which can be executed with `Client::execute()`.
|
||||||
|
pub struct Request {
|
||||||
|
method: Method,
|
||||||
|
url: Url,
|
||||||
|
headers: HeaderMap,
|
||||||
|
body: Option<Body>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder to construct the properties of a `Request`.
|
||||||
|
pub struct RequestBuilder {
|
||||||
|
client: Client,
|
||||||
|
request: crate::Result<Request>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub(super) fn new(method: Method, url: Url) -> Self {
|
||||||
|
Request {
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers: HeaderMap::new(),
|
||||||
|
body: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the method.
|
||||||
|
#[inline]
|
||||||
|
pub fn method(&self) -> &Method {
|
||||||
|
&self.method
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the method.
|
||||||
|
#[inline]
|
||||||
|
pub fn method_mut(&mut self) -> &mut Method {
|
||||||
|
&mut self.method
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the url.
|
||||||
|
#[inline]
|
||||||
|
pub fn url(&self) -> &Url {
|
||||||
|
&self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the url.
|
||||||
|
#[inline]
|
||||||
|
pub fn url_mut(&mut self) -> &mut Url {
|
||||||
|
&mut self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the headers.
|
||||||
|
#[inline]
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the headers.
|
||||||
|
#[inline]
|
||||||
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
|
&mut self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the body.
|
||||||
|
#[inline]
|
||||||
|
pub fn body(&self) -> Option<&Body> {
|
||||||
|
self.body.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the body.
|
||||||
|
#[inline]
|
||||||
|
pub fn body_mut(&mut self) -> &mut Option<Body> {
|
||||||
|
&mut self.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestBuilder {
|
||||||
|
pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
||||||
|
RequestBuilder { client, request }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Set the request body.
|
||||||
|
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
|
||||||
|
if let Ok(ref mut req) = self.request {
|
||||||
|
req.body = Some(body.into());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs the Request and sends it to the target URL, returning a
|
||||||
|
/// future Response.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails if there was an error while sending request.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use reqwest::Error;
|
||||||
|
/// #
|
||||||
|
/// # async fn run() -> Result<(), Error> {
|
||||||
|
/// let response = reqwest::Client::new()
|
||||||
|
/// .get("https://hyper.rs")
|
||||||
|
/// .send()
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn send(self) -> crate::Result<Response> {
|
||||||
|
let req = self.request?;
|
||||||
|
self.client.execute_request(req).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Request {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for RequestBuilder {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut builder = f.debug_struct("RequestBuilder");
|
||||||
|
match self.request {
|
||||||
|
Ok(ref req) => fmt_request_fields(&mut builder, req).finish(),
|
||||||
|
Err(ref err) => builder.field("error", err).finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_request_fields<'a, 'b>(
|
||||||
|
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||||
|
req: &Request,
|
||||||
|
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||||
|
f.field("method", &req.method)
|
||||||
|
.field("url", &req.url)
|
||||||
|
.field("headers", &req.headers)
|
||||||
|
}
|
||||||
73
src/wasm/response.rs
Normal file
73
src/wasm/response.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use http::{HeaderMap, StatusCode};
|
||||||
|
|
||||||
|
/// A Response to a submitted `Request`.
|
||||||
|
pub struct Response {
|
||||||
|
http: http::Response<web_sys::Response>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub(super) fn new(
|
||||||
|
res: http::Response<web_sys::Response>,
|
||||||
|
//url: Url,
|
||||||
|
) -> Response {
|
||||||
|
Response {
|
||||||
|
http: res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `StatusCode` of this `Response`.
|
||||||
|
#[inline]
|
||||||
|
pub fn status(&self) -> StatusCode {
|
||||||
|
self.http.status()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `Headers` of this `Response`.
|
||||||
|
#[inline]
|
||||||
|
pub fn headers(&self) -> &HeaderMap {
|
||||||
|
self.http.headers()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the `Headers` of this `Response`.
|
||||||
|
#[inline]
|
||||||
|
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||||
|
self.http.headers_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It might not be possible to detect this in JS?
|
||||||
|
/// Get the HTTP `Version` of this `Response`.
|
||||||
|
#[inline]
|
||||||
|
pub fn version(&self) -> Version {
|
||||||
|
self.http.version()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// pub async fn json()
|
||||||
|
|
||||||
|
|
||||||
|
/// Get the response text.
|
||||||
|
pub async fn text(self) -> crate::Result<String> {
|
||||||
|
let p = self.http.body().text()
|
||||||
|
.map_err(crate::error::wasm)
|
||||||
|
.map_err(crate::error::decode)?;
|
||||||
|
let js_val = super::promise::<wasm_bindgen::JsValue>(p)
|
||||||
|
.await
|
||||||
|
.map_err(crate::error::decode)?;
|
||||||
|
if let Some(s) = js_val.as_string() {
|
||||||
|
Ok(s)
|
||||||
|
} else {
|
||||||
|
Err(crate::error::decode("response.text isn't string"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Response {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("Response")
|
||||||
|
//.field("url", self.url())
|
||||||
|
.field("status", &self.status())
|
||||||
|
.field("headers", self.headers())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user