1 /*
2  *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #ifndef API_RTC_ERROR_H_
12 #define API_RTC_ERROR_H_
13 
14 #ifdef UNIT_TEST
15 #include <ostream>
16 #endif  // UNIT_TEST
17 #include <string>
18 #include <utility>  // For std::move.
19 
20 #include "absl/types/optional.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/logging.h"
23 #include "rtc_base/system/rtc_export.h"
24 
25 namespace webrtc {
26 
27 // Enumeration to represent distinct classes of errors that an application
28 // may wish to act upon differently. These roughly map to DOMExceptions or
29 // RTCError "errorDetailEnum" values in the web API, as described in the
30 // comments below.
31 enum class RTCErrorType {
32   // No error.
33   NONE,
34 
35   // An operation is valid, but currently unsupported.
36   // Maps to OperationError DOMException.
37   UNSUPPORTED_OPERATION,
38 
39   // A supplied parameter is valid, but currently unsupported.
40   // Maps to OperationError DOMException.
41   UNSUPPORTED_PARAMETER,
42 
43   // General error indicating that a supplied parameter is invalid.
44   // Maps to InvalidAccessError or TypeError DOMException depending on context.
45   INVALID_PARAMETER,
46 
47   // Slightly more specific than INVALID_PARAMETER; a parameter's value was
48   // outside the allowed range.
49   // Maps to RangeError DOMException.
50   INVALID_RANGE,
51 
52   // Slightly more specific than INVALID_PARAMETER; an error occurred while
53   // parsing string input.
54   // Maps to SyntaxError DOMException.
55   SYNTAX_ERROR,
56 
57   // The object does not support this operation in its current state.
58   // Maps to InvalidStateError DOMException.
59   INVALID_STATE,
60 
61   // An attempt was made to modify the object in an invalid way.
62   // Maps to InvalidModificationError DOMException.
63   INVALID_MODIFICATION,
64 
65   // An error occurred within an underlying network protocol.
66   // Maps to NetworkError DOMException.
67   NETWORK_ERROR,
68 
69   // Some resource has been exhausted; file handles, hardware resources, ports,
70   // etc.
71   // Maps to OperationError DOMException.
72   RESOURCE_EXHAUSTED,
73 
74   // The operation failed due to an internal error.
75   // Maps to OperationError DOMException.
76   INTERNAL_ERROR,
77 
78   // An error occured that has additional data.
79   // The additional data is specified in
80   // https://w3c.github.io/webrtc-pc/#rtcerror-interface
81   // Maps to RTCError DOMException.
82   OPERATION_ERROR_WITH_DATA,
83 };
84 
85 // Detail information, showing what further information should be present.
86 // https://w3c.github.io/webrtc-pc/#rtcerrordetailtype-enum
87 enum class RTCErrorDetailType {
88   NONE,
89   DATA_CHANNEL_FAILURE,
90   DTLS_FAILURE,
91   FINGERPRINT_FAILURE,
92   SCTP_FAILURE,
93   SDP_SYNTAX_ERROR,
94   HARDWARE_ENCODER_NOT_AVAILABLE,
95   HARDWARE_ENCODER_ERROR,
96 };
97 
98 // Roughly corresponds to RTCError in the web api. Holds an error type, a
99 // message, and possibly additional information specific to that error.
100 //
101 // Doesn't contain anything beyond a type and message now, but will in the
102 // future as more errors are implemented.
103 class RTC_EXPORT RTCError {
104  public:
105   // Constructors.
106 
107   // Creates a "no error" error.
RTCError()108   RTCError() {}
RTCError(RTCErrorType type)109   explicit RTCError(RTCErrorType type) : type_(type) {}
110 
RTCError(RTCErrorType type,std::string message)111   RTCError(RTCErrorType type, std::string message)
112       : type_(type), message_(std::move(message)) {}
113 
114   // In many use cases, it is better to use move than copy,
115   // but copy and assignment are provided for those cases that need it.
116   // Note that this has extra overhead because it copies strings.
117   RTCError(const RTCError& other) = default;
118   RTCError(RTCError&&) = default;
119   RTCError& operator=(const RTCError& other) = default;
120   RTCError& operator=(RTCError&&) = default;
121 
122   // Identical to default constructed error.
123   //
124   // Preferred over the default constructor for code readability.
125   static RTCError OK();
126 
127   // Error type.
type()128   RTCErrorType type() const { return type_; }
set_type(RTCErrorType type)129   void set_type(RTCErrorType type) { type_ = type; }
130 
131   // Human-readable message describing the error. Shouldn't be used for
132   // anything but logging/diagnostics, since messages are not guaranteed to be
133   // stable.
134   const char* message() const;
135 
136   void set_message(std::string message);
137 
error_detail()138   RTCErrorDetailType error_detail() const { return error_detail_; }
set_error_detail(RTCErrorDetailType detail)139   void set_error_detail(RTCErrorDetailType detail) { error_detail_ = detail; }
sctp_cause_code()140   absl::optional<uint16_t> sctp_cause_code() { return sctp_cause_code_; }
set_sctp_cause_code(uint16_t cause_code)141   void set_sctp_cause_code(uint16_t cause_code) {
142     sctp_cause_code_ = cause_code;
143   }
144 
145   // Convenience method for situations where you only care whether or not an
146   // error occurred.
ok()147   bool ok() const { return type_ == RTCErrorType::NONE; }
148 
149  private:
150   RTCErrorType type_ = RTCErrorType::NONE;
151   std::string message_;
152   RTCErrorDetailType error_detail_ = RTCErrorDetailType::NONE;
153   absl::optional<uint16_t> sctp_cause_code_;
154 };
155 
156 // Outputs the error as a friendly string. Update this method when adding a new
157 // error type.
158 //
159 // Only intended to be used for logging/diagnostics. The returned char* points
160 // to literal string that lives for the whole duration of the program.
161 RTC_EXPORT const char* ToString(RTCErrorType error);
162 RTC_EXPORT const char* ToString(RTCErrorDetailType error);
163 
164 #ifdef UNIT_TEST
165 inline std::ostream& operator<<(  // no-presubmit-check TODO(webrtc:8982)
166     std::ostream& stream,         // no-presubmit-check TODO(webrtc:8982)
167     RTCErrorType error) {
168   return stream << ToString(error);
169 }
170 
171 inline std::ostream& operator<<(  // no-presubmit-check TODO(webrtc:8982)
172     std::ostream& stream,         // no-presubmit-check TODO(webrtc:8982)
173     RTCErrorDetailType error) {
174   return stream << ToString(error);
175 }
176 #endif  // UNIT_TEST
177 
178 // Helper macro that can be used by implementations to create an error with a
179 // message and log it. |message| should be a string literal or movable
180 // std::string.
181 #define LOG_AND_RETURN_ERROR_EX(type, message, severity)           \
182   {                                                                \
183     RTC_DCHECK(type != RTCErrorType::NONE);                        \
184     RTC_LOG(severity) << message << " (" << ToString(type) << ")"; \
185     return webrtc::RTCError(type, message);                        \
186   }
187 
188 #define LOG_AND_RETURN_ERROR(type, message) \
189   LOG_AND_RETURN_ERROR_EX(type, message, LS_ERROR)
190 
191 // RTCErrorOr<T> is the union of an RTCError object and a T object. RTCErrorOr
192 // models the concept of an object that is either a usable value, or an error
193 // Status explaining why such a value is not present. To this end RTCErrorOr<T>
194 // does not allow its RTCErrorType value to be RTCErrorType::NONE. This is
195 // enforced by a debug check in most cases.
196 //
197 // The primary use-case for RTCErrorOr<T> is as the return value of a function
198 // which may fail. For example, CreateRtpSender will fail if the parameters
199 // could not be successfully applied at the media engine level, but if
200 // successful will return a unique_ptr to an RtpSender.
201 //
202 // Example client usage for a RTCErrorOr<std::unique_ptr<T>>:
203 //
204 //  RTCErrorOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
205 //  if (result.ok()) {
206 //    std::unique_ptr<Foo> foo = result.ConsumeValue();
207 //    foo->DoSomethingCool();
208 //  } else {
209 //    RTC_LOG(LS_ERROR) << result.error();
210 //  }
211 //
212 // Example factory implementation returning RTCErrorOr<std::unique_ptr<T>>:
213 //
214 //  RTCErrorOr<std::unique_ptr<Foo>> FooFactory::MakeNewFoo(int arg) {
215 //    if (arg <= 0) {
216 //      return RTCError(RTCErrorType::INVALID_RANGE, "Arg must be positive");
217 //    } else {
218 //      return std::unique_ptr<Foo>(new Foo(arg));
219 //    }
220 //  }
221 //
222 template <typename T>
223 class RTCErrorOr {
224   // Used to convert between RTCErrorOr<Foo>/RtcErrorOr<Bar>, when an implicit
225   // conversion from Foo to Bar exists.
226   template <typename U>
227   friend class RTCErrorOr;
228 
229  public:
230   typedef T element_type;
231 
232   // Constructs a new RTCErrorOr with RTCErrorType::INTERNAL_ERROR error. This
233   // is marked 'explicit' to try to catch cases like 'return {};', where people
234   // think RTCErrorOr<std::vector<int>> will be initialized with an empty
235   // vector, instead of a RTCErrorType::INTERNAL_ERROR error.
RTCErrorOr()236   RTCErrorOr() : error_(RTCErrorType::INTERNAL_ERROR) {}
237 
238   // Constructs a new RTCErrorOr with the given non-ok error. After calling
239   // this constructor, calls to value() will DCHECK-fail.
240   //
241   // NOTE: Not explicit - we want to use RTCErrorOr<T> as a return
242   // value, so it is convenient and sensible to be able to do 'return
243   // RTCError(...)' when the return type is RTCErrorOr<T>.
244   //
245   // REQUIRES: !error.ok(). This requirement is DCHECKed.
RTCErrorOr(RTCError && error)246   RTCErrorOr(RTCError&& error) : error_(std::move(error)) {  // NOLINT
247     RTC_DCHECK(!error.ok());
248   }
249 
250   // Constructs a new RTCErrorOr with the given value. After calling this
251   // constructor, calls to value() will succeed, and calls to error() will
252   // return a default-constructed RTCError.
253   //
254   // NOTE: Not explicit - we want to use RTCErrorOr<T> as a return type
255   // so it is convenient and sensible to be able to do 'return T()'
256   // when the return type is RTCErrorOr<T>.
RTCErrorOr(const T & value)257   RTCErrorOr(const T& value) : value_(value) {}        // NOLINT
RTCErrorOr(T && value)258   RTCErrorOr(T&& value) : value_(std::move(value)) {}  // NOLINT
259 
260   // Delete the copy constructor and assignment operator; there aren't any use
261   // cases where you should need to copy an RTCErrorOr, as opposed to moving
262   // it. Can revisit this decision if use cases arise in the future.
263   RTCErrorOr(const RTCErrorOr& other) = delete;
264   RTCErrorOr& operator=(const RTCErrorOr& other) = delete;
265 
266   // Move constructor and move-assignment operator.
267   //
268   // Visual Studio doesn't support "= default" with move constructors or
269   // assignment operators (even though they compile, they segfault), so define
270   // them explicitly.
RTCErrorOr(RTCErrorOr && other)271   RTCErrorOr(RTCErrorOr&& other)
272       : error_(std::move(other.error_)), value_(std::move(other.value_)) {}
273   RTCErrorOr& operator=(RTCErrorOr&& other) {
274     error_ = std::move(other.error_);
275     value_ = std::move(other.value_);
276     return *this;
277   }
278 
279   // Conversion constructor and assignment operator; T must be copy or move
280   // constructible from U.
281   template <typename U>
RTCErrorOr(RTCErrorOr<U> other)282   RTCErrorOr(RTCErrorOr<U> other)  // NOLINT
283       : error_(std::move(other.error_)), value_(std::move(other.value_)) {}
284   template <typename U>
285   RTCErrorOr& operator=(RTCErrorOr<U> other) {
286     error_ = std::move(other.error_);
287     value_ = std::move(other.value_);
288     return *this;
289   }
290 
291   // Returns a reference to our error. If this contains a T, then returns
292   // default-constructed RTCError.
error()293   const RTCError& error() const { return error_; }
294 
295   // Moves the error. Can be useful if, say "CreateFoo" returns an
296   // RTCErrorOr<Foo>, and internally calls "CreateBar" which returns an
297   // RTCErrorOr<Bar>, and wants to forward the error up the stack.
MoveError()298   RTCError MoveError() { return std::move(error_); }
299 
300   // Returns this->error().ok()
ok()301   bool ok() const { return error_.ok(); }
302 
303   // Returns a reference to our current value, or DCHECK-fails if !this->ok().
304   //
305   // Can be convenient for the implementation; for example, a method may want
306   // to access the value in some way before returning it to the next method on
307   // the stack.
value()308   const T& value() const {
309     RTC_DCHECK(ok());
310     return value_;
311   }
value()312   T& value() {
313     RTC_DCHECK(ok());
314     return value_;
315   }
316 
317   // Moves our current value out of this object and returns it, or DCHECK-fails
318   // if !this->ok().
MoveValue()319   T MoveValue() {
320     RTC_DCHECK(ok());
321     return std::move(value_);
322   }
323 
324  private:
325   RTCError error_;
326   T value_;
327 };
328 
329 }  // namespace webrtc
330 
331 #endif  // API_RTC_ERROR_H_
332