Skip to content

Commit b46daa9

Browse files
authored
feat: Add HTTP/2 support for WebSocket (#373)
* feat: Add `HTTP/2` support for `WebSocket` * update examples * update deps
1 parent 44ec8c6 commit b46daa9

21 files changed

+232
-69
lines changed

Cargo.toml

+10-2
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ hyper-util = { version = "0.1.10", features = [
176176
"server-auto",
177177
"tokio",
178178
] }
179-
env_logger = "0.11.6"
180179
serde = { version = "1.0", features = ["derive"] }
181180
libflate = "2.0.0"
182181
zstd = { version = "0.13" }
@@ -190,6 +189,10 @@ tower = { version = "0.5.2", default-features = false, features = ["limit"] }
190189
num_cpus = "1.0"
191190
libc = "0.2"
192191

192+
env_logger = "0.11.6"
193+
tracing = "0.1"
194+
tracing-subscriber = "0.3.19"
195+
193196
[lib]
194197
doctest = false
195198

@@ -309,7 +312,7 @@ required-features = ["native-roots", "webpki-roots"]
309312
[[example]]
310313
name = "websocket"
311314
path = "examples/websocket.rs"
312-
required-features = ["websocket", "futures-util/std"]
315+
required-features = ["websocket", "http2-tracing", "futures-util/std"]
313316

314317
[[example]]
315318
name = "client_chain"
@@ -358,3 +361,8 @@ required-features = ["full"]
358361
name = "request_with_interface"
359362
path = "examples/request_with_interface.rs"
360363
required-features = ["full"]
364+
365+
[[example]]
366+
name = "http2_websocket"
367+
path = "examples/http2_websocket.rs"
368+
required-features = ["websocket", "http2-tracing", "futures-util/std"]

examples/base_url.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{Client, Impersonate};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("debug"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Edge131
810
let mut client = Client::builder()

examples/http2_websocket.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use futures_util::{SinkExt, StreamExt, TryStreamExt};
2+
use http::header;
3+
use rquest::{Client, Impersonate, Message, RequestBuilder};
4+
use std::time::Duration;
5+
6+
#[tokio::main]
7+
async fn main() -> Result<(), rquest::Error> {
8+
tracing_subscriber::fmt()
9+
.with_max_level(tracing::Level::TRACE)
10+
.init();
11+
12+
// Build a client to impersonate Firefox133
13+
let client = Client::builder()
14+
.impersonate(Impersonate::Firefox133)
15+
.danger_accept_invalid_certs(true)
16+
.build()?;
17+
18+
// Use the API you're already familiar with
19+
let websocket = client
20+
.websocket("wss://127.0.0.1:3000/ws")
21+
.configure_request(configure_request)
22+
.http2_only()
23+
.send()
24+
.await?;
25+
26+
assert_eq!(websocket.version(), http::Version::HTTP_2);
27+
28+
let (mut tx, mut rx) = websocket.into_websocket().await?.split();
29+
30+
tokio::spawn(async move {
31+
for i in 1..11 {
32+
tx.send(Message::Text(format!("Hello, World! #{i}")))
33+
.await
34+
.unwrap();
35+
}
36+
});
37+
38+
while let Some(message) = rx.try_next().await? {
39+
match message {
40+
Message::Text(text) => println!("received: {text}"),
41+
_ => {}
42+
}
43+
}
44+
45+
Ok(())
46+
}
47+
48+
/// We can also set HTTP options here
49+
fn configure_request(builder: RequestBuilder) -> RequestBuilder {
50+
builder
51+
.header(header::USER_AGENT, env!("CARGO_PKG_NAME"))
52+
.timeout(Duration::from_secs(10))
53+
}

examples/impersonate_builder.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{Client, Impersonate, ImpersonateOS};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Firefox128
810
let impersonate = Impersonate::builder()

examples/impersonate_psk.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::Impersonate;
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Firefox133
810
let client = rquest::Client::builder()

examples/impersonate_settings.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ const HEADER_ORDER: &[HeaderName] = &[
112112

113113
#[tokio::main]
114114
async fn main() -> Result<(), rquest::Error> {
115-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
115+
tracing_subscriber::fmt()
116+
.with_max_level(tracing::Level::TRACE)
117+
.init();
116118

117119
// TLS settings
118120
let tls = TlsSettings::builder()

examples/request_with_cookie_store.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use url::Url;
99

1010
#[tokio::main]
1111
async fn main() -> Result<(), Box<dyn std::error::Error>> {
12-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
12+
tracing_subscriber::fmt()
13+
.with_max_level(tracing::Level::TRACE)
14+
.init();
1315

1416
let url = Url::parse("https://google.com/")?;
1517

examples/request_with_interface.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{Client, Impersonate};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Firefox128
810
let client = Client::builder()

examples/request_with_local_address.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::net::IpAddr;
33

44
#[tokio::main]
55
async fn main() -> Result<(), rquest::Error> {
6-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
6+
tracing_subscriber::fmt()
7+
.with_max_level(tracing::Level::TRACE)
8+
.init();
79

810
// Build a client to impersonate Safari18
911
let client = rquest::Client::builder()

examples/request_with_proxy.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::Impersonate;
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Firefox133
810
let client = rquest::Client::builder()

examples/request_with_redirect.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{redirect::Policy, Impersonate};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Safari18
810
let client = rquest::Client::builder()

examples/request_with_version.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use rquest::{redirect::Policy, Impersonate};
33

44
#[tokio::main]
55
async fn main() -> Result<(), rquest::Error> {
6-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
6+
tracing_subscriber::fmt()
7+
.with_max_level(tracing::Level::TRACE)
8+
.init();
79

810
// Build a client to impersonate Safari18
911
let client = rquest::Client::builder()

examples/set_proxies.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{Client, Impersonate};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Chrome130
810
let mut client = Client::builder()

examples/set_redirect.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rquest::{redirect::Policy, Impersonate};
22

33
#[tokio::main]
44
async fn main() -> Result<(), rquest::Error> {
5-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
5+
tracing_subscriber::fmt()
6+
.with_max_level(tracing::Level::TRACE)
7+
.init();
68

79
// Build a client to impersonate Safari18
810
let mut client = rquest::Client::builder()

examples/set_root_cert_store.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use std::sync::LazyLock;
44

55
#[tokio::main]
66
async fn main() -> Result<(), rquest::Error> {
7-
env_logger::init_from_env(env_logger::Env::default().default_filter_or("trace"));
7+
tracing_subscriber::fmt()
8+
.with_max_level(tracing::Level::TRACE)
9+
.init();
810
use_static_root_certs().await?;
911
use_dynamic_root_certs().await?;
1012
Ok(())

examples/websocket.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
1-
use std::time::Duration;
2-
31
use futures_util::{SinkExt, StreamExt, TryStreamExt};
42
use http::header;
53
use rquest::{Client, Impersonate, Message, RequestBuilder};
4+
use std::time::Duration;
65

76
#[tokio::main]
87
async fn main() -> Result<(), rquest::Error> {
8+
tracing_subscriber::fmt()
9+
.with_max_level(tracing::Level::TRACE)
10+
.init();
11+
912
// Build a client to impersonate Firefox133
1013
let client = Client::builder()
1114
.impersonate(Impersonate::Firefox133)
15+
.danger_accept_invalid_certs(true)
1216
.build()?;
1317

1418
// Use the API you're already familiar with
1519
let websocket = client
16-
.websocket("wss://echo.websocket.org")
20+
.websocket("wss://127.0.0.1:3000/ws")
1721
.configure_request(configure_request)
1822
.send()
19-
.await?
20-
.into_websocket()
2123
.await?;
2224

23-
let (mut tx, mut rx) = websocket.split();
25+
assert_eq!(websocket.version(), http::Version::HTTP_11);
26+
27+
let (mut tx, mut rx) = websocket.into_websocket().await?.split();
2428

2529
tokio::spawn(async move {
2630
for i in 1..11 {
@@ -43,7 +47,6 @@ async fn main() -> Result<(), rquest::Error> {
4347
/// We can also set HTTP options here
4448
fn configure_request(builder: RequestBuilder) -> RequestBuilder {
4549
builder
46-
.proxy("http://127.0.0.1:6152")
4750
.header(header::USER_AGENT, env!("CARGO_PKG_NAME"))
4851
.timeout(Duration::from_secs(10))
4952
}

src/client/http.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ impl Client {
12981298
redirect,
12991299
_cookie_store,
13001300
network_scheme,
1301+
protocal,
13011302
) = req.pieces();
13021303

13031304
if url.scheme() != "http" && url.scheme() != "https" {
@@ -1357,12 +1358,13 @@ impl Client {
13571358

13581359
let in_flight = {
13591360
let res = InnerRequest::builder()
1360-
.network_scheme(network_scheme.clone())
13611361
.uri(uri)
13621362
.method(method.clone())
13631363
.version(version)
13641364
.headers(headers.clone())
13651365
.headers_order(self.inner.headers_order.as_deref())
1366+
.network_scheme(network_scheme.clone())
1367+
.extension(protocal)
13661368
.body(body);
13671369

13681370
match res {
@@ -1944,12 +1946,12 @@ impl PendingRequest {
19441946

19451947
*self.as_mut().in_flight().get_mut() = {
19461948
let res = InnerRequest::builder()
1947-
.network_scheme(self.network_scheme.clone())
19481949
.uri(uri)
19491950
.method(self.method.clone())
19501951
.version(self.version)
19511952
.headers(self.headers.clone())
19521953
.headers_order(self.client.headers_order.as_deref())
1954+
.network_scheme(self.network_scheme.clone())
19531955
.body(body);
19541956

19551957
if let Ok(req) = res {
@@ -2203,12 +2205,12 @@ impl Future for PendingRequest {
22032205

22042206
*self.as_mut().in_flight().get_mut() = {
22052207
let req = InnerRequest::builder()
2206-
.network_scheme(self.network_scheme.clone())
22072208
.uri(uri)
22082209
.method(self.method.clone())
22092210
.version(self.version)
22102211
.headers(headers.clone())
22112212
.headers_order(self.client.headers_order.as_deref())
2213+
.network_scheme(self.network_scheme.clone())
22122214
.body(body)?;
22132215

22142216
std::mem::swap(self.as_mut().headers(), &mut headers);

src/client/request.rs

+12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type PiecesWithCookieStore = (
3232
Option<redirect::Policy>,
3333
(),
3434
NetworkScheme,
35+
Option<hyper2::ext::Protocol>,
3536
);
3637

3738
#[cfg(feature = "cookies")]
@@ -46,6 +47,7 @@ type PiecesWithCookieStore = (
4647
Option<redirect::Policy>,
4748
Option<Arc<dyn cookie::CookieStore>>,
4849
NetworkScheme,
50+
Option<hyper2::ext::Protocol>,
4951
);
5052

5153
/// A request which can be executed with `Client::execute()`.
@@ -61,6 +63,7 @@ pub struct Request {
6163
#[cfg(feature = "cookies")]
6264
cookie_store: Option<Arc<dyn cookie::CookieStore>>,
6365
network_scheme: NetworkSchemeBuilder,
66+
protocol: Option<hyper2::ext::Protocol>,
6467
}
6568

6669
/// A builder to construct the properties of a `Request`.
@@ -88,6 +91,7 @@ impl Request {
8891
#[cfg(feature = "cookies")]
8992
cookie_store: None,
9093
network_scheme: NetworkScheme::builder(),
94+
protocol: None,
9195
}
9296
}
9397

@@ -194,6 +198,12 @@ impl Request {
194198
&mut self.version
195199
}
196200

201+
/// Set the mutable reference to the protocol.
202+
#[inline]
203+
pub fn protocol_mut(&mut self) -> &mut Option<hyper2::ext::Protocol> {
204+
&mut self.protocol
205+
}
206+
197207
/// Attempt to clone the request.
198208
///
199209
/// `None` is returned if the request can not be cloned, i.e. if the body is a stream.
@@ -232,6 +242,7 @@ impl Request {
232242
#[cfg(not(feature = "cookies"))]
233243
(),
234244
self.network_scheme.build(),
245+
self.protocol,
235246
)
236247
}
237248
}
@@ -824,6 +835,7 @@ where
824835
#[cfg(feature = "cookies")]
825836
cookie_store: None,
826837
network_scheme: NetworkScheme::builder(),
838+
protocol: None,
827839
})
828840
}
829841
}

0 commit comments

Comments
 (0)