Skip to main content

viceroy_lib/
error.rs

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