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