add ClientBuilder.default_headers() for wasm32 target (#1084)
This commit is contained in:
@@ -132,6 +132,7 @@ winreg = "0.7"
|
|||||||
js-sys = "0.3.45"
|
js-sys = "0.3.45"
|
||||||
wasm-bindgen = { version = "0.2.68", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "0.2.68", features = ["serde-serialize"] }
|
||||||
wasm-bindgen-futures = "0.4.18"
|
wasm-bindgen-futures = "0.4.18"
|
||||||
|
wasm-bindgen-test = "0.3"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
|
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
|
||||||
version = "0.3.25"
|
version = "0.3.25"
|
||||||
@@ -168,6 +169,14 @@ name = "tor_socks"
|
|||||||
path = "examples/tor_socks.rs"
|
path = "examples/tor_socks.rs"
|
||||||
required-features = ["socks"]
|
required-features = ["socks"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "form"
|
||||||
|
path = "examples/form.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "simple"
|
||||||
|
path = "examples/simple.rs"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "blocking"
|
name = "blocking"
|
||||||
path = "tests/blocking.rs"
|
path = "tests/blocking.rs"
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
|
// Short example of a POST request with form data.
|
||||||
|
//
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
reqwest::Client::new()
|
let response = reqwest::Client::new()
|
||||||
.post("http://www.baidu.com")
|
.post("http://www.baidu.com")
|
||||||
.form(&[("one", "1")])
|
.form(&[("one", "1")])
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("send");
|
||||||
|
println!("Response status {}", response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The [cfg(not(target_arch = "wasm32"))] above prevent building the tokio::main function
|
||||||
|
// for wasm32 target, because tokio isn't compatible with wasm32.
|
||||||
|
// If you aren't building for wasm32, you don't need that line.
|
||||||
|
// The two lines below avoid the "'main' function not found" error when building for wasm32 target.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn main() {}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// This is using the `tokio` runtime. You'll need the following dependency:
|
// This is using the `tokio` runtime. You'll need the following dependency:
|
||||||
//
|
//
|
||||||
// `tokio = { version = "0.2", features = ["macros"] }`
|
// `tokio = { version = "0.2", features = ["macros"] }`
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), reqwest::Error> {
|
async fn main() -> Result<(), reqwest::Error> {
|
||||||
let res = reqwest::get("https://hyper.rs").await?;
|
let res = reqwest::get("https://hyper.rs").await?;
|
||||||
@@ -15,3 +16,10 @@ async fn main() -> Result<(), reqwest::Error> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The [cfg(not(target_arch = "wasm32"))] above prevent building the tokio::main function
|
||||||
|
// for wasm32 target, because tokio isn't compatible with wasm32.
|
||||||
|
// If you aren't building for wasm32, you don't need that line.
|
||||||
|
// The two lines below avoid the "'main' function not found" error when building for wasm32 target.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn main() {}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use http::Method;
|
use http::{HeaderMap, Method};
|
||||||
use std::future::Future;
|
|
||||||
use wasm_bindgen::prelude::{wasm_bindgen, UnwrapThrowExt as _};
|
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
|
use std::{fmt, future::Future, sync::Arc};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use wasm_bindgen::prelude::{wasm_bindgen, UnwrapThrowExt as _};
|
||||||
|
|
||||||
use super::{Request, RequestBuilder, Response};
|
use super::{Request, RequestBuilder, Response};
|
||||||
use crate::IntoUrl;
|
use crate::IntoUrl;
|
||||||
@@ -30,12 +30,15 @@ fn js_fetch(req: &web_sys::Request) -> Promise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// dox
|
/// dox
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct Client(());
|
pub struct Client {
|
||||||
|
config: Arc<Config>,
|
||||||
|
}
|
||||||
|
|
||||||
/// dox
|
/// dox
|
||||||
#[derive(Debug)]
|
pub struct ClientBuilder {
|
||||||
pub struct ClientBuilder(());
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// dox
|
/// dox
|
||||||
@@ -134,10 +137,24 @@ impl Client {
|
|||||||
self.execute_request(request)
|
self.execute_request(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// merge request headers with Client default_headers, prior to external http fetch
|
||||||
|
fn merge_headers(&self, req: &mut Request) {
|
||||||
|
use http::header::Entry;
|
||||||
|
let headers: &mut HeaderMap = req.headers_mut();
|
||||||
|
// insert default headers in the request headers
|
||||||
|
// without overwriting already appended headers.
|
||||||
|
for (key, value) in self.config.headers.iter() {
|
||||||
|
if let Entry::Vacant(entry) = headers.entry(key) {
|
||||||
|
entry.insert(value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn execute_request(
|
pub(super) fn execute_request(
|
||||||
&self,
|
&self,
|
||||||
req: Request,
|
mut req: Request,
|
||||||
) -> impl Future<Output = crate::Result<Response>> {
|
) -> impl Future<Output = crate::Result<Response>> {
|
||||||
|
self.merge_headers(&mut req);
|
||||||
fetch(req)
|
fetch(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,11 +165,28 @@ impl Default for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Client {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut builder = f.debug_struct("Client");
|
||||||
|
self.config.fmt_fields(&mut builder);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ClientBuilder {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut builder = f.debug_struct("ClientBuilder");
|
||||||
|
self.config.fmt_fields(&mut builder);
|
||||||
|
builder.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch(req: Request) -> crate::Result<Response> {
|
async fn fetch(req: Request) -> crate::Result<Response> {
|
||||||
// Build the js Request
|
// Build the js Request
|
||||||
let mut init = web_sys::RequestInit::new();
|
let mut init = web_sys::RequestInit::new();
|
||||||
init.method(req.method().as_str());
|
init.method(req.method().as_str());
|
||||||
|
|
||||||
|
// convert HeaderMap to Headers
|
||||||
let js_headers = web_sys::Headers::new()
|
let js_headers = web_sys::Headers::new()
|
||||||
.map_err(crate::error::wasm)
|
.map_err(crate::error::wasm)
|
||||||
.map_err(crate::error::builder)?;
|
.map_err(crate::error::builder)?;
|
||||||
@@ -190,8 +224,7 @@ async fn fetch(req: Request) -> crate::Result<Response> {
|
|||||||
.map_err(crate::error::request)?;
|
.map_err(crate::error::request)?;
|
||||||
|
|
||||||
// Convert from the js Response
|
// Convert from the js Response
|
||||||
let mut resp = http::Response::builder()
|
let mut resp = http::Response::builder().status(js_resp.status());
|
||||||
.status(js_resp.status());
|
|
||||||
|
|
||||||
let url = Url::parse(&js_resp.url()).expect_throw("url parse");
|
let url = Url::parse(&js_resp.url()).expect_throw("url parse");
|
||||||
|
|
||||||
@@ -219,12 +252,25 @@ async fn fetch(req: Request) -> crate::Result<Response> {
|
|||||||
impl ClientBuilder {
|
impl ClientBuilder {
|
||||||
/// dox
|
/// dox
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ClientBuilder(())
|
ClientBuilder {
|
||||||
|
config: Config::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dox
|
/// Returns a 'Client' that uses this ClientBuilder configuration
|
||||||
pub fn build(self) -> Result<Client, crate::Error> {
|
pub fn build(mut self) -> Result<Client, crate::Error> {
|
||||||
Ok(Client(()))
|
let config = std::mem::take(&mut self.config);
|
||||||
|
Ok(Client {
|
||||||
|
config: Arc::new(config),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the default headers for every request
|
||||||
|
pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
|
||||||
|
for (key, value) in headers.iter() {
|
||||||
|
self.config.headers.insert(key, value.clone());
|
||||||
|
}
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,3 +279,91 @@ impl Default for ClientBuilder {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Config {
|
||||||
|
headers: HeaderMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
headers: HeaderMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
fn fmt_fields(&self, f: &mut fmt::DebugStruct<'_, '_>) {
|
||||||
|
f.field("default_headers", &self.headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
|
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
async fn default_headers() {
|
||||||
|
use crate::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||||
|
headers.insert("x-custom", HeaderValue::from_static("flibbertigibbet"));
|
||||||
|
let client = crate::Client::builder()
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()
|
||||||
|
.expect("client");
|
||||||
|
let mut req = client
|
||||||
|
.get("https://www.example.com")
|
||||||
|
.build()
|
||||||
|
.expect("request");
|
||||||
|
// merge headers as if client were about to issue fetch
|
||||||
|
client.merge_headers(&mut req);
|
||||||
|
|
||||||
|
let test_headers = req.headers();
|
||||||
|
assert!(test_headers.get(CONTENT_TYPE).is_some(), "content-type");
|
||||||
|
assert!(test_headers.get("x-custom").is_some(), "custom header");
|
||||||
|
assert!(test_headers.get("accept").is_none(), "no accept header");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
async fn default_headers_clone() {
|
||||||
|
use crate::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||||
|
headers.insert("x-custom", HeaderValue::from_static("flibbertigibbet"));
|
||||||
|
let client = crate::Client::builder()
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()
|
||||||
|
.expect("client");
|
||||||
|
|
||||||
|
let mut req = client
|
||||||
|
.get("https://www.example.com")
|
||||||
|
.header(CONTENT_TYPE, "text/plain")
|
||||||
|
.build()
|
||||||
|
.expect("request");
|
||||||
|
client.merge_headers(&mut req);
|
||||||
|
let headers1 = req.headers();
|
||||||
|
|
||||||
|
// confirm that request headers override defaults
|
||||||
|
assert_eq!(
|
||||||
|
headers1.get(CONTENT_TYPE).unwrap(),
|
||||||
|
"text/plain",
|
||||||
|
"request headers override defaults"
|
||||||
|
);
|
||||||
|
|
||||||
|
// confirm that request headers don't change client defaults
|
||||||
|
let mut req2 = client
|
||||||
|
.get("https://www.example.com/x")
|
||||||
|
.build()
|
||||||
|
.expect("req 2");
|
||||||
|
client.merge_headers(&mut req2);
|
||||||
|
let headers2 = req2.headers();
|
||||||
|
assert_eq!(
|
||||||
|
headers2.get(CONTENT_TYPE).unwrap(),
|
||||||
|
"application/json",
|
||||||
|
"request headers don't change client defaults"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
||||||
#[cfg(feature = "__tls")]
|
#[cfg(feature = "__tls")]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_badssl_modern() {
|
async fn test_badssl_modern() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
mod support;
|
mod support;
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
mod support;
|
mod support;
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
mod support;
|
mod support;
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
mod support;
|
mod support;
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
mod support;
|
mod support;
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|
||||||
|
|||||||
24
tests/wasm_simple.rs
Normal file
24
tests/wasm_simple.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#![cfg(target_arch = "wasm32")]
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||||||
|
// `log(..)`
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
async fn simple_example() {
|
||||||
|
let res = reqwest::get("https://hyper.rs")
|
||||||
|
.await
|
||||||
|
.expect("http get example");
|
||||||
|
log(&format!("Status: {}", res.status()));
|
||||||
|
|
||||||
|
let body = res.text().await.expect("response to utf-8 text");
|
||||||
|
log(&format!("Body:\n\n{}", body));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user