wreq-rb 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +2688 -0
- data/Cargo.toml +6 -0
- data/README.md +179 -0
- data/ext/wreq_rb/Cargo.toml +39 -0
- data/ext/wreq_rb/extconf.rb +22 -0
- data/ext/wreq_rb/src/client.rs +565 -0
- data/ext/wreq_rb/src/error.rs +25 -0
- data/ext/wreq_rb/src/lib.rs +20 -0
- data/ext/wreq_rb/src/response.rs +132 -0
- data/lib/wreq-rb/version.rb +5 -0
- data/lib/wreq-rb.rb +17 -0
- data/patches/0001-add-transfer-size-tracking.patch +292 -0
- data/vendor/wreq/Cargo.toml +306 -0
- data/vendor/wreq/LICENSE +202 -0
- data/vendor/wreq/README.md +122 -0
- data/vendor/wreq/examples/cert_store.rs +77 -0
- data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
- data/vendor/wreq/examples/emulation.rs +118 -0
- data/vendor/wreq/examples/form.rs +14 -0
- data/vendor/wreq/examples/http1_websocket.rs +37 -0
- data/vendor/wreq/examples/http2_websocket.rs +45 -0
- data/vendor/wreq/examples/json_dynamic.rs +41 -0
- data/vendor/wreq/examples/json_typed.rs +47 -0
- data/vendor/wreq/examples/keylog.rs +16 -0
- data/vendor/wreq/examples/request_with_emulation.rs +115 -0
- data/vendor/wreq/examples/request_with_interface.rs +37 -0
- data/vendor/wreq/examples/request_with_local_address.rs +16 -0
- data/vendor/wreq/examples/request_with_proxy.rs +13 -0
- data/vendor/wreq/examples/request_with_redirect.rs +22 -0
- data/vendor/wreq/examples/request_with_version.rs +15 -0
- data/vendor/wreq/examples/tor_socks.rs +24 -0
- data/vendor/wreq/examples/unix_socket.rs +33 -0
- data/vendor/wreq/src/client/body.rs +304 -0
- data/vendor/wreq/src/client/conn/conn.rs +231 -0
- data/vendor/wreq/src/client/conn/connector.rs +549 -0
- data/vendor/wreq/src/client/conn/http.rs +1023 -0
- data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
- data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
- data/vendor/wreq/src/client/conn/proxy.rs +39 -0
- data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
- data/vendor/wreq/src/client/conn/uds.rs +44 -0
- data/vendor/wreq/src/client/conn/verbose.rs +149 -0
- data/vendor/wreq/src/client/conn.rs +323 -0
- data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
- data/vendor/wreq/src/client/core/body/length.rs +118 -0
- data/vendor/wreq/src/client/core/body.rs +34 -0
- data/vendor/wreq/src/client/core/common/buf.rs +149 -0
- data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
- data/vendor/wreq/src/client/core/common/watch.rs +76 -0
- data/vendor/wreq/src/client/core/common.rs +3 -0
- data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
- data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
- data/vendor/wreq/src/client/core/conn.rs +11 -0
- data/vendor/wreq/src/client/core/dispatch.rs +299 -0
- data/vendor/wreq/src/client/core/error.rs +435 -0
- data/vendor/wreq/src/client/core/ext.rs +201 -0
- data/vendor/wreq/src/client/core/http1.rs +178 -0
- data/vendor/wreq/src/client/core/http2.rs +483 -0
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
- data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
- data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
- data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
- data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
- data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
- data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
- data/vendor/wreq/src/client/core/proto.rs +58 -0
- data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
- data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
- data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
- data/vendor/wreq/src/client/core/rt.rs +25 -0
- data/vendor/wreq/src/client/core/upgrade.rs +267 -0
- data/vendor/wreq/src/client/core.rs +16 -0
- data/vendor/wreq/src/client/emulation.rs +161 -0
- data/vendor/wreq/src/client/http/client/error.rs +142 -0
- data/vendor/wreq/src/client/http/client/exec.rs +29 -0
- data/vendor/wreq/src/client/http/client/extra.rs +77 -0
- data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
- data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
- data/vendor/wreq/src/client/http/client/util.rs +104 -0
- data/vendor/wreq/src/client/http/client.rs +1003 -0
- data/vendor/wreq/src/client/http/future.rs +99 -0
- data/vendor/wreq/src/client/http.rs +1629 -0
- data/vendor/wreq/src/client/layer/config/options.rs +156 -0
- data/vendor/wreq/src/client/layer/config.rs +116 -0
- data/vendor/wreq/src/client/layer/cookie.rs +161 -0
- data/vendor/wreq/src/client/layer/decoder.rs +139 -0
- data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
- data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
- data/vendor/wreq/src/client/layer/redirect.rs +145 -0
- data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
- data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
- data/vendor/wreq/src/client/layer/retry.rs +151 -0
- data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
- data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
- data/vendor/wreq/src/client/layer/timeout.rs +177 -0
- data/vendor/wreq/src/client/layer.rs +15 -0
- data/vendor/wreq/src/client/multipart.rs +717 -0
- data/vendor/wreq/src/client/request.rs +818 -0
- data/vendor/wreq/src/client/response.rs +534 -0
- data/vendor/wreq/src/client/ws/json.rs +99 -0
- data/vendor/wreq/src/client/ws/message.rs +453 -0
- data/vendor/wreq/src/client/ws.rs +714 -0
- data/vendor/wreq/src/client.rs +27 -0
- data/vendor/wreq/src/config.rs +140 -0
- data/vendor/wreq/src/cookie.rs +579 -0
- data/vendor/wreq/src/dns/gai.rs +249 -0
- data/vendor/wreq/src/dns/hickory.rs +78 -0
- data/vendor/wreq/src/dns/resolve.rs +180 -0
- data/vendor/wreq/src/dns.rs +69 -0
- data/vendor/wreq/src/error.rs +502 -0
- data/vendor/wreq/src/ext.rs +398 -0
- data/vendor/wreq/src/hash.rs +143 -0
- data/vendor/wreq/src/header.rs +506 -0
- data/vendor/wreq/src/into_uri.rs +187 -0
- data/vendor/wreq/src/lib.rs +586 -0
- data/vendor/wreq/src/proxy/mac.rs +82 -0
- data/vendor/wreq/src/proxy/matcher.rs +806 -0
- data/vendor/wreq/src/proxy/uds.rs +66 -0
- data/vendor/wreq/src/proxy/win.rs +31 -0
- data/vendor/wreq/src/proxy.rs +569 -0
- data/vendor/wreq/src/redirect.rs +575 -0
- data/vendor/wreq/src/retry.rs +198 -0
- data/vendor/wreq/src/sync.rs +129 -0
- data/vendor/wreq/src/tls/conn/cache.rs +123 -0
- data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
- data/vendor/wreq/src/tls/conn/ext.rs +82 -0
- data/vendor/wreq/src/tls/conn/macros.rs +34 -0
- data/vendor/wreq/src/tls/conn/service.rs +138 -0
- data/vendor/wreq/src/tls/conn.rs +681 -0
- data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
- data/vendor/wreq/src/tls/keylog.rs +99 -0
- data/vendor/wreq/src/tls/options.rs +464 -0
- data/vendor/wreq/src/tls/x509/identity.rs +122 -0
- data/vendor/wreq/src/tls/x509/parser.rs +71 -0
- data/vendor/wreq/src/tls/x509/store.rs +228 -0
- data/vendor/wreq/src/tls/x509.rs +68 -0
- data/vendor/wreq/src/tls.rs +154 -0
- data/vendor/wreq/src/trace.rs +55 -0
- data/vendor/wreq/src/util.rs +122 -0
- data/vendor/wreq/tests/badssl.rs +228 -0
- data/vendor/wreq/tests/brotli.rs +350 -0
- data/vendor/wreq/tests/client.rs +1098 -0
- data/vendor/wreq/tests/connector_layers.rs +227 -0
- data/vendor/wreq/tests/cookie.rs +306 -0
- data/vendor/wreq/tests/deflate.rs +347 -0
- data/vendor/wreq/tests/emulation.rs +260 -0
- data/vendor/wreq/tests/gzip.rs +347 -0
- data/vendor/wreq/tests/layers.rs +261 -0
- data/vendor/wreq/tests/multipart.rs +165 -0
- data/vendor/wreq/tests/proxy.rs +438 -0
- data/vendor/wreq/tests/redirect.rs +629 -0
- data/vendor/wreq/tests/retry.rs +135 -0
- data/vendor/wreq/tests/support/delay_server.rs +117 -0
- data/vendor/wreq/tests/support/error.rs +16 -0
- data/vendor/wreq/tests/support/layer.rs +183 -0
- data/vendor/wreq/tests/support/mod.rs +9 -0
- data/vendor/wreq/tests/support/server.rs +232 -0
- data/vendor/wreq/tests/timeouts.rs +281 -0
- data/vendor/wreq/tests/unix_socket.rs +135 -0
- data/vendor/wreq/tests/upgrade.rs +98 -0
- data/vendor/wreq/tests/zstd.rs +559 -0
- metadata +225 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
use std::time::Duration;
|
|
2
|
+
|
|
3
|
+
use wreq::{
|
|
4
|
+
Client,
|
|
5
|
+
tls::{CertStore, TlsInfo},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/// Certificate Store Example
|
|
9
|
+
///
|
|
10
|
+
/// In most cases, you don't need to manually configure certificate stores. wreq automatically
|
|
11
|
+
/// uses appropriate default certificates:
|
|
12
|
+
/// - With `webpki-roots` feature enabled: Uses Mozilla's maintained root certificate collection
|
|
13
|
+
/// - Without this feature: Uses system default certificate store paths
|
|
14
|
+
///
|
|
15
|
+
/// Manual certificate store configuration is only needed in the following special cases:
|
|
16
|
+
///
|
|
17
|
+
/// ## Scenarios requiring custom certificate store:
|
|
18
|
+
///
|
|
19
|
+
/// ### 1. Self-signed Certificates
|
|
20
|
+
/// - Connect to internal services using self-signed certificates
|
|
21
|
+
/// - Test servers in development environments
|
|
22
|
+
///
|
|
23
|
+
/// ### 2. Enterprise Internal CA
|
|
24
|
+
/// - Add root certificates from enterprise internal certificate authorities
|
|
25
|
+
/// - Access HTTPS services on corporate intranets
|
|
26
|
+
///
|
|
27
|
+
/// ### 3. Certificate Updates and Management
|
|
28
|
+
/// - Dynamically update certificates in the certificate store
|
|
29
|
+
/// - Remove revoked or expired certificates
|
|
30
|
+
///
|
|
31
|
+
/// ### 4. Compliance Requirements
|
|
32
|
+
/// - Special compliance requirements for certain industries or regions
|
|
33
|
+
/// - Need to use specific certificate collections
|
|
34
|
+
///
|
|
35
|
+
/// ### 5. Performance Optimization
|
|
36
|
+
/// - Reduce certificate store size to improve TLS handshake performance
|
|
37
|
+
/// - Include only necessary root certificates
|
|
38
|
+
#[tokio::main]
|
|
39
|
+
async fn main() -> wreq::Result<()> {
|
|
40
|
+
// Create a client with a custom certificate store using webpki-roots
|
|
41
|
+
let client = Client::builder()
|
|
42
|
+
.cert_store(CertStore::from_der_certs(
|
|
43
|
+
webpki_root_certs::TLS_SERVER_ROOT_CERTS,
|
|
44
|
+
)?)
|
|
45
|
+
.build()?;
|
|
46
|
+
|
|
47
|
+
// Use the API you're already familiar with
|
|
48
|
+
client.get("https://www.google.com").send().await?;
|
|
49
|
+
|
|
50
|
+
// Self-signed certificate Client
|
|
51
|
+
// Skip certificate verification for self-signed certificates
|
|
52
|
+
let client = Client::builder()
|
|
53
|
+
.tls_info(true)
|
|
54
|
+
.cert_verification(false)
|
|
55
|
+
.build()?;
|
|
56
|
+
|
|
57
|
+
// Use the API you're already familiar with
|
|
58
|
+
let resp = client.get("https://self-signed.badssl.com/").send().await?;
|
|
59
|
+
if let Some(tls_info) = resp.extensions().get::<TlsInfo>() {
|
|
60
|
+
if let Some(peer_cert_der) = tls_info.peer_certificate() {
|
|
61
|
+
// Create self-signed certificate Store
|
|
62
|
+
let self_signed_store = CertStore::from_der_certs(&[peer_cert_der])?;
|
|
63
|
+
|
|
64
|
+
// Create a client with self-signed certificate store
|
|
65
|
+
let client = Client::builder()
|
|
66
|
+
.cert_store(self_signed_store)
|
|
67
|
+
.connect_timeout(Duration::from_secs(10))
|
|
68
|
+
.build()?;
|
|
69
|
+
|
|
70
|
+
// Use the API you're already familiar with
|
|
71
|
+
let resp = client.get("https://self-signed.badssl.com/").send().await?;
|
|
72
|
+
println!("{}", resp.text().await?);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Ok(())
|
|
77
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// This example demonstrates how to delegate the connect calls, which contain TLS handshakes,
|
|
2
|
+
// to a secondary tokio runtime of lower OS thread priority using a custom tower layer.
|
|
3
|
+
// This helps to ensure that long-running futures during handshake crypto operations don't block
|
|
4
|
+
// other I/O futures.
|
|
5
|
+
//
|
|
6
|
+
// This does introduce overhead of additional threads, channels, extra vtables, etc,
|
|
7
|
+
// so it is best suited to services with large numbers of incoming connections or that
|
|
8
|
+
// are otherwise very sensitive to any blocking futures. Or, you might want fewer threads
|
|
9
|
+
// and/or to use the current_thread runtime.
|
|
10
|
+
//
|
|
11
|
+
// This is using the `tokio` runtime and certain other dependencies:
|
|
12
|
+
//
|
|
13
|
+
// `tokio = { version = "1", features = ["full"] }`
|
|
14
|
+
// `libc = "0"`
|
|
15
|
+
// `pin-project-lite = "0.2"`
|
|
16
|
+
// `tower = { version = "0.5", default-features = false}`
|
|
17
|
+
|
|
18
|
+
#[tokio::main]
|
|
19
|
+
async fn main() -> wreq::Result<()> {
|
|
20
|
+
tracing_subscriber::fmt()
|
|
21
|
+
.with_max_level(tracing::Level::TRACE)
|
|
22
|
+
.init();
|
|
23
|
+
background_threadpool::init_background_runtime();
|
|
24
|
+
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
|
25
|
+
|
|
26
|
+
let client = wreq::Client::builder()
|
|
27
|
+
.connector_layer(background_threadpool::BackgroundProcessorLayer::new())
|
|
28
|
+
.build()
|
|
29
|
+
.expect("should be able to build wreq client");
|
|
30
|
+
|
|
31
|
+
let url = if let Some(url) = std::env::args().nth(1) {
|
|
32
|
+
url
|
|
33
|
+
} else {
|
|
34
|
+
println!("No CLI URL provided, using default.");
|
|
35
|
+
"https://hyper.rs".into()
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
eprintln!("Fetching {url:?}...");
|
|
39
|
+
|
|
40
|
+
let res = client.get(url).send().await?;
|
|
41
|
+
|
|
42
|
+
eprintln!("Response: {:?} {}", res.version(), res.status());
|
|
43
|
+
eprintln!("Headers: {:#?}\n", res.headers());
|
|
44
|
+
|
|
45
|
+
let body = res.text().await?;
|
|
46
|
+
|
|
47
|
+
println!("{body}");
|
|
48
|
+
|
|
49
|
+
Ok(())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// separating out for convenience to avoid a million
|
|
53
|
+
mod background_threadpool {
|
|
54
|
+
use std::{
|
|
55
|
+
future::Future,
|
|
56
|
+
pin::Pin,
|
|
57
|
+
sync::OnceLock,
|
|
58
|
+
task::{Context, Poll},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
use futures_util::TryFutureExt;
|
|
62
|
+
use pin_project_lite::pin_project;
|
|
63
|
+
use tokio::{runtime::Handle, select, sync::mpsc::error::TrySendError};
|
|
64
|
+
use tower::{BoxError, Layer, Service};
|
|
65
|
+
|
|
66
|
+
static CPU_HEAVY_THREAD_POOL: OnceLock<
|
|
67
|
+
tokio::sync::mpsc::Sender<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
|
|
68
|
+
> = OnceLock::new();
|
|
69
|
+
|
|
70
|
+
pub(crate) fn init_background_runtime() {
|
|
71
|
+
std::thread::Builder::new()
|
|
72
|
+
.name("cpu-heavy-background-threadpool".to_string())
|
|
73
|
+
.spawn(move || {
|
|
74
|
+
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
75
|
+
.thread_name("cpu-heavy-background-pool-thread")
|
|
76
|
+
.worker_threads(std::thread::available_parallelism().unwrap().get())
|
|
77
|
+
// ref: https://github.com/tokio-rs/tokio/issues/4941
|
|
78
|
+
// consider uncommenting if seeing heavy task contention
|
|
79
|
+
// .disable_lifo_slot()
|
|
80
|
+
.on_thread_start(move || {
|
|
81
|
+
#[cfg(target_os = "linux")]
|
|
82
|
+
unsafe {
|
|
83
|
+
// Increase thread pool thread niceness, so they are lower priority
|
|
84
|
+
// than the foreground executor and don't interfere with I/O tasks
|
|
85
|
+
{
|
|
86
|
+
*libc::__errno_location() = 0;
|
|
87
|
+
if libc::nice(10) == -1 && *libc::__errno_location() != 0 {
|
|
88
|
+
let error = std::io::Error::last_os_error();
|
|
89
|
+
tracing::error!("failed to set threadpool niceness: {error}");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
.enable_all()
|
|
95
|
+
.build()
|
|
96
|
+
.unwrap_or_else(|e| panic!("cpu heavy runtime failed_to_initialize: {e}"));
|
|
97
|
+
rt.block_on(async {
|
|
98
|
+
tracing::debug!("starting background cpu-heavy work");
|
|
99
|
+
process_cpu_work().await;
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
.unwrap_or_else(|e| panic!("cpu heavy thread failed_to_initialize: {e}"));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async fn process_cpu_work() {
|
|
106
|
+
// we only use this channel for routing work, it should move pretty quick, it can be small
|
|
107
|
+
let (tx, mut rx) = tokio::sync::mpsc::channel(10);
|
|
108
|
+
// share the handle to the background channel globally
|
|
109
|
+
CPU_HEAVY_THREAD_POOL.set(tx).unwrap();
|
|
110
|
+
|
|
111
|
+
while let Some(work) = rx.recv().await {
|
|
112
|
+
tokio::task::spawn(work);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// retrieve the sender to the background channel, and send the future over to it for execution
|
|
117
|
+
fn send_to_background_runtime(future: impl Future<Output = ()> + Send + 'static) {
|
|
118
|
+
let tx = CPU_HEAVY_THREAD_POOL.get().expect(
|
|
119
|
+
"start up the secondary tokio runtime before sending to `CPU_HEAVY_THREAD_POOL`",
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
match tx.try_send(Box::pin(future)) {
|
|
123
|
+
Ok(_) => (),
|
|
124
|
+
Err(TrySendError::Closed(_)) => {
|
|
125
|
+
panic!("background cpu heavy runtime channel is closed")
|
|
126
|
+
}
|
|
127
|
+
Err(TrySendError::Full(msg)) => {
|
|
128
|
+
tracing::warn!(
|
|
129
|
+
"background cpu heavy runtime channel is full, task spawning loop delayed"
|
|
130
|
+
);
|
|
131
|
+
let tx = tx.clone();
|
|
132
|
+
Handle::current().spawn(async move {
|
|
133
|
+
tx.send(msg)
|
|
134
|
+
.await
|
|
135
|
+
.expect("background cpu heavy runtime channel is closed")
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// This tower layer injects futures with a oneshot channel, and then sends them to the
|
|
142
|
+
// background runtime for processing. We don't use the Buffer service because that is
|
|
143
|
+
// intended to process sequentially on a single task, whereas we want to spawn a new task
|
|
144
|
+
// per call.
|
|
145
|
+
#[derive(Copy, Clone)]
|
|
146
|
+
pub struct BackgroundProcessorLayer {}
|
|
147
|
+
impl BackgroundProcessorLayer {
|
|
148
|
+
pub fn new() -> Self {
|
|
149
|
+
Self {}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
impl<S> Layer<S> for BackgroundProcessorLayer {
|
|
153
|
+
type Service = BackgroundProcessor<S>;
|
|
154
|
+
fn layer(&self, service: S) -> Self::Service {
|
|
155
|
+
BackgroundProcessor::new(service)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
impl std::fmt::Debug for BackgroundProcessorLayer {
|
|
160
|
+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
161
|
+
f.debug_struct("BackgroundProcessorLayer").finish()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// This tower service injects futures with a oneshot channel, and then sends them to the
|
|
166
|
+
// background runtime for processing.
|
|
167
|
+
#[derive(Debug, Clone)]
|
|
168
|
+
pub struct BackgroundProcessor<S> {
|
|
169
|
+
inner: S,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
impl<S> BackgroundProcessor<S> {
|
|
173
|
+
pub fn new(inner: S) -> Self {
|
|
174
|
+
BackgroundProcessor { inner }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
impl<S, Request> Service<Request> for BackgroundProcessor<S>
|
|
179
|
+
where
|
|
180
|
+
S: Service<Request>,
|
|
181
|
+
S::Response: Send + 'static,
|
|
182
|
+
S::Error: Into<BoxError> + Send,
|
|
183
|
+
S::Future: Send + 'static,
|
|
184
|
+
{
|
|
185
|
+
type Response = S::Response;
|
|
186
|
+
|
|
187
|
+
type Error = BoxError;
|
|
188
|
+
|
|
189
|
+
type Future = BackgroundResponseFuture<S::Response>;
|
|
190
|
+
|
|
191
|
+
fn poll_ready(
|
|
192
|
+
&mut self,
|
|
193
|
+
cx: &mut std::task::Context<'_>,
|
|
194
|
+
) -> std::task::Poll<Result<(), Self::Error>> {
|
|
195
|
+
match self.inner.poll_ready(cx) {
|
|
196
|
+
Poll::Pending => Poll::Pending,
|
|
197
|
+
Poll::Ready(r) => Poll::Ready(r.map_err(Into::into)),
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fn call(&mut self, req: Request) -> Self::Future {
|
|
202
|
+
let response = self.inner.call(req);
|
|
203
|
+
|
|
204
|
+
// wrap our inner service's future with a future that writes to this oneshot channel
|
|
205
|
+
let (mut tx, rx) = tokio::sync::oneshot::channel();
|
|
206
|
+
let future = async move {
|
|
207
|
+
select!(
|
|
208
|
+
_ = tx.closed() => {
|
|
209
|
+
// receiver already dropped, don't need to do anything
|
|
210
|
+
}
|
|
211
|
+
result = response.map_err(Into::<BoxError>::into) => {
|
|
212
|
+
// if this fails, the receiver already dropped, so we don't need to do anything
|
|
213
|
+
let _ = tx.send(result);
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
};
|
|
217
|
+
// send the wrapped future to the background
|
|
218
|
+
send_to_background_runtime(future);
|
|
219
|
+
|
|
220
|
+
BackgroundResponseFuture::new(rx)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// `BackgroundProcessor` response future
|
|
225
|
+
pin_project! {
|
|
226
|
+
#[derive(Debug)]
|
|
227
|
+
pub struct BackgroundResponseFuture<S> {
|
|
228
|
+
#[pin]
|
|
229
|
+
rx: tokio::sync::oneshot::Receiver<Result<S, BoxError>>,
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
impl<S> BackgroundResponseFuture<S> {
|
|
234
|
+
pub(crate) fn new(rx: tokio::sync::oneshot::Receiver<Result<S, BoxError>>) -> Self {
|
|
235
|
+
BackgroundResponseFuture { rx }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
impl<S> Future for BackgroundResponseFuture<S>
|
|
240
|
+
where
|
|
241
|
+
S: Send + 'static,
|
|
242
|
+
{
|
|
243
|
+
type Output = Result<S, BoxError>;
|
|
244
|
+
|
|
245
|
+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
246
|
+
let this = self.project();
|
|
247
|
+
|
|
248
|
+
// now poll on the receiver end of the oneshot to get the result
|
|
249
|
+
match this.rx.poll(cx) {
|
|
250
|
+
Poll::Ready(v) => match v {
|
|
251
|
+
Ok(v) => Poll::Ready(v),
|
|
252
|
+
Err(err) => Poll::Ready(Err(Box::new(err) as BoxError)),
|
|
253
|
+
},
|
|
254
|
+
Poll::Pending => Poll::Pending,
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
use wreq::{
|
|
2
|
+
Client, Emulation,
|
|
3
|
+
header::{self, HeaderMap, HeaderValue, OrigHeaderMap},
|
|
4
|
+
http2::{Http2Options, PseudoId, PseudoOrder},
|
|
5
|
+
tls::{AlpnProtocol, TlsOptions, TlsVersion},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
macro_rules! join {
|
|
9
|
+
($sep:expr, $first:expr $(, $rest:expr)*) => {
|
|
10
|
+
concat!($first $(, $sep, $rest)*)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[tokio::main]
|
|
15
|
+
async fn main() -> wreq::Result<()> {
|
|
16
|
+
tracing_subscriber::fmt()
|
|
17
|
+
.with_max_level(tracing::Level::TRACE)
|
|
18
|
+
.init();
|
|
19
|
+
|
|
20
|
+
// TLS options config
|
|
21
|
+
let tls = TlsOptions::builder()
|
|
22
|
+
.enable_ocsp_stapling(true)
|
|
23
|
+
.curves_list(join!(":", "X25519", "P-256", "P-384"))
|
|
24
|
+
.cipher_list(join!(
|
|
25
|
+
":",
|
|
26
|
+
"TLS_AES_128_GCM_SHA256",
|
|
27
|
+
"TLS_AES_256_GCM_SHA384",
|
|
28
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
29
|
+
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
|
30
|
+
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
|
31
|
+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
32
|
+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
33
|
+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
34
|
+
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
|
|
35
|
+
))
|
|
36
|
+
.sigalgs_list(join!(
|
|
37
|
+
":",
|
|
38
|
+
"ecdsa_secp256r1_sha256",
|
|
39
|
+
"rsa_pss_rsae_sha256",
|
|
40
|
+
"rsa_pkcs1_sha256",
|
|
41
|
+
"ecdsa_secp384r1_sha384",
|
|
42
|
+
"rsa_pss_rsae_sha384",
|
|
43
|
+
"rsa_pkcs1_sha384",
|
|
44
|
+
"rsa_pss_rsae_sha512",
|
|
45
|
+
"rsa_pkcs1_sha512",
|
|
46
|
+
"rsa_pkcs1_sha1"
|
|
47
|
+
))
|
|
48
|
+
.alpn_protocols([AlpnProtocol::HTTP2, AlpnProtocol::HTTP1])
|
|
49
|
+
.min_tls_version(TlsVersion::TLS_1_2)
|
|
50
|
+
.max_tls_version(TlsVersion::TLS_1_3)
|
|
51
|
+
.build();
|
|
52
|
+
|
|
53
|
+
// HTTP/2 options config
|
|
54
|
+
let http2 = Http2Options::builder()
|
|
55
|
+
.initial_stream_id(3)
|
|
56
|
+
.initial_window_size(16777216)
|
|
57
|
+
.initial_connection_window_size(16711681 + 65535)
|
|
58
|
+
.headers_pseudo_order(
|
|
59
|
+
PseudoOrder::builder()
|
|
60
|
+
.extend([
|
|
61
|
+
PseudoId::Method,
|
|
62
|
+
PseudoId::Path,
|
|
63
|
+
PseudoId::Authority,
|
|
64
|
+
PseudoId::Scheme,
|
|
65
|
+
])
|
|
66
|
+
.build(),
|
|
67
|
+
)
|
|
68
|
+
.build();
|
|
69
|
+
|
|
70
|
+
// Default headers
|
|
71
|
+
let headers = {
|
|
72
|
+
let mut headers = HeaderMap::new();
|
|
73
|
+
headers.insert(header::USER_AGENT, HeaderValue::from_static("TwitterAndroid/10.89.0-release.0 (310890000-r-0) G011A/9 (google;G011A;google;G011A;0;;1;2016)"));
|
|
74
|
+
headers.insert(header::ACCEPT_LANGUAGE, HeaderValue::from_static("en-US"));
|
|
75
|
+
headers.insert(
|
|
76
|
+
header::ACCEPT_ENCODING,
|
|
77
|
+
HeaderValue::from_static("br, gzip, deflate"),
|
|
78
|
+
);
|
|
79
|
+
headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
|
|
80
|
+
headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-store"));
|
|
81
|
+
headers.insert(
|
|
82
|
+
header::COOKIE,
|
|
83
|
+
HeaderValue::from_static("ct0=YOUR_CT0_VALUE;"),
|
|
84
|
+
);
|
|
85
|
+
headers
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// The headers keep the original case and order
|
|
89
|
+
let orig_headers = {
|
|
90
|
+
let mut orig_headers = OrigHeaderMap::new();
|
|
91
|
+
orig_headers.insert("cookie");
|
|
92
|
+
orig_headers.insert("content-length");
|
|
93
|
+
orig_headers.insert("User-Agent");
|
|
94
|
+
orig_headers.insert("Accept-Language");
|
|
95
|
+
orig_headers.insert("Accept-Encoding");
|
|
96
|
+
orig_headers
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// This provider encapsulates TLS, HTTP/1, HTTP/2, default headers, and original headers
|
|
100
|
+
let emulation = Emulation::builder()
|
|
101
|
+
.tls_options(tls)
|
|
102
|
+
.http2_options(http2)
|
|
103
|
+
.headers(headers)
|
|
104
|
+
.orig_headers(orig_headers)
|
|
105
|
+
.build();
|
|
106
|
+
|
|
107
|
+
// Build a client with emulation config
|
|
108
|
+
let client = Client::builder()
|
|
109
|
+
.emulation(emulation)
|
|
110
|
+
.cert_verification(false)
|
|
111
|
+
.build()?;
|
|
112
|
+
|
|
113
|
+
// Use the API you're already familiar with
|
|
114
|
+
let resp = client.get("https://tls.browserleaks.com/").send().await?;
|
|
115
|
+
println!("{}", resp.text().await?);
|
|
116
|
+
|
|
117
|
+
Ok(())
|
|
118
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Short example of a POST request with form data.
|
|
2
|
+
//
|
|
3
|
+
// This is using the `tokio` runtime. You'll need the following dependency:
|
|
4
|
+
//
|
|
5
|
+
// `tokio = { version = "1", features = ["full"] }`
|
|
6
|
+
#[tokio::main]
|
|
7
|
+
async fn main() {
|
|
8
|
+
let response = wreq::post("http://www.baidu.com")
|
|
9
|
+
.form(&[("one", "1")])
|
|
10
|
+
.send()
|
|
11
|
+
.await
|
|
12
|
+
.expect("send");
|
|
13
|
+
println!("Response status {}", response.status());
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
use futures_util::{SinkExt, StreamExt, TryStreamExt};
|
|
2
|
+
use wreq::{header, ws::message::Message};
|
|
3
|
+
|
|
4
|
+
#[tokio::main]
|
|
5
|
+
async fn main() -> wreq::Result<()> {
|
|
6
|
+
// Use the API you're already familiar with
|
|
7
|
+
let resp = wreq::websocket("wss://echo.websocket.org")
|
|
8
|
+
.header(header::USER_AGENT, env!("CARGO_PKG_NAME"))
|
|
9
|
+
.read_buffer_size(1024 * 1024)
|
|
10
|
+
.send()
|
|
11
|
+
.await?;
|
|
12
|
+
|
|
13
|
+
assert_eq!(resp.version(), http::Version::HTTP_11);
|
|
14
|
+
|
|
15
|
+
let websocket = resp.into_websocket().await?;
|
|
16
|
+
if let Some(protocol) = websocket.protocol() {
|
|
17
|
+
println!("WebSocket subprotocol: {:?}", protocol);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let (mut tx, mut rx) = websocket.split();
|
|
21
|
+
|
|
22
|
+
tokio::spawn(async move {
|
|
23
|
+
for i in 1..11 {
|
|
24
|
+
if let Err(err) = tx.send(Message::text(format!("Hello, World! {i}"))).await {
|
|
25
|
+
eprintln!("failed to send message: {err}");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
while let Some(message) = rx.try_next().await? {
|
|
31
|
+
if let Message::Text(text) = message {
|
|
32
|
+
println!("received: {text}");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Ok(())
|
|
37
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Run websocket server
|
|
2
|
+
//!
|
|
3
|
+
//! ```not_rust
|
|
4
|
+
//! git clone https://github.com/tokio-rs/axum && cd axum
|
|
5
|
+
//! cargo run -p example-websockets-http2
|
|
6
|
+
//! ```
|
|
7
|
+
|
|
8
|
+
use futures_util::{SinkExt, StreamExt, TryStreamExt};
|
|
9
|
+
use wreq::{header, ws::message::Message};
|
|
10
|
+
|
|
11
|
+
#[tokio::main]
|
|
12
|
+
async fn main() -> wreq::Result<()> {
|
|
13
|
+
// Use the API you're already familiar with
|
|
14
|
+
let resp = wreq::websocket("wss://127.0.0.1:3000/ws")
|
|
15
|
+
.force_http2()
|
|
16
|
+
.header(header::USER_AGENT, env!("CARGO_PKG_NAME"))
|
|
17
|
+
.read_buffer_size(1024 * 1024)
|
|
18
|
+
.send()
|
|
19
|
+
.await?;
|
|
20
|
+
|
|
21
|
+
assert_eq!(resp.version(), http::Version::HTTP_2);
|
|
22
|
+
|
|
23
|
+
let websocket = resp.into_websocket().await?;
|
|
24
|
+
if let Some(protocol) = websocket.protocol() {
|
|
25
|
+
println!("WebSocket subprotocol: {:?}", protocol);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let (mut tx, mut rx) = websocket.split();
|
|
29
|
+
|
|
30
|
+
tokio::spawn(async move {
|
|
31
|
+
for i in 1..11 {
|
|
32
|
+
if let Err(err) = tx.send(Message::text(format!("Hello, World! #{i}"))).await {
|
|
33
|
+
eprintln!("failed to send message: {err}");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
while let Some(message) = rx.try_next().await? {
|
|
39
|
+
if let Message::Text(text) = message {
|
|
40
|
+
println!("received: {text}");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Ok(())
|
|
45
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//! This example illustrates the way to send and receive arbitrary JSON.
|
|
2
|
+
//!
|
|
3
|
+
//! This is useful for some ad-hoc experiments and situations when you don't
|
|
4
|
+
//! really care about the structure of the JSON and just need to display it or
|
|
5
|
+
//! process it at runtime.
|
|
6
|
+
|
|
7
|
+
// This is using the `tokio` runtime. You'll need the following dependency:
|
|
8
|
+
//
|
|
9
|
+
// `tokio = { version = "1", features = ["full"] }`
|
|
10
|
+
#[tokio::main]
|
|
11
|
+
async fn main() -> wreq::Result<()> {
|
|
12
|
+
let echo_json: serde_json::Value = wreq::post("https://jsonplaceholder.typicode.com/posts")
|
|
13
|
+
.json(&serde_json::json!({
|
|
14
|
+
"title": "wreq.rs",
|
|
15
|
+
"body": "https://docs.rs/wreq",
|
|
16
|
+
"userId": 1
|
|
17
|
+
}))
|
|
18
|
+
.send()
|
|
19
|
+
.await?
|
|
20
|
+
.json()
|
|
21
|
+
.await?;
|
|
22
|
+
|
|
23
|
+
println!("{echo_json:#?}");
|
|
24
|
+
// Object(
|
|
25
|
+
// {
|
|
26
|
+
// "body": String(
|
|
27
|
+
// "https://docs.rs/wreq"
|
|
28
|
+
// ),
|
|
29
|
+
// "id": Number(
|
|
30
|
+
// 101
|
|
31
|
+
// ),
|
|
32
|
+
// "title": String(
|
|
33
|
+
// "wreq.rs"
|
|
34
|
+
// ),
|
|
35
|
+
// "userId": Number(
|
|
36
|
+
// 1
|
|
37
|
+
// )
|
|
38
|
+
// }
|
|
39
|
+
// )
|
|
40
|
+
Ok(())
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//! This example illustrates the way to send and receive statically typed JSON.
|
|
2
|
+
//!
|
|
3
|
+
//! In contrast to the arbitrary JSON example, this brings up the full power of
|
|
4
|
+
//! Rust compile-time type system guaranties though it requires a little bit
|
|
5
|
+
//! more code.
|
|
6
|
+
|
|
7
|
+
// These require the `serde` dependency.
|
|
8
|
+
use serde::{Deserialize, Serialize};
|
|
9
|
+
|
|
10
|
+
#[derive(Debug, Serialize, Deserialize)]
|
|
11
|
+
struct Post {
|
|
12
|
+
id: Option<i32>,
|
|
13
|
+
title: String,
|
|
14
|
+
body: String,
|
|
15
|
+
#[serde(rename = "userId")]
|
|
16
|
+
user_id: i32,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// This is using the `tokio` runtime. You'll need the following dependency:
|
|
20
|
+
//
|
|
21
|
+
// `tokio = { version = "1", features = ["full"] }`
|
|
22
|
+
#[tokio::main]
|
|
23
|
+
async fn main() -> wreq::Result<()> {
|
|
24
|
+
let new_post = Post {
|
|
25
|
+
id: None,
|
|
26
|
+
title: "wreq.rs".into(),
|
|
27
|
+
body: "https://docs.rs/wreq".into(),
|
|
28
|
+
user_id: 1,
|
|
29
|
+
};
|
|
30
|
+
let new_post: Post = wreq::post("https://jsonplaceholder.typicode.com/posts")
|
|
31
|
+
.json(&new_post)
|
|
32
|
+
.send()
|
|
33
|
+
.await?
|
|
34
|
+
.json()
|
|
35
|
+
.await?;
|
|
36
|
+
|
|
37
|
+
println!("{new_post:#?}");
|
|
38
|
+
// Post {
|
|
39
|
+
// id: Some(
|
|
40
|
+
// 101
|
|
41
|
+
// ),
|
|
42
|
+
// title: "wreq.rs",
|
|
43
|
+
// body: "https://docs.rs/wreq",
|
|
44
|
+
// user_id: 1
|
|
45
|
+
// }
|
|
46
|
+
Ok(())
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use wreq::tls::KeyLog;
|
|
2
|
+
|
|
3
|
+
#[tokio::main]
|
|
4
|
+
async fn main() -> wreq::Result<()> {
|
|
5
|
+
// Build a client
|
|
6
|
+
let client = wreq::Client::builder()
|
|
7
|
+
.keylog(KeyLog::from_file("keylog.txt"))
|
|
8
|
+
.cert_verification(false)
|
|
9
|
+
.build()?;
|
|
10
|
+
|
|
11
|
+
// Use the API you're already familiar with
|
|
12
|
+
let resp = client.get("https://yande.re/post.json").send().await?;
|
|
13
|
+
println!("{}", resp.text().await?);
|
|
14
|
+
|
|
15
|
+
Ok(())
|
|
16
|
+
}
|