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,149 @@
|
|
|
1
|
+
use std::{collections::VecDeque, io::IoSlice};
|
|
2
|
+
|
|
3
|
+
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
|
4
|
+
|
|
5
|
+
pub(crate) struct BufList<T> {
|
|
6
|
+
bufs: VecDeque<T>,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
impl<T: Buf> BufList<T> {
|
|
10
|
+
pub(crate) fn new() -> BufList<T> {
|
|
11
|
+
BufList {
|
|
12
|
+
bufs: VecDeque::new(),
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[inline]
|
|
17
|
+
pub(crate) fn push(&mut self, buf: T) {
|
|
18
|
+
debug_assert!(buf.has_remaining());
|
|
19
|
+
self.bufs.push_back(buf);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[inline]
|
|
23
|
+
pub(crate) fn bufs_cnt(&self) -> usize {
|
|
24
|
+
self.bufs.len()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl<T: Buf> Buf for BufList<T> {
|
|
29
|
+
#[inline]
|
|
30
|
+
fn remaining(&self) -> usize {
|
|
31
|
+
self.bufs.iter().map(|buf| buf.remaining()).sum()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[inline]
|
|
35
|
+
fn chunk(&self) -> &[u8] {
|
|
36
|
+
self.bufs.front().map(Buf::chunk).unwrap_or_default()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[inline]
|
|
40
|
+
fn advance(&mut self, mut cnt: usize) {
|
|
41
|
+
while cnt > 0 {
|
|
42
|
+
{
|
|
43
|
+
let front = &mut self.bufs[0];
|
|
44
|
+
let rem = front.remaining();
|
|
45
|
+
if rem > cnt {
|
|
46
|
+
front.advance(cnt);
|
|
47
|
+
return;
|
|
48
|
+
} else {
|
|
49
|
+
front.advance(rem);
|
|
50
|
+
cnt -= rem;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
self.bufs.pop_front();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[inline]
|
|
58
|
+
fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
|
|
59
|
+
if dst.is_empty() {
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
let mut vecs = 0;
|
|
63
|
+
for buf in &self.bufs {
|
|
64
|
+
vecs += buf.chunks_vectored(&mut dst[vecs..]);
|
|
65
|
+
if vecs == dst.len() {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
vecs
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#[inline]
|
|
73
|
+
fn copy_to_bytes(&mut self, len: usize) -> Bytes {
|
|
74
|
+
// Our inner buffer may have an optimized version of copy_to_bytes, and if the whole
|
|
75
|
+
// request can be fulfilled by the front buffer, we can take advantage.
|
|
76
|
+
match self.bufs.front_mut() {
|
|
77
|
+
Some(front) if front.remaining() == len => {
|
|
78
|
+
let b = front.copy_to_bytes(len);
|
|
79
|
+
self.bufs.pop_front();
|
|
80
|
+
b
|
|
81
|
+
}
|
|
82
|
+
Some(front) if front.remaining() > len => front.copy_to_bytes(len),
|
|
83
|
+
_ => {
|
|
84
|
+
assert!(len <= self.remaining(), "`len` greater than remaining");
|
|
85
|
+
let mut bm = BytesMut::with_capacity(len);
|
|
86
|
+
bm.put(self.take(len));
|
|
87
|
+
bm.freeze()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#[cfg(test)]
|
|
94
|
+
mod tests {
|
|
95
|
+
use std::ptr;
|
|
96
|
+
|
|
97
|
+
use super::*;
|
|
98
|
+
|
|
99
|
+
fn hello_world_buf() -> BufList<Bytes> {
|
|
100
|
+
BufList {
|
|
101
|
+
bufs: vec![Bytes::from("Hello"), Bytes::from(" "), Bytes::from("World")].into(),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[test]
|
|
106
|
+
fn to_bytes_shorter() {
|
|
107
|
+
let mut bufs = hello_world_buf();
|
|
108
|
+
let old_ptr = bufs.chunk().as_ptr();
|
|
109
|
+
let start = bufs.copy_to_bytes(4);
|
|
110
|
+
assert_eq!(start, "Hell");
|
|
111
|
+
assert!(ptr::eq(old_ptr, start.as_ptr()));
|
|
112
|
+
assert_eq!(bufs.chunk(), b"o");
|
|
113
|
+
assert!(ptr::eq(old_ptr.wrapping_add(4), bufs.chunk().as_ptr()));
|
|
114
|
+
assert_eq!(bufs.remaining(), 7);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#[test]
|
|
118
|
+
fn to_bytes_eq() {
|
|
119
|
+
let mut bufs = hello_world_buf();
|
|
120
|
+
let old_ptr = bufs.chunk().as_ptr();
|
|
121
|
+
let start = bufs.copy_to_bytes(5);
|
|
122
|
+
assert_eq!(start, "Hello");
|
|
123
|
+
assert!(ptr::eq(old_ptr, start.as_ptr()));
|
|
124
|
+
assert_eq!(bufs.chunk(), b" ");
|
|
125
|
+
assert_eq!(bufs.remaining(), 6);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn to_bytes_longer() {
|
|
130
|
+
let mut bufs = hello_world_buf();
|
|
131
|
+
let start = bufs.copy_to_bytes(7);
|
|
132
|
+
assert_eq!(start, "Hello W");
|
|
133
|
+
assert_eq!(bufs.remaining(), 4);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[test]
|
|
137
|
+
fn one_long_buf_to_bytes() {
|
|
138
|
+
let mut buf = BufList::new();
|
|
139
|
+
buf.push(b"Hello World" as &[_]);
|
|
140
|
+
assert_eq!(buf.copy_to_bytes(5), "Hello");
|
|
141
|
+
assert_eq!(buf.chunk(), b" World");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[test]
|
|
145
|
+
#[should_panic(expected = "`len` greater than remaining")]
|
|
146
|
+
fn buf_to_bytes_too_many() {
|
|
147
|
+
hello_world_buf().copy_to_bytes(42);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
cmp, io,
|
|
3
|
+
pin::Pin,
|
|
4
|
+
task::{Context, Poll},
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
use bytes::{Buf, Bytes};
|
|
8
|
+
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
|
9
|
+
|
|
10
|
+
/// Combine a buffer with an IO, rewinding reads to use the buffer.
|
|
11
|
+
#[derive(Debug)]
|
|
12
|
+
pub(crate) struct Rewind<T> {
|
|
13
|
+
pre: Option<Bytes>,
|
|
14
|
+
inner: T,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl<T> Rewind<T> {
|
|
18
|
+
pub(crate) fn new_buffered(io: T, buf: Bytes) -> Self {
|
|
19
|
+
Rewind {
|
|
20
|
+
pre: Some(buf),
|
|
21
|
+
inner: io,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[cfg(test)]
|
|
26
|
+
pub(crate) fn rewind(&mut self, bs: Bytes) {
|
|
27
|
+
debug_assert!(self.pre.is_none());
|
|
28
|
+
self.pre = Some(bs);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl<T> AsyncRead for Rewind<T>
|
|
33
|
+
where
|
|
34
|
+
T: AsyncRead + Unpin,
|
|
35
|
+
{
|
|
36
|
+
fn poll_read(
|
|
37
|
+
mut self: Pin<&mut Self>,
|
|
38
|
+
cx: &mut Context<'_>,
|
|
39
|
+
buf: &mut ReadBuf<'_>,
|
|
40
|
+
) -> Poll<io::Result<()>> {
|
|
41
|
+
if let Some(mut prefix) = self.pre.take() {
|
|
42
|
+
// If there are no remaining bytes, let the bytes get dropped.
|
|
43
|
+
if !prefix.is_empty() {
|
|
44
|
+
let copy_len = cmp::min(prefix.len(), buf.remaining());
|
|
45
|
+
// TODO: There should be a way to do following two lines cleaner...
|
|
46
|
+
buf.put_slice(&prefix[..copy_len]);
|
|
47
|
+
prefix.advance(copy_len);
|
|
48
|
+
// Put back what's left
|
|
49
|
+
if !prefix.is_empty() {
|
|
50
|
+
self.pre = Some(prefix);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return Poll::Ready(Ok(()));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
Pin::new(&mut self.inner).poll_read(cx, buf)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl<T> AsyncWrite for Rewind<T>
|
|
61
|
+
where
|
|
62
|
+
T: AsyncWrite + Unpin,
|
|
63
|
+
{
|
|
64
|
+
fn poll_write(
|
|
65
|
+
mut self: Pin<&mut Self>,
|
|
66
|
+
cx: &mut Context<'_>,
|
|
67
|
+
buf: &[u8],
|
|
68
|
+
) -> Poll<io::Result<usize>> {
|
|
69
|
+
Pin::new(&mut self.inner).poll_write(cx, buf)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn poll_write_vectored(
|
|
73
|
+
mut self: Pin<&mut Self>,
|
|
74
|
+
cx: &mut Context<'_>,
|
|
75
|
+
bufs: &[io::IoSlice<'_>],
|
|
76
|
+
) -> Poll<io::Result<usize>> {
|
|
77
|
+
Pin::new(&mut self.inner).poll_write_vectored(cx, bufs)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
81
|
+
Pin::new(&mut self.inner).poll_flush(cx)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
85
|
+
Pin::new(&mut self.inner).poll_shutdown(cx)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn is_write_vectored(&self) -> bool {
|
|
89
|
+
self.inner.is_write_vectored()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#[cfg(test)]
|
|
94
|
+
mod tests {
|
|
95
|
+
use bytes::Bytes;
|
|
96
|
+
use tokio::io::AsyncReadExt;
|
|
97
|
+
|
|
98
|
+
use super::Rewind;
|
|
99
|
+
|
|
100
|
+
#[tokio::test]
|
|
101
|
+
async fn partial_rewind() {
|
|
102
|
+
let underlying = [104, 101, 108, 108, 111];
|
|
103
|
+
|
|
104
|
+
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
|
105
|
+
|
|
106
|
+
let mut stream = Rewind::new_buffered(mock, Bytes::new());
|
|
107
|
+
|
|
108
|
+
// Read off some bytes, ensure we filled o1
|
|
109
|
+
let mut buf = [0; 2];
|
|
110
|
+
stream.read_exact(&mut buf).await.expect("read1");
|
|
111
|
+
|
|
112
|
+
// Rewind the stream so that it is as if we never read in the first place.
|
|
113
|
+
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
|
114
|
+
|
|
115
|
+
let mut buf = [0; 5];
|
|
116
|
+
stream.read_exact(&mut buf).await.expect("read1");
|
|
117
|
+
|
|
118
|
+
// At this point we should have read everything that was in the MockStream
|
|
119
|
+
assert_eq!(&buf, &underlying);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[tokio::test]
|
|
123
|
+
async fn full_rewind() {
|
|
124
|
+
let underlying = [104, 101, 108, 108, 111];
|
|
125
|
+
|
|
126
|
+
let mock = tokio_test::io::Builder::new().read(&underlying).build();
|
|
127
|
+
|
|
128
|
+
let mut stream = Rewind::new_buffered(mock, Bytes::new());
|
|
129
|
+
|
|
130
|
+
let mut buf = [0; 5];
|
|
131
|
+
stream.read_exact(&mut buf).await.expect("read1");
|
|
132
|
+
|
|
133
|
+
// Rewind the stream so that it is as if we never read in the first place.
|
|
134
|
+
stream.rewind(Bytes::copy_from_slice(&buf[..]));
|
|
135
|
+
|
|
136
|
+
let mut buf = [0; 5];
|
|
137
|
+
stream.read_exact(&mut buf).await.expect("read1");
|
|
138
|
+
|
|
139
|
+
assert_eq!(&buf, &underlying);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//! An SPSC broadcast channel.
|
|
2
|
+
//!
|
|
3
|
+
//! - The value can only be a `usize`.
|
|
4
|
+
//! - The consumer is only notified if the value is different.
|
|
5
|
+
//! - The value `0` is reserved for closed.
|
|
6
|
+
|
|
7
|
+
use std::{
|
|
8
|
+
sync::{
|
|
9
|
+
Arc,
|
|
10
|
+
atomic::{AtomicUsize, Ordering},
|
|
11
|
+
},
|
|
12
|
+
task,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
use futures_util::task::AtomicWaker;
|
|
16
|
+
|
|
17
|
+
type Value = usize;
|
|
18
|
+
|
|
19
|
+
pub(crate) const CLOSED: usize = 0;
|
|
20
|
+
|
|
21
|
+
pub(crate) fn channel(initial: Value) -> (Sender, Receiver) {
|
|
22
|
+
debug_assert!(
|
|
23
|
+
initial != CLOSED,
|
|
24
|
+
"watch::channel initial state of 0 is reserved"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
let shared = Arc::new(Shared {
|
|
28
|
+
value: AtomicUsize::new(initial),
|
|
29
|
+
waker: AtomicWaker::new(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
(
|
|
33
|
+
Sender {
|
|
34
|
+
shared: shared.clone(),
|
|
35
|
+
},
|
|
36
|
+
Receiver { shared },
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub(crate) struct Sender {
|
|
41
|
+
shared: Arc<Shared>,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pub(crate) struct Receiver {
|
|
45
|
+
shared: Arc<Shared>,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
struct Shared {
|
|
49
|
+
value: AtomicUsize,
|
|
50
|
+
waker: AtomicWaker,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
impl Sender {
|
|
54
|
+
pub(crate) fn send(&mut self, value: Value) {
|
|
55
|
+
if self.shared.value.swap(value, Ordering::SeqCst) != value {
|
|
56
|
+
self.shared.waker.wake();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
impl Drop for Sender {
|
|
62
|
+
fn drop(&mut self) {
|
|
63
|
+
self.send(CLOSED);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl Receiver {
|
|
68
|
+
pub(crate) fn load(&mut self, cx: &mut task::Context<'_>) -> Value {
|
|
69
|
+
self.shared.waker.register(cx.waker());
|
|
70
|
+
self.shared.value.load(Ordering::SeqCst)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pub(crate) fn peek(&self) -> Value {
|
|
74
|
+
self.shared.value.load(Ordering::Relaxed)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
//! HTTP/1 client connections
|
|
2
|
+
|
|
3
|
+
use std::{
|
|
4
|
+
fmt,
|
|
5
|
+
future::Future,
|
|
6
|
+
pin::Pin,
|
|
7
|
+
task::{Context, Poll, ready},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
use bytes::Bytes;
|
|
11
|
+
use http::{Request, Response};
|
|
12
|
+
use http_body::Body;
|
|
13
|
+
use httparse::ParserConfig;
|
|
14
|
+
use tokio::io::{AsyncRead, AsyncWrite};
|
|
15
|
+
|
|
16
|
+
use crate::client::core::{
|
|
17
|
+
Error, Result,
|
|
18
|
+
body::Incoming as IncomingBody,
|
|
19
|
+
dispatch::{self, TrySendError},
|
|
20
|
+
error::BoxError,
|
|
21
|
+
http1::Http1Options,
|
|
22
|
+
proto,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type Dispatcher<T, B> =
|
|
26
|
+
proto::dispatch::Dispatcher<proto::dispatch::Client<B>, B, T, proto::h1::ClientTransaction>;
|
|
27
|
+
|
|
28
|
+
/// The sender side of an established connection.
|
|
29
|
+
pub struct SendRequest<B> {
|
|
30
|
+
dispatch: dispatch::Sender<Request<B>, Response<IncomingBody>>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Deconstructed parts of a `Connection`.
|
|
34
|
+
///
|
|
35
|
+
/// This allows taking apart a `Connection` at a later time, in order to
|
|
36
|
+
/// reclaim the IO object, and additional related pieces.
|
|
37
|
+
#[derive(Debug)]
|
|
38
|
+
#[non_exhaustive]
|
|
39
|
+
pub struct Parts<T> {
|
|
40
|
+
/// The original IO object used in the handshake.
|
|
41
|
+
pub io: T,
|
|
42
|
+
/// A buffer of bytes that have been read but not processed as HTTP.
|
|
43
|
+
///
|
|
44
|
+
/// For instance, if the `Connection` is used for an HTTP upgrade request,
|
|
45
|
+
/// it is possible the server sent back the first bytes of the new protocol
|
|
46
|
+
/// along with the response upgrade.
|
|
47
|
+
///
|
|
48
|
+
/// You will want to check for any existing bytes if you plan to continue
|
|
49
|
+
/// communicating on the IO object.
|
|
50
|
+
pub read_buf: Bytes,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// A future that processes all HTTP state for the IO object.
|
|
54
|
+
///
|
|
55
|
+
/// In most cases, this should just be spawned into an executor, so that it
|
|
56
|
+
/// can process incoming and outgoing messages, notice hangups, and the like.
|
|
57
|
+
#[must_use = "futures do nothing unless polled"]
|
|
58
|
+
pub struct Connection<T, B>
|
|
59
|
+
where
|
|
60
|
+
T: AsyncRead + AsyncWrite,
|
|
61
|
+
B: Body + 'static,
|
|
62
|
+
{
|
|
63
|
+
inner: Dispatcher<T, B>,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
impl<T, B> Connection<T, B>
|
|
67
|
+
where
|
|
68
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
69
|
+
B: Body + 'static,
|
|
70
|
+
B::Error: Into<BoxError>,
|
|
71
|
+
{
|
|
72
|
+
/// Return the inner IO object, and additional information.
|
|
73
|
+
///
|
|
74
|
+
/// Only works for HTTP/1 connections. HTTP/2 connections will panic.
|
|
75
|
+
pub fn into_parts(self) -> Parts<T> {
|
|
76
|
+
let (io, read_buf, _) = self.inner.into_inner();
|
|
77
|
+
Parts { io, read_buf }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// A builder to configure an HTTP connection.
|
|
82
|
+
///
|
|
83
|
+
/// After setting options, the builder is used to create a handshake future.
|
|
84
|
+
///
|
|
85
|
+
/// **Note**: The default values of options are *not considered stable*. They
|
|
86
|
+
/// are subject to change at any time.
|
|
87
|
+
#[derive(Clone, Debug)]
|
|
88
|
+
pub struct Builder {
|
|
89
|
+
opts: Http1Options,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ===== impl SendRequest
|
|
93
|
+
|
|
94
|
+
impl<B> SendRequest<B> {
|
|
95
|
+
/// Polls to determine whether this sender can be used yet for a request.
|
|
96
|
+
///
|
|
97
|
+
/// If the associated connection is closed, this returns an Error.
|
|
98
|
+
pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
99
|
+
self.dispatch.poll_ready(cx)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Waits until the dispatcher is ready
|
|
103
|
+
///
|
|
104
|
+
/// If the associated connection is closed, this returns an Error.
|
|
105
|
+
pub async fn ready(&mut self) -> Result<()> {
|
|
106
|
+
std::future::poll_fn(|cx| self.poll_ready(cx)).await
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Checks if the connection is currently ready to send a request.
|
|
110
|
+
///
|
|
111
|
+
/// # Note
|
|
112
|
+
///
|
|
113
|
+
/// This is mostly a hint. Due to inherent latency of networks, it is
|
|
114
|
+
/// possible that even after checking this is ready, sending a request
|
|
115
|
+
/// may still fail because the connection was closed in the meantime.
|
|
116
|
+
pub fn is_ready(&self) -> bool {
|
|
117
|
+
self.dispatch.is_ready()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl<B> SendRequest<B>
|
|
122
|
+
where
|
|
123
|
+
B: Body + 'static,
|
|
124
|
+
{
|
|
125
|
+
/// Sends a `Request` on the associated connection.
|
|
126
|
+
///
|
|
127
|
+
/// Returns a future that if successful, yields the `Response`.
|
|
128
|
+
///
|
|
129
|
+
/// # Error
|
|
130
|
+
///
|
|
131
|
+
/// If there was an error before trying to serialize the request to the
|
|
132
|
+
/// connection, the message will be returned as part of this error.
|
|
133
|
+
pub fn try_send_request(
|
|
134
|
+
&mut self,
|
|
135
|
+
req: Request<B>,
|
|
136
|
+
) -> impl Future<Output = std::result::Result<Response<IncomingBody>, TrySendError<Request<B>>>>
|
|
137
|
+
{
|
|
138
|
+
let sent = self.dispatch.try_send(req);
|
|
139
|
+
async move {
|
|
140
|
+
match sent {
|
|
141
|
+
Ok(rx) => match rx.await {
|
|
142
|
+
Ok(Ok(res)) => Ok(res),
|
|
143
|
+
Ok(Err(err)) => Err(err),
|
|
144
|
+
// this is definite bug if it happens, but it shouldn't happen!
|
|
145
|
+
Err(_) => panic!("dispatch dropped without returning error"),
|
|
146
|
+
},
|
|
147
|
+
Err(req) => {
|
|
148
|
+
debug!("connection was not ready");
|
|
149
|
+
let error = Error::new_canceled().with("connection was not ready");
|
|
150
|
+
Err(TrySendError {
|
|
151
|
+
error,
|
|
152
|
+
message: Some(req),
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
impl<B> fmt::Debug for SendRequest<B> {
|
|
161
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
162
|
+
f.debug_struct("SendRequest").finish()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ===== impl Connection
|
|
167
|
+
|
|
168
|
+
impl<T, B> Connection<T, B>
|
|
169
|
+
where
|
|
170
|
+
T: AsyncRead + AsyncWrite + Unpin + Send,
|
|
171
|
+
B: Body + 'static,
|
|
172
|
+
B::Error: Into<BoxError>,
|
|
173
|
+
{
|
|
174
|
+
/// Enable this connection to support higher-level HTTP upgrades.
|
|
175
|
+
pub fn with_upgrades(self) -> upgrades::UpgradeableConnection<T, B> {
|
|
176
|
+
upgrades::UpgradeableConnection { inner: Some(self) }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
impl<T, B> fmt::Debug for Connection<T, B>
|
|
181
|
+
where
|
|
182
|
+
T: AsyncRead + AsyncWrite + fmt::Debug,
|
|
183
|
+
B: Body + 'static,
|
|
184
|
+
{
|
|
185
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
186
|
+
f.debug_struct("Connection").finish()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
impl<T, B> Future for Connection<T, B>
|
|
191
|
+
where
|
|
192
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
193
|
+
B: Body + 'static,
|
|
194
|
+
B::Data: Send,
|
|
195
|
+
B::Error: Into<BoxError>,
|
|
196
|
+
{
|
|
197
|
+
type Output = Result<()>;
|
|
198
|
+
|
|
199
|
+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
200
|
+
match ready!(Pin::new(&mut self.inner).poll(cx))? {
|
|
201
|
+
proto::Dispatched::Shutdown => Poll::Ready(Ok(())),
|
|
202
|
+
proto::Dispatched::Upgrade(pending) => {
|
|
203
|
+
// With no `Send` bound on `I`, we can't try to do
|
|
204
|
+
// upgrades here. In case a user was trying to use
|
|
205
|
+
// `upgrade` with this API, send a special
|
|
206
|
+
// error letting them know about that.
|
|
207
|
+
pending.manual();
|
|
208
|
+
Poll::Ready(Ok(()))
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ===== impl Builder
|
|
215
|
+
|
|
216
|
+
impl Default for Builder {
|
|
217
|
+
fn default() -> Self {
|
|
218
|
+
Self::new()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
impl Builder {
|
|
223
|
+
/// Creates a new connection builder.
|
|
224
|
+
#[inline]
|
|
225
|
+
pub fn new() -> Builder {
|
|
226
|
+
Builder {
|
|
227
|
+
opts: Default::default(),
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// Provide a options configuration for the HTTP/1 connection.
|
|
232
|
+
#[inline]
|
|
233
|
+
pub fn options(&mut self, opts: Http1Options) {
|
|
234
|
+
self.opts = opts;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// Constructs a connection with the configured options and IO.
|
|
238
|
+
///
|
|
239
|
+
/// Note, if [`Connection`] is not `await`-ed, [`SendRequest`] will
|
|
240
|
+
/// do nothing.
|
|
241
|
+
pub async fn handshake<T, B>(self, io: T) -> Result<(SendRequest<B>, Connection<T, B>)>
|
|
242
|
+
where
|
|
243
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
244
|
+
B: Body + 'static,
|
|
245
|
+
B::Data: Send,
|
|
246
|
+
B::Error: Into<BoxError>,
|
|
247
|
+
{
|
|
248
|
+
trace!("client handshake HTTP/1");
|
|
249
|
+
|
|
250
|
+
let (tx, rx) = dispatch::channel();
|
|
251
|
+
let mut conn = proto::Conn::new(io);
|
|
252
|
+
|
|
253
|
+
// Set the HTTP/1 parser configuration
|
|
254
|
+
let h1_parser_config = {
|
|
255
|
+
let mut h1_parser_config = ParserConfig::default();
|
|
256
|
+
h1_parser_config
|
|
257
|
+
.ignore_invalid_headers_in_responses(self.opts.ignore_invalid_headers_in_responses)
|
|
258
|
+
.allow_spaces_after_header_name_in_responses(
|
|
259
|
+
self.opts.allow_spaces_after_header_name_in_responses,
|
|
260
|
+
)
|
|
261
|
+
.allow_obsolete_multiline_headers_in_responses(
|
|
262
|
+
self.opts.allow_obsolete_multiline_headers_in_responses,
|
|
263
|
+
);
|
|
264
|
+
h1_parser_config
|
|
265
|
+
};
|
|
266
|
+
conn.set_h1_parser_config(h1_parser_config);
|
|
267
|
+
|
|
268
|
+
// Set the h1 write strategy
|
|
269
|
+
if let Some(writev) = self.opts.h1_writev {
|
|
270
|
+
if writev {
|
|
271
|
+
conn.set_write_strategy_queue();
|
|
272
|
+
} else {
|
|
273
|
+
conn.set_write_strategy_flatten();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Set the maximum size of the request headers
|
|
278
|
+
if let Some(max_headers) = self.opts.h1_max_headers {
|
|
279
|
+
conn.set_http1_max_headers(max_headers);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Enable HTTP/0.9 responses if requested
|
|
283
|
+
if self.opts.h09_responses {
|
|
284
|
+
conn.set_h09_responses();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Set the read buffer size if specified
|
|
288
|
+
if let Some(sz) = self.opts.h1_read_buf_exact_size {
|
|
289
|
+
conn.set_read_buf_exact_size(sz);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Set the maximum buffer size for HTTP/1 connections
|
|
293
|
+
if let Some(max) = self.opts.h1_max_buf_size {
|
|
294
|
+
conn.set_max_buf_size(max);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let cd = proto::h1::dispatch::Client::new(rx);
|
|
298
|
+
let proto = proto::h1::Dispatcher::new(cd, conn);
|
|
299
|
+
|
|
300
|
+
Ok((SendRequest { dispatch: tx }, Connection { inner: proto }))
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
mod upgrades {
|
|
305
|
+
use super::*;
|
|
306
|
+
use crate::client::core::upgrade::Upgraded;
|
|
307
|
+
|
|
308
|
+
// A future binding a connection with a Service with Upgrade support.
|
|
309
|
+
//
|
|
310
|
+
// This type is unnameable outside the crate.
|
|
311
|
+
#[must_use = "futures do nothing unless polled"]
|
|
312
|
+
pub struct UpgradeableConnection<T, B>
|
|
313
|
+
where
|
|
314
|
+
T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
315
|
+
B: Body + 'static,
|
|
316
|
+
B::Error: Into<BoxError>,
|
|
317
|
+
{
|
|
318
|
+
pub(super) inner: Option<Connection<T, B>>,
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
impl<I, B> Future for UpgradeableConnection<I, B>
|
|
322
|
+
where
|
|
323
|
+
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
|
324
|
+
B: Body + 'static,
|
|
325
|
+
B::Data: Send,
|
|
326
|
+
B::Error: Into<BoxError>,
|
|
327
|
+
{
|
|
328
|
+
type Output = Result<()>;
|
|
329
|
+
|
|
330
|
+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
331
|
+
match ready!(Pin::new(&mut self.inner.as_mut().unwrap().inner).poll(cx)) {
|
|
332
|
+
Ok(proto::Dispatched::Shutdown) => Poll::Ready(Ok(())),
|
|
333
|
+
Ok(proto::Dispatched::Upgrade(pending)) => {
|
|
334
|
+
let Parts { io, read_buf } = self.inner.take().unwrap().into_parts();
|
|
335
|
+
pending.fulfill(Upgraded::new(io, read_buf));
|
|
336
|
+
Poll::Ready(Ok(()))
|
|
337
|
+
}
|
|
338
|
+
Err(e) => Poll::Ready(Err(e)),
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|