thrift/
errors.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::convert::TryFrom;
19use std::convert::{From, Into};
20use std::fmt::{Debug, Display, Formatter};
21use std::{error, fmt, io, string};
22
23use crate::protocol::{
24    TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentifier, TType,
25};
26
27// FIXME: should all my error structs impl error::Error as well?
28// FIXME: should all fields in TransportError, ProtocolError and ApplicationError be optional?
29
30/// Error type returned by all runtime library functions.
31///
32/// `thrift::Error` is used throughout this crate as well as in auto-generated
33/// Rust code. It consists of four variants defined by convention across Thrift
34/// implementations:
35///
36/// 1. `Transport`: errors encountered while operating on I/O channels
37/// 2. `Protocol`: errors encountered during runtime-library processing
38/// 3. `Application`: errors encountered within auto-generated code
39/// 4. `User`: IDL-defined exception structs
40///
41/// The `Application` variant also functions as a catch-all: all handler errors
42/// are automatically turned into application errors.
43///
44/// All error variants except `Error::User` take an eponymous struct with two
45/// required fields:
46///
47/// 1. `kind`: variant-specific enum identifying the error sub-type
48/// 2. `message`: human-readable error info string
49///
50/// `kind` is defined by convention while `message` is freeform. If none of the
51/// enumerated kinds are suitable use `Unknown`.
52///
53/// To simplify error creation convenience constructors are defined for all
54/// variants, and conversions from their structs (`thrift::TransportError`,
55/// `thrift::ProtocolError` and `thrift::ApplicationError` into `thrift::Error`.
56///
57/// # Examples
58///
59/// Create a `TransportError`.
60///
61/// ```
62/// use thrift::{TransportError, TransportErrorKind};
63///
64/// // explicit
65/// let err0: thrift::Result<()> = Err(
66///   thrift::Error::Transport(
67///     TransportError {
68///       kind: TransportErrorKind::TimedOut,
69///       message: format!("connection to server timed out")
70///     }
71///   )
72/// );
73///
74/// // use conversion
75/// let err1: thrift::Result<()> = Err(
76///   thrift::Error::from(
77///     TransportError {
78///       kind: TransportErrorKind::TimedOut,
79///       message: format!("connection to server timed out")
80///     }
81///   )
82/// );
83///
84/// // use struct constructor
85/// let err2: thrift::Result<()> = Err(
86///   thrift::Error::Transport(
87///     TransportError::new(
88///       TransportErrorKind::TimedOut,
89///       "connection to server timed out"
90///     )
91///   )
92/// );
93///
94///
95/// // use error variant constructor
96/// let err3: thrift::Result<()> = Err(
97///   thrift::new_transport_error(
98///     TransportErrorKind::TimedOut,
99///     "connection to server timed out"
100///   )
101/// );
102/// ```
103///
104/// Create an error from a string.
105///
106/// ```
107/// use thrift::{ApplicationError, ApplicationErrorKind};
108///
109/// // we just use `From::from` to convert a `String` into a `thrift::Error`
110/// let err0: thrift::Result<()> = Err(
111///   thrift::Error::from("This is an error")
112/// );
113///
114/// // err0 is equivalent to...
115/// let err1: thrift::Result<()> = Err(
116///   thrift::Error::Application(
117///     ApplicationError {
118///       kind: ApplicationErrorKind::Unknown,
119///       message: format!("This is an error")
120///     }
121///   )
122/// );
123/// ```
124///
125/// Return an IDL-defined exception.
126///
127/// ```text
128/// // Thrift IDL exception definition.
129/// exception Xception {
130///   1: i32 errorCode,
131///   2: string message
132/// }
133/// ```
134///
135/// ```
136/// use std::error::Error;
137/// use std::fmt;
138/// use std::fmt::{Display, Formatter};
139///
140/// // auto-generated by the Thrift compiler
141/// #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
142/// pub struct Xception {
143///   pub error_code: Option<i32>,
144///   pub message: Option<String>,
145/// }
146///
147/// // auto-generated by the Thrift compiler
148/// impl Error for Xception { }
149///
150/// // auto-generated by the Thrift compiler
151/// impl From<Xception> for thrift::Error {
152///   fn from(e: Xception) -> Self {
153///     thrift::Error::User(Box::new(e))
154///   }
155/// }
156///
157/// // auto-generated by the Thrift compiler
158/// impl Display for Xception {
159///   fn fmt(&self, f: &mut Formatter) -> fmt::Result {
160///     write!(f, "remote service threw Xception")
161///   }
162/// }
163///
164/// // in user code...
165/// let err: thrift::Result<()> = Err(
166///   thrift::Error::from(Xception { error_code: Some(1), message: None })
167/// );
168/// ```
169pub enum Error {
170    /// Errors encountered while operating on I/O channels.
171    ///
172    /// These include *connection closed* and *bind failure*.
173    Transport(TransportError),
174    /// Errors encountered during runtime-library processing.
175    ///
176    /// These include *message too large* and *unsupported protocol version*.
177    Protocol(ProtocolError),
178    /// Errors encountered within auto-generated code, or when incoming
179    /// or outgoing messages violate the Thrift spec.
180    ///
181    /// These include *out-of-order messages* and *missing required struct
182    /// fields*.
183    ///
184    /// This variant also functions as a catch-all: errors from handler
185    /// functions are automatically returned as an `ApplicationError`.
186    Application(ApplicationError),
187    /// IDL-defined exception structs.
188    User(Box<dyn error::Error + Sync + Send>),
189}
190
191impl Error {
192    /// Create an `ApplicationError` from its wire representation.
193    ///
194    /// Application code **should never** call this method directly.
195    pub fn read_application_error_from_in_protocol(
196        i: &mut dyn TInputProtocol,
197    ) -> crate::Result<ApplicationError> {
198        let mut message = "general remote error".to_owned();
199        let mut kind = ApplicationErrorKind::Unknown;
200
201        i.read_struct_begin()?;
202
203        loop {
204            let field_ident = i.read_field_begin()?;
205
206            if field_ident.field_type == TType::Stop {
207                break;
208            }
209
210            let id = field_ident
211                .id
212                .expect("sender should always specify id for non-STOP field");
213
214            match id {
215                1 => {
216                    let remote_message = i.read_string()?;
217                    i.read_field_end()?;
218                    message = remote_message;
219                }
220                2 => {
221                    let remote_type_as_int = i.read_i32()?;
222                    let remote_kind: ApplicationErrorKind = TryFrom::try_from(remote_type_as_int)
223                        .unwrap_or(ApplicationErrorKind::Unknown);
224                    i.read_field_end()?;
225                    kind = remote_kind;
226                }
227                _ => {
228                    i.skip(field_ident.field_type)?;
229                }
230            }
231        }
232
233        i.read_struct_end()?;
234
235        Ok(ApplicationError { kind, message })
236    }
237
238    /// Convert an `ApplicationError` into its wire representation and write
239    /// it to the remote.
240    ///
241    /// Application code **should never** call this method directly.
242    pub fn write_application_error_to_out_protocol(
243        e: &ApplicationError,
244        o: &mut dyn TOutputProtocol,
245    ) -> crate::Result<()> {
246        o.write_struct_begin(&TStructIdentifier {
247            name: "TApplicationException".to_owned(),
248        })?;
249
250        let message_field = TFieldIdentifier::new("message", TType::String, 1);
251        let type_field = TFieldIdentifier::new("type", TType::I32, 2);
252
253        o.write_field_begin(&message_field)?;
254        o.write_string(&e.message)?;
255        o.write_field_end()?;
256
257        o.write_field_begin(&type_field)?;
258        o.write_i32(e.kind as i32)?;
259        o.write_field_end()?;
260
261        o.write_field_stop()?;
262        o.write_struct_end()?;
263
264        o.flush()
265    }
266}
267
268impl error::Error for Error {}
269
270impl Debug for Error {
271    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
272        match *self {
273            Error::Transport(ref e) => Debug::fmt(e, f),
274            Error::Protocol(ref e) => Debug::fmt(e, f),
275            Error::Application(ref e) => Debug::fmt(e, f),
276            Error::User(ref e) => Debug::fmt(e, f),
277        }
278    }
279}
280
281impl Display for Error {
282    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
283        match *self {
284            Error::Transport(ref e) => Display::fmt(e, f),
285            Error::Protocol(ref e) => Display::fmt(e, f),
286            Error::Application(ref e) => Display::fmt(e, f),
287            Error::User(ref e) => Display::fmt(e, f),
288        }
289    }
290}
291
292impl From<String> for Error {
293    fn from(s: String) -> Self {
294        Error::Application(ApplicationError {
295            kind: ApplicationErrorKind::Unknown,
296            message: s,
297        })
298    }
299}
300
301impl<'a> From<&'a str> for Error {
302    fn from(s: &'a str) -> Self {
303        Error::Application(ApplicationError {
304            kind: ApplicationErrorKind::Unknown,
305            message: String::from(s),
306        })
307    }
308}
309
310impl From<TransportError> for Error {
311    fn from(e: TransportError) -> Self {
312        Error::Transport(e)
313    }
314}
315
316impl From<ProtocolError> for Error {
317    fn from(e: ProtocolError) -> Self {
318        Error::Protocol(e)
319    }
320}
321
322impl From<ApplicationError> for Error {
323    fn from(e: ApplicationError) -> Self {
324        Error::Application(e)
325    }
326}
327
328/// Create a new `Error` instance of type `Transport` that wraps a
329/// `TransportError`.
330pub fn new_transport_error<S: Into<String>>(kind: TransportErrorKind, message: S) -> Error {
331    Error::Transport(TransportError::new(kind, message))
332}
333
334/// Information about I/O errors.
335#[derive(Debug, Eq, PartialEq)]
336pub struct TransportError {
337    /// I/O error variant.
338    ///
339    /// If a specific `TransportErrorKind` does not apply use
340    /// `TransportErrorKind::Unknown`.
341    pub kind: TransportErrorKind,
342    /// Human-readable error message.
343    pub message: String,
344}
345
346impl TransportError {
347    /// Create a new `TransportError`.
348    pub fn new<S: Into<String>>(kind: TransportErrorKind, message: S) -> TransportError {
349        TransportError {
350            kind,
351            message: message.into(),
352        }
353    }
354}
355
356/// I/O error categories.
357///
358/// This list may grow, and it is not recommended to match against it.
359#[non_exhaustive]
360#[derive(Clone, Copy, Eq, Debug, PartialEq)]
361pub enum TransportErrorKind {
362    /// Catch-all I/O error.
363    Unknown = 0,
364    /// An I/O operation was attempted when the transport channel was not open.
365    NotOpen = 1,
366    /// The transport channel cannot be opened because it was opened previously.
367    AlreadyOpen = 2,
368    /// An I/O operation timed out.
369    TimedOut = 3,
370    /// A read could not complete because no bytes were available.
371    EndOfFile = 4,
372    /// An invalid (buffer/message) size was requested or received.
373    NegativeSize = 5,
374    /// Too large a buffer or message size was requested or received.
375    SizeLimit = 6,
376}
377
378impl Display for TransportError {
379    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
380        let error_text = match self.kind {
381            TransportErrorKind::Unknown => "transport error",
382            TransportErrorKind::NotOpen => "not open",
383            TransportErrorKind::AlreadyOpen => "already open",
384            TransportErrorKind::TimedOut => "timed out",
385            TransportErrorKind::EndOfFile => "end of file",
386            TransportErrorKind::NegativeSize => "negative size message",
387            TransportErrorKind::SizeLimit => "message too long",
388        };
389
390        write!(f, "{}", error_text)
391    }
392}
393
394impl TryFrom<i32> for TransportErrorKind {
395    type Error = Error;
396    fn try_from(from: i32) -> Result<Self, Self::Error> {
397        match from {
398            0 => Ok(TransportErrorKind::Unknown),
399            1 => Ok(TransportErrorKind::NotOpen),
400            2 => Ok(TransportErrorKind::AlreadyOpen),
401            3 => Ok(TransportErrorKind::TimedOut),
402            4 => Ok(TransportErrorKind::EndOfFile),
403            5 => Ok(TransportErrorKind::NegativeSize),
404            6 => Ok(TransportErrorKind::SizeLimit),
405            _ => Err(Error::Protocol(ProtocolError {
406                kind: ProtocolErrorKind::Unknown,
407                message: format!("cannot convert {} to TransportErrorKind", from),
408            })),
409        }
410    }
411}
412
413impl From<io::Error> for Error {
414    fn from(err: io::Error) -> Self {
415        match err.kind() {
416            io::ErrorKind::ConnectionReset
417            | io::ErrorKind::ConnectionRefused
418            | io::ErrorKind::NotConnected => Error::Transport(TransportError {
419                kind: TransportErrorKind::NotOpen,
420                message: err.to_string(),
421            }),
422            io::ErrorKind::AlreadyExists => Error::Transport(TransportError {
423                kind: TransportErrorKind::AlreadyOpen,
424                message: err.to_string(),
425            }),
426            io::ErrorKind::TimedOut => Error::Transport(TransportError {
427                kind: TransportErrorKind::TimedOut,
428                message: err.to_string(),
429            }),
430            io::ErrorKind::UnexpectedEof => Error::Transport(TransportError {
431                kind: TransportErrorKind::EndOfFile,
432                message: err.to_string(),
433            }),
434            _ => {
435                Error::Transport(TransportError {
436                    kind: TransportErrorKind::Unknown,
437                    message: err.to_string(), // FIXME: use io error's debug string
438                })
439            }
440        }
441    }
442}
443
444impl From<string::FromUtf8Error> for Error {
445    fn from(err: string::FromUtf8Error) -> Self {
446        Error::Protocol(ProtocolError {
447            kind: ProtocolErrorKind::InvalidData,
448            message: err.to_string(), // FIXME: use fmt::Error's debug string
449        })
450    }
451}
452
453/// Create a new `Error` instance of type `Protocol` that wraps a
454/// `ProtocolError`.
455pub fn new_protocol_error<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> Error {
456    Error::Protocol(ProtocolError::new(kind, message))
457}
458
459/// Information about errors that occur in the runtime library.
460#[derive(Debug, Eq, PartialEq)]
461pub struct ProtocolError {
462    /// Protocol error variant.
463    ///
464    /// If a specific `ProtocolErrorKind` does not apply use
465    /// `ProtocolErrorKind::Unknown`.
466    pub kind: ProtocolErrorKind,
467    /// Human-readable error message.
468    pub message: String,
469}
470
471impl ProtocolError {
472    /// Create a new `ProtocolError`.
473    pub fn new<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> ProtocolError {
474        ProtocolError {
475            kind,
476            message: message.into(),
477        }
478    }
479}
480
481/// Runtime library error categories.
482///
483/// This list may grow, and it is not recommended to match against it.
484#[non_exhaustive]
485#[derive(Clone, Copy, Eq, Debug, PartialEq)]
486pub enum ProtocolErrorKind {
487    /// Catch-all runtime-library error.
488    Unknown = 0,
489    /// An invalid argument was supplied to a library function, or invalid data
490    /// was received from a Thrift endpoint.
491    InvalidData = 1,
492    /// An invalid size was received in an encoded field.
493    NegativeSize = 2,
494    /// Thrift message or field was too long.
495    SizeLimit = 3,
496    /// Unsupported or unknown Thrift protocol version.
497    BadVersion = 4,
498    /// Unsupported Thrift protocol, server or field type.
499    NotImplemented = 5,
500    /// Reached the maximum nested depth to which an encoded Thrift field could
501    /// be skipped.
502    DepthLimit = 6,
503}
504
505impl Display for ProtocolError {
506    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
507        let error_text = match self.kind {
508            ProtocolErrorKind::Unknown => "protocol error",
509            ProtocolErrorKind::InvalidData => "bad data",
510            ProtocolErrorKind::NegativeSize => "negative message size",
511            ProtocolErrorKind::SizeLimit => "message too long",
512            ProtocolErrorKind::BadVersion => "invalid thrift version",
513            ProtocolErrorKind::NotImplemented => "not implemented",
514            ProtocolErrorKind::DepthLimit => "maximum skip depth reached",
515        };
516
517        write!(f, "{}", error_text)
518    }
519}
520
521impl TryFrom<i32> for ProtocolErrorKind {
522    type Error = Error;
523    fn try_from(from: i32) -> Result<Self, Self::Error> {
524        match from {
525            0 => Ok(ProtocolErrorKind::Unknown),
526            1 => Ok(ProtocolErrorKind::InvalidData),
527            2 => Ok(ProtocolErrorKind::NegativeSize),
528            3 => Ok(ProtocolErrorKind::SizeLimit),
529            4 => Ok(ProtocolErrorKind::BadVersion),
530            5 => Ok(ProtocolErrorKind::NotImplemented),
531            6 => Ok(ProtocolErrorKind::DepthLimit),
532            _ => Err(Error::Protocol(ProtocolError {
533                kind: ProtocolErrorKind::Unknown,
534                message: format!("cannot convert {} to ProtocolErrorKind", from),
535            })),
536        }
537    }
538}
539
540/// Create a new `Error` instance of type `Application` that wraps an
541/// `ApplicationError`.
542pub fn new_application_error<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> Error {
543    Error::Application(ApplicationError::new(kind, message))
544}
545
546/// Information about errors in auto-generated code or in user-implemented
547/// service handlers.
548#[derive(Debug, Eq, PartialEq)]
549pub struct ApplicationError {
550    /// Application error variant.
551    ///
552    /// If a specific `ApplicationErrorKind` does not apply use
553    /// `ApplicationErrorKind::Unknown`.
554    pub kind: ApplicationErrorKind,
555    /// Human-readable error message.
556    pub message: String,
557}
558
559impl ApplicationError {
560    /// Create a new `ApplicationError`.
561    pub fn new<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> ApplicationError {
562        ApplicationError {
563            kind,
564            message: message.into(),
565        }
566    }
567}
568
569/// Auto-generated or user-implemented code error categories.
570///
571/// This list may grow, and it is not recommended to match against it.
572#[non_exhaustive]
573#[derive(Clone, Copy, Debug, Eq, PartialEq)]
574pub enum ApplicationErrorKind {
575    /// Catch-all application error.
576    Unknown = 0,
577    /// Made service call to an unknown service method.
578    UnknownMethod = 1,
579    /// Received an unknown Thrift message type. That is, not one of the
580    /// `thrift::protocol::TMessageType` variants.
581    InvalidMessageType = 2,
582    /// Method name in a service reply does not match the name of the
583    /// receiving service method.
584    WrongMethodName = 3,
585    /// Received an out-of-order Thrift message.
586    BadSequenceId = 4,
587    /// Service reply is missing required fields.
588    MissingResult = 5,
589    /// Auto-generated code failed unexpectedly.
590    InternalError = 6,
591    /// Thrift protocol error. When possible use `Error::ProtocolError` with a
592    /// specific `ProtocolErrorKind` instead.
593    ProtocolError = 7,
594    /// *Unknown*. Included only for compatibility with existing Thrift implementations.
595    InvalidTransform = 8, // ??
596    /// Thrift endpoint requested, or is using, an unsupported encoding.
597    InvalidProtocol = 9, // ??
598    /// Thrift endpoint requested, or is using, an unsupported auto-generated client type.
599    UnsupportedClientType = 10, // ??
600}
601
602impl Display for ApplicationError {
603    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
604        let error_text = match self.kind {
605            ApplicationErrorKind::Unknown => "service error",
606            ApplicationErrorKind::UnknownMethod => "unknown service method",
607            ApplicationErrorKind::InvalidMessageType => "wrong message type received",
608            ApplicationErrorKind::WrongMethodName => "unknown method reply received",
609            ApplicationErrorKind::BadSequenceId => "out of order sequence id",
610            ApplicationErrorKind::MissingResult => "missing method result",
611            ApplicationErrorKind::InternalError => "remote service threw exception",
612            ApplicationErrorKind::ProtocolError => "protocol error",
613            ApplicationErrorKind::InvalidTransform => "invalid transform",
614            ApplicationErrorKind::InvalidProtocol => "invalid protocol requested",
615            ApplicationErrorKind::UnsupportedClientType => "unsupported protocol client",
616        };
617
618        write!(f, "{}", error_text)
619    }
620}
621
622impl TryFrom<i32> for ApplicationErrorKind {
623    type Error = Error;
624    fn try_from(from: i32) -> Result<Self, Self::Error> {
625        match from {
626            0 => Ok(ApplicationErrorKind::Unknown),
627            1 => Ok(ApplicationErrorKind::UnknownMethod),
628            2 => Ok(ApplicationErrorKind::InvalidMessageType),
629            3 => Ok(ApplicationErrorKind::WrongMethodName),
630            4 => Ok(ApplicationErrorKind::BadSequenceId),
631            5 => Ok(ApplicationErrorKind::MissingResult),
632            6 => Ok(ApplicationErrorKind::InternalError),
633            7 => Ok(ApplicationErrorKind::ProtocolError),
634            8 => Ok(ApplicationErrorKind::InvalidTransform),
635            9 => Ok(ApplicationErrorKind::InvalidProtocol),
636            10 => Ok(ApplicationErrorKind::UnsupportedClientType),
637            _ => Err(Error::Application(ApplicationError {
638                kind: ApplicationErrorKind::Unknown,
639                message: format!("cannot convert {} to ApplicationErrorKind", from),
640            })),
641        }
642    }
643}