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