You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
416 lines
11 KiB
416 lines
11 KiB
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef PLATFORM_BASE_ERROR_H_
|
|
#define PLATFORM_BASE_ERROR_H_
|
|
|
|
#include <cassert>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "platform/base/macros.h"
|
|
|
|
namespace openscreen {
|
|
|
|
// Represents an error returned by an OSP library operation. An error has a
|
|
// code and an optional message.
|
|
class Error {
|
|
public:
|
|
// TODO(crbug.com/openscreen/65): Group/rename OSP-specific errors
|
|
enum class Code : int8_t {
|
|
// No error occurred.
|
|
kNone = 0,
|
|
|
|
// A transient condition prevented the operation from proceeding (e.g.,
|
|
// cannot send on a non-blocking socket without blocking). This indicates
|
|
// the caller should try again later.
|
|
kAgain = -1,
|
|
|
|
// CBOR errors.
|
|
kCborParsing = 1,
|
|
kCborEncoding,
|
|
kCborIncompleteMessage,
|
|
kCborInvalidResponseId,
|
|
kCborInvalidMessage,
|
|
|
|
// Presentation start errors.
|
|
kNoAvailableReceivers,
|
|
kRequestCancelled,
|
|
kNoPresentationFound,
|
|
kPreviousStartInProgress,
|
|
kUnknownStartError,
|
|
kUnknownRequestId,
|
|
|
|
kAddressInUse,
|
|
kDomainNameTooLong,
|
|
kDomainNameLabelTooLong,
|
|
|
|
kIOFailure,
|
|
kInitializationFailure,
|
|
kInvalidIPV4Address,
|
|
kInvalidIPV6Address,
|
|
kConnectionFailed,
|
|
|
|
kSocketOptionSettingFailure,
|
|
kSocketAcceptFailure,
|
|
kSocketBindFailure,
|
|
kSocketClosedFailure,
|
|
kSocketConnectFailure,
|
|
kSocketInvalidState,
|
|
kSocketListenFailure,
|
|
kSocketReadFailure,
|
|
kSocketSendFailure,
|
|
|
|
// MDNS errors.
|
|
kMdnsRegisterFailure,
|
|
kMdnsReadFailure,
|
|
kMdnsNonConformingFailure,
|
|
|
|
kParseError,
|
|
kUnknownMessageType,
|
|
|
|
kNoActiveConnection,
|
|
kAlreadyClosed,
|
|
kInvalidConnectionState,
|
|
kNoStartedPresentation,
|
|
kPresentationAlreadyStarted,
|
|
|
|
kJsonParseError,
|
|
kJsonWriteError,
|
|
|
|
// OpenSSL errors.
|
|
|
|
// Was unable to generate an RSA key.
|
|
kRSAKeyGenerationFailure,
|
|
kRSAKeyParseError,
|
|
|
|
// Was unable to initialize an EVP_PKEY type.
|
|
kEVPInitializationError,
|
|
|
|
// Was unable to generate a certificate.
|
|
kCertificateCreationError,
|
|
|
|
// Certificate failed validation.
|
|
kCertificateValidationError,
|
|
|
|
// Failed to produce a hashing digest.
|
|
kSha256HashFailure,
|
|
|
|
// A non-recoverable SSL library error has occurred.
|
|
kFatalSSLError,
|
|
kFileLoadFailure,
|
|
|
|
// Cast certificate errors.
|
|
|
|
// Certificates were not provided for verification.
|
|
kErrCertsMissing,
|
|
|
|
// The certificates provided could not be parsed.
|
|
kErrCertsParse,
|
|
|
|
// Key usage is missing or is not set to Digital Signature.
|
|
// This error could also be thrown if the CN is missing.
|
|
kErrCertsRestrictions,
|
|
|
|
// The current date is before the notBefore date or after the notAfter date.
|
|
kErrCertsDateInvalid,
|
|
|
|
// The certificate failed to chain to a trusted root.
|
|
kErrCertsVerifyGeneric,
|
|
|
|
// The certificate was not found in the trust store.
|
|
kErrCertsVerifyUntrustedCert,
|
|
|
|
// The CRL is missing or failed to verify.
|
|
kErrCrlInvalid,
|
|
|
|
// One of the certificates in the chain is revoked.
|
|
kErrCertsRevoked,
|
|
|
|
// The pathlen constraint of the root certificate was exceeded.
|
|
kErrCertsPathlen,
|
|
|
|
// Cast authentication errors.
|
|
kCastV2PeerCertEmpty,
|
|
kCastV2WrongPayloadType,
|
|
kCastV2NoPayload,
|
|
kCastV2PayloadParsingFailed,
|
|
kCastV2MessageError,
|
|
kCastV2NoResponse,
|
|
kCastV2FingerprintNotFound,
|
|
kCastV2CertNotSignedByTrustedCa,
|
|
kCastV2CannotExtractPublicKey,
|
|
kCastV2SignedBlobsMismatch,
|
|
kCastV2TlsCertValidityPeriodTooLong,
|
|
kCastV2TlsCertValidStartDateInFuture,
|
|
kCastV2TlsCertExpired,
|
|
kCastV2SenderNonceMismatch,
|
|
kCastV2DigestUnsupported,
|
|
kCastV2SignatureEmpty,
|
|
|
|
// Cast channel errors.
|
|
kCastV2ChannelNotOpen,
|
|
kCastV2AuthenticationError,
|
|
kCastV2ConnectError,
|
|
kCastV2CastSocketError,
|
|
kCastV2TransportError,
|
|
kCastV2InvalidMessage,
|
|
kCastV2InvalidChannelId,
|
|
kCastV2ConnectTimeout,
|
|
kCastV2PingTimeout,
|
|
kCastV2ChannelPolicyMismatch,
|
|
|
|
kCreateSignatureFailed,
|
|
|
|
// Discovery errors.
|
|
kUpdateReceivedRecordFailure,
|
|
kRecordPublicationError,
|
|
kProcessReceivedRecordFailure,
|
|
|
|
// Generic errors.
|
|
kUnknownError,
|
|
kNotImplemented,
|
|
kInsufficientBuffer,
|
|
kParameterInvalid,
|
|
kParameterOutOfRange,
|
|
kParameterNullPointer,
|
|
kIndexOutOfBounds,
|
|
kItemAlreadyExists,
|
|
kItemNotFound,
|
|
kOperationInvalid,
|
|
kOperationInProgress,
|
|
kOperationCancelled,
|
|
|
|
// Cast streaming errors
|
|
kTypeError,
|
|
kUnknownCodec,
|
|
kSocketFailure,
|
|
kUnencryptedOffer
|
|
};
|
|
|
|
Error();
|
|
Error(const Error& error);
|
|
Error(Error&& error) noexcept;
|
|
|
|
Error(Code code); // NOLINT
|
|
Error(Code code, const std::string& message);
|
|
Error(Code code, std::string&& message);
|
|
~Error();
|
|
|
|
Error& operator=(const Error& other);
|
|
Error& operator=(Error&& other);
|
|
bool operator==(const Error& other) const;
|
|
bool operator!=(const Error& other) const;
|
|
|
|
// Special case comparison with codes. Without this case, comparisons will
|
|
// not work as expected, e.g.
|
|
// const Error foo(Error::Code::kItemNotFound, "Didn't find an item");
|
|
// foo == Error::Code::kItemNotFound is actually false.
|
|
bool operator==(Code code) const;
|
|
bool operator!=(Code code) const;
|
|
bool ok() const { return code_ == Code::kNone; }
|
|
|
|
Code code() const { return code_; }
|
|
const std::string& message() const { return message_; }
|
|
std::string& message() { return message_; }
|
|
|
|
static const Error& None();
|
|
|
|
std::string ToString() const;
|
|
|
|
private:
|
|
Code code_ = Code::kNone;
|
|
std::string message_;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Error::Code& code);
|
|
|
|
std::ostream& operator<<(std::ostream& out, const Error& error);
|
|
|
|
// A convenience function to return a single value from a function that can
|
|
// return a value or an error. For normal results, construct with a ValueType*
|
|
// (ErrorOr takes ownership) and the Error will be kNone with an empty message.
|
|
// For Error results, construct with an error code and value.
|
|
//
|
|
// Example:
|
|
//
|
|
// ErrorOr<Bar> Foo::DoSomething() {
|
|
// if (success) {
|
|
// return Bar();
|
|
// } else {
|
|
// return Error(kBadThingHappened, "No can do");
|
|
// }
|
|
// }
|
|
//
|
|
// TODO(mfoltz): Add support for type conversions.
|
|
template <typename ValueType>
|
|
class ErrorOr {
|
|
public:
|
|
static ErrorOr<ValueType> None() {
|
|
static ErrorOr<ValueType> error(Error::Code::kNone);
|
|
return error;
|
|
}
|
|
|
|
ErrorOr(const ValueType& value) : value_(value), is_value_(true) {} // NOLINT
|
|
ErrorOr(ValueType&& value) noexcept // NOLINT
|
|
: value_(std::move(value)), is_value_(true) {}
|
|
|
|
ErrorOr(const Error& error) : error_(error), is_value_(false) { // NOLINT
|
|
assert(error_.code() != Error::Code::kNone);
|
|
}
|
|
ErrorOr(Error&& error) noexcept // NOLINT
|
|
: error_(std::move(error)), is_value_(false) {
|
|
assert(error_.code() != Error::Code::kNone);
|
|
}
|
|
ErrorOr(Error::Code code) : error_(code), is_value_(false) { // NOLINT
|
|
assert(error_.code() != Error::Code::kNone);
|
|
}
|
|
ErrorOr(Error::Code code, std::string message)
|
|
: error_(code, std::move(message)), is_value_(false) {
|
|
assert(error_.code() != Error::Code::kNone);
|
|
}
|
|
|
|
ErrorOr(const ErrorOr& other) = delete;
|
|
ErrorOr(ErrorOr&& other) noexcept : is_value_(other.is_value_) {
|
|
// NB: Both |value_| and |error_| are uninitialized memory at this point!
|
|
// Unlike the other constructors, the compiler will not auto-generate
|
|
// constructor calls for either union member because neither appeared in
|
|
// this constructor's initializer list.
|
|
if (other.is_value_) {
|
|
new (&value_) ValueType(std::move(other.value_));
|
|
} else {
|
|
new (&error_) Error(std::move(other.error_));
|
|
}
|
|
}
|
|
|
|
ErrorOr& operator=(const ErrorOr& other) = delete;
|
|
ErrorOr& operator=(ErrorOr&& other) noexcept {
|
|
this->~ErrorOr<ValueType>();
|
|
new (this) ErrorOr<ValueType>(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
~ErrorOr() {
|
|
// NB: |value_| or |error_| must be explicitly destroyed since the compiler
|
|
// will not auto-generate the destructor calls for union members.
|
|
if (is_value_) {
|
|
value_.~ValueType();
|
|
} else {
|
|
error_.~Error();
|
|
}
|
|
}
|
|
|
|
bool is_error() const { return !is_value_; }
|
|
bool is_value() const { return is_value_; }
|
|
|
|
// Unlike Error, we CAN provide an operator bool here, since it is
|
|
// more obvious to callers that ErrorOr<Foo> will be true if it's Foo.
|
|
operator bool() const { return is_value_; }
|
|
|
|
const Error& error() const {
|
|
assert(!is_value_);
|
|
return error_;
|
|
}
|
|
Error& error() {
|
|
assert(!is_value_);
|
|
return error_;
|
|
}
|
|
|
|
const ValueType& value() const {
|
|
assert(is_value_);
|
|
return value_;
|
|
}
|
|
ValueType& value() {
|
|
assert(is_value_);
|
|
return value_;
|
|
}
|
|
|
|
// Move only value or fallback
|
|
ValueType&& value(ValueType&& fallback) {
|
|
if (is_value()) {
|
|
return std::move(value());
|
|
}
|
|
return std::forward<ValueType>(fallback);
|
|
}
|
|
|
|
// Copy only value or fallback
|
|
ValueType value(ValueType fallback) const {
|
|
if (is_value()) {
|
|
return value();
|
|
}
|
|
return std::move(fallback);
|
|
}
|
|
|
|
private:
|
|
// Only one of these is an active member, determined by |is_value_|. Since
|
|
// they are union'ed, they must be explicitly constructed and destroyed.
|
|
union {
|
|
ValueType value_;
|
|
Error error_;
|
|
};
|
|
|
|
// If true, |value_| is initialized and active. Otherwise, |error_| is
|
|
// initialized and active.
|
|
const bool is_value_;
|
|
};
|
|
|
|
// Define comparison operators using SFINAE.
|
|
template <typename ValueType>
|
|
bool operator<(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
// Handle the cases where one side is an error.
|
|
if (lhs.is_error() != rhs.is_error()) {
|
|
return lhs.is_error();
|
|
}
|
|
|
|
// Handle the case where both sides are errors.
|
|
if (lhs.is_error()) {
|
|
return static_cast<int8_t>(lhs.error().code()) <
|
|
static_cast<int8_t>(rhs.error().code());
|
|
}
|
|
|
|
// Handle the case where both are values.
|
|
return lhs.value() < rhs.value();
|
|
}
|
|
|
|
template <typename ValueType>
|
|
bool operator>(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
return rhs < lhs;
|
|
}
|
|
|
|
template <typename ValueType>
|
|
bool operator<=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
return !(lhs > rhs);
|
|
}
|
|
|
|
template <typename ValueType>
|
|
bool operator>=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
return !(rhs < lhs);
|
|
}
|
|
|
|
template <typename ValueType>
|
|
bool operator==(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
// Handle the cases where one side is an error.
|
|
if (lhs.is_error() != rhs.is_error()) {
|
|
return false;
|
|
}
|
|
|
|
// Handle the case where both sides are errors.
|
|
if (lhs.is_error()) {
|
|
return lhs.error() == rhs.error();
|
|
}
|
|
|
|
// Handle the case where both are values.
|
|
return lhs.value() == rhs.value();
|
|
}
|
|
|
|
template <typename ValueType>
|
|
bool operator!=(const ErrorOr<ValueType>& lhs, const ErrorOr<ValueType>& rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
} // namespace openscreen
|
|
|
|
#endif // PLATFORM_BASE_ERROR_H_
|