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