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,379 @@
|
|
|
1
|
+
pub(crate) mod client;
|
|
2
|
+
pub(crate) mod ping;
|
|
3
|
+
|
|
4
|
+
use std::{
|
|
5
|
+
future::Future,
|
|
6
|
+
io::{self, Cursor, IoSlice},
|
|
7
|
+
pin::Pin,
|
|
8
|
+
task::{Context, Poll, ready},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
use bytes::{Buf, Bytes};
|
|
12
|
+
use http::{
|
|
13
|
+
HeaderMap,
|
|
14
|
+
header::{CONNECTION, HeaderName, TE, TRANSFER_ENCODING, UPGRADE},
|
|
15
|
+
};
|
|
16
|
+
use http_body::Body;
|
|
17
|
+
use http2::{Reason, RecvStream, SendStream};
|
|
18
|
+
use pin_project_lite::pin_project;
|
|
19
|
+
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
|
20
|
+
|
|
21
|
+
pub(crate) use self::client::ClientTask;
|
|
22
|
+
use crate::client::core::{self, Error, error::BoxError, proto::h2::ping::Recorder};
|
|
23
|
+
|
|
24
|
+
/// Default initial stream window size defined in HTTP2 spec.
|
|
25
|
+
pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535;
|
|
26
|
+
|
|
27
|
+
// List of connection headers from RFC 9110 Section 7.6.1
|
|
28
|
+
//
|
|
29
|
+
// TE headers are allowed in HTTP/2 requests as long as the value is "trailers", so they're
|
|
30
|
+
// tested separately.
|
|
31
|
+
static CONNECTION_HEADERS: [HeaderName; 4] = [
|
|
32
|
+
HeaderName::from_static("keep-alive"),
|
|
33
|
+
HeaderName::from_static("proxy-connection"),
|
|
34
|
+
TRANSFER_ENCODING,
|
|
35
|
+
UPGRADE,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
|
|
39
|
+
for header in &CONNECTION_HEADERS {
|
|
40
|
+
if headers.remove(header).is_some() {
|
|
41
|
+
warn!("Connection header illegal in HTTP/2: {}", header.as_str());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if is_request {
|
|
46
|
+
if headers
|
|
47
|
+
.get(TE)
|
|
48
|
+
.is_some_and(|te_header| te_header != "trailers")
|
|
49
|
+
{
|
|
50
|
+
warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
|
|
51
|
+
headers.remove(TE);
|
|
52
|
+
}
|
|
53
|
+
} else if headers.remove(TE).is_some() {
|
|
54
|
+
warn!("TE headers illegal in HTTP/2 responses");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if let Some(header) = headers.remove(CONNECTION) {
|
|
58
|
+
warn!(
|
|
59
|
+
"Connection header illegal in HTTP/2: {}",
|
|
60
|
+
CONNECTION.as_str()
|
|
61
|
+
);
|
|
62
|
+
let header_contents = header.to_str().unwrap();
|
|
63
|
+
|
|
64
|
+
// A `Connection` header may have a comma-separated list of names of other headers that
|
|
65
|
+
// are meant for only this specific connection.
|
|
66
|
+
//
|
|
67
|
+
// Iterate these names and remove them as headers. Connection-specific headers are
|
|
68
|
+
// forbidden in HTTP2, as that information has been moved into frame types of the h2
|
|
69
|
+
// protocol.
|
|
70
|
+
for name in header_contents.split(',') {
|
|
71
|
+
let name = name.trim();
|
|
72
|
+
headers.remove(name);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// body adapters used by both Client and Server
|
|
78
|
+
|
|
79
|
+
pin_project! {
|
|
80
|
+
pub(crate) struct PipeToSendStream<S>
|
|
81
|
+
where
|
|
82
|
+
S: Body,
|
|
83
|
+
{
|
|
84
|
+
body_tx: SendStream<SendBuf<S::Data>>,
|
|
85
|
+
data_done: bool,
|
|
86
|
+
#[pin]
|
|
87
|
+
stream: S,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
impl<S> PipeToSendStream<S>
|
|
92
|
+
where
|
|
93
|
+
S: Body,
|
|
94
|
+
{
|
|
95
|
+
fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
|
|
96
|
+
PipeToSendStream {
|
|
97
|
+
body_tx: tx,
|
|
98
|
+
data_done: false,
|
|
99
|
+
stream,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
impl<S> Future for PipeToSendStream<S>
|
|
105
|
+
where
|
|
106
|
+
S: Body,
|
|
107
|
+
S::Error: Into<BoxError>,
|
|
108
|
+
{
|
|
109
|
+
type Output = core::Result<()>;
|
|
110
|
+
|
|
111
|
+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
112
|
+
let mut me = self.project();
|
|
113
|
+
loop {
|
|
114
|
+
// we don't have the next chunk of data yet, so just reserve 1 byte to make
|
|
115
|
+
// sure there's some capacity available. h2 will handle the capacity management
|
|
116
|
+
// for the actual body chunk.
|
|
117
|
+
me.body_tx.reserve_capacity(1);
|
|
118
|
+
|
|
119
|
+
if me.body_tx.capacity() == 0 {
|
|
120
|
+
loop {
|
|
121
|
+
match ready!(me.body_tx.poll_capacity(cx)) {
|
|
122
|
+
Some(Ok(0)) => {}
|
|
123
|
+
Some(Ok(_)) => break,
|
|
124
|
+
Some(Err(e)) => {
|
|
125
|
+
return Poll::Ready(Err(Error::new_body_write(e)));
|
|
126
|
+
}
|
|
127
|
+
None => {
|
|
128
|
+
// None means the stream is no longer in a
|
|
129
|
+
// streaming state, we either finished it
|
|
130
|
+
// somehow, or the remote reset us.
|
|
131
|
+
return Poll::Ready(Err(Error::new_body_write(
|
|
132
|
+
"send stream capacity unexpectedly closed",
|
|
133
|
+
)));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} else if let Poll::Ready(reason) =
|
|
138
|
+
me.body_tx.poll_reset(cx).map_err(Error::new_body_write)?
|
|
139
|
+
{
|
|
140
|
+
debug!("stream received RST_STREAM: {:?}", reason);
|
|
141
|
+
return Poll::Ready(Err(Error::new_body_write(::http2::Error::from(reason))));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
match ready!(me.stream.as_mut().poll_frame(cx)) {
|
|
145
|
+
Some(Ok(frame)) => {
|
|
146
|
+
if frame.is_data() {
|
|
147
|
+
let chunk = frame.into_data().unwrap_or_else(|_| unreachable!());
|
|
148
|
+
let is_eos = me.stream.is_end_stream();
|
|
149
|
+
trace!(
|
|
150
|
+
"send body chunk: {} bytes, eos={}",
|
|
151
|
+
chunk.remaining(),
|
|
152
|
+
is_eos,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
let buf = SendBuf::Buf(chunk);
|
|
156
|
+
me.body_tx
|
|
157
|
+
.send_data(buf, is_eos)
|
|
158
|
+
.map_err(Error::new_body_write)?;
|
|
159
|
+
|
|
160
|
+
if is_eos {
|
|
161
|
+
return Poll::Ready(Ok(()));
|
|
162
|
+
}
|
|
163
|
+
} else if frame.is_trailers() {
|
|
164
|
+
// no more DATA, so give any capacity back
|
|
165
|
+
me.body_tx.reserve_capacity(0);
|
|
166
|
+
me.body_tx
|
|
167
|
+
.send_trailers(frame.into_trailers().unwrap_or_else(|_| unreachable!()))
|
|
168
|
+
.map_err(Error::new_body_write)?;
|
|
169
|
+
return Poll::Ready(Ok(()));
|
|
170
|
+
} else {
|
|
171
|
+
trace!("discarding unknown frame");
|
|
172
|
+
// loop again
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
Some(Err(e)) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
|
|
176
|
+
None => {
|
|
177
|
+
// no more frames means we're done here
|
|
178
|
+
// but at this point, we haven't sent an EOS DATA, or
|
|
179
|
+
// any trailers, so send an empty EOS DATA.
|
|
180
|
+
return Poll::Ready(me.body_tx.send_eos_frame());
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
trait SendStreamExt {
|
|
188
|
+
fn on_user_err<E>(&mut self, err: E) -> Error
|
|
189
|
+
where
|
|
190
|
+
E: Into<BoxError>;
|
|
191
|
+
fn send_eos_frame(&mut self) -> core::Result<()>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
impl<B: Buf> SendStreamExt for SendStream<SendBuf<B>> {
|
|
195
|
+
fn on_user_err<E>(&mut self, err: E) -> Error
|
|
196
|
+
where
|
|
197
|
+
E: Into<BoxError>,
|
|
198
|
+
{
|
|
199
|
+
let err = Error::new_user_body(err);
|
|
200
|
+
debug!("send body user stream error: {}", err);
|
|
201
|
+
self.send_reset(err.h2_reason());
|
|
202
|
+
err
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fn send_eos_frame(&mut self) -> core::Result<()> {
|
|
206
|
+
trace!("send body eos");
|
|
207
|
+
self.send_data(SendBuf::None, true)
|
|
208
|
+
.map_err(Error::new_body_write)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[repr(usize)]
|
|
213
|
+
enum SendBuf<B> {
|
|
214
|
+
Buf(B),
|
|
215
|
+
Cursor(Cursor<Box<[u8]>>),
|
|
216
|
+
None,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
impl<B: Buf> Buf for SendBuf<B> {
|
|
220
|
+
#[inline]
|
|
221
|
+
fn remaining(&self) -> usize {
|
|
222
|
+
match *self {
|
|
223
|
+
Self::Buf(ref b) => b.remaining(),
|
|
224
|
+
Self::Cursor(ref c) => Buf::remaining(c),
|
|
225
|
+
Self::None => 0,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
#[inline]
|
|
230
|
+
fn chunk(&self) -> &[u8] {
|
|
231
|
+
match *self {
|
|
232
|
+
Self::Buf(ref b) => b.chunk(),
|
|
233
|
+
Self::Cursor(ref c) => c.chunk(),
|
|
234
|
+
Self::None => &[],
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#[inline]
|
|
239
|
+
fn advance(&mut self, cnt: usize) {
|
|
240
|
+
match *self {
|
|
241
|
+
Self::Buf(ref mut b) => b.advance(cnt),
|
|
242
|
+
Self::Cursor(ref mut c) => c.advance(cnt),
|
|
243
|
+
Self::None => {}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
|
|
248
|
+
match *self {
|
|
249
|
+
Self::Buf(ref b) => b.chunks_vectored(dst),
|
|
250
|
+
Self::Cursor(ref c) => c.chunks_vectored(dst),
|
|
251
|
+
Self::None => 0,
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
struct H2Upgraded<B>
|
|
257
|
+
where
|
|
258
|
+
B: Buf,
|
|
259
|
+
{
|
|
260
|
+
ping: Recorder,
|
|
261
|
+
send_stream: SendStream<SendBuf<B>>,
|
|
262
|
+
recv_stream: RecvStream,
|
|
263
|
+
buf: Bytes,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
impl<B> AsyncRead for H2Upgraded<B>
|
|
267
|
+
where
|
|
268
|
+
B: Buf,
|
|
269
|
+
{
|
|
270
|
+
fn poll_read(
|
|
271
|
+
mut self: Pin<&mut Self>,
|
|
272
|
+
cx: &mut Context<'_>,
|
|
273
|
+
read_buf: &mut ReadBuf<'_>,
|
|
274
|
+
) -> Poll<io::Result<()>> {
|
|
275
|
+
if self.buf.is_empty() {
|
|
276
|
+
self.buf = loop {
|
|
277
|
+
match ready!(self.recv_stream.poll_data(cx)) {
|
|
278
|
+
None => return Poll::Ready(Ok(())),
|
|
279
|
+
Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
Some(Ok(buf)) => {
|
|
283
|
+
self.ping.record_data(buf.len());
|
|
284
|
+
break buf;
|
|
285
|
+
}
|
|
286
|
+
Some(Err(e)) => {
|
|
287
|
+
return Poll::Ready(match e.reason() {
|
|
288
|
+
Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()),
|
|
289
|
+
Some(Reason::STREAM_CLOSED) => {
|
|
290
|
+
Err(io::Error::new(io::ErrorKind::BrokenPipe, e))
|
|
291
|
+
}
|
|
292
|
+
_ => Err(h2_to_io_error(e)),
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
|
|
299
|
+
read_buf.put_slice(&self.buf[..cnt]);
|
|
300
|
+
self.buf.advance(cnt);
|
|
301
|
+
let _ = self.recv_stream.flow_control().release_capacity(cnt);
|
|
302
|
+
Poll::Ready(Ok(()))
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
impl<B> AsyncWrite for H2Upgraded<B>
|
|
307
|
+
where
|
|
308
|
+
B: Buf,
|
|
309
|
+
{
|
|
310
|
+
fn poll_write(
|
|
311
|
+
mut self: Pin<&mut Self>,
|
|
312
|
+
cx: &mut Context<'_>,
|
|
313
|
+
buf: &[u8],
|
|
314
|
+
) -> Poll<io::Result<usize>> {
|
|
315
|
+
if buf.is_empty() {
|
|
316
|
+
return Poll::Ready(Ok(0));
|
|
317
|
+
}
|
|
318
|
+
self.send_stream.reserve_capacity(buf.len());
|
|
319
|
+
|
|
320
|
+
// We ignore all errors returned by `poll_capacity` and `write`, as we
|
|
321
|
+
// will get the correct from `poll_reset` anyway.
|
|
322
|
+
let cnt = match ready!(self.send_stream.poll_capacity(cx)) {
|
|
323
|
+
None => Some(0),
|
|
324
|
+
Some(Ok(cnt)) => self
|
|
325
|
+
.send_stream
|
|
326
|
+
.send_data(SendBuf::Cursor(Cursor::new(buf[..cnt].into())), false)
|
|
327
|
+
.ok()
|
|
328
|
+
.map(|()| cnt),
|
|
329
|
+
Some(Err(_)) => None,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if let Some(cnt) = cnt {
|
|
333
|
+
return Poll::Ready(Ok(cnt));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
Poll::Ready(Err(h2_to_io_error(
|
|
337
|
+
match ready!(self.send_stream.poll_reset(cx)) {
|
|
338
|
+
Ok(Reason::NO_ERROR) | Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => {
|
|
339
|
+
return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
|
|
340
|
+
}
|
|
341
|
+
Ok(reason) => reason.into(),
|
|
342
|
+
Err(e) => e,
|
|
343
|
+
},
|
|
344
|
+
)))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
348
|
+
Poll::Ready(Ok(()))
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
352
|
+
if self
|
|
353
|
+
.send_stream
|
|
354
|
+
.send_data(SendBuf::Cursor(Cursor::new([].into())), true)
|
|
355
|
+
.is_ok()
|
|
356
|
+
{
|
|
357
|
+
return Poll::Ready(Ok(()));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
Poll::Ready(Err(h2_to_io_error(
|
|
361
|
+
match ready!(self.send_stream.poll_reset(cx)) {
|
|
362
|
+
Ok(Reason::NO_ERROR) => return Poll::Ready(Ok(())),
|
|
363
|
+
Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => {
|
|
364
|
+
return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
|
|
365
|
+
}
|
|
366
|
+
Ok(reason) => reason.into(),
|
|
367
|
+
Err(e) => e,
|
|
368
|
+
},
|
|
369
|
+
)))
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
fn h2_to_io_error(e: http2::Error) -> std::io::Error {
|
|
374
|
+
if e.is_io() {
|
|
375
|
+
e.into_io().unwrap()
|
|
376
|
+
} else {
|
|
377
|
+
std::io::Error::other(e)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
use bytes::BytesMut;
|
|
2
|
+
use http::{
|
|
3
|
+
HeaderMap, Method,
|
|
4
|
+
header::{CONTENT_LENGTH, HeaderValue, ValueIter},
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub(super) fn connection_keep_alive(value: &HeaderValue) -> bool {
|
|
8
|
+
connection_has(value, "keep-alive")
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
pub(super) fn connection_close(value: &HeaderValue) -> bool {
|
|
12
|
+
connection_has(value, "close")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn connection_has(value: &HeaderValue, needle: &str) -> bool {
|
|
16
|
+
if let Ok(s) = value.to_str() {
|
|
17
|
+
for val in s.split(',') {
|
|
18
|
+
if val.trim().eq_ignore_ascii_case(needle) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub(super) fn content_length_parse_all(headers: &HeaderMap) -> Option<u64> {
|
|
27
|
+
content_length_parse_all_values(headers.get_all(CONTENT_LENGTH).into_iter())
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub(super) fn content_length_parse_all_values(values: ValueIter<'_, HeaderValue>) -> Option<u64> {
|
|
31
|
+
// If multiple Content-Length headers were sent, everything can still
|
|
32
|
+
// be alright if they all contain the same value, and all parse
|
|
33
|
+
// correctly. If not, then it's an error.
|
|
34
|
+
|
|
35
|
+
let mut content_length: Option<u64> = None;
|
|
36
|
+
for h in values {
|
|
37
|
+
if let Ok(line) = h.to_str() {
|
|
38
|
+
for v in line.split(',') {
|
|
39
|
+
if let Some(n) = from_digits(v.trim().as_bytes()) {
|
|
40
|
+
if content_length.is_none() {
|
|
41
|
+
content_length = Some(n)
|
|
42
|
+
} else if content_length != Some(n) {
|
|
43
|
+
return None;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
return None;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
return None;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
content_length
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn from_digits(bytes: &[u8]) -> Option<u64> {
|
|
58
|
+
// cannot use FromStr for u64, since it allows a signed prefix
|
|
59
|
+
let mut result = 0u64;
|
|
60
|
+
const RADIX: u64 = 10;
|
|
61
|
+
|
|
62
|
+
if bytes.is_empty() {
|
|
63
|
+
return None;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for &b in bytes {
|
|
67
|
+
// can't use char::to_digit, since we haven't verified these bytes
|
|
68
|
+
// are utf-8.
|
|
69
|
+
match b {
|
|
70
|
+
b'0'..=b'9' => {
|
|
71
|
+
result = result.checked_mul(RADIX)?;
|
|
72
|
+
result = result.checked_add((b - b'0') as u64)?;
|
|
73
|
+
}
|
|
74
|
+
_ => {
|
|
75
|
+
// not a DIGIT, get outta here!
|
|
76
|
+
return None;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Some(result)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
pub(super) fn method_has_defined_payload_semantics(method: &Method) -> bool {
|
|
85
|
+
!matches!(
|
|
86
|
+
*method,
|
|
87
|
+
Method::GET | Method::HEAD | Method::DELETE | Method::CONNECT | Method::OPTIONS
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub(super) fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) {
|
|
92
|
+
headers
|
|
93
|
+
.entry(CONTENT_LENGTH)
|
|
94
|
+
.or_insert_with(|| HeaderValue::from(len));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub(super) fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool {
|
|
98
|
+
is_chunked(headers.get_all(http::header::TRANSFER_ENCODING).into_iter())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
pub(super) fn is_chunked(mut encodings: ValueIter<'_, HeaderValue>) -> bool {
|
|
102
|
+
// chunked must always be the last encoding, according to spec
|
|
103
|
+
if let Some(line) = encodings.next_back() {
|
|
104
|
+
return is_chunked_(line);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pub(super) fn is_chunked_(value: &HeaderValue) -> bool {
|
|
111
|
+
// chunked must always be the last encoding, according to spec
|
|
112
|
+
if let Ok(s) = value.to_str() {
|
|
113
|
+
if let Some(encoding) = s.rsplit(',').next() {
|
|
114
|
+
return encoding.trim().eq_ignore_ascii_case("chunked");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub(super) fn add_chunked(mut entry: http::header::OccupiedEntry<'_, HeaderValue>) {
|
|
122
|
+
const CHUNKED: &str = "chunked";
|
|
123
|
+
|
|
124
|
+
if let Some(line) = entry.iter_mut().next_back() {
|
|
125
|
+
// + 2 for ", "
|
|
126
|
+
let new_cap = line.as_bytes().len() + CHUNKED.len() + 2;
|
|
127
|
+
let mut buf = BytesMut::with_capacity(new_cap);
|
|
128
|
+
buf.extend_from_slice(line.as_bytes());
|
|
129
|
+
buf.extend_from_slice(b", ");
|
|
130
|
+
buf.extend_from_slice(CHUNKED.as_bytes());
|
|
131
|
+
|
|
132
|
+
*line = HeaderValue::from_maybe_shared(buf.freeze())
|
|
133
|
+
.expect("original header value plus ascii is valid");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
entry.insert(HeaderValue::from_static(CHUNKED));
|
|
138
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//! Pieces pertaining to the HTTP message protocol.
|
|
2
|
+
|
|
3
|
+
mod headers;
|
|
4
|
+
|
|
5
|
+
pub(crate) mod h1;
|
|
6
|
+
pub(crate) mod h2;
|
|
7
|
+
|
|
8
|
+
pub(crate) use self::h1::{Conn, dispatch};
|
|
9
|
+
use crate::client::core::upgrade;
|
|
10
|
+
|
|
11
|
+
/// An Incoming Message head. Includes request/status line, and headers.
|
|
12
|
+
#[derive(Debug, Default)]
|
|
13
|
+
pub(crate) struct MessageHead<S> {
|
|
14
|
+
/// HTTP version of the message.
|
|
15
|
+
pub(crate) version: http::Version,
|
|
16
|
+
/// Subject (request line or status line) of Incoming message.
|
|
17
|
+
pub(crate) subject: S,
|
|
18
|
+
/// Headers of the Incoming message.
|
|
19
|
+
pub(crate) headers: http::HeaderMap,
|
|
20
|
+
/// Extensions.
|
|
21
|
+
extensions: http::Extensions,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// An incoming request message.
|
|
25
|
+
pub(crate) type RequestHead = MessageHead<RequestLine>;
|
|
26
|
+
|
|
27
|
+
#[derive(Debug, Default, PartialEq)]
|
|
28
|
+
pub(crate) struct RequestLine(pub(crate) http::Method, pub(crate) http::Uri);
|
|
29
|
+
|
|
30
|
+
/// An incoming response message.
|
|
31
|
+
pub(crate) type ResponseHead = MessageHead<http::StatusCode>;
|
|
32
|
+
|
|
33
|
+
#[derive(Debug)]
|
|
34
|
+
pub(crate) enum BodyLength {
|
|
35
|
+
/// Content-Length
|
|
36
|
+
Known(u64),
|
|
37
|
+
/// Transfer-Encoding: chunked (if h1)
|
|
38
|
+
Unknown,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Status of when a Dispatcher future completes.
|
|
42
|
+
pub(crate) enum Dispatched {
|
|
43
|
+
/// Dispatcher completely shutdown connection.
|
|
44
|
+
Shutdown,
|
|
45
|
+
/// Dispatcher has pending upgrade, and so did not shutdown.
|
|
46
|
+
Upgrade(upgrade::Pending),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl MessageHead<http::StatusCode> {
|
|
50
|
+
fn into_response<B>(self, body: B) -> http::Response<B> {
|
|
51
|
+
let mut res = http::Response::new(body);
|
|
52
|
+
*res.status_mut() = self.subject;
|
|
53
|
+
*res.headers_mut() = self.headers;
|
|
54
|
+
*res.version_mut() = self.version;
|
|
55
|
+
*res.extensions_mut() = self.extensions;
|
|
56
|
+
res
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//! Trait aliases
|
|
2
|
+
//!
|
|
3
|
+
//! Traits in this module ease setting bounds and usually automatically
|
|
4
|
+
//! implemented by implementing another trait.
|
|
5
|
+
|
|
6
|
+
pub use self::h2_client::Http2ClientConnExec;
|
|
7
|
+
|
|
8
|
+
mod h2_client {
|
|
9
|
+
use std::future::Future;
|
|
10
|
+
|
|
11
|
+
use tokio::io::{AsyncRead, AsyncWrite};
|
|
12
|
+
|
|
13
|
+
use crate::client::core::{error::BoxError, proto::h2::client::H2ClientFuture, rt::Executor};
|
|
14
|
+
|
|
15
|
+
/// An executor to spawn http2 futures for the client.
|
|
16
|
+
///
|
|
17
|
+
/// This trait is implemented for any type that implements [`Executor`]
|
|
18
|
+
/// trait for any future.
|
|
19
|
+
///
|
|
20
|
+
/// This trait is sealed and cannot be implemented for types outside this crate.
|
|
21
|
+
pub trait Http2ClientConnExec<B, T>: sealed_client::Sealed<(B, T)>
|
|
22
|
+
where
|
|
23
|
+
B: http_body::Body,
|
|
24
|
+
B::Error: Into<BoxError>,
|
|
25
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
26
|
+
{
|
|
27
|
+
#[doc(hidden)]
|
|
28
|
+
fn execute_h2_future(&mut self, future: H2ClientFuture<B, T>);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl<E, B, T> Http2ClientConnExec<B, T> for E
|
|
32
|
+
where
|
|
33
|
+
E: Executor<H2ClientFuture<B, T>>,
|
|
34
|
+
B: http_body::Body + 'static,
|
|
35
|
+
B::Error: Into<BoxError>,
|
|
36
|
+
H2ClientFuture<B, T>: Future<Output = ()>,
|
|
37
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
38
|
+
{
|
|
39
|
+
fn execute_h2_future(&mut self, future: H2ClientFuture<B, T>) {
|
|
40
|
+
self.execute(future)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
impl<E, B, T> sealed_client::Sealed<(B, T)> for E
|
|
45
|
+
where
|
|
46
|
+
E: Executor<H2ClientFuture<B, T>>,
|
|
47
|
+
B: http_body::Body + 'static,
|
|
48
|
+
B::Error: Into<BoxError>,
|
|
49
|
+
H2ClientFuture<B, T>: Future<Output = ()>,
|
|
50
|
+
T: AsyncRead + AsyncWrite + Unpin,
|
|
51
|
+
{
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
mod sealed_client {
|
|
55
|
+
pub trait Sealed<X> {}
|
|
56
|
+
}
|
|
57
|
+
}
|