deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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 | |
Steve Anton | 10542f2 | 2019-01-11 09:11:00 -0800 | [diff] [blame] | 11 | #ifndef API_RTC_ERROR_H_ |
| 12 | #define API_RTC_ERROR_H_ |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 13 | |
Jonas Olsson | 3e18c82 | 2018-04-18 10:11:07 +0200 | [diff] [blame] | 14 | #ifdef UNIT_TEST |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 15 | #include <ostream> |
Jonas Olsson | 3e18c82 | 2018-04-18 10:11:07 +0200 | [diff] [blame] | 16 | #endif // UNIT_TEST |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 17 | #include <string> |
| 18 | #include <utility> // For std::move. |
| 19 | |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 20 | #include "absl/types/optional.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 21 | #include "rtc_base/checks.h" |
| 22 | #include "rtc_base/logging.h" |
Mirko Bonadei | 66e7679 | 2019-04-02 11:33:59 +0200 | [diff] [blame] | 23 | #include "rtc_base/system/rtc_export.h" |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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, |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 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, |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
Mirko Bonadei | 66e7679 | 2019-04-02 11:33:59 +0200 | [diff] [blame] | 103 | class RTC_EXPORT RTCError { |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 104 | public: |
| 105 | // Constructors. |
| 106 | |
| 107 | // Creates a "no error" error. |
| 108 | RTCError() {} |
| 109 | explicit RTCError(RTCErrorType type) : type_(type) {} |
Jonas Olsson | 941a07c | 2018-09-13 10:07:07 +0200 | [diff] [blame] | 110 | |
| 111 | RTCError(RTCErrorType type, std::string message) |
| 112 | : type_(type), message_(std::move(message)) {} |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 113 | |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 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; |
Steve Anton | 2a3190f | 2020-01-08 11:20:04 -0800 | [diff] [blame] | 118 | RTCError(RTCError&&) = default; |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 119 | RTCError& operator=(const RTCError& other) = default; |
Steve Anton | 2a3190f | 2020-01-08 11:20:04 -0800 | [diff] [blame] | 120 | RTCError& operator=(RTCError&&) = default; |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 121 | |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 122 | // Identical to default constructed error. |
| 123 | // |
| 124 | // Preferred over the default constructor for code readability. |
| 125 | static RTCError OK(); |
| 126 | |
| 127 | // Error type. |
| 128 | RTCErrorType type() const { return 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; |
Jonas Olsson | 941a07c | 2018-09-13 10:07:07 +0200 | [diff] [blame] | 135 | |
| 136 | void set_message(std::string message); |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 137 | |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 138 | RTCErrorDetailType error_detail() const { return error_detail_; } |
| 139 | void set_error_detail(RTCErrorDetailType detail) { error_detail_ = detail; } |
| 140 | absl::optional<uint16_t> sctp_cause_code() { return sctp_cause_code_; } |
| 141 | void set_sctp_cause_code(uint16_t cause_code) { |
| 142 | sctp_cause_code_ = cause_code; |
| 143 | } |
| 144 | |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 145 | // Convenience method for situations where you only care whether or not an |
| 146 | // error occurred. |
| 147 | bool ok() const { return type_ == RTCErrorType::NONE; } |
| 148 | |
| 149 | private: |
| 150 | RTCErrorType type_ = RTCErrorType::NONE; |
Jonas Olsson | 941a07c | 2018-09-13 10:07:07 +0200 | [diff] [blame] | 151 | std::string message_; |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 152 | RTCErrorDetailType error_detail_ = RTCErrorDetailType::NONE; |
| 153 | absl::optional<uint16_t> sctp_cause_code_; |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 154 | }; |
| 155 | |
| 156 | // Outputs the error as a friendly string. Update this method when adding a new |
| 157 | // error type. |
| 158 | // |
Mirko Bonadei | fb59a6a | 2019-09-20 09:33:02 +0200 | [diff] [blame] | 159 | // Only intended to be used for logging/diagnostics. The returned char* points |
Mirko Bonadei | 254ecff | 2019-01-16 12:14:29 +0100 | [diff] [blame] | 160 | // to literal string that lives for the whole duration of the program. |
Mirko Bonadei | 35214fc | 2019-09-23 14:54:28 +0200 | [diff] [blame] | 161 | RTC_EXPORT const char* ToString(RTCErrorType error); |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 162 | RTC_EXPORT const char* ToString(RTCErrorDetailType error); |
Jonas Olsson | d7ee720 | 2018-04-18 10:11:07 +0200 | [diff] [blame] | 163 | |
Jonas Olsson | 3e18c82 | 2018-04-18 10:11:07 +0200 | [diff] [blame] | 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 | } |
Harald Alvestrand | dfbfb46 | 2019-12-08 05:55:43 +0100 | [diff] [blame] | 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 | } |
Jonas Olsson | 3e18c82 | 2018-04-18 10:11:07 +0200 | [diff] [blame] | 176 | #endif // UNIT_TEST |
| 177 | |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
Jonas Olsson | abbe841 | 2018-04-03 13:40:05 +0200 | [diff] [blame] | 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); \ |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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 { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 209 | // RTC_LOG(LS_ERROR) << result.error(); |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 236 | RTCErrorOr() : error_(RTCErrorType::INTERNAL_ERROR) {} |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 246 | RTCErrorOr(RTCError&& error) : error_(std::move(error)) { // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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>. |
Mirko Bonadei | 9f3a44f | 2019-01-30 13:47:42 +0100 | [diff] [blame] | 257 | RTCErrorOr(const T& value) : value_(value) {} // NOLINT |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 258 | RTCErrorOr(T&& value) : value_(std::move(value)) {} // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
deadbeef | b5388d7 | 2017-02-24 01:17:43 -0800 | [diff] [blame] | 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. |
| 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 | } |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 278 | |
| 279 | // Conversion constructor and assignment operator; T must be copy or move |
| 280 | // constructible from U. |
| 281 | template <typename U> |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 282 | RTCErrorOr(RTCErrorOr<U> other) // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 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. |
| 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. |
| 298 | RTCError MoveError() { return std::move(error_); } |
| 299 | |
| 300 | // Returns this->error().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. |
| 308 | const T& value() const { |
| 309 | RTC_DCHECK(ok()); |
| 310 | return value_; |
| 311 | } |
| 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(). |
| 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 | |
Steve Anton | 10542f2 | 2019-01-11 09:11:00 -0800 | [diff] [blame] | 331 | #endif // API_RTC_ERROR_H_ |