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.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +2688 -0
  3. data/Cargo.toml +6 -0
  4. data/README.md +179 -0
  5. data/ext/wreq_rb/Cargo.toml +39 -0
  6. data/ext/wreq_rb/extconf.rb +22 -0
  7. data/ext/wreq_rb/src/client.rs +565 -0
  8. data/ext/wreq_rb/src/error.rs +25 -0
  9. data/ext/wreq_rb/src/lib.rs +20 -0
  10. data/ext/wreq_rb/src/response.rs +132 -0
  11. data/lib/wreq-rb/version.rb +5 -0
  12. data/lib/wreq-rb.rb +17 -0
  13. data/patches/0001-add-transfer-size-tracking.patch +292 -0
  14. data/vendor/wreq/Cargo.toml +306 -0
  15. data/vendor/wreq/LICENSE +202 -0
  16. data/vendor/wreq/README.md +122 -0
  17. data/vendor/wreq/examples/cert_store.rs +77 -0
  18. data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
  19. data/vendor/wreq/examples/emulation.rs +118 -0
  20. data/vendor/wreq/examples/form.rs +14 -0
  21. data/vendor/wreq/examples/http1_websocket.rs +37 -0
  22. data/vendor/wreq/examples/http2_websocket.rs +45 -0
  23. data/vendor/wreq/examples/json_dynamic.rs +41 -0
  24. data/vendor/wreq/examples/json_typed.rs +47 -0
  25. data/vendor/wreq/examples/keylog.rs +16 -0
  26. data/vendor/wreq/examples/request_with_emulation.rs +115 -0
  27. data/vendor/wreq/examples/request_with_interface.rs +37 -0
  28. data/vendor/wreq/examples/request_with_local_address.rs +16 -0
  29. data/vendor/wreq/examples/request_with_proxy.rs +13 -0
  30. data/vendor/wreq/examples/request_with_redirect.rs +22 -0
  31. data/vendor/wreq/examples/request_with_version.rs +15 -0
  32. data/vendor/wreq/examples/tor_socks.rs +24 -0
  33. data/vendor/wreq/examples/unix_socket.rs +33 -0
  34. data/vendor/wreq/src/client/body.rs +304 -0
  35. data/vendor/wreq/src/client/conn/conn.rs +231 -0
  36. data/vendor/wreq/src/client/conn/connector.rs +549 -0
  37. data/vendor/wreq/src/client/conn/http.rs +1023 -0
  38. data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
  39. data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
  40. data/vendor/wreq/src/client/conn/proxy.rs +39 -0
  41. data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
  42. data/vendor/wreq/src/client/conn/uds.rs +44 -0
  43. data/vendor/wreq/src/client/conn/verbose.rs +149 -0
  44. data/vendor/wreq/src/client/conn.rs +323 -0
  45. data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
  46. data/vendor/wreq/src/client/core/body/length.rs +118 -0
  47. data/vendor/wreq/src/client/core/body.rs +34 -0
  48. data/vendor/wreq/src/client/core/common/buf.rs +149 -0
  49. data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
  50. data/vendor/wreq/src/client/core/common/watch.rs +76 -0
  51. data/vendor/wreq/src/client/core/common.rs +3 -0
  52. data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
  53. data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
  54. data/vendor/wreq/src/client/core/conn.rs +11 -0
  55. data/vendor/wreq/src/client/core/dispatch.rs +299 -0
  56. data/vendor/wreq/src/client/core/error.rs +435 -0
  57. data/vendor/wreq/src/client/core/ext.rs +201 -0
  58. data/vendor/wreq/src/client/core/http1.rs +178 -0
  59. data/vendor/wreq/src/client/core/http2.rs +483 -0
  60. data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
  61. data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
  62. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
  63. data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
  64. data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
  65. data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
  66. data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
  67. data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
  68. data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
  69. data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
  70. data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
  71. data/vendor/wreq/src/client/core/proto.rs +58 -0
  72. data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
  73. data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
  74. data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
  75. data/vendor/wreq/src/client/core/rt.rs +25 -0
  76. data/vendor/wreq/src/client/core/upgrade.rs +267 -0
  77. data/vendor/wreq/src/client/core.rs +16 -0
  78. data/vendor/wreq/src/client/emulation.rs +161 -0
  79. data/vendor/wreq/src/client/http/client/error.rs +142 -0
  80. data/vendor/wreq/src/client/http/client/exec.rs +29 -0
  81. data/vendor/wreq/src/client/http/client/extra.rs +77 -0
  82. data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
  83. data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
  84. data/vendor/wreq/src/client/http/client/util.rs +104 -0
  85. data/vendor/wreq/src/client/http/client.rs +1003 -0
  86. data/vendor/wreq/src/client/http/future.rs +99 -0
  87. data/vendor/wreq/src/client/http.rs +1629 -0
  88. data/vendor/wreq/src/client/layer/config/options.rs +156 -0
  89. data/vendor/wreq/src/client/layer/config.rs +116 -0
  90. data/vendor/wreq/src/client/layer/cookie.rs +161 -0
  91. data/vendor/wreq/src/client/layer/decoder.rs +139 -0
  92. data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
  93. data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
  94. data/vendor/wreq/src/client/layer/redirect.rs +145 -0
  95. data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
  96. data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
  97. data/vendor/wreq/src/client/layer/retry.rs +151 -0
  98. data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
  99. data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
  100. data/vendor/wreq/src/client/layer/timeout.rs +177 -0
  101. data/vendor/wreq/src/client/layer.rs +15 -0
  102. data/vendor/wreq/src/client/multipart.rs +717 -0
  103. data/vendor/wreq/src/client/request.rs +818 -0
  104. data/vendor/wreq/src/client/response.rs +534 -0
  105. data/vendor/wreq/src/client/ws/json.rs +99 -0
  106. data/vendor/wreq/src/client/ws/message.rs +453 -0
  107. data/vendor/wreq/src/client/ws.rs +714 -0
  108. data/vendor/wreq/src/client.rs +27 -0
  109. data/vendor/wreq/src/config.rs +140 -0
  110. data/vendor/wreq/src/cookie.rs +579 -0
  111. data/vendor/wreq/src/dns/gai.rs +249 -0
  112. data/vendor/wreq/src/dns/hickory.rs +78 -0
  113. data/vendor/wreq/src/dns/resolve.rs +180 -0
  114. data/vendor/wreq/src/dns.rs +69 -0
  115. data/vendor/wreq/src/error.rs +502 -0
  116. data/vendor/wreq/src/ext.rs +398 -0
  117. data/vendor/wreq/src/hash.rs +143 -0
  118. data/vendor/wreq/src/header.rs +506 -0
  119. data/vendor/wreq/src/into_uri.rs +187 -0
  120. data/vendor/wreq/src/lib.rs +586 -0
  121. data/vendor/wreq/src/proxy/mac.rs +82 -0
  122. data/vendor/wreq/src/proxy/matcher.rs +806 -0
  123. data/vendor/wreq/src/proxy/uds.rs +66 -0
  124. data/vendor/wreq/src/proxy/win.rs +31 -0
  125. data/vendor/wreq/src/proxy.rs +569 -0
  126. data/vendor/wreq/src/redirect.rs +575 -0
  127. data/vendor/wreq/src/retry.rs +198 -0
  128. data/vendor/wreq/src/sync.rs +129 -0
  129. data/vendor/wreq/src/tls/conn/cache.rs +123 -0
  130. data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
  131. data/vendor/wreq/src/tls/conn/ext.rs +82 -0
  132. data/vendor/wreq/src/tls/conn/macros.rs +34 -0
  133. data/vendor/wreq/src/tls/conn/service.rs +138 -0
  134. data/vendor/wreq/src/tls/conn.rs +681 -0
  135. data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
  136. data/vendor/wreq/src/tls/keylog.rs +99 -0
  137. data/vendor/wreq/src/tls/options.rs +464 -0
  138. data/vendor/wreq/src/tls/x509/identity.rs +122 -0
  139. data/vendor/wreq/src/tls/x509/parser.rs +71 -0
  140. data/vendor/wreq/src/tls/x509/store.rs +228 -0
  141. data/vendor/wreq/src/tls/x509.rs +68 -0
  142. data/vendor/wreq/src/tls.rs +154 -0
  143. data/vendor/wreq/src/trace.rs +55 -0
  144. data/vendor/wreq/src/util.rs +122 -0
  145. data/vendor/wreq/tests/badssl.rs +228 -0
  146. data/vendor/wreq/tests/brotli.rs +350 -0
  147. data/vendor/wreq/tests/client.rs +1098 -0
  148. data/vendor/wreq/tests/connector_layers.rs +227 -0
  149. data/vendor/wreq/tests/cookie.rs +306 -0
  150. data/vendor/wreq/tests/deflate.rs +347 -0
  151. data/vendor/wreq/tests/emulation.rs +260 -0
  152. data/vendor/wreq/tests/gzip.rs +347 -0
  153. data/vendor/wreq/tests/layers.rs +261 -0
  154. data/vendor/wreq/tests/multipart.rs +165 -0
  155. data/vendor/wreq/tests/proxy.rs +438 -0
  156. data/vendor/wreq/tests/redirect.rs +629 -0
  157. data/vendor/wreq/tests/retry.rs +135 -0
  158. data/vendor/wreq/tests/support/delay_server.rs +117 -0
  159. data/vendor/wreq/tests/support/error.rs +16 -0
  160. data/vendor/wreq/tests/support/layer.rs +183 -0
  161. data/vendor/wreq/tests/support/mod.rs +9 -0
  162. data/vendor/wreq/tests/support/server.rs +232 -0
  163. data/vendor/wreq/tests/timeouts.rs +281 -0
  164. data/vendor/wreq/tests/unix_socket.rs +135 -0
  165. data/vendor/wreq/tests/upgrade.rs +98 -0
  166. data/vendor/wreq/tests/zstd.rs +559 -0
  167. 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
+ }