Skip to main content

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<uuid::Error> for Error {
445    fn from(err: uuid::Error) -> 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
453impl From<string::FromUtf8Error> for Error {
454    fn from(err: string::FromUtf8Error) -> Self {
455        Error::Protocol(ProtocolError {
456            kind: ProtocolErrorKind::InvalidData,
457            message: err.to_string(), // FIXME: use fmt::Error's debug string
458        })
459    }
460}
461
462/// Create a new `Error` instance of type `Protocol` that wraps a
463/// `ProtocolError`.
464pub fn new_protocol_error<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> Error {
465    Error::Protocol(ProtocolError::new(kind, message))
466}
467
468/// Information about errors that occur in the runtime library.
469#[derive(Debug, Eq, PartialEq)]
470pub struct ProtocolError {
471    /// Protocol error variant.
472    ///
473    /// If a specific `ProtocolErrorKind` does not apply use
474    /// `ProtocolErrorKind::Unknown`.
475    pub kind: ProtocolErrorKind,
476    /// Human-readable error message.
477    pub message: String,
478}
479
480impl ProtocolError {
481    /// Create a new `ProtocolError`.
482    pub fn new<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> ProtocolError {
483        ProtocolError {
484            kind,
485            message: message.into(),
486        }
487    }
488}
489
490/// Runtime library error categories.
491///
492/// This list may grow, and it is not recommended to match against it.
493#[non_exhaustive]
494#[derive(Clone, Copy, Eq, Debug, PartialEq)]
495pub enum ProtocolErrorKind {
496    /// Catch-all runtime-library error.
497    Unknown = 0,
498    /// An invalid argument was supplied to a library function, or invalid data
499    /// was received from a Thrift endpoint.
500    InvalidData = 1,
501    /// An invalid size was received in an encoded field.
502    NegativeSize = 2,
503    /// Thrift message or field was too long.
504    SizeLimit = 3,
505    /// Unsupported or unknown Thrift protocol version.
506    BadVersion = 4,
507    /// Unsupported Thrift protocol, server or field type.
508    NotImplemented = 5,
509    /// Reached the maximum nested depth to which an encoded Thrift field could
510    /// be skipped.
511    DepthLimit = 6,
512}
513
514impl Display for ProtocolError {
515    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
516        let error_text = match self.kind {
517            ProtocolErrorKind::Unknown => "protocol error",
518            ProtocolErrorKind::InvalidData => "bad data",
519            ProtocolErrorKind::NegativeSize => "negative message size",
520            ProtocolErrorKind::SizeLimit => "message too long",
521            ProtocolErrorKind::BadVersion => "invalid thrift version",
522            ProtocolErrorKind::NotImplemented => "not implemented",
523            ProtocolErrorKind::DepthLimit => "maximum skip depth reached",
524        };
525
526        write!(f, "{}", error_text)
527    }
528}
529
530impl TryFrom<i32> for ProtocolErrorKind {
531    type Error = Error;
532    fn try_from(from: i32) -> Result<Self, Self::Error> {
533        match from {
534            0 => Ok(ProtocolErrorKind::Unknown),
535            1 => Ok(ProtocolErrorKind::InvalidData),
536            2 => Ok(ProtocolErrorKind::NegativeSize),
537            3 => Ok(ProtocolErrorKind::SizeLimit),
538            4 => Ok(ProtocolErrorKind::BadVersion),
539            5 => Ok(ProtocolErrorKind::NotImplemented),
540            6 => Ok(ProtocolErrorKind::DepthLimit),
541            _ => Err(Error::Protocol(ProtocolError {
542                kind: ProtocolErrorKind::Unknown,
543                message: format!("cannot convert {} to ProtocolErrorKind", from),
544            })),
545        }
546    }
547}
548
549/// Create a new `Error` instance of type `Application` that wraps an
550/// `ApplicationError`.
551pub fn new_application_error<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> Error {
552    Error::Application(ApplicationError::new(kind, message))
553}
554
555/// Information about errors in auto-generated code or in user-implemented
556/// service handlers.
557#[derive(Debug, Eq, PartialEq)]
558pub struct ApplicationError {
559    /// Application error variant.
560    ///
561    /// If a specific `ApplicationErrorKind` does not apply use
562    /// `ApplicationErrorKind::Unknown`.
563    pub kind: ApplicationErrorKind,
564    /// Human-readable error message.
565    pub message: String,
566}
567
568impl ApplicationError {
569    /// Create a new `ApplicationError`.
570    pub fn new<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> ApplicationError {
571        ApplicationError {
572            kind,
573            message: message.into(),
574        }
575    }
576}
577
578/// Auto-generated or user-implemented code error categories.
579///
580/// This list may grow, and it is not recommended to match against it.
581#[non_exhaustive]
582#[derive(Clone, Copy, Debug, Eq, PartialEq)]
583pub enum ApplicationErrorKind {
584    /// Catch-all application error.
585    Unknown = 0,
586    /// Made service call to an unknown service method.
587    UnknownMethod = 1,
588    /// Received an unknown Thrift message type. That is, not one of the
589    /// `thrift::protocol::TMessageType` variants.
590    InvalidMessageType = 2,
591    /// Method name in a service reply does not match the name of the
592    /// receiving service method.
593    WrongMethodName = 3,
594    /// Received an out-of-order Thrift message.
595    BadSequenceId = 4,
596    /// Service reply is missing required fields.
597    MissingResult = 5,
598    /// Auto-generated code failed unexpectedly.
599    InternalError = 6,
600    /// Thrift protocol error. When possible use `Error::ProtocolError` with a
601    /// specific `ProtocolErrorKind` instead.
602    ProtocolError = 7,
603    /// *Unknown*. Included only for compatibility with existing Thrift implementations.
604    InvalidTransform = 8, // ??
605    /// Thrift endpoint requested, or is using, an unsupported encoding.
606    InvalidProtocol = 9, // ??
607    /// Thrift endpoint requested, or is using, an unsupported auto-generated client type.
608    UnsupportedClientType = 10, // ??
609}
610
611impl Display for ApplicationError {
612    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
613        let error_text = match self.kind {
614            ApplicationErrorKind::Unknown => "service error",
615            ApplicationErrorKind::UnknownMethod => "unknown service method",
616            ApplicationErrorKind::InvalidMessageType => "wrong message type received",
617            ApplicationErrorKind::WrongMethodName => "unknown method reply received",
618            ApplicationErrorKind::BadSequenceId => "out of order sequence id",
619            ApplicationErrorKind::MissingResult => "missing method result",
620            ApplicationErrorKind::InternalError => "remote service threw exception",
621            ApplicationErrorKind::ProtocolError => "protocol error",
622            ApplicationErrorKind::InvalidTransform => "invalid transform",
623            ApplicationErrorKind::InvalidProtocol => "invalid protocol requested",
624            ApplicationErrorKind::UnsupportedClientType => "unsupported protocol client",
625        };
626
627        write!(f, "{}", error_text)
628    }
629}
630
631impl TryFrom<i32> for ApplicationErrorKind {
632    type Error = Error;
633    fn try_from(from: i32) -> Result<Self, Self::Error> {
634        match from {
635            0 => Ok(ApplicationErrorKind::Unknown),
636            1 => Ok(ApplicationErrorKind::UnknownMethod),
637            2 => Ok(ApplicationErrorKind::InvalidMessageType),
638            3 => Ok(ApplicationErrorKind::WrongMethodName),
639            4 => Ok(ApplicationErrorKind::BadSequenceId),
640            5 => Ok(ApplicationErrorKind::MissingResult),
641            6 => Ok(ApplicationErrorKind::InternalError),
642            7 => Ok(ApplicationErrorKind::ProtocolError),
643            8 => Ok(ApplicationErrorKind::InvalidTransform),
644            9 => Ok(ApplicationErrorKind::InvalidProtocol),
645            10 => Ok(ApplicationErrorKind::UnsupportedClientType),
646            _ => Err(Error::Application(ApplicationError {
647                kind: ApplicationErrorKind::Unknown,
648                message: format!("cannot convert {} to ApplicationErrorKind", from),
649            })),
650        }
651    }
652}