Skip to main content

viceroy_lib/
error.rs

1//! Error types.
2
3use crate::{
4    component::compute::backend::HealthcheckCreationError, handoff::HandoffInfo,
5    wiggle_abi::types::FastlyStatus,
6};
7use hyper::StatusCode;
8use std::error::Error as StdError;
9use std::io;
10use url::Url;
11use wiggle::GuestError;
12
13#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum Error {
16    /// Thrown by hostcalls when a buffer is larger than its `*_len` limit.
17    #[error("Buffer length error: {buf} too long to fit in {len}")]
18    BufferLengthError {
19        buf: &'static str,
20        len: &'static str,
21    },
22
23    /// Error when viceroy has encountered a fatal error and the underlying wasmtime
24    /// instance must be terminated with a Trap.
25    #[error("Fatal error: [{0}]")]
26    FatalError(String),
27
28    /// Error when viceroy has been given an invalid file.
29    #[error("Expected a valid Wasm file")]
30    FileFormat,
31
32    #[error("Expected a valid wastime's profiling strategy")]
33    ProfilingStrategy,
34
35    #[error(transparent)]
36    FastlyConfig(#[from] FastlyConfigError),
37
38    #[error("Could not determine address from backend URL: {0}")]
39    BackendUrl(Url),
40
41    /// An error from guest-provided arguments to a hostcall. These errors may be created
42    /// automatically by the Wiggle-generated glue code that converts parameters from their ABI
43    /// representation into richer Rust types, or by fallible methods of `GuestPtr` in the
44    /// wiggle_abi trait implementations.
45    #[error("Guest error: [{0}]")]
46    GuestError(#[from] wiggle::GuestError),
47
48    #[error(transparent)]
49    HandleError(#[from] HandleError),
50
51    #[error(transparent)]
52    HyperError(#[from] hyper::Error),
53
54    #[error(transparent)]
55    HealthcheckCreationError(#[from] HealthcheckCreationError),
56
57    #[error("Backend connection error for '{backend_name}' ({uri}): {source}")]
58    BackendConnectionError {
59        backend_name: String,
60        uri: String,
61        #[source]
62        source: hyper::Error,
63    },
64
65    #[error(transparent)]
66    Infallible(#[from] std::convert::Infallible),
67
68    /// Error when an invalid argument is supplied to a hostcall.
69    #[error("Invalid argument given")]
70    InvalidArgument,
71
72    #[error(transparent)]
73    InvalidHeaderName(#[from] http::header::InvalidHeaderName),
74
75    #[error(transparent)]
76    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
77
78    #[error(transparent)]
79    InvalidMethod(#[from] http::method::InvalidMethod),
80
81    #[error(transparent)]
82    InvalidStatusCode(#[from] http::status::InvalidStatusCode),
83
84    #[error(transparent)]
85    InvalidUri(#[from] http::uri::InvalidUri),
86
87    #[error(transparent)]
88    IoError(#[from] std::io::Error),
89
90    #[error("Limit exceeded: {msg}")]
91    LimitExceeded { msg: &'static str },
92
93    #[error("Missing downstream metadata for request")]
94    MissingDownstreamMetadata,
95
96    #[error(transparent)]
97    Other(#[from] anyhow::Error),
98
99    #[error("Unsupported operation: {msg}")]
100    Unsupported { msg: &'static str },
101
102    /// Downstream response is already sending.
103    #[error("Downstream response already sending")]
104    DownstreamRespSending,
105
106    #[error("Unexpected error sending a chunk to a streaming body")]
107    StreamingChunkSend,
108
109    #[error("Unknown backend: {0}")]
110    UnknownBackend(String),
111
112    #[error(transparent)]
113    DictionaryError(#[from] crate::wiggle_abi::DictionaryError),
114
115    #[error(transparent)]
116    DeviceDetectionError(#[from] crate::wiggle_abi::DeviceDetectionError),
117
118    #[error(transparent)]
119    ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
120
121    #[error(transparent)]
122    KvStoreError(#[from] crate::object_store::KvStoreError),
123
124    #[error(transparent)]
125    SecretStoreError(#[from] crate::wiggle_abi::SecretStoreError),
126
127    #[error{"Expected UTF-8"}]
128    Utf8Expected(#[from] std::str::Utf8Error),
129
130    #[error{"Unsupported ABI version"}]
131    AbiVersionMismatch,
132
133    #[error(transparent)]
134    DownstreamRequestError(#[from] DownstreamRequestError),
135
136    #[error("{0} is not currently supported for local testing")]
137    NotAvailable(&'static str),
138
139    #[error("Could not load native certificates: {0}")]
140    BadCerts(std::io::Error),
141
142    #[error("No CA certificates available")]
143    TlsNoCAAvailable,
144
145    #[error("No valid CA certificates found in provided certificate bundle")]
146    TlsNoValidCACerts,
147
148    #[error("Invalid or missing host for TLS connection")]
149    TlsInvalidHost,
150
151    #[error("TLS certificate validation failed")]
152    TlsCertificateValidationFailed,
153
154    #[error("Could not generate new backend name from '{0}'")]
155    BackendNameRegistryError(String),
156
157    #[error(transparent)]
158    HttpError(#[from] http::Error),
159
160    #[error("Object Store '{0}' does not exist")]
161    UnknownObjectStore(String),
162
163    #[error("Invalid Object Store `key` value used: {0}.")]
164    ObjectStoreKeyValidationError(#[from] crate::object_store::KeyValidationError),
165
166    #[error("Unfinished streaming body")]
167    UnfinishedStreamingBody,
168
169    #[error("Shared memory not supported yet")]
170    SharedMemory,
171
172    #[error("Value absent from structure")]
173    ValueAbsent,
174
175    #[error("String conversion error")]
176    ToStr(#[from] http::header::ToStrError),
177
178    #[error("invalid client certificate")]
179    InvalidClientCert(#[from] crate::config::ClientCertError),
180
181    #[error("Invalid response to ALPN request; wanted '{0}', got '{1}'")]
182    InvalidAlpnResponse(&'static str, String),
183
184    #[error("Resource temporarily unavailable")]
185    Again,
186
187    #[error("cache error: {0}")]
188    CacheError(crate::cache::Error),
189
190    #[error("Timed out waiting for first byte of response: {0}")]
191    FirstByteTimeout(tokio::time::error::Elapsed),
192
193    #[error("Timed out waiting for an internal byte of response")]
194    BetweenBytesTimeout,
195}
196
197impl Error {
198    /// Map this error onto an appropriate 5XX response code during proxying.
199    pub fn as_status_code(&self) -> StatusCode {
200        match self {
201            Self::FirstByteTimeout(_) | Self::BetweenBytesTimeout => StatusCode::GATEWAY_TIMEOUT,
202            Self::HyperError(e) => {
203                if e.is_timeout() {
204                    StatusCode::GATEWAY_TIMEOUT
205                } else {
206                    StatusCode::BAD_GATEWAY
207                }
208            }
209
210            Self::BackendUrl(..)
211            | Self::BackendConnectionError { .. }
212            | Self::InvalidArgument
213            | Self::InvalidHeaderName(..)
214            | Self::InvalidHeaderValue(..)
215            | Self::InvalidMethod(..)
216            | Self::InvalidStatusCode(..)
217            | Self::InvalidUri(..)
218            | Self::IoError(..)
219            | Self::MissingDownstreamMetadata
220            | Self::StreamingChunkSend
221            | Self::UnknownBackend(..)
222            | Self::BadCerts(..)
223            | Self::TlsNoCAAvailable
224            | Self::TlsNoValidCACerts
225            | Self::TlsInvalidHost
226            | Self::TlsCertificateValidationFailed
227            | Self::HttpError(..)
228            | Self::UnfinishedStreamingBody
229            | Self::InvalidClientCert(..)
230            | Self::InvalidAlpnResponse(..)
231            | Self::CacheError(..) => StatusCode::BAD_GATEWAY,
232
233            _ => StatusCode::INTERNAL_SERVER_ERROR,
234        }
235    }
236
237    /// Convert to an error code representation suitable for passing across the ABI boundary.
238    ///
239    /// For more information about specific error codes see [`fastly_shared::FastlyStatus`][status],
240    /// as well as the `witx` interface definition located in `wasm_abi/typenames.witx`.
241    ///
242    /// [status]: fastly_shared/struct.FastlyStatus.html
243    pub fn to_fastly_status(&self) -> FastlyStatus {
244        match self {
245            Error::BufferLengthError { .. } => FastlyStatus::Buflen,
246            Error::InvalidArgument | Error::HealthcheckCreationError(_) => FastlyStatus::Inval,
247            Error::ValueAbsent => FastlyStatus::None,
248            Error::Unsupported { .. } | Error::NotAvailable(_) => FastlyStatus::Unsupported,
249            Error::HandleError { .. } => FastlyStatus::Badf,
250            Error::InvalidStatusCode { .. } => FastlyStatus::Inval,
251            Error::UnknownBackend(_) | Error::InvalidClientCert(_) => FastlyStatus::Inval,
252            // Map specific kinds of `hyper::Error` into their respective error codes.
253            Error::HyperError(e) if e.is_parse() => FastlyStatus::Httpinvalid,
254            Error::HyperError(e) if e.is_user() => FastlyStatus::Httpuser,
255            Error::HyperError(e) if e.is_incomplete_message() => FastlyStatus::Httpincomplete,
256            Error::HyperError(e)
257                if e.source()
258                    .and_then(|e| e.downcast_ref::<io::Error>())
259                    .map(|ioe| ioe.kind())
260                    == Some(io::ErrorKind::UnexpectedEof) =>
261            {
262                FastlyStatus::Httpincomplete
263            }
264            Error::HyperError(_) => FastlyStatus::Error,
265            // BackendConnectionError contains detailed context but maps to same status as HyperError
266            Error::BackendConnectionError { source, .. } if source.is_parse() => {
267                FastlyStatus::Httpinvalid
268            }
269            Error::BackendConnectionError { source, .. } if source.is_user() => {
270                FastlyStatus::Httpuser
271            }
272            Error::BackendConnectionError { source, .. } if source.is_incomplete_message() => {
273                FastlyStatus::Httpincomplete
274            }
275            Error::BackendConnectionError { source, .. }
276                if source
277                    .source()
278                    .and_then(|e| e.downcast_ref::<io::Error>())
279                    .map(|ioe| ioe.kind())
280                    == Some(io::ErrorKind::UnexpectedEof) =>
281            {
282                FastlyStatus::Httpincomplete
283            }
284            Error::BackendConnectionError { .. } => FastlyStatus::Error,
285            // Destructuring a GuestError is recursive, so we use a helper function:
286            Error::GuestError(e) => Self::guest_error_fastly_status(e),
287            // We delegate to some error types' own implementation of `to_fastly_status`.
288            Error::DictionaryError(e) => e.to_fastly_status(),
289            Error::DeviceDetectionError(e) => e.to_fastly_status(),
290            Error::ObjectStoreError(e) => e.into(),
291            Error::KvStoreError(e) => e.into(),
292            Error::SecretStoreError(e) => e.into(),
293            Error::Again => FastlyStatus::Again,
294            Error::LimitExceeded { .. } => FastlyStatus::Limitexceeded,
295            Error::CacheError(e) => e.into(),
296            // All other hostcall errors map to a generic `ERROR` value.
297            Error::AbiVersionMismatch
298            | Error::BackendUrl(_)
299            | Error::BadCerts(_)
300            | Error::TlsNoCAAvailable
301            | Error::TlsNoValidCACerts
302            | Error::TlsInvalidHost
303            | Error::TlsCertificateValidationFailed
304            | Error::DownstreamRequestError(_)
305            | Error::DownstreamRespSending
306            | Error::FastlyConfig(_)
307            | Error::FatalError(_)
308            | Error::FileFormat
309            | Error::Infallible(_)
310            | Error::InvalidHeaderName(_)
311            | Error::InvalidHeaderValue(_)
312            | Error::InvalidMethod(_)
313            | Error::InvalidUri(_)
314            | Error::IoError(_)
315            | Error::MissingDownstreamMetadata
316            | Error::Other(_)
317            | Error::ProfilingStrategy
318            | Error::StreamingChunkSend
319            | Error::Utf8Expected(_)
320            | Error::BackendNameRegistryError(_)
321            | Error::HttpError(_)
322            | Error::UnknownObjectStore(_)
323            | Error::ObjectStoreKeyValidationError(_)
324            | Error::UnfinishedStreamingBody
325            | Error::SharedMemory
326            | Error::ToStr(_)
327            | Error::FirstByteTimeout(_)
328            | Error::BetweenBytesTimeout
329            | Error::InvalidAlpnResponse(_, _) => FastlyStatus::Error,
330        }
331    }
332
333    fn guest_error_fastly_status(e: &GuestError) -> FastlyStatus {
334        use GuestError::*;
335        match e {
336            PtrNotAligned { .. } => FastlyStatus::Badalign,
337            // We may want to expand the FastlyStatus enum to distinguish between more of these
338            // values.
339            InvalidFlagValue { .. }
340            | InvalidEnumValue { .. }
341            | PtrOutOfBounds { .. }
342            | PtrOverflow
343            | InvalidUtf8 { .. }
344            | TryFromIntError { .. } => FastlyStatus::Inval,
345            // These errors indicate either a pathological user input or an internal programming
346            // error
347            SliceLengthsDiffer => FastlyStatus::Error,
348            // Recursive case: InFunc wraps a GuestError with some context which
349            // doesn't determine what sort of FastlyStatus we return.
350            InFunc { err, .. } => Self::guest_error_fastly_status(err),
351        }
352    }
353}
354
355/// Errors thrown due to an invalid resource handle of some kind.
356#[derive(Debug, thiserror::Error)]
357pub enum HandleError {
358    /// A request handle was not valid.
359    #[error("Invalid request handle: {0}")]
360    InvalidRequestHandle(crate::wiggle_abi::types::RequestHandle),
361
362    /// A response handle was not valid.
363    #[error("Invalid response handle: {0}")]
364    InvalidResponseHandle(crate::wiggle_abi::types::ResponseHandle),
365
366    /// A body handle was not valid.
367    #[error("Invalid body handle: {0}")]
368    InvalidBodyHandle(crate::wiggle_abi::types::BodyHandle),
369
370    /// A logging endpoint handle was not valid.
371    #[error("Invalid endpoint handle: {0}")]
372    InvalidEndpointHandle(crate::wiggle_abi::types::EndpointHandle),
373
374    /// A request handle was not valid.
375    #[error("Invalid pending request promise handle: {0}")]
376    InvalidPendingRequestHandle(crate::wiggle_abi::types::PendingRequestHandle),
377
378    /// A request handle was not valid.
379    #[error("Invalid pending downstream request handle: {0}")]
380    InvalidPendingDownstreamHandle(crate::wiggle_abi::types::AsyncItemHandle),
381
382    /// A lookup handle was not valid.
383    #[error("Invalid pending KV lookup handle: {0}")]
384    InvalidPendingKvLookupHandle(crate::wiggle_abi::types::PendingKvLookupHandle),
385
386    /// A insert handle was not valid.
387    #[error("Invalid pending KV insert handle: {0}")]
388    InvalidPendingKvInsertHandle(crate::wiggle_abi::types::PendingKvInsertHandle),
389
390    /// A delete handle was not valid.
391    #[error("Invalid pending KV delete handle: {0}")]
392    InvalidPendingKvDeleteHandle(crate::wiggle_abi::types::PendingKvDeleteHandle),
393
394    /// A list handle was not valid.
395    #[error("Invalid pending KV list handle: {0}")]
396    InvalidPendingKvListHandle(crate::wiggle_abi::types::PendingKvListHandle),
397
398    /// A dictionary handle was not valid.
399    #[error("Invalid dictionary handle: {0}")]
400    InvalidDictionaryHandle(crate::wiggle_abi::types::DictionaryHandle),
401
402    /// An object-store handle was not valid.
403    #[error("Invalid object-store handle: {0}")]
404    InvalidObjectStoreHandle(crate::wiggle_abi::types::ObjectStoreHandle),
405
406    /// A secret store handle was not valid.
407    #[error("Invalid secret store handle: {0}")]
408    InvalidSecretStoreHandle(crate::wiggle_abi::types::SecretStoreHandle),
409
410    /// A secret handle was not valid.
411    #[error("Invalid secret handle: {0}")]
412    InvalidSecretHandle(crate::wiggle_abi::types::SecretHandle),
413
414    /// An async item handle was not valid.
415    #[error("Invalid async item handle: {0}")]
416    InvalidAsyncItemHandle(crate::wiggle_abi::types::AsyncItemHandle),
417
418    /// An acl handle was not valid.
419    #[error("Invalid acl handle: {0}")]
420    InvalidAclHandle(crate::wiggle_abi::types::AclHandle),
421
422    /// A cache handle was not valid.
423    #[error("Invalid cache handle: {0}")]
424    InvalidCacheHandle(crate::wiggle_abi::types::CacheHandle),
425}
426
427/// Errors that can occur in a worker thread running a guest module.
428///
429/// See [`ExecuteCtx::handle_request`][handle_request] and [`ExecuteCtx::run_guest`][run_guest] for
430/// more information about guest execution.
431///
432/// [handle_request]: ../execute/struct.ExecuteCtx.html#method.handle_request
433/// [run_guest]: ../execute/struct.ExecuteCtx.html#method.run_guest
434#[derive(Debug, thiserror::Error)]
435pub(crate) enum ExecutionError {
436    /// Errors thrown by the guest's entrypoint.
437    ///
438    /// See [`wasmtime::Func::call`][call] for more information.
439    ///
440    /// [call]: https://docs.rs/wasmtime/latest/wasmtime/struct.Func.html#method.call
441    #[error("WebAssembly execution trapped: {0}")]
442    WasmTrap(anyhow::Error),
443
444    /// Errors thrown when trying to instantiate a guest context.
445    #[error("Error creating context: {0}")]
446    Context(anyhow::Error),
447
448    /// Errors thrown when type-checking WebAssembly before instantiation
449    #[error("Error type-checking WebAssembly instantiation: {0}")]
450    Typechecking(anyhow::Error),
451
452    /// Errors thrown when trying to instantiate a guest module.
453    #[error("Error instantiating WebAssembly: {0}")]
454    Instantiation(anyhow::Error),
455}
456
457/// Errors that can occur while parsing a `fastly.toml` file.
458#[derive(Debug, thiserror::Error)]
459pub enum FastlyConfigError {
460    /// An I/O error that occurred while reading the file.
461    #[error("error reading '{path}': {err}")]
462    IoError {
463        path: String,
464        #[source]
465        err: std::io::Error,
466    },
467
468    #[error("invalid configuration for '{name}': {err}")]
469    InvalidDeviceDetectionDefinition {
470        name: String,
471        #[source]
472        err: DeviceDetectionConfigError,
473    },
474
475    #[error("invalid configuration for '{name}': {err}")]
476    InvalidGeolocationDefinition {
477        name: String,
478        #[source]
479        err: GeolocationConfigError,
480    },
481
482    #[error("invalid configuration for '{name}': {err}")]
483    InvalidAclDefinition {
484        name: String,
485        #[source]
486        err: AclConfigError,
487    },
488
489    #[error("invalid configuration for '{name}': {err}")]
490    InvalidBackendDefinition {
491        name: String,
492        #[source]
493        err: BackendConfigError,
494    },
495
496    #[error("invalid configuration for '{name}': {err}")]
497    InvalidDictionaryDefinition {
498        name: String,
499        #[source]
500        err: DictionaryConfigError,
501    },
502
503    #[error("invalid configuration for '{name}': {err}")]
504    InvalidObjectStoreDefinition {
505        name: String,
506        #[source]
507        err: ObjectStoreConfigError,
508    },
509
510    #[error("invalid configuration for '{name}': {err}")]
511    InvalidSecretStoreDefinition {
512        name: String,
513        #[source]
514        err: SecretStoreConfigError,
515    },
516
517    #[error("invalid configuration for '{name}': {err}")]
518    InvalidShieldingSiteDefinition {
519        name: String,
520        #[source]
521        err: ShieldingSiteConfigError,
522    },
523
524    /// An error that occurred while deserializing the file.
525    ///
526    /// This represents errors caused by syntactically invalid TOML data, missing fields, etc.
527    #[error("error parsing `fastly.toml`: {0}")]
528    InvalidFastlyToml(#[from] toml::de::Error),
529
530    /// The `manifest_version` field contained a value greater than is
531    /// currently supported.
532    #[error("unsupported manifest version {0}; max supported version is {1}")]
533    UnsupportedManifestVersion(u32, u32),
534}
535
536/// Errors that may occur while validating acl configurations.
537#[derive(Debug, thiserror::Error)]
538pub enum AclConfigError {
539    /// An I/O error that occurred while processing a file.
540    #[error(transparent)]
541    IoError(std::io::Error),
542
543    /// An error occurred parsing JSON.
544    #[error(transparent)]
545    JsonError(serde_json::error::Error),
546
547    #[error("acl must be a TOML table or string")]
548    InvalidType,
549
550    #[error("missing 'file' field")]
551    MissingFile,
552}
553
554/// Errors that may occur while validating backend configurations.
555#[derive(Debug, thiserror::Error)]
556pub enum BackendConfigError {
557    #[error("definition was not provided as a TOML table")]
558    InvalidEntryType,
559
560    #[error("invalid override_host: {0}")]
561    InvalidOverrideHost(#[from] http::header::InvalidHeaderValue),
562
563    #[error("'override_host' field is empty")]
564    EmptyOverrideHost,
565
566    #[error("'override_host' field was not a string")]
567    InvalidOverrideHostEntry,
568
569    #[error("'cert_host' field is empty")]
570    EmptyCertHost,
571
572    #[error("'cert_host' field was not a string")]
573    InvalidCertHostEntry,
574
575    #[error("'ca_certificate' field is empty")]
576    EmptyCACert,
577
578    #[error("'ca_certificate' field was invalid: {0}")]
579    InvalidCACertEntry(String),
580
581    #[error("'use_sni' field was not a boolean")]
582    InvalidUseSniEntry,
583
584    #[error("'grpc' field was not a boolean")]
585    InvalidGrpcEntry,
586
587    #[error(
588        "'health' field has invalid value '{0}' (expected 'unknown', 'healthy', or 'unhealthy')"
589    )]
590    InvalidHealthEntry(String),
591
592    #[error("invalid url: {0}")]
593    InvalidUrl(#[from] http::uri::InvalidUri),
594
595    #[error("'url' field was not a string")]
596    InvalidUrlEntry,
597
598    #[error("no default definition provided")]
599    MissingDefault,
600
601    #[error("missing 'url' field")]
602    MissingUrl,
603
604    #[error("unrecognized key '{0}'")]
605    UnrecognizedKey(String),
606
607    #[error(transparent)]
608    ClientCertError(#[from] crate::config::ClientCertError),
609
610    #[error("Illegal timeout value (must be an integer >= 0)")]
611    InvalidTimeoutValue,
612}
613
614/// Errors that may occur while validating dictionary configurations.
615#[derive(Debug, thiserror::Error)]
616pub enum DictionaryConfigError {
617    /// An I/O error that occurred while reading the file.
618    #[error(transparent)]
619    IoError(std::io::Error),
620
621    #[error("'contents' was not provided as a TOML table")]
622    InvalidContentsType,
623
624    #[error("inline dictionary value was not a string")]
625    InvalidInlineEntryType,
626
627    #[error("definition was not provided as a TOML table")]
628    InvalidEntryType,
629
630    #[error("'name' field was not a string")]
631    InvalidNameEntry,
632
633    #[error(
634        "'{0}' is not a valid format for the dictionary. Supported format(s) are: 'inline-toml', 'json'."
635    )]
636    InvalidDictionaryFormat(String),
637
638    #[error("'file' field is empty")]
639    EmptyFileEntry,
640
641    #[error("'format' field is empty")]
642    EmptyFormatEntry,
643
644    #[error("'file' field was not a string")]
645    InvalidFileEntry,
646
647    #[error("'format' field was not a string")]
648    InvalidFormatEntry,
649
650    #[error("missing 'contents' field")]
651    MissingContents,
652
653    #[error("no default definition provided")]
654    MissingDefault,
655
656    #[error("missing 'name' field")]
657    MissingName,
658
659    #[error("missing 'file' field")]
660    MissingFile,
661
662    #[error("missing 'format' field")]
663    MissingFormat,
664
665    #[error("unrecognized key '{0}'")]
666    UnrecognizedKey(String),
667
668    #[error("Item key named '{key}' is too long, max size is {size}")]
669    DictionaryItemKeyTooLong { key: String, size: i32 },
670
671    #[error("too many items, max amount is {size}")]
672    DictionaryCountTooLong { size: i32 },
673
674    #[error(
675        "Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String"
676    )]
677    DictionaryItemValueWrongFormat { key: String },
678
679    #[error("Item value named '{key}' is too long, max size is {size}")]
680    DictionaryItemValueTooLong { key: String, size: i32 },
681
682    #[error(
683        "The file is of the wrong format. The file is expected to contain a single JSON Object"
684    )]
685    DictionaryFileWrongFormat,
686}
687
688/// Errors that may occur while validating device detection configurations.
689#[derive(Debug, thiserror::Error)]
690pub enum DeviceDetectionConfigError {
691    /// An I/O error that occurred while reading the file.
692    #[error(transparent)]
693    IoError(std::io::Error),
694
695    #[error("definition was not provided as a TOML table")]
696    InvalidEntryType,
697
698    #[error("missing 'file' field")]
699    MissingFile,
700
701    #[error("'file' field is empty")]
702    EmptyFileEntry,
703
704    #[error("missing 'user_agents' field")]
705    MissingUserAgents,
706
707    #[error("inline device detection value was not a string")]
708    InvalidInlineEntryType,
709
710    #[error("'file' field was not a string")]
711    InvalidFileEntry,
712
713    #[error("'user_agents' was not provided as a TOML table")]
714    InvalidUserAgentsType,
715
716    #[error("unrecognized key '{0}'")]
717    UnrecognizedKey(String),
718
719    #[error("missing 'format' field")]
720    MissingFormat,
721
722    #[error("'format' field was not a string")]
723    InvalidFormatEntry,
724
725    #[error(
726        "'{0}' is not a valid format for the device detection mapping. Supported format(s) are: 'inline-toml', 'json'."
727    )]
728    InvalidDeviceDetectionMappingFormat(String),
729
730    #[error(
731        "The file is of the wrong format. The file is expected to contain a single JSON Object"
732    )]
733    DeviceDetectionFileWrongFormat,
734
735    #[error("'format' field is empty")]
736    EmptyFormatEntry,
737
738    #[error(
739        "Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String"
740    )]
741    DeviceDetectionItemValueWrongFormat { key: String },
742}
743
744/// Errors that may occur while validating geolocation configurations.
745#[derive(Debug, thiserror::Error)]
746pub enum GeolocationConfigError {
747    /// An I/O error that occurred while reading the file.
748    #[error(transparent)]
749    IoError(std::io::Error),
750
751    #[error("definition was not provided as a TOML table")]
752    InvalidEntryType,
753
754    #[error("missing 'file' field")]
755    MissingFile,
756
757    #[error("'file' field is empty")]
758    EmptyFileEntry,
759
760    #[error("missing 'addresses' field")]
761    MissingAddresses,
762
763    #[error("inline geolocation value was not a string")]
764    InvalidInlineEntryType,
765
766    #[error("'file' field was not a string")]
767    InvalidFileEntry,
768
769    #[error("'addresses' was not provided as a TOML table")]
770    InvalidAddressesType,
771
772    #[error("unrecognized key '{0}'")]
773    UnrecognizedKey(String),
774
775    #[error("missing 'format' field")]
776    MissingFormat,
777
778    #[error("'format' field was not a string")]
779    InvalidFormatEntry,
780
781    #[error("IP address not valid: '{0}'")]
782    InvalidAddressEntry(String),
783
784    #[error(
785        "'{0}' is not a valid format for the geolocation mapping. Supported format(s) are: 'inline-toml', 'json'."
786    )]
787    InvalidGeolocationMappingFormat(String),
788
789    #[error(
790        "The file is of the wrong format. The file is expected to contain a single JSON Object"
791    )]
792    GeolocationFileWrongFormat,
793
794    #[error("'format' field is empty")]
795    EmptyFormatEntry,
796
797    #[error(
798        "Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String"
799    )]
800    GeolocationItemValueWrongFormat { key: String },
801}
802
803/// Errors that may occur while validating object store configurations.
804#[derive(Debug, thiserror::Error)]
805pub enum ObjectStoreConfigError {
806    /// An I/O error that occurred while reading the file.
807    #[error(transparent)]
808    IoError(std::io::Error),
809    #[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
810    FileAndData(String),
811    #[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
812    NoFileOrData(String),
813    #[error("The `data` value for the object `{0}` is not a string.")]
814    DataNotAString(String),
815    #[error("The `metadata` value for the object `{0}` is not a string.")]
816    MetadataNotAString(String),
817    #[error("The `file` value for the object `{0}` is not a string.")]
818    FileNotAString(String),
819    #[error("The `key` key for an object is not set. It must be used.")]
820    NoKey,
821    #[error("The `key` value for an object is not a string.")]
822    KeyNotAString,
823    #[error("There is no array of objects for the given store.")]
824    NotAnArray,
825    #[error("There is an object in the given store that is not a table of keys.")]
826    NotATable,
827    #[error("There was an error when manipulating the ObjectStore: {0}.")]
828    ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
829    #[error("There was an error when manipulating the KvStore: {0}.")]
830    KvStoreError(#[from] crate::object_store::KvStoreError),
831    #[error("Invalid `key` value used: {0}.")]
832    KeyValidationError(#[from] crate::object_store::KeyValidationError),
833    #[error("'{0}' is not a valid format for the config store. Supported format(s) are: 'json'.")]
834    InvalidFileFormat(String),
835    #[error("When using a top-level 'file' to load data, both 'file' and 'format' must be set.")]
836    OnlyOneFormatOrFileSet,
837    #[error(
838        "The file is of the wrong format. The file is expected to contain a single JSON Object."
839    )]
840    FileWrongFormat,
841    #[error(
842        "Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String."
843    )]
844    FileValueWrongFormat { key: String },
845    #[error(
846        "Item value under key named '{key}' is of the wrong format. 'data' and 'file' are mutually exclusive."
847    )]
848    BothDataAndFilePresent { key: String },
849    #[error(
850        "Item value under key named '{key}' is of the wrong format. One of 'data' or 'file' must be present."
851    )]
852    MissingDataOrFile { key: String },
853}
854
855/// Errors that may occur while validating secret store configurations.
856#[derive(Debug, thiserror::Error)]
857pub enum SecretStoreConfigError {
858    /// An I/O error that occurred while reading the file.
859    #[error(transparent)]
860    IoError(std::io::Error),
861
862    #[error("'{0}' is not a valid format for the secret store. Supported format(s) are: 'json'.")]
863    InvalidFileFormat(String),
864    #[error("When using a top-level 'file' to load data, both 'file' and 'format' must be set.")]
865    OnlyOneFormatOrFileSet,
866    #[error(
867        "The file is of the wrong format. The file is expected to contain a single JSON object."
868    )]
869    FileWrongFormat,
870    #[error(
871        "More than one of `file`, `data`, or `env` keys for the object `{0}` are set. Only one can be used."
872    )]
873    FileDataEnvExclusive(String),
874    #[error("None of `file`, `data`, or `env` keys for the object `{0}` is set. One must be used.")]
875    FileDataEnvNotSet(String),
876    #[error("The `data` value for the object `{0}` is not a string.")]
877    DataNotAString(String),
878    #[error("The `file` value for the object `{0}` is not a string.")]
879    FileNotAString(String),
880    #[error("The `env` value for the object `{0}` is not a string.")]
881    EnvNotAString(String),
882
883    #[error("The `key` key for an object is not set. It must be used.")]
884    NoKey,
885    #[error("The `key` value for an object is not a string.")]
886    KeyNotAString,
887
888    #[error("There is no array of objects for the given store.")]
889    NotAnArray,
890    #[error("There is an object in the given store that is not a table of keys.")]
891    NotATable,
892
893    #[error("Invalid secret store name: {0}")]
894    InvalidSecretStoreName(String),
895
896    #[error("Invalid secret name: {0}")]
897    InvalidSecretName(String),
898}
899
900/// Errors that may occur while validating shielding site configurations.
901#[derive(Debug, thiserror::Error)]
902pub enum ShieldingSiteConfigError {
903    #[error(
904        "Illegal TOML value for shielding site; must be either the string 'local' or a table containing an encrypted and unencrypted URL."
905    )]
906    IllegalSiteValue,
907
908    #[error("Illegal TOML string for shielding site; must be 'local'")]
909    IllegalSiteString,
910
911    #[error(
912        "Illegal table for shielding site; must have exactly one key named 'encrypted', and one named 'unencrypted'"
913    )]
914    IllegalSiteDefinition,
915
916    #[error("Illegal URL ({url}): {error}")]
917    IllegalUrl { url: String, error: url::ParseError },
918}
919
920/// Errors related to the downstream request.
921#[derive(Debug, thiserror::Error)]
922pub enum DownstreamRequestError {
923    #[error("Request HOST header is missing or invalid")]
924    InvalidHost,
925
926    #[error("Request URL is invalid")]
927    InvalidUrl,
928}
929
930/// An enum representing outcomes where the guest does not produce a standard
931/// HTTP response, but instead signals for a different action.
932#[derive(Debug, thiserror::Error)]
933pub enum NonHttpResponse {
934    #[error("graceful Pushpin handoff")]
935    HandoffToPushpin(HandoffInfo),
936    #[error("graceful Backend handoff")]
937    HandoffToBackend(HandoffInfo),
938}