Asylo
status.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * 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, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_UTIL_STATUS_H_
20 #define ASYLO_UTIL_STATUS_H_
21 
22 #include <functional>
23 #include <ostream>
24 #include <string>
25 #include <type_traits>
26 #include <utility>
27 #include <vector>
28 
29 #include "absl/base/attributes.h"
30 #include "absl/container/flat_hash_map.h"
31 #include "absl/meta/type_traits.h"
32 #include "absl/status/status.h"
33 #include "absl/status/statusor.h"
34 #include "absl/strings/cord.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/types/optional.h"
37 #include "asylo/util/logging.h"
38 #include "asylo/util/error_codes.h" // IWYU pragma: export
39 #include "asylo/util/error_space.h" // IWYU pragma: export
40 #include "asylo/util/status.pb.h"
41 #include "asylo/util/status_error_space.h"
42 #include "asylo/util/status_internal.h"
43 
44 namespace asylo {
45 
46 /// Status contains information about an error. Status contains an error code
47 /// from some error space and a message string suitable for logging or
48 /// debugging. Status can also contain any number of (type URL -> byte string)
49 /// associations called "payloads". These function similarly to payloads in
50 /// `absl::Status`.
51 class Status {
52  public:
53  /// Builds an OK Status in the canonical error space.
54  Status();
55 
56  /// Constructs a Status object containing an error code and message.
57  ///
58  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
59  /// payloads to communicate additional error information if
60  /// needed.
61  /// \param space The ErrorSpace this code belongs to.
62  /// \param code An integer error code.
63  /// \param message The associated error message.
64  ABSL_DEPRECATED(
65  "Deprecated as part of Asylo's absl::Status migration. Use payloads to "
66  "communicate additional error information if needed.")
67  Status(const error::ErrorSpace *space, int code, absl::string_view message);
68 
69  /// Constructs a Status object containing an error code and a message. The
70  /// error space is deduced from `code`.
71  ///
72  /// \param code A symbolic error code.
73  /// \param message The associated error message.
74  template <typename Enum>
75  Status(Enum code, absl::string_view message) {
76  Set(code, message);
77  }
78 
79  Status(const Status &other) = default;
80 
81  // Non-default move constructor since the moved status should be changed to a
82  // valid but unspecified state.
83  Status(Status &&other);
84 
85  /// Constructs a Status object from `StatusT`. `StatusT` must be a status-type
86  /// object. I.e.,
87  ///
88  /// * It must have a two-parameter constructor that takes an enum as its
89  /// first parameter and a string as its second parameter.
90  /// * It must have non-static error_code(), error_message(), and ok()
91  /// methods.
92  ///
93  /// This constructor is provided for the convenience of Asylo-SDK consumers
94  /// utilizing other status types such as `::grpc::Status`.
95  ///
96  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
97  /// `ConvertStatus()` instead.
98  /// \param other A status-like object to copy.
99  template <typename StatusT,
100  typename E = typename absl::enable_if_t<
101  status_internal::status_type_traits<StatusT>::is_status>>
102  ABSL_DEPRECATED(
103  "Deprecated as part of Asylo's absl::Status migration. Use "
104  "ConvertStatus() from status_helpers.h instead.")
105  explicit Status(const StatusT &other) {
106  Set(status_internal::status_type_traits<StatusT>::CanonicalCode(other),
107  other.error_message());
108  }
109 
110  Status(const absl::Status &other);
111 
112  Status &operator=(const Status &other) = default;
113 
114  // Non-default move assignment operator since the moved status should be
115  // changed to a valid but unspecified state.
116  Status &operator=(Status &&other);
117 
118  /// Constructs an OK status object.
119  ///
120  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
121  /// `absl::OkStatus()` or `asylo::OkStatus()` instead.
122  /// \return A Status indicating no error occurred.
123  ABSL_DEPRECATED(
124  "Deprecated as part of Asylo's absl::Status migration. Use "
125  "absl::OkStatus() or asylo::OkStatus() instead.")
126  static Status OkStatus();
127 
128  /// Copy this object to a status type `StatusT`. The method first converts the
129  /// ::asylo::Status object to its canonical form, and then constructs a
130  /// `StatusT` from the error code and message fields of the converted object.
131  /// `StatusT` must be a status-type object. I.e.,
132  ///
133  /// * It must have a two-parameter constructor that takes an enum as its
134  /// first parameter and a string as its second parameter.
135  /// * It must have non-static error_code(), error_message(), and ok()
136  /// methods.
137  ///
138  /// This operator is provided for the convenience of the Asylo SDK users
139  /// that utilize other status types, such as `::grpc::Status`.
140  ///
141  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
142  /// `ConvertStatus()` instead.
143  /// \return A status-like object copied from this object.
144  template <typename StatusT,
145  typename E = typename absl::enable_if_t<
146  status_internal::status_type_traits<StatusT>::is_status>>
147  ABSL_DEPRECATED(
148  "Deprecated as part of Asylo's absl::Status migration. Use "
149  "ConvertStatus() from status_helpers.h instead.")
150  StatusT ToOtherStatus() {
151  Status status = ToCanonical();
152  return StatusT(status_internal::ErrorCodeHolder(status.error_code_),
153  status.message_);
154  }
155 
156  // Type-cast operators from
157  // ::asylo::Status -> ::absl::Status, and
158  // ::asylo::Status -> ::absl::StatusOr<T>
159  //
160  // These operators are provided for convenience to consumers of the Asylo SDK
161  // who are also using absl. They enable implicit conversions between
162  // ::asylo::Status and the related ::absl::Status and ::absl::StatusOr types.
163  //
164  // Example usage:
165  // ::absl::Status CallEnclave1() {
166  // ::asylo::Status status = client_->EnterAndRun(...);
167  // if (!status.ok()) {
168  // return status;
169  // }
170  // return ::absl::Status();
171  // }
172  //
173  // ::absl::StatusOr<Foo> CallEnclave2() {
174  // Foo foo = ...
175  // ::asylo::Status status = client_->EnterAndRun(...);
176  // if (!status.ok()) {
177  // return status;
178  // }
179  // return foo;
180  // }
181  operator ::absl::Status() const;
182 
183  template <class T>
184  operator absl::StatusOr<T>() const {
185  return absl::StatusOr<T>(::absl::Status(*this));
186  }
187 
188  /// Gets the integer error code for this object.
189  ///
190  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
191  /// `raw_code()` instead.
192  /// \return The associated integer error code.
193  ABSL_DEPRECATED(
194  "Deprecated as part of Asylo's absl::Status migration. Use raw_code() "
195  "instead.")
196  int error_code() const;
197 
198  /// Gets the error code for this object as an `int`.
199  ///
200  /// \return The associated integer error code.
201  int raw_code() const;
202 
203  /// Gets the string error message for this object.
204  ///
205  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
206  /// `message()` instead.
207  /// \return The associated error message.
208  ABSL_DEPRECATED(
209  "Deprecated as part of Asylo's absl::Status migration. Use message() "
210  "instead.")
211  absl::string_view error_message() const;
212 
213  /// Gets the string error message for this object.
214  ///
215  /// \return The associated error message.
216  absl::string_view message() const;
217 
218  /// Gets the error space for this object.
219  ///
220  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
221  /// payloads instead of error spaces.
222  /// \return The associated error space.
223  ABSL_DEPRECATED(
224  "Deprecated as part of Asylo's absl::Status migration. Use payloads "
225  "instead of error spaces.")
226  const error::ErrorSpace *error_space() const;
227 
228  /// Indicates whether this object is OK (indicates no error).
229  ///
230  /// \return True if this object indicates no error.
231  bool ok() const;
232 
233  /// Gets a string representation of this object.
234  ///
235  /// Gets a string containing the error space name, error code name, and error
236  /// message if this object is a non-OK Status, or just a string containing the
237  /// error code name if this object is an OK Status.
238  ///
239  /// The string also contains a list of payloads contained in this Status.
240  ///
241  /// \return A string representation of this object.
242  std::string ToString() const;
243 
244  /// Gets a copy of this object in the canonical error space.
245  ///
246  /// This operation has no effect if the Status object is already in the
247  /// canonical error space. Otherwise, this translation involves the following:
248  ///
249  /// * Error code is converted to the equivalent error code in the canonical
250  /// error space.
251  /// * The new error message is set to the `ToString()` representation of the
252  /// old Status object, excluding any payloads, in order to preserve the
253  /// previous error code information.
254  ///
255  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
256  /// payloads instead of error spaces.
257  ABSL_DEPRECATED(
258  "Deprecated as part of Asylo's absl::Status migration. Use payloads "
259  "instead of error spaces.")
260  Status ToCanonical() const;
261 
262  /// Gets the canonical error code for this object's error code.
263  ///
264  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
265  /// `code()` instead.
266  /// \return A canonical `error::GoogleError` code.
267  ABSL_DEPRECATED(
268  "Deprecated as part of Asylo's absl::Status migration. Use code() "
269  "instead.")
270  error::GoogleError CanonicalCode() const;
271 
272  /// Gets the canonical error code for this object.
273  ///
274  /// \return The `absl::StatusCode` code.
275  absl::StatusCode code() const;
276 
277  /// Exports the contents of this object into `status_proto`. This method sets
278  /// the `space` and `canonical_code` fields in `status_proto` even if this
279  /// object is in the canonical error space.
280  ///
281  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
282  /// `StatusToProto()` instead.
283  /// \param[out] status_proto A protobuf object to populate.
284  ABSL_DEPRECATED(
285  "Deprecated as part of Asylo's absl::Status migration. Use "
286  "StatusToProto() from status_helpers.h instead.")
287  void SaveTo(StatusProto *status_proto) const;
288 
289  /// Populates this object using the contents of the given `status_proto`.
290  ///
291  /// If the error space given by `status_proto.space()` is unrecognized, sets
292  /// the error space to the canonical error space and sets the error code using
293  /// the value given by `status_proto.canonical_code()`. If there is no
294  /// canonical code, sets the error code to `absl::StatusCode::kUnknown`. Note
295  /// that the error message is only set if `status_proto` represents a non-ok
296  /// Status.
297  ///
298  /// If the given `status_proto` is invalid, sets an appropriate error code and
299  /// message. A StatusProto is valid if and only if all the following
300  /// conditions hold:
301  ///
302  /// * If `code()` is 0, then `canonical_code()` is set to 0.
303  /// * If `canonical_code()` is 0, then `code()` is set to 0.
304  /// * If the error space is recognized, then `canonical_code()` is equal to
305  /// the equivalent canonical code given by the error space.
306  ///
307  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
308  /// `StatusFromProto()` instead.
309  /// \param status_proto A protobuf object to set this object from.
310  ABSL_DEPRECATED(
311  "Deprecated as part of Asylo's absl::Status migration. Use "
312  "StatusFromProto() from status_helpers.h instead.")
313  void RestoreFrom(const StatusProto &status_proto);
314 
315  /// Indicates whether this object is the same as `code`.
316  ///
317  /// This object is considered to be the same as `code if `code` matches both
318  /// the error code and error space of this object.
319  ///
320  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
321  /// payloads instead of error spaces.
322  /// \return True if this object matches `code`.
323  template <typename Enum>
324  ABSL_DEPRECATED(
325  "Deprecated as part of Asylo's absl::Status migration. Use payloads "
326  "instead of error spaces.")
327  bool Is(Enum code) const {
328  return (static_cast<int>(code) == error_code_) &&
329  (error::error_enum_traits<Enum>::get_error_space() == error_space_);
330  }
331 
332  /// Modifies this object to have `context` prepended to the error message.
333  ///
334  /// \deprecated Deprecated as part of Asylo's `absl::Status` migration. Use
335  /// `WithContext()` instead.
336  /// \param context The context information to be prepended to the error
337  /// message.
338  /// \return This object with the same error code and an error message of
339  /// `context` + ": " + the original error message.
340  ABSL_DEPRECATED(
341  "Deprecated as part of Asylo's absl::Status migration. Use WithContext() "
342  "from status_helpers.h instead.")
343  Status WithPrependedContext(absl::string_view context);
344 
345  /// Gets the payload associated with the given type URL.
346  ///
347  /// \param type_url A type URL.
348  /// \return The payload corresponding to `type_url`, or `absl::nullopt` if no
349  /// such payload is contained in this `Status`.
350  absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
351 
352  /// Sets the payload for a given type URL, overwriting any previous value.
353  ///
354  /// \param type_url A type URL.
355  /// \param payload The payload to assoicate with `type_url`.
356  void SetPayload(absl::string_view type_url, absl::Cord payload);
357 
358  /// Removes the payload associated with a given type URL, if one exists.
359  ///
360  /// \param type_url The type URL to clear.
361  /// \return True if a payload was removed, false otherwise.
362  bool ErasePayload(absl::string_view type_url);
363 
364  /// Executes a function for each payload in this `Status`.
365  ///
366  /// Specifically, calls `visitor` exactly once on each payload contained in
367  /// this `Status`. The order in which the payloads are visited is unspecified
368  /// and may change between calls to `ForEachPayload()`.
369  ///
370  /// Modifying the `Status` object from within `visitor` is disallowed and may
371  /// result in undefined behavior.
372  ///
373  /// \param visitor A function to call on each type URL and associated payload.
374  void ForEachPayload(
375  const std::function<void(absl::string_view, const absl::Cord &)> &visitor)
376  const;
377 
378  private:
379  friend bool operator==(const Status &lhs, const Status &rhs);
380 
381  // Sets this object to hold an error code |code| and error message |message|.
382  template <typename Enum, typename StringT>
383  void Set(Enum code, StringT &&message) {
384  error_space_ = error::error_enum_traits<Enum>::get_error_space();
385  error_code_ = static_cast<int>(code);
386  if (error_code_ != 0) {
387  message_ = std::string(std::forward<StringT>(message));
388  } else {
389  message_.clear();
390  }
391  }
392 
393  // Returns true if the error code for this object is in the canonical error
394  // space.
395  bool IsCanonical() const;
396 
397  // Returns the part of ToString() that doesn't include payload information.
398  std::string ToStringWithoutPayloads() const;
399 
400  const error::ErrorSpace *error_space_;
401  int error_code_;
402 
403  // An optional error-message if error_code_ is non-zero. If error_code_ is
404  // zero, then message_ is empty.
405  std::string message_;
406 
407  absl::flat_hash_map<std::string, absl::Cord> payloads_;
408 };
409 
410 bool operator==(const Status &lhs, const Status &rhs);
411 
412 bool operator!=(const Status &lhs, const Status &rhs);
413 
414 std::ostream &operator<<(std::ostream &os, const Status &status);
415 
416 bool operator==(const Status &lhs, const absl::Status &rhs);
417 
418 bool operator!=(const Status &lhs, const absl::Status &rhs);
419 
420 bool operator==(const absl::Status &lhs, const Status &rhs);
421 
422 bool operator!=(const absl::Status &lhs, const Status &rhs);
423 
424 /// Returns an OK status object.
425 ///
426 /// \return A Status indicating no error occurred.
427 Status OkStatus();
428 
429 /// Checks that the `Status` object in `val` is OK.
430 #define ASYLO_CHECK_OK(val) CHECK_EQ(::asylo::OkStatus(), (val))
431 
432 } // namespace asylo
433 
434 #endif // ASYLO_UTIL_STATUS_H_