hyper/
error.rs

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 = f.debug_tuple("hyper::Error");
528        f.field(&self.inner.kind);
529        if let Some(ref cause) = 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) = 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| &**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("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}