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,435 @@
|
|
|
1
|
+
//! Error and Result module.
|
|
2
|
+
use std::{error::Error as StdError, fmt};
|
|
3
|
+
|
|
4
|
+
/// Result type often returned from methods that can have crate::core: `Error`s.
|
|
5
|
+
pub type Result<T> = std::result::Result<T, Error>;
|
|
6
|
+
|
|
7
|
+
pub type BoxError = Box<dyn StdError + Send + Sync>;
|
|
8
|
+
|
|
9
|
+
type Cause = BoxError;
|
|
10
|
+
|
|
11
|
+
/// Represents errors that can occur handling HTTP streams.
|
|
12
|
+
///
|
|
13
|
+
/// # Formatting
|
|
14
|
+
///
|
|
15
|
+
/// The `Display` implementation of this type will only print the details of
|
|
16
|
+
/// this level of error, even though it may have been caused by another error
|
|
17
|
+
/// and contain that error in its source. To print all the relevant
|
|
18
|
+
/// information, including the source chain, using something like
|
|
19
|
+
/// `std::error::Report`, or equivalent 3rd party types.
|
|
20
|
+
///
|
|
21
|
+
/// The contents of the formatted error message of this specific `Error` type
|
|
22
|
+
/// is unspecified. **You must not depend on it.** The wording and details may
|
|
23
|
+
/// change in any version, with the goal of improving error messages.
|
|
24
|
+
///
|
|
25
|
+
/// # Source
|
|
26
|
+
///
|
|
27
|
+
/// A `crate::core::Error` may be caused by another error. To aid in debugging,
|
|
28
|
+
/// those are exposed in `Error::source()` as erased types. While it is
|
|
29
|
+
/// possible to check the exact type of the sources, they **can not be depended
|
|
30
|
+
/// on**. They may come from private internal dependencies, and are subject to
|
|
31
|
+
/// change at any moment.
|
|
32
|
+
pub struct Error {
|
|
33
|
+
inner: Box<ErrorImpl>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct ErrorImpl {
|
|
37
|
+
kind: Kind,
|
|
38
|
+
cause: Option<Cause>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#[derive(Debug)]
|
|
42
|
+
pub(super) enum Kind {
|
|
43
|
+
Parse(Parse),
|
|
44
|
+
User(User),
|
|
45
|
+
/// A message reached EOF, but is not complete.
|
|
46
|
+
IncompleteMessage,
|
|
47
|
+
/// A connection received a message (or bytes) when not waiting for one.
|
|
48
|
+
UnexpectedMessage,
|
|
49
|
+
/// A pending item was dropped before ever being processed.
|
|
50
|
+
Canceled,
|
|
51
|
+
/// Indicates a channel (client or body sender) is closed.
|
|
52
|
+
ChannelClosed,
|
|
53
|
+
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
|
54
|
+
Io,
|
|
55
|
+
/// Error while reading a body from connection.
|
|
56
|
+
Body,
|
|
57
|
+
/// Error while writing a body to connection.
|
|
58
|
+
BodyWrite,
|
|
59
|
+
/// Error calling AsyncWrite::shutdown()
|
|
60
|
+
Shutdown,
|
|
61
|
+
/// A general error from h2.
|
|
62
|
+
Http2,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[derive(Debug)]
|
|
66
|
+
pub(crate) enum Parse {
|
|
67
|
+
Method,
|
|
68
|
+
Version,
|
|
69
|
+
VersionH2,
|
|
70
|
+
Uri,
|
|
71
|
+
Header(Header),
|
|
72
|
+
TooLarge,
|
|
73
|
+
Status,
|
|
74
|
+
Internal,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[derive(Debug)]
|
|
78
|
+
pub(crate) enum Header {
|
|
79
|
+
Token,
|
|
80
|
+
ContentLengthInvalid,
|
|
81
|
+
TransferEncodingUnexpected,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[derive(Debug)]
|
|
85
|
+
pub(super) enum User {
|
|
86
|
+
/// Error calling user's Body::poll_data().
|
|
87
|
+
Body,
|
|
88
|
+
/// The user aborted writing of the outgoing body.
|
|
89
|
+
BodyWriteAborted,
|
|
90
|
+
|
|
91
|
+
/// User tried to send a connect request with a nonzero body
|
|
92
|
+
InvalidConnectWithBody,
|
|
93
|
+
|
|
94
|
+
/// Error from future of user's Service.
|
|
95
|
+
Service,
|
|
96
|
+
|
|
97
|
+
/// User tried polling for an upgrade that doesn't exist.
|
|
98
|
+
NoUpgrade,
|
|
99
|
+
|
|
100
|
+
/// User polled for an upgrade, but low-level API is not using upgrades.
|
|
101
|
+
ManualUpgrade,
|
|
102
|
+
|
|
103
|
+
/// The dispatch task is gone.
|
|
104
|
+
DispatchGone,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Sentinel type to indicate the error was caused by a timeout.
|
|
108
|
+
#[derive(Debug)]
|
|
109
|
+
pub(super) struct TimedOut;
|
|
110
|
+
|
|
111
|
+
impl Error {
|
|
112
|
+
/// Returns true if this was an HTTP parse error.
|
|
113
|
+
pub fn is_parse(&self) -> bool {
|
|
114
|
+
matches!(self.inner.kind, Kind::Parse(_))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Returns true if this was an HTTP parse error caused by an invalid response status code or
|
|
118
|
+
/// reason phrase.
|
|
119
|
+
pub fn is_parse_status(&self) -> bool {
|
|
120
|
+
matches!(self.inner.kind, Kind::Parse(Parse::Status))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Returns true if this error was caused by user code.
|
|
124
|
+
pub fn is_user(&self) -> bool {
|
|
125
|
+
matches!(self.inner.kind, Kind::User(_))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Returns true if this was about a `Request` that was canceled.
|
|
129
|
+
pub fn is_canceled(&self) -> bool {
|
|
130
|
+
matches!(self.inner.kind, Kind::Canceled)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Returns true if a sender's channel is closed.
|
|
134
|
+
pub fn is_closed(&self) -> bool {
|
|
135
|
+
matches!(self.inner.kind, Kind::ChannelClosed)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Returns true if the connection closed before a message could complete.
|
|
139
|
+
pub fn is_incomplete_message(&self) -> bool {
|
|
140
|
+
matches!(self.inner.kind, Kind::IncompleteMessage)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Returns true if the body write was aborted.
|
|
144
|
+
pub fn is_body_write_aborted(&self) -> bool {
|
|
145
|
+
matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Returns true if the error was caused by a timeout.
|
|
149
|
+
pub fn is_timeout(&self) -> bool {
|
|
150
|
+
self.find_source::<TimedOut>().is_some()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
pub(super) fn new(kind: Kind) -> Error {
|
|
154
|
+
Error {
|
|
155
|
+
inner: Box::new(ErrorImpl { kind, cause: None }),
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
|
|
160
|
+
self.inner.cause = Some(cause.into());
|
|
161
|
+
self
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
|
|
165
|
+
let mut cause = self.source();
|
|
166
|
+
while let Some(err) = cause {
|
|
167
|
+
if let Some(typed) = err.downcast_ref() {
|
|
168
|
+
return Some(typed);
|
|
169
|
+
}
|
|
170
|
+
cause = err.source();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// else
|
|
174
|
+
None
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pub(super) fn h2_reason(&self) -> http2::Reason {
|
|
178
|
+
// Find an http2::Reason somewhere in the cause stack, if it exists,
|
|
179
|
+
// otherwise assume an INTERNAL_ERROR.
|
|
180
|
+
self.find_source::<http2::Error>()
|
|
181
|
+
.and_then(|h2_err| h2_err.reason())
|
|
182
|
+
.unwrap_or(http2::Reason::INTERNAL_ERROR)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub(super) fn new_canceled() -> Error {
|
|
186
|
+
Error::new(Kind::Canceled)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
pub(super) fn new_incomplete() -> Error {
|
|
190
|
+
Error::new(Kind::IncompleteMessage)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
pub(super) fn new_too_large() -> Error {
|
|
194
|
+
Error::new(Kind::Parse(Parse::TooLarge))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
pub(super) fn new_version_h2() -> Error {
|
|
198
|
+
Error::new(Kind::Parse(Parse::VersionH2))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
pub(super) fn new_unexpected_message() -> Error {
|
|
202
|
+
Error::new(Kind::UnexpectedMessage)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
pub(super) fn new_io(cause: std::io::Error) -> Error {
|
|
206
|
+
Error::new(Kind::Io).with(cause)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
pub(super) fn new_closed() -> Error {
|
|
210
|
+
Error::new(Kind::ChannelClosed)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
|
|
214
|
+
Error::new(Kind::Body).with(cause)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
|
|
218
|
+
Error::new(Kind::BodyWrite).with(cause)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
pub(super) fn new_body_write_aborted() -> Error {
|
|
222
|
+
Error::new(Kind::User(User::BodyWriteAborted))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn new_user(user: User) -> Error {
|
|
226
|
+
Error::new(Kind::User(user))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
pub(super) fn new_user_no_upgrade() -> Error {
|
|
230
|
+
Error::new_user(User::NoUpgrade)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
pub(super) fn new_user_manual_upgrade() -> Error {
|
|
234
|
+
Error::new_user(User::ManualUpgrade)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
|
|
238
|
+
Error::new_user(User::Service).with(cause)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
|
|
242
|
+
Error::new_user(User::Body).with(cause)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
pub(super) fn new_user_invalid_connect() -> Error {
|
|
246
|
+
Error::new_user(User::InvalidConnectWithBody)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
|
|
250
|
+
Error::new(Kind::Shutdown).with(cause)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
pub(super) fn new_user_dispatch_gone() -> Error {
|
|
254
|
+
Error::new(Kind::User(User::DispatchGone))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
pub(super) fn new_h2(cause: ::http2::Error) -> Error {
|
|
258
|
+
if cause.is_io() {
|
|
259
|
+
Error::new_io(cause.into_io().expect("http2::Error::is_io"))
|
|
260
|
+
} else {
|
|
261
|
+
Error::new(Kind::Http2).with(cause)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
fn description(&self) -> &str {
|
|
266
|
+
match self.inner.kind {
|
|
267
|
+
Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
|
|
268
|
+
Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
|
|
269
|
+
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
|
|
270
|
+
Kind::Parse(Parse::Uri) => "invalid URI",
|
|
271
|
+
Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
|
|
272
|
+
Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
|
|
273
|
+
"invalid content-length parsed"
|
|
274
|
+
}
|
|
275
|
+
Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
|
|
276
|
+
"unexpected transfer-encoding parsed"
|
|
277
|
+
}
|
|
278
|
+
Kind::Parse(Parse::TooLarge) => "message head is too large",
|
|
279
|
+
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
|
|
280
|
+
Kind::Parse(Parse::Internal) => {
|
|
281
|
+
"internal error inside wreq and/or its dependencies, please report"
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Kind::IncompleteMessage => "connection closed before message completed",
|
|
285
|
+
Kind::UnexpectedMessage => "received unexpected message from connection",
|
|
286
|
+
Kind::ChannelClosed => "channel closed",
|
|
287
|
+
Kind::Canceled => "operation was canceled",
|
|
288
|
+
Kind::Body => "error reading a body from connection",
|
|
289
|
+
Kind::BodyWrite => "error writing a body to connection",
|
|
290
|
+
Kind::Shutdown => "error shutting down connection",
|
|
291
|
+
Kind::Http2 => "http2 error",
|
|
292
|
+
Kind::Io => "connection error",
|
|
293
|
+
|
|
294
|
+
Kind::User(User::Body) => "error from user's Body stream",
|
|
295
|
+
Kind::User(User::BodyWriteAborted) => "user body write aborted",
|
|
296
|
+
Kind::User(User::InvalidConnectWithBody) => {
|
|
297
|
+
"user sent CONNECT request with non-zero body"
|
|
298
|
+
}
|
|
299
|
+
Kind::User(User::Service) => "error from user's Service",
|
|
300
|
+
Kind::User(User::NoUpgrade) => "no upgrade available",
|
|
301
|
+
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
|
|
302
|
+
Kind::User(User::DispatchGone) => "dispatch task is gone",
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
impl fmt::Debug for Error {
|
|
308
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
309
|
+
let mut f = f.debug_tuple("crate::core::Error");
|
|
310
|
+
f.field(&self.inner.kind);
|
|
311
|
+
if let Some(ref cause) = self.inner.cause {
|
|
312
|
+
f.field(cause);
|
|
313
|
+
}
|
|
314
|
+
f.finish()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
impl fmt::Display for Error {
|
|
319
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
320
|
+
f.write_str(self.description())
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
impl StdError for Error {
|
|
325
|
+
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
|
326
|
+
self.inner
|
|
327
|
+
.cause
|
|
328
|
+
.as_ref()
|
|
329
|
+
.map(|cause| &**cause as &(dyn StdError + 'static))
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#[doc(hidden)]
|
|
334
|
+
impl From<Parse> for Error {
|
|
335
|
+
fn from(err: Parse) -> Error {
|
|
336
|
+
Error::new(Kind::Parse(err))
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
impl Parse {
|
|
341
|
+
pub(crate) fn content_length_invalid() -> Self {
|
|
342
|
+
Parse::Header(Header::ContentLengthInvalid)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
pub(crate) fn transfer_encoding_unexpected() -> Self {
|
|
346
|
+
Parse::Header(Header::TransferEncodingUnexpected)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
impl From<httparse::Error> for Parse {
|
|
351
|
+
fn from(err: httparse::Error) -> Parse {
|
|
352
|
+
match err {
|
|
353
|
+
httparse::Error::HeaderName
|
|
354
|
+
| httparse::Error::HeaderValue
|
|
355
|
+
| httparse::Error::NewLine
|
|
356
|
+
| httparse::Error::Token => Parse::Header(Header::Token),
|
|
357
|
+
httparse::Error::Status => Parse::Status,
|
|
358
|
+
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
|
359
|
+
httparse::Error::Version => Parse::Version,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
impl From<http::method::InvalidMethod> for Parse {
|
|
365
|
+
fn from(_: http::method::InvalidMethod) -> Parse {
|
|
366
|
+
Parse::Method
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
impl From<http::status::InvalidStatusCode> for Parse {
|
|
371
|
+
fn from(_: http::status::InvalidStatusCode) -> Parse {
|
|
372
|
+
Parse::Status
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
impl From<http::uri::InvalidUri> for Parse {
|
|
377
|
+
fn from(_: http::uri::InvalidUri) -> Parse {
|
|
378
|
+
Parse::Uri
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
impl From<http::uri::InvalidUriParts> for Parse {
|
|
383
|
+
fn from(_: http::uri::InvalidUriParts) -> Parse {
|
|
384
|
+
Parse::Uri
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ===== impl TimedOut ====
|
|
389
|
+
|
|
390
|
+
impl fmt::Display for TimedOut {
|
|
391
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
392
|
+
f.write_str("operation timed out")
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
impl StdError for TimedOut {}
|
|
397
|
+
|
|
398
|
+
#[cfg(test)]
|
|
399
|
+
mod tests {
|
|
400
|
+
use std::mem;
|
|
401
|
+
|
|
402
|
+
use super::*;
|
|
403
|
+
|
|
404
|
+
fn assert_send_sync<T: Send + Sync + 'static>() {}
|
|
405
|
+
|
|
406
|
+
#[test]
|
|
407
|
+
fn error_satisfies_send_sync() {
|
|
408
|
+
assert_send_sync::<Error>()
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
#[test]
|
|
412
|
+
fn error_size_of() {
|
|
413
|
+
assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#[test]
|
|
417
|
+
fn h2_reason_unknown() {
|
|
418
|
+
let closed = Error::new_closed();
|
|
419
|
+
assert_eq!(closed.h2_reason(), http2::Reason::INTERNAL_ERROR);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#[test]
|
|
423
|
+
fn h2_reason_one_level() {
|
|
424
|
+
let body_err = Error::new_user_body(http2::Error::from(http2::Reason::ENHANCE_YOUR_CALM));
|
|
425
|
+
assert_eq!(body_err.h2_reason(), http2::Reason::ENHANCE_YOUR_CALM);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
#[test]
|
|
429
|
+
fn h2_reason_nested() {
|
|
430
|
+
let recvd = Error::new_h2(http2::Error::from(http2::Reason::HTTP_1_1_REQUIRED));
|
|
431
|
+
// Suppose a user were proxying the received error
|
|
432
|
+
let svc_err = Error::new_user_service(recvd);
|
|
433
|
+
assert_eq!(svc_err.h2_reason(), http2::Reason::HTTP_1_1_REQUIRED);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
//! HTTP extensions.
|
|
2
|
+
|
|
3
|
+
use bytes::Bytes;
|
|
4
|
+
|
|
5
|
+
/// A reason phrase in an HTTP/1 response.
|
|
6
|
+
///
|
|
7
|
+
/// # Clients
|
|
8
|
+
///
|
|
9
|
+
/// For clients, a `ReasonPhrase` will be present in the extensions of the `http::Response` returned
|
|
10
|
+
/// for a request if the reason phrase is different from the canonical reason phrase for the
|
|
11
|
+
/// response's status code. For example, if a server returns `HTTP/1.1 200 Awesome`, the
|
|
12
|
+
/// `ReasonPhrase` will be present and contain `Awesome`, but if a server returns `HTTP/1.1 200 OK`,
|
|
13
|
+
/// the response will not contain a `ReasonPhrase`.
|
|
14
|
+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
15
|
+
pub struct ReasonPhrase(Bytes);
|
|
16
|
+
|
|
17
|
+
impl ReasonPhrase {
|
|
18
|
+
/// Gets the reason phrase as bytes.
|
|
19
|
+
pub fn as_bytes(&self) -> &[u8] {
|
|
20
|
+
&self.0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Converts a static byte slice to a reason phrase.
|
|
24
|
+
pub const fn from_static(reason: &'static [u8]) -> Self {
|
|
25
|
+
// TODO: this can be made const once MSRV is >= 1.57.0
|
|
26
|
+
if find_invalid_byte(reason).is_some() {
|
|
27
|
+
panic!("invalid byte in static reason phrase");
|
|
28
|
+
}
|
|
29
|
+
Self(Bytes::from_static(reason))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Not public on purpose.
|
|
33
|
+
/// Converts a `Bytes` directly into a `ReasonPhrase` without validating.
|
|
34
|
+
///
|
|
35
|
+
/// Use with care; invalid bytes in a reason phrase can cause serious security problems if
|
|
36
|
+
/// emitted in a response.
|
|
37
|
+
pub(crate) fn from_bytes_unchecked(reason: Bytes) -> Self {
|
|
38
|
+
Self(reason)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl TryFrom<&[u8]> for ReasonPhrase {
|
|
43
|
+
type Error = InvalidReasonPhrase;
|
|
44
|
+
|
|
45
|
+
fn try_from(reason: &[u8]) -> Result<Self, Self::Error> {
|
|
46
|
+
if let Some(bad_byte) = find_invalid_byte(reason) {
|
|
47
|
+
Err(InvalidReasonPhrase { bad_byte })
|
|
48
|
+
} else {
|
|
49
|
+
Ok(Self(Bytes::copy_from_slice(reason)))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl TryFrom<Vec<u8>> for ReasonPhrase {
|
|
55
|
+
type Error = InvalidReasonPhrase;
|
|
56
|
+
|
|
57
|
+
fn try_from(reason: Vec<u8>) -> Result<Self, Self::Error> {
|
|
58
|
+
if let Some(bad_byte) = find_invalid_byte(&reason) {
|
|
59
|
+
Err(InvalidReasonPhrase { bad_byte })
|
|
60
|
+
} else {
|
|
61
|
+
Ok(Self(Bytes::from(reason)))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
impl TryFrom<String> for ReasonPhrase {
|
|
67
|
+
type Error = InvalidReasonPhrase;
|
|
68
|
+
|
|
69
|
+
fn try_from(reason: String) -> Result<Self, Self::Error> {
|
|
70
|
+
if let Some(bad_byte) = find_invalid_byte(reason.as_bytes()) {
|
|
71
|
+
Err(InvalidReasonPhrase { bad_byte })
|
|
72
|
+
} else {
|
|
73
|
+
Ok(Self(Bytes::from(reason)))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl TryFrom<Bytes> for ReasonPhrase {
|
|
79
|
+
type Error = InvalidReasonPhrase;
|
|
80
|
+
|
|
81
|
+
fn try_from(reason: Bytes) -> Result<Self, Self::Error> {
|
|
82
|
+
if let Some(bad_byte) = find_invalid_byte(&reason) {
|
|
83
|
+
Err(InvalidReasonPhrase { bad_byte })
|
|
84
|
+
} else {
|
|
85
|
+
Ok(Self(reason))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl From<ReasonPhrase> for Bytes {
|
|
91
|
+
fn from(reason: ReasonPhrase) -> Self {
|
|
92
|
+
reason.0
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
impl AsRef<[u8]> for ReasonPhrase {
|
|
97
|
+
fn as_ref(&self) -> &[u8] {
|
|
98
|
+
&self.0
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Error indicating an invalid byte when constructing a `ReasonPhrase`.
|
|
103
|
+
///
|
|
104
|
+
/// See [the spec][spec] for details on allowed bytes.
|
|
105
|
+
///
|
|
106
|
+
/// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.7
|
|
107
|
+
#[derive(Debug)]
|
|
108
|
+
pub struct InvalidReasonPhrase {
|
|
109
|
+
bad_byte: u8,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
impl std::fmt::Display for InvalidReasonPhrase {
|
|
113
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
114
|
+
write!(f, "Invalid byte in reason phrase: {}", self.bad_byte)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
impl std::error::Error for InvalidReasonPhrase {}
|
|
119
|
+
|
|
120
|
+
const fn is_valid_byte(b: u8) -> bool {
|
|
121
|
+
// See https://www.rfc-editor.org/rfc/rfc5234.html#appendix-B.1
|
|
122
|
+
const fn is_vchar(b: u8) -> bool {
|
|
123
|
+
0x21 <= b && b <= 0x7E
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// See https://httpwg.org/http-core/draft-ietf-httpbis-semantics-latest.html#fields.values
|
|
127
|
+
//
|
|
128
|
+
// The 0xFF comparison is technically redundant, but it matches the text of the spec more
|
|
129
|
+
// clearly and will be optimized away.
|
|
130
|
+
#[allow(unused_comparisons, clippy::absurd_extreme_comparisons)]
|
|
131
|
+
const fn is_obs_text(b: u8) -> bool {
|
|
132
|
+
0x80 <= b && b <= 0xFF
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// See https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.7
|
|
136
|
+
b == b'\t' || b == b' ' || is_vchar(b) || is_obs_text(b)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const fn find_invalid_byte(bytes: &[u8]) -> Option<u8> {
|
|
140
|
+
let mut i = 0;
|
|
141
|
+
while i < bytes.len() {
|
|
142
|
+
let b = bytes[i];
|
|
143
|
+
if !is_valid_byte(b) {
|
|
144
|
+
return Some(b);
|
|
145
|
+
}
|
|
146
|
+
i += 1;
|
|
147
|
+
}
|
|
148
|
+
None
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[cfg(test)]
|
|
152
|
+
mod tests {
|
|
153
|
+
use super::*;
|
|
154
|
+
|
|
155
|
+
#[test]
|
|
156
|
+
fn basic_valid() {
|
|
157
|
+
const PHRASE: &[u8] = b"OK";
|
|
158
|
+
assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE);
|
|
159
|
+
assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#[test]
|
|
163
|
+
fn empty_valid() {
|
|
164
|
+
const PHRASE: &[u8] = b"";
|
|
165
|
+
assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE);
|
|
166
|
+
assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#[test]
|
|
170
|
+
fn obs_text_valid() {
|
|
171
|
+
const PHRASE: &[u8] = b"hyp\xe9r";
|
|
172
|
+
assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE);
|
|
173
|
+
assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const NEWLINE_PHRASE: &[u8] = b"hyp\ner";
|
|
177
|
+
|
|
178
|
+
#[test]
|
|
179
|
+
#[should_panic]
|
|
180
|
+
fn newline_invalid_panic() {
|
|
181
|
+
ReasonPhrase::from_static(NEWLINE_PHRASE);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#[test]
|
|
185
|
+
fn newline_invalid_err() {
|
|
186
|
+
assert!(ReasonPhrase::try_from(NEWLINE_PHRASE).is_err());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const CR_PHRASE: &[u8] = b"hyp\rer";
|
|
190
|
+
|
|
191
|
+
#[test]
|
|
192
|
+
#[should_panic]
|
|
193
|
+
fn cr_invalid_panic() {
|
|
194
|
+
ReasonPhrase::from_static(CR_PHRASE);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
fn cr_invalid_err() {
|
|
199
|
+
assert!(ReasonPhrase::try_from(CR_PHRASE).is_err());
|
|
200
|
+
}
|
|
201
|
+
}
|