use std::error::Error as StdError;
use std::io;
use {crate::wiggle_abi::types::FastlyStatus, url::Url, wiggle::GuestError};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("Buffer length error: {buf} too long to fit in {len}")]
BufferLengthError {
buf: &'static str,
len: &'static str,
},
#[error("Fatal error: [{0}]")]
FatalError(String),
#[error("Expected a valid Wasm file")]
FileFormat,
#[error("Expected a valid wastime's profiling strategy")]
ProfilingStrategy,
#[error(transparent)]
FastlyConfig(#[from] FastlyConfigError),
#[error("Could not determine address from backend URL: {0}")]
BackendUrl(Url),
#[error("Guest error: [{0}]")]
GuestError(#[from] wiggle::GuestError),
#[error(transparent)]
HandleError(#[from] HandleError),
#[error(transparent)]
HyperError(#[from] hyper::Error),
#[error(transparent)]
Infallible(#[from] std::convert::Infallible),
#[error("Invalid argument given")]
InvalidArgument,
#[error(transparent)]
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
#[error(transparent)]
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
#[error(transparent)]
InvalidMethod(#[from] http::method::InvalidMethod),
#[error(transparent)]
InvalidStatusCode(#[from] http::status::InvalidStatusCode),
#[error(transparent)]
InvalidUri(#[from] http::uri::InvalidUri),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
Other(#[from] anyhow::Error),
#[error("Unsupported operation: {msg}")]
Unsupported { msg: &'static str },
#[error("Downstream response already sending")]
DownstreamRespSending,
#[error("Unexpected error sending a chunk to a streaming body")]
StreamingChunkSend,
#[error("Unknown backend: {0}")]
UnknownBackend(String),
#[error(transparent)]
DictionaryError(#[from] crate::wiggle_abi::DictionaryError),
#[error(transparent)]
DeviceDetectionError(#[from] crate::wiggle_abi::DeviceDetectionError),
#[error(transparent)]
ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
#[error(transparent)]
SecretStoreError(#[from] crate::wiggle_abi::SecretStoreError),
#[error{"Expected UTF-8"}]
Utf8Expected(#[from] std::str::Utf8Error),
#[error{"Unsupported ABI version"}]
AbiVersionMismatch,
#[error(transparent)]
DownstreamRequestError(#[from] DownstreamRequestError),
#[error("{0} is not currently supported for local testing")]
NotAvailable(&'static str),
#[error("Could not load native certificates: {0}")]
BadCerts(std::io::Error),
#[error("Could not generate new backend name from '{0}'")]
BackendNameRegistryError(String),
#[error(transparent)]
HttpError(#[from] http::Error),
#[error("Object Store '{0}' does not exist")]
UnknownObjectStore(String),
#[error("Invalid Object Store `key` value used: {0}.")]
ObjectStoreKeyValidationError(#[from] crate::object_store::KeyValidationError),
#[error("Unfinished streaming body")]
UnfinishedStreamingBody,
#[error("Shared memory not supported yet")]
SharedMemory,
#[error("Value absent from structure")]
ValueAbsent,
#[error("String conversion error")]
ToStr(#[from] http::header::ToStrError),
#[error("invalid client certificate")]
InvalidClientCert(#[from] crate::config::ClientCertError),
#[error("Invalid response to ALPN request; wanted '{0}', got '{1}'")]
InvalidAlpnRepsonse(&'static str, String),
#[error("Resource temporarily unavailable")]
Again,
}
impl Error {
pub fn to_fastly_status(&self) -> FastlyStatus {
match self {
Error::BufferLengthError { .. } => FastlyStatus::Buflen,
Error::InvalidArgument => FastlyStatus::Inval,
Error::ValueAbsent => FastlyStatus::None,
Error::Unsupported { .. } | Error::NotAvailable(_) => FastlyStatus::Unsupported,
Error::HandleError { .. } => FastlyStatus::Badf,
Error::InvalidStatusCode { .. } => FastlyStatus::Inval,
Error::UnknownBackend(_) | Error::InvalidClientCert(_) => FastlyStatus::Inval,
Error::HyperError(e) if e.is_parse() => FastlyStatus::Httpinvalid,
Error::HyperError(e) if e.is_user() => FastlyStatus::Httpuser,
Error::HyperError(e) if e.is_incomplete_message() => FastlyStatus::Httpincomplete,
Error::HyperError(e)
if e.source()
.and_then(|e| e.downcast_ref::<io::Error>())
.map(|ioe| ioe.kind())
== Some(io::ErrorKind::UnexpectedEof) =>
{
FastlyStatus::Httpincomplete
}
Error::HyperError(_) => FastlyStatus::Error,
Error::GuestError(e) => Self::guest_error_fastly_status(e),
Error::DictionaryError(e) => e.to_fastly_status(),
Error::DeviceDetectionError(e) => e.to_fastly_status(),
Error::ObjectStoreError(e) => e.into(),
Error::SecretStoreError(e) => e.into(),
Error::Again => FastlyStatus::Again,
Error::AbiVersionMismatch
| Error::BackendUrl(_)
| Error::BadCerts(_)
| Error::DownstreamRequestError(_)
| Error::DownstreamRespSending
| Error::FastlyConfig(_)
| Error::FatalError(_)
| Error::FileFormat
| Error::Infallible(_)
| Error::InvalidHeaderName(_)
| Error::InvalidHeaderValue(_)
| Error::InvalidMethod(_)
| Error::InvalidUri(_)
| Error::IoError(_)
| Error::Other(_)
| Error::ProfilingStrategy
| Error::StreamingChunkSend
| Error::Utf8Expected(_)
| Error::BackendNameRegistryError(_)
| Error::HttpError(_)
| Error::UnknownObjectStore(_)
| Error::ObjectStoreKeyValidationError(_)
| Error::UnfinishedStreamingBody
| Error::SharedMemory
| Error::ToStr(_)
| Error::InvalidAlpnRepsonse(_, _) => FastlyStatus::Error,
}
}
fn guest_error_fastly_status(e: &GuestError) -> FastlyStatus {
use GuestError::*;
match e {
PtrNotAligned { .. } => FastlyStatus::Badalign,
InvalidFlagValue { .. }
| InvalidEnumValue { .. }
| PtrOutOfBounds { .. }
| PtrBorrowed { .. }
| PtrOverflow { .. }
| InvalidUtf8 { .. }
| TryFromIntError { .. } => FastlyStatus::Inval,
BorrowCheckerOutOfHandles | SliceLengthsDiffer => FastlyStatus::Error,
InFunc { err, .. } => Self::guest_error_fastly_status(err),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum HandleError {
#[error("Invalid request handle: {0}")]
InvalidRequestHandle(crate::wiggle_abi::types::RequestHandle),
#[error("Invalid response handle: {0}")]
InvalidResponseHandle(crate::wiggle_abi::types::ResponseHandle),
#[error("Invalid body handle: {0}")]
InvalidBodyHandle(crate::wiggle_abi::types::BodyHandle),
#[error("Invalid endpoint handle: {0}")]
InvalidEndpointHandle(crate::wiggle_abi::types::EndpointHandle),
#[error("Invalid pending request handle: {0}")]
InvalidPendingRequestHandle(crate::wiggle_abi::types::PendingRequestHandle),
#[error("Invalid pending KV lookup handle: {0}")]
InvalidPendingKvLookupHandle(crate::wiggle_abi::types::PendingKvLookupHandle),
#[error("Invalid pending KV insert handle: {0}")]
InvalidPendingKvInsertHandle(crate::wiggle_abi::types::PendingKvInsertHandle),
#[error("Invalid pending KV delete handle: {0}")]
InvalidPendingKvDeleteHandle(crate::wiggle_abi::types::PendingKvDeleteHandle),
#[error("Invalid dictionary handle: {0}")]
InvalidDictionaryHandle(crate::wiggle_abi::types::DictionaryHandle),
#[error("Invalid object-store handle: {0}")]
InvalidObjectStoreHandle(crate::wiggle_abi::types::ObjectStoreHandle),
#[error("Invalid secret store handle: {0}")]
InvalidSecretStoreHandle(crate::wiggle_abi::types::SecretStoreHandle),
#[error("Invalid secret handle: {0}")]
InvalidSecretHandle(crate::wiggle_abi::types::SecretHandle),
#[error("Invalid async item handle: {0}")]
InvalidAsyncItemHandle(crate::wiggle_abi::types::AsyncItemHandle),
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum ExecutionError {
#[error("WebAssembly execution trapped: {0}")]
WasmTrap(anyhow::Error),
#[error("Error creating context: {0}")]
Context(anyhow::Error),
#[error("Error type-checking WebAssembly instantiation: {0}")]
Typechecking(anyhow::Error),
#[error("Error instantiating WebAssembly: {0}")]
Instantiation(anyhow::Error),
}
#[derive(Debug, thiserror::Error)]
pub enum FastlyConfigError {
#[error("error reading '{path}': {err}")]
IoError {
path: String,
#[source]
err: std::io::Error,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidDeviceDetectionDefinition {
name: String,
#[source]
err: DeviceDetectionConfigError,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidGeolocationDefinition {
name: String,
#[source]
err: GeolocationConfigError,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidBackendDefinition {
name: String,
#[source]
err: BackendConfigError,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidDictionaryDefinition {
name: String,
#[source]
err: DictionaryConfigError,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidObjectStoreDefinition {
name: String,
#[source]
err: ObjectStoreConfigError,
},
#[error("invalid configuration for '{name}': {err}")]
InvalidSecretStoreDefinition {
name: String,
#[source]
err: SecretStoreConfigError,
},
#[error("error parsing `fastly.toml`: {0}")]
InvalidFastlyToml(#[from] toml::de::Error),
#[error("invalid manifest version: {0}")]
InvalidManifestVersion(#[from] semver::SemVerError),
}
#[derive(Debug, thiserror::Error)]
pub enum BackendConfigError {
#[error("definition was not provided as a TOML table")]
InvalidEntryType,
#[error("invalid override_host: {0}")]
InvalidOverrideHost(#[from] http::header::InvalidHeaderValue),
#[error("'override_host' field is empty")]
EmptyOverrideHost,
#[error("'override_host' field was not a string")]
InvalidOverrideHostEntry,
#[error("'cert_host' field is empty")]
EmptyCertHost,
#[error("'cert_host' field was not a string")]
InvalidCertHostEntry,
#[error("'ca_certificate' field is empty")]
EmptyCACert,
#[error("'ca_certificate' field was invalid: {0}")]
InvalidCACertEntry(String),
#[error("'use_sni' field was not a boolean")]
InvalidUseSniEntry,
#[error("'grpc' field was not a boolean")]
InvalidGrpcEntry,
#[error("invalid url: {0}")]
InvalidUrl(#[from] http::uri::InvalidUri),
#[error("'url' field was not a string")]
InvalidUrlEntry,
#[error("no default definition provided")]
MissingDefault,
#[error("missing 'url' field")]
MissingUrl,
#[error("unrecognized key '{0}'")]
UnrecognizedKey(String),
}
#[derive(Debug, thiserror::Error)]
pub enum DictionaryConfigError {
#[error(transparent)]
IoError(std::io::Error),
#[error("'contents' was not provided as a TOML table")]
InvalidContentsType,
#[error("inline dictionary value was not a string")]
InvalidInlineEntryType,
#[error("definition was not provided as a TOML table")]
InvalidEntryType,
#[error("'name' field was not a string")]
InvalidNameEntry,
#[error("'{0}' is not a valid format for the dictionary. Supported format(s) are: 'inline-toml', 'json'.")]
InvalidDictionaryFormat(String),
#[error("'file' field is empty")]
EmptyFileEntry,
#[error("'format' field is empty")]
EmptyFormatEntry,
#[error("'file' field was not a string")]
InvalidFileEntry,
#[error("'format' field was not a string")]
InvalidFormatEntry,
#[error("missing 'contents' field")]
MissingContents,
#[error("no default definition provided")]
MissingDefault,
#[error("missing 'name' field")]
MissingName,
#[error("missing 'file' field")]
MissingFile,
#[error("missing 'format' field")]
MissingFormat,
#[error("unrecognized key '{0}'")]
UnrecognizedKey(String),
#[error("Item key named '{key}' is too long, max size is {size}")]
DictionaryItemKeyTooLong { key: String, size: i32 },
#[error("too many items, max amount is {size}")]
DictionaryCountTooLong { size: i32 },
#[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
DictionaryItemValueWrongFormat { key: String },
#[error("Item value named '{key}' is too long, max size is {size}")]
DictionaryItemValueTooLong { key: String, size: i32 },
#[error(
"The file is of the wrong format. The file is expected to contain a single JSON Object"
)]
DictionaryFileWrongFormat,
}
#[derive(Debug, thiserror::Error)]
pub enum DeviceDetectionConfigError {
#[error(transparent)]
IoError(std::io::Error),
#[error("definition was not provided as a TOML table")]
InvalidEntryType,
#[error("missing 'file' field")]
MissingFile,
#[error("'file' field is empty")]
EmptyFileEntry,
#[error("missing 'user_agents' field")]
MissingUserAgents,
#[error("inline device detection value was not a string")]
InvalidInlineEntryType,
#[error("'file' field was not a string")]
InvalidFileEntry,
#[error("'user_agents' was not provided as a TOML table")]
InvalidUserAgentsType,
#[error("unrecognized key '{0}'")]
UnrecognizedKey(String),
#[error("missing 'format' field")]
MissingFormat,
#[error("'format' field was not a string")]
InvalidFormatEntry,
#[error("'{0}' is not a valid format for the device detection mapping. Supported format(s) are: 'inline-toml', 'json'.")]
InvalidDeviceDetectionMappingFormat(String),
#[error(
"The file is of the wrong format. The file is expected to contain a single JSON Object"
)]
DeviceDetectionFileWrongFormat,
#[error("'format' field is empty")]
EmptyFormatEntry,
#[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
DeviceDetectionItemValueWrongFormat { key: String },
}
#[derive(Debug, thiserror::Error)]
pub enum GeolocationConfigError {
#[error(transparent)]
IoError(std::io::Error),
#[error("definition was not provided as a TOML table")]
InvalidEntryType,
#[error("missing 'file' field")]
MissingFile,
#[error("'file' field is empty")]
EmptyFileEntry,
#[error("missing 'addresses' field")]
MissingAddresses,
#[error("inline geolocation value was not a string")]
InvalidInlineEntryType,
#[error("'file' field was not a string")]
InvalidFileEntry,
#[error("'addresses' was not provided as a TOML table")]
InvalidAddressesType,
#[error("unrecognized key '{0}'")]
UnrecognizedKey(String),
#[error("missing 'format' field")]
MissingFormat,
#[error("'format' field was not a string")]
InvalidFormatEntry,
#[error("IP address not valid: '{0}'")]
InvalidAddressEntry(String),
#[error("'{0}' is not a valid format for the geolocation mapping. Supported format(s) are: 'inline-toml', 'json'.")]
InvalidGeolocationMappingFormat(String),
#[error(
"The file is of the wrong format. The file is expected to contain a single JSON Object"
)]
GeolocationFileWrongFormat,
#[error("'format' field is empty")]
EmptyFormatEntry,
#[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
GeolocationItemValueWrongFormat { key: String },
}
#[derive(Debug, thiserror::Error)]
pub enum ObjectStoreConfigError {
#[error(transparent)]
IoError(std::io::Error),
#[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
FileAndData(String),
#[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
NoFileOrData(String),
#[error("The `data` value for the object `{0}` is not a string.")]
DataNotAString(String),
#[error("The `file` value for the object `{0}` is not a string.")]
FileNotAString(String),
#[error("The `key` key for an object is not set. It must be used.")]
NoKey,
#[error("The `key` value for an object is not a string.")]
KeyNotAString,
#[error("There is no array of objects for the given store.")]
NotAnArray,
#[error("There is an object in the given store that is not a table of keys.")]
NotATable,
#[error("There was an error when manipulating the ObjectStore: {0}.")]
ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
#[error("Invalid `key` value used: {0}.")]
KeyValidationError(#[from] crate::object_store::KeyValidationError),
}
#[derive(Debug, thiserror::Error)]
pub enum SecretStoreConfigError {
#[error(transparent)]
IoError(std::io::Error),
#[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
FileAndData(String),
#[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
NoFileOrData(String),
#[error("The `data` value for the object `{0}` is not a string.")]
DataNotAString(String),
#[error("The `file` value for the object `{0}` is not a string.")]
FileNotAString(String),
#[error("The `key` key for an object is not set. It must be used.")]
NoKey,
#[error("The `key` value for an object is not a string.")]
KeyNotAString,
#[error("There is no array of objects for the given store.")]
NotAnArray,
#[error("There is an object in the given store that is not a table of keys.")]
NotATable,
#[error("Invalid secret store name: {0}")]
InvalidSecretStoreName(String),
#[error("Invalid secret name: {0}")]
InvalidSecretName(String),
}
#[derive(Debug, thiserror::Error)]
pub enum DownstreamRequestError {
#[error("Request HOST header is missing or invalid")]
InvalidHost,
#[error("Request URL is invalid")]
InvalidUrl,
}