1//! Error and Result module.
2
3#[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
4use crate::client::connect::Connected;
5use std::error::Error as StdError;
6use std::fmt;
7
8/// Result type often returned from methods that can have hyper `Error`s.
9pub type Result<T> = std::result::Result<T, Error>;
10
11type Cause = Box<dyn StdError + Send + Sync>;
12
13/// Represents errors that can occur handling HTTP streams.
14pub struct Error {
15 inner: Box<ErrorImpl>,
16}
17
18struct ErrorImpl {
19 kind: Kind,
20 cause: Option<Cause>,
21 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
22 connect_info: Option<Connected>,
23}
24
25#[derive(Debug)]
26pub(super) enum Kind {
27 Parse(Parse),
28 User(User),
29 /// A message reached EOF, but is not complete.
30 #[allow(unused)]
31 IncompleteMessage,
32 /// A connection received a message (or bytes) when not waiting for one.
33 #[cfg(feature = "http1")]
34 UnexpectedMessage,
35 /// A pending item was dropped before ever being processed.
36 Canceled,
37 /// Indicates a channel (client or body sender) is closed.
38 ChannelClosed,
39 /// An `io::Error` that occurred while trying to read or write to a network stream.
40 #[cfg(any(feature = "http1", feature = "http2"))]
41 Io,
42 /// Error occurred while connecting.
43 #[allow(unused)]
44 Connect,
45 /// Error creating a TcpListener.
46 #[cfg(all(feature = "tcp", feature = "server"))]
47 Listen,
48 /// Error accepting on an Incoming stream.
49 #[cfg(any(feature = "http1", feature = "http2"))]
50 #[cfg(feature = "server")]
51 Accept,
52 /// User took too long to send headers
53 #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
54 HeaderTimeout,
55 /// Error while reading a body from connection.
56 #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
57 Body,
58 /// Error while writing a body to connection.
59 #[cfg(any(feature = "http1", feature = "http2"))]
60 BodyWrite,
61 /// Error calling AsyncWrite::shutdown()
62 #[cfg(feature = "http1")]
63 Shutdown,
64
65 /// A general error from h2.
66 #[cfg(feature = "http2")]
67 Http2,
68}
69
70#[derive(Debug)]
71pub(super) enum Parse {
72 Method,
73 Version,
74 #[cfg(feature = "http1")]
75 VersionH2,
76 Uri,
77 #[cfg_attr(not(all(feature = "http1", feature = "server")), allow(unused))]
78 UriTooLong,
79 Header(Header),
80 TooLarge,
81 Status,
82 #[cfg_attr(debug_assertions, allow(unused))]
83 Internal,
84}
85
86#[derive(Debug)]
87pub(super) enum Header {
88 Token,
89 #[cfg(feature = "http1")]
90 ContentLengthInvalid,
91 #[cfg(all(feature = "http1", feature = "server"))]
92 TransferEncodingInvalid,
93 #[cfg(feature = "http1")]
94 TransferEncodingUnexpected,
95}
96
97#[derive(Debug)]
98pub(super) enum User {
99 /// Error calling user's HttpBody::poll_data().
100 #[cfg(any(feature = "http1", feature = "http2"))]
101 Body,
102 /// The user aborted writing of the outgoing body.
103 BodyWriteAborted,
104 /// Error calling user's MakeService.
105 #[cfg(any(feature = "http1", feature = "http2"))]
106 #[cfg(feature = "server")]
107 MakeService,
108 /// Error from future of user's Service.
109 #[cfg(any(feature = "http1", feature = "http2"))]
110 Service,
111 /// User tried to send a certain header in an unexpected context.
112 ///
113 /// For example, sending both `content-length` and `transfer-encoding`.
114 #[cfg(any(feature = "http1", feature = "http2"))]
115 #[cfg(feature = "server")]
116 UnexpectedHeader,
117 /// User tried to create a Request with bad version.
118 #[cfg(any(feature = "http1", feature = "http2"))]
119 #[cfg(feature = "client")]
120 UnsupportedVersion,
121 /// User tried to create a CONNECT Request with the Client.
122 #[cfg(any(feature = "http1", feature = "http2"))]
123 #[cfg(feature = "client")]
124 UnsupportedRequestMethod,
125 /// User tried to respond with a 1xx (not 101) response code.
126 #[cfg(feature = "http1")]
127 #[cfg(feature = "server")]
128 UnsupportedStatusCode,
129 /// User tried to send a Request with Client with non-absolute URI.
130 #[cfg(any(feature = "http1", feature = "http2"))]
131 #[cfg(feature = "client")]
132 AbsoluteUriRequired,
133
134 /// User tried polling for an upgrade that doesn't exist.
135 NoUpgrade,
136
137 /// User polled for an upgrade, but low-level API is not using upgrades.
138 #[cfg(feature = "http1")]
139 ManualUpgrade,
140
141 /// User called `server::Connection::without_shutdown()` on an HTTP/2 conn.
142 #[cfg(feature = "server")]
143 WithoutShutdownNonHttp1,
144
145 /// The dispatch task is gone.
146 #[cfg(feature = "client")]
147 DispatchGone,
148
149 /// User aborted in an FFI callback.
150 #[cfg(feature = "ffi")]
151 AbortedByCallback,
152}
153
154// Sentinel type to indicate the error was caused by a timeout.
155#[derive(Debug)]
156pub(super) struct TimedOut;
157
158impl Error {
159 /// Returns true if this was an HTTP parse error.
160 pub fn is_parse(&self) -> bool {
161 matches!(self.inner.kind, Kind::Parse(_))
162 }
163
164 /// Returns true if this was an HTTP parse error caused by a message that was too large.
165 pub fn is_parse_too_large(&self) -> bool {
166 matches!(
167 self.inner.kind,
168 Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong)
169 )
170 }
171
172 /// Returns true if this was an HTTP parse error caused by an invalid response status code or
173 /// reason phrase.
174 pub fn is_parse_status(&self) -> bool {
175 matches!(self.inner.kind, Kind::Parse(Parse::Status))
176 }
177
178 /// Returns true if this error was caused by user code.
179 pub fn is_user(&self) -> bool {
180 matches!(self.inner.kind, Kind::User(_))
181 }
182
183 /// Returns true if this was about a `Request` that was canceled.
184 pub fn is_canceled(&self) -> bool {
185 matches!(self.inner.kind, Kind::Canceled)
186 }
187
188 /// Returns true if a sender's channel is closed.
189 pub fn is_closed(&self) -> bool {
190 matches!(self.inner.kind, Kind::ChannelClosed)
191 }
192
193 /// Returns true if this was an error from `Connect`.
194 pub fn is_connect(&self) -> bool {
195 matches!(self.inner.kind, Kind::Connect)
196 }
197
198 /// Returns true if the connection closed before a message could complete.
199 pub fn is_incomplete_message(&self) -> bool {
200 matches!(self.inner.kind, Kind::IncompleteMessage)
201 }
202
203 /// Returns true if the body write was aborted.
204 pub fn is_body_write_aborted(&self) -> bool {
205 matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
206 }
207
208 /// Returns true if the error was caused by a timeout.
209 pub fn is_timeout(&self) -> bool {
210 self.find_source::<TimedOut>().is_some()
211 }
212
213 /// Consumes the error, returning its cause.
214 pub fn into_cause(self) -> Option<Box<dyn StdError + Send + Sync>> {
215 self.inner.cause
216 }
217
218 /// Returns the info of the client connection on which this error occurred.
219 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
220 pub fn client_connect_info(&self) -> Option<&Connected> {
221 self.inner.connect_info.as_ref()
222 }
223
224 pub(super) fn new(kind: Kind) -> Error {
225 Error {
226 inner: Box::new(ErrorImpl {
227 kind,
228 cause: None,
229 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
230 connect_info: None,
231 }),
232 }
233 }
234
235 pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
236 self.inner.cause = Some(cause.into());
237 self
238 }
239
240 #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))]
241 pub(super) fn with_client_connect_info(mut self, connect_info: Connected) -> Error {
242 self.inner.connect_info = Some(connect_info);
243 self
244 }
245
246 #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))]
247 pub(super) fn kind(&self) -> &Kind {
248 &self.inner.kind
249 }
250
251 pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
252 let mut cause = self.source();
253 while let Some(err) = cause {
254 if let Some(ref typed) = err.downcast_ref() {
255 return Some(typed);
256 }
257 cause = err.source();
258 }
259
260 // else
261 None
262 }
263
264 #[cfg(feature = "http2")]
265 pub(super) fn h2_reason(&self) -> h2::Reason {
266 // Find an h2::Reason somewhere in the cause stack, if it exists,
267 // otherwise assume an INTERNAL_ERROR.
268 self.find_source::<h2::Error>()
269 .and_then(|h2_err| h2_err.reason())
270 .unwrap_or(h2::Reason::INTERNAL_ERROR)
271 }
272
273 pub(super) fn new_canceled() -> Error {
274 Error::new(Kind::Canceled)
275 }
276
277 #[cfg(feature = "http1")]
278 pub(super) fn new_incomplete() -> Error {
279 Error::new(Kind::IncompleteMessage)
280 }
281
282 #[cfg(feature = "http1")]
283 pub(super) fn new_too_large() -> Error {
284 Error::new(Kind::Parse(Parse::TooLarge))
285 }
286
287 #[cfg(feature = "http1")]
288 pub(super) fn new_version_h2() -> Error {
289 Error::new(Kind::Parse(Parse::VersionH2))
290 }
291
292 #[cfg(feature = "http1")]
293 pub(super) fn new_unexpected_message() -> Error {
294 Error::new(Kind::UnexpectedMessage)
295 }
296
297 #[cfg(any(feature = "http1", feature = "http2"))]
298 pub(super) fn new_io(cause: std::io::Error) -> Error {
299 Error::new(Kind::Io).with(cause)
300 }
301
302 #[cfg(all(feature = "server", feature = "tcp"))]
303 pub(super) fn new_listen<E: Into<Cause>>(cause: E) -> Error {
304 Error::new(Kind::Listen).with(cause)
305 }
306
307 #[cfg(any(feature = "http1", feature = "http2"))]
308 #[cfg(feature = "server")]
309 pub(super) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
310 Error::new(Kind::Accept).with(cause)
311 }
312
313 #[cfg(any(feature = "http1", feature = "http2"))]
314 #[cfg(feature = "client")]
315 pub(super) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
316 Error::new(Kind::Connect).with(cause)
317 }
318
319 pub(super) fn new_closed() -> Error {
320 Error::new(Kind::ChannelClosed)
321 }
322
323 #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
324 pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
325 Error::new(Kind::Body).with(cause)
326 }
327
328 #[cfg(any(feature = "http1", feature = "http2"))]
329 pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
330 Error::new(Kind::BodyWrite).with(cause)
331 }
332
333 pub(super) fn new_body_write_aborted() -> Error {
334 Error::new(Kind::User(User::BodyWriteAborted))
335 }
336
337 fn new_user(user: User) -> Error {
338 Error::new(Kind::User(user))
339 }
340
341 #[cfg(any(feature = "http1", feature = "http2"))]
342 #[cfg(feature = "server")]
343 pub(super) fn new_user_header() -> Error {
344 Error::new_user(User::UnexpectedHeader)
345 }
346
347 #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
348 pub(super) fn new_header_timeout() -> Error {
349 Error::new(Kind::HeaderTimeout)
350 }
351
352 #[cfg(any(feature = "http1", feature = "http2"))]
353 #[cfg(feature = "client")]
354 pub(super) fn new_user_unsupported_version() -> Error {
355 Error::new_user(User::UnsupportedVersion)
356 }
357
358 #[cfg(any(feature = "http1", feature = "http2"))]
359 #[cfg(feature = "client")]
360 pub(super) fn new_user_unsupported_request_method() -> Error {
361 Error::new_user(User::UnsupportedRequestMethod)
362 }
363
364 #[cfg(feature = "http1")]
365 #[cfg(feature = "server")]
366 pub(super) fn new_user_unsupported_status_code() -> Error {
367 Error::new_user(User::UnsupportedStatusCode)
368 }
369
370 #[cfg(any(feature = "http1", feature = "http2"))]
371 #[cfg(feature = "client")]
372 pub(super) fn new_user_absolute_uri_required() -> Error {
373 Error::new_user(User::AbsoluteUriRequired)
374 }
375
376 pub(super) fn new_user_no_upgrade() -> Error {
377 Error::new_user(User::NoUpgrade)
378 }
379
380 #[cfg(feature = "http1")]
381 pub(super) fn new_user_manual_upgrade() -> Error {
382 Error::new_user(User::ManualUpgrade)
383 }
384
385 #[cfg(any(feature = "http1", feature = "http2"))]
386 #[cfg(feature = "server")]
387 pub(super) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error {
388 Error::new_user(User::MakeService).with(cause)
389 }
390
391 #[cfg(any(feature = "http1", feature = "http2"))]
392 pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
393 Error::new_user(User::Service).with(cause)
394 }
395
396 #[cfg(any(feature = "http1", feature = "http2"))]
397 pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
398 Error::new_user(User::Body).with(cause)
399 }
400
401 #[cfg(feature = "server")]
402 pub(super) fn new_without_shutdown_not_h1() -> Error {
403 Error::new(Kind::User(User::WithoutShutdownNonHttp1))
404 }
405
406 #[cfg(feature = "http1")]
407 pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
408 Error::new(Kind::Shutdown).with(cause)
409 }
410
411 #[cfg(feature = "ffi")]
412 pub(super) fn new_user_aborted_by_callback() -> Error {
413 Error::new_user(User::AbortedByCallback)
414 }
415
416 #[cfg(feature = "client")]
417 pub(super) fn new_user_dispatch_gone() -> Error {
418 Error::new(Kind::User(User::DispatchGone))
419 }
420
421 #[cfg(feature = "http2")]
422 pub(super) fn new_h2(cause: ::h2::Error) -> Error {
423 if cause.is_io() {
424 Error::new_io(cause.into_io().expect("h2::Error::is_io"))
425 } else {
426 Error::new(Kind::Http2).with(cause)
427 }
428 }
429
430 /// The error's standalone message, without the message from the source.
431 pub fn message(&self) -> impl fmt::Display + '_ {
432 self.description()
433 }
434
435 fn description(&self) -> &str {
436 match self.inner.kind {
437 Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
438 Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
439 #[cfg(feature = "http1")]
440 Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
441 Kind::Parse(Parse::Uri) => "invalid URI",
442 Kind::Parse(Parse::UriTooLong) => "URI too long",
443 Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
444 #[cfg(feature = "http1")]
445 Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
446 "invalid content-length parsed"
447 }
448 #[cfg(all(feature = "http1", feature = "server"))]
449 Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
450 "invalid transfer-encoding parsed"
451 }
452 #[cfg(feature = "http1")]
453 Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
454 "unexpected transfer-encoding parsed"
455 }
456 Kind::Parse(Parse::TooLarge) => "message head is too large",
457 Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
458 Kind::Parse(Parse::Internal) => {
459 "internal error inside Hyper and/or its dependencies, please report"
460 }
461 Kind::IncompleteMessage => "connection closed before message completed",
462 #[cfg(feature = "http1")]
463 Kind::UnexpectedMessage => "received unexpected message from connection",
464 Kind::ChannelClosed => "channel closed",
465 Kind::Connect => "error trying to connect",
466 Kind::Canceled => "operation was canceled",
467 #[cfg(all(feature = "server", feature = "tcp"))]
468 Kind::Listen => "error creating server listener",
469 #[cfg(any(feature = "http1", feature = "http2"))]
470 #[cfg(feature = "server")]
471 Kind::Accept => "error accepting connection",
472 #[cfg(all(feature = "http1", feature = "server", feature = "runtime"))]
473 Kind::HeaderTimeout => "read header from client timeout",
474 #[cfg(any(feature = "http1", feature = "http2", feature = "stream"))]
475 Kind::Body => "error reading a body from connection",
476 #[cfg(any(feature = "http1", feature = "http2"))]
477 Kind::BodyWrite => "error writing a body to connection",
478 #[cfg(feature = "http1")]
479 Kind::Shutdown => "error shutting down connection",
480 #[cfg(feature = "http2")]
481 Kind::Http2 => "http2 error",
482 #[cfg(any(feature = "http1", feature = "http2"))]
483 Kind::Io => "connection error",
484
485 #[cfg(any(feature = "http1", feature = "http2"))]
486 Kind::User(User::Body) => "error from user's HttpBody stream",
487 Kind::User(User::BodyWriteAborted) => "user body write aborted",
488 #[cfg(any(feature = "http1", feature = "http2"))]
489 #[cfg(feature = "server")]
490 Kind::User(User::MakeService) => "error from user's MakeService",
491 #[cfg(any(feature = "http1", feature = "http2"))]
492 Kind::User(User::Service) => "error from user's Service",
493 #[cfg(any(feature = "http1", feature = "http2"))]
494 #[cfg(feature = "server")]
495 Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
496 #[cfg(any(feature = "http1", feature = "http2"))]
497 #[cfg(feature = "client")]
498 Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
499 #[cfg(any(feature = "http1", feature = "http2"))]
500 #[cfg(feature = "client")]
501 Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
502 #[cfg(feature = "http1")]
503 #[cfg(feature = "server")]
504 Kind::User(User::UnsupportedStatusCode) => {
505 "response has 1xx status code, not supported by server"
506 }
507 #[cfg(any(feature = "http1", feature = "http2"))]
508 #[cfg(feature = "client")]
509 Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
510 Kind::User(User::NoUpgrade) => "no upgrade available",
511 #[cfg(feature = "http1")]
512 Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
513 #[cfg(feature = "server")]
514 Kind::User(User::WithoutShutdownNonHttp1) => {
515 "without_shutdown() called on a non-HTTP/1 connection"
516 }
517 #[cfg(feature = "client")]
518 Kind::User(User::DispatchGone) => "dispatch task is gone",
519 #[cfg(feature = "ffi")]
520 Kind::User(User::AbortedByCallback) => "operation aborted by an application callback",
521 }
522 }
523}
524
525impl fmt::Debug for Error {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 let mut f: DebugTuple<'_, '_> = f.debug_tuple(name:"hyper::Error");
528 f.field(&self.inner.kind);
529 if let Some(ref cause: &Box) = self.inner.cause {
530 f.field(cause);
531 }
532 f.finish()
533 }
534}
535
536impl fmt::Display for Error {
537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538 if let Some(ref cause: &Box) = self.inner.cause {
539 write!(f, "{}: {}", self.description(), cause)
540 } else {
541 f.write_str(self.description())
542 }
543 }
544}
545
546impl StdError for Error {
547 fn source(&self) -> Option<&(dyn StdError + 'static)> {
548 self.inner
549 .cause
550 .as_ref()
551 .map(|cause: &Box| &**cause as &(dyn StdError + 'static))
552 }
553}
554
555#[doc(hidden)]
556impl From<Parse> for Error {
557 fn from(err: Parse) -> Error {
558 Error::new(Kind::Parse(err))
559 }
560}
561
562#[cfg(feature = "http1")]
563impl Parse {
564 pub(crate) fn content_length_invalid() -> Self {
565 Parse::Header(Header::ContentLengthInvalid)
566 }
567
568 #[cfg(all(feature = "http1", feature = "server"))]
569 pub(crate) fn transfer_encoding_invalid() -> Self {
570 Parse::Header(Header::TransferEncodingInvalid)
571 }
572
573 pub(crate) fn transfer_encoding_unexpected() -> Self {
574 Parse::Header(Header::TransferEncodingUnexpected)
575 }
576}
577
578impl From<httparse::Error> for Parse {
579 fn from(err: httparse::Error) -> Parse {
580 match err {
581 httparse::Error::HeaderName
582 | httparse::Error::HeaderValue
583 | httparse::Error::NewLine
584 | httparse::Error::Token => Parse::Header(Header::Token),
585 httparse::Error::Status => Parse::Status,
586 httparse::Error::TooManyHeaders => Parse::TooLarge,
587 httparse::Error::Version => Parse::Version,
588 }
589 }
590}
591
592impl From<http::method::InvalidMethod> for Parse {
593 fn from(_: http::method::InvalidMethod) -> Parse {
594 Parse::Method
595 }
596}
597
598impl From<http::status::InvalidStatusCode> for Parse {
599 fn from(_: http::status::InvalidStatusCode) -> Parse {
600 Parse::Status
601 }
602}
603
604impl From<http::uri::InvalidUri> for Parse {
605 fn from(_: http::uri::InvalidUri) -> Parse {
606 Parse::Uri
607 }
608}
609
610impl From<http::uri::InvalidUriParts> for Parse {
611 fn from(_: http::uri::InvalidUriParts) -> Parse {
612 Parse::Uri
613 }
614}
615
616#[doc(hidden)]
617trait AssertSendSync: Send + Sync + 'static {}
618#[doc(hidden)]
619impl AssertSendSync for Error {}
620
621// ===== impl TimedOut ====
622
623impl fmt::Display for TimedOut {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 f.write_str(data:"operation timed out")
626 }
627}
628
629impl StdError for TimedOut {}
630
631#[cfg(test)]
632mod tests {
633 use super::*;
634 use std::mem;
635
636 #[test]
637 fn error_size_of() {
638 assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
639 }
640
641 #[cfg(feature = "http2")]
642 #[test]
643 fn h2_reason_unknown() {
644 let closed = Error::new_closed();
645 assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR);
646 }
647
648 #[cfg(feature = "http2")]
649 #[test]
650 fn h2_reason_one_level() {
651 let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM));
652 assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM);
653 }
654
655 #[cfg(feature = "http2")]
656 #[test]
657 fn h2_reason_nested() {
658 let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED));
659 // Suppose a user were proxying the received error
660 let svc_err = Error::new_user_service(recvd);
661 assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
662 }
663}
664