1use std::error::Error as StdError;
4use std::io;
5use {crate::wiggle_abi::types::FastlyStatus, url::Url, wiggle::GuestError};
6
7#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum Error {
10 #[error("Buffer length error: {buf} too long to fit in {len}")]
12 BufferLengthError {
13 buf: &'static str,
14 len: &'static str,
15 },
16
17 #[error("Fatal error: [{0}]")]
20 FatalError(String),
21
22 #[error("Expected a valid Wasm file")]
24 FileFormat,
25
26 #[error("Expected a valid wastime's profiling strategy")]
27 ProfilingStrategy,
28
29 #[error(transparent)]
30 FastlyConfig(#[from] FastlyConfigError),
31
32 #[error("Could not determine address from backend URL: {0}")]
33 BackendUrl(Url),
34
35 #[error("Guest error: [{0}]")]
40 GuestError(#[from] wiggle::GuestError),
41
42 #[error(transparent)]
43 HandleError(#[from] HandleError),
44
45 #[error(transparent)]
46 HyperError(#[from] hyper::Error),
47
48 #[error(transparent)]
49 Infallible(#[from] std::convert::Infallible),
50
51 #[error("Invalid argument given")]
53 InvalidArgument,
54
55 #[error(transparent)]
56 InvalidHeaderName(#[from] http::header::InvalidHeaderName),
57
58 #[error(transparent)]
59 InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
60
61 #[error(transparent)]
62 InvalidMethod(#[from] http::method::InvalidMethod),
63
64 #[error(transparent)]
65 InvalidStatusCode(#[from] http::status::InvalidStatusCode),
66
67 #[error(transparent)]
68 InvalidUri(#[from] http::uri::InvalidUri),
69
70 #[error(transparent)]
71 IoError(#[from] std::io::Error),
72
73 #[error(transparent)]
74 Other(#[from] anyhow::Error),
75
76 #[error("Unsupported operation: {msg}")]
77 Unsupported { msg: &'static str },
78
79 #[error("Downstream response already sending")]
81 DownstreamRespSending,
82
83 #[error("Unexpected error sending a chunk to a streaming body")]
84 StreamingChunkSend,
85
86 #[error("Unknown backend: {0}")]
87 UnknownBackend(String),
88
89 #[error(transparent)]
90 DictionaryError(#[from] crate::wiggle_abi::DictionaryError),
91
92 #[error(transparent)]
93 DeviceDetectionError(#[from] crate::wiggle_abi::DeviceDetectionError),
94
95 #[error(transparent)]
96 ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
97
98 #[error(transparent)]
99 KvStoreError(#[from] crate::object_store::KvStoreError),
100
101 #[error(transparent)]
102 SecretStoreError(#[from] crate::wiggle_abi::SecretStoreError),
103
104 #[error{"Expected UTF-8"}]
105 Utf8Expected(#[from] std::str::Utf8Error),
106
107 #[error{"Unsupported ABI version"}]
108 AbiVersionMismatch,
109
110 #[error(transparent)]
111 DownstreamRequestError(#[from] DownstreamRequestError),
112
113 #[error("{0} is not currently supported for local testing")]
114 NotAvailable(&'static str),
115
116 #[error("Could not load native certificates: {0}")]
117 BadCerts(std::io::Error),
118
119 #[error("Could not generate new backend name from '{0}'")]
120 BackendNameRegistryError(String),
121
122 #[error(transparent)]
123 HttpError(#[from] http::Error),
124
125 #[error("Object Store '{0}' does not exist")]
126 UnknownObjectStore(String),
127
128 #[error("Invalid Object Store `key` value used: {0}.")]
129 ObjectStoreKeyValidationError(#[from] crate::object_store::KeyValidationError),
130
131 #[error("Unfinished streaming body")]
132 UnfinishedStreamingBody,
133
134 #[error("Shared memory not supported yet")]
135 SharedMemory,
136
137 #[error("Value absent from structure")]
138 ValueAbsent,
139
140 #[error("String conversion error")]
141 ToStr(#[from] http::header::ToStrError),
142
143 #[error("invalid client certificate")]
144 InvalidClientCert(#[from] crate::config::ClientCertError),
145
146 #[error("Invalid response to ALPN request; wanted '{0}', got '{1}'")]
147 InvalidAlpnRepsonse(&'static str, String),
148
149 #[error("Resource temporarily unavailable")]
150 Again,
151}
152
153impl Error {
154 pub fn to_fastly_status(&self) -> FastlyStatus {
161 match self {
162 Error::BufferLengthError { .. } => FastlyStatus::Buflen,
163 Error::InvalidArgument => FastlyStatus::Inval,
164 Error::ValueAbsent => FastlyStatus::None,
165 Error::Unsupported { .. } | Error::NotAvailable(_) => FastlyStatus::Unsupported,
166 Error::HandleError { .. } => FastlyStatus::Badf,
167 Error::InvalidStatusCode { .. } => FastlyStatus::Inval,
168 Error::UnknownBackend(_) | Error::InvalidClientCert(_) => FastlyStatus::Inval,
169 Error::HyperError(e) if e.is_parse() => FastlyStatus::Httpinvalid,
171 Error::HyperError(e) if e.is_user() => FastlyStatus::Httpuser,
172 Error::HyperError(e) if e.is_incomplete_message() => FastlyStatus::Httpincomplete,
173 Error::HyperError(e)
174 if e.source()
175 .and_then(|e| e.downcast_ref::<io::Error>())
176 .map(|ioe| ioe.kind())
177 == Some(io::ErrorKind::UnexpectedEof) =>
178 {
179 FastlyStatus::Httpincomplete
180 }
181 Error::HyperError(_) => FastlyStatus::Error,
182 Error::GuestError(e) => Self::guest_error_fastly_status(e),
184 Error::DictionaryError(e) => e.to_fastly_status(),
186 Error::DeviceDetectionError(e) => e.to_fastly_status(),
187 Error::ObjectStoreError(e) => e.into(),
188 Error::KvStoreError(e) => e.into(),
189 Error::SecretStoreError(e) => e.into(),
190 Error::Again => FastlyStatus::Again,
191 Error::AbiVersionMismatch
193 | Error::BackendUrl(_)
194 | Error::BadCerts(_)
195 | Error::DownstreamRequestError(_)
196 | Error::DownstreamRespSending
197 | Error::FastlyConfig(_)
198 | Error::FatalError(_)
199 | Error::FileFormat
200 | Error::Infallible(_)
201 | Error::InvalidHeaderName(_)
202 | Error::InvalidHeaderValue(_)
203 | Error::InvalidMethod(_)
204 | Error::InvalidUri(_)
205 | Error::IoError(_)
206 | Error::Other(_)
207 | Error::ProfilingStrategy
208 | Error::StreamingChunkSend
209 | Error::Utf8Expected(_)
210 | Error::BackendNameRegistryError(_)
211 | Error::HttpError(_)
212 | Error::UnknownObjectStore(_)
213 | Error::ObjectStoreKeyValidationError(_)
214 | Error::UnfinishedStreamingBody
215 | Error::SharedMemory
216 | Error::ToStr(_)
217 | Error::InvalidAlpnRepsonse(_, _) => FastlyStatus::Error,
218 }
219 }
220
221 fn guest_error_fastly_status(e: &GuestError) -> FastlyStatus {
222 use GuestError::*;
223 match e {
224 PtrNotAligned { .. } => FastlyStatus::Badalign,
225 InvalidFlagValue { .. }
228 | InvalidEnumValue { .. }
229 | PtrOutOfBounds { .. }
230 | PtrBorrowed { .. }
231 | PtrOverflow { .. }
232 | InvalidUtf8 { .. }
233 | TryFromIntError { .. } => FastlyStatus::Inval,
234 BorrowCheckerOutOfHandles | SliceLengthsDiffer => FastlyStatus::Error,
237 InFunc { err, .. } => Self::guest_error_fastly_status(err),
240 }
241 }
242}
243
244#[derive(Debug, thiserror::Error)]
246pub enum HandleError {
247 #[error("Invalid request handle: {0}")]
249 InvalidRequestHandle(crate::wiggle_abi::types::RequestHandle),
250
251 #[error("Invalid response handle: {0}")]
253 InvalidResponseHandle(crate::wiggle_abi::types::ResponseHandle),
254
255 #[error("Invalid body handle: {0}")]
257 InvalidBodyHandle(crate::wiggle_abi::types::BodyHandle),
258
259 #[error("Invalid endpoint handle: {0}")]
261 InvalidEndpointHandle(crate::wiggle_abi::types::EndpointHandle),
262
263 #[error("Invalid pending request handle: {0}")]
265 InvalidPendingRequestHandle(crate::wiggle_abi::types::PendingRequestHandle),
266
267 #[error("Invalid pending KV lookup handle: {0}")]
269 InvalidPendingKvLookupHandle(crate::wiggle_abi::types::PendingKvLookupHandle),
270
271 #[error("Invalid pending KV insert handle: {0}")]
273 InvalidPendingKvInsertHandle(crate::wiggle_abi::types::PendingKvInsertHandle),
274
275 #[error("Invalid pending KV delete handle: {0}")]
277 InvalidPendingKvDeleteHandle(crate::wiggle_abi::types::PendingKvDeleteHandle),
278
279 #[error("Invalid pending KV list handle: {0}")]
281 InvalidPendingKvListHandle(crate::wiggle_abi::types::PendingKvListHandle),
282
283 #[error("Invalid dictionary handle: {0}")]
285 InvalidDictionaryHandle(crate::wiggle_abi::types::DictionaryHandle),
286
287 #[error("Invalid object-store handle: {0}")]
289 InvalidObjectStoreHandle(crate::wiggle_abi::types::ObjectStoreHandle),
290
291 #[error("Invalid secret store handle: {0}")]
293 InvalidSecretStoreHandle(crate::wiggle_abi::types::SecretStoreHandle),
294
295 #[error("Invalid secret handle: {0}")]
297 InvalidSecretHandle(crate::wiggle_abi::types::SecretHandle),
298
299 #[error("Invalid async item handle: {0}")]
301 InvalidAsyncItemHandle(crate::wiggle_abi::types::AsyncItemHandle),
302
303 #[error("Invalid acl handle: {0}")]
305 InvalidAclHandle(crate::wiggle_abi::types::AclHandle),
306}
307
308#[derive(Debug, thiserror::Error)]
316pub(crate) enum ExecutionError {
317 #[error("WebAssembly execution trapped: {0}")]
323 WasmTrap(anyhow::Error),
324
325 #[error("Error creating context: {0}")]
327 Context(anyhow::Error),
328
329 #[error("Error type-checking WebAssembly instantiation: {0}")]
331 Typechecking(anyhow::Error),
332
333 #[error("Error instantiating WebAssembly: {0}")]
335 Instantiation(anyhow::Error),
336}
337
338#[derive(Debug, thiserror::Error)]
340pub enum FastlyConfigError {
341 #[error("error reading '{path}': {err}")]
343 IoError {
344 path: String,
345 #[source]
346 err: std::io::Error,
347 },
348
349 #[error("invalid configuration for '{name}': {err}")]
350 InvalidDeviceDetectionDefinition {
351 name: String,
352 #[source]
353 err: DeviceDetectionConfigError,
354 },
355
356 #[error("invalid configuration for '{name}': {err}")]
357 InvalidGeolocationDefinition {
358 name: String,
359 #[source]
360 err: GeolocationConfigError,
361 },
362
363 #[error("invalid configuration for '{name}': {err}")]
364 InvalidAclDefinition {
365 name: String,
366 #[source]
367 err: AclConfigError,
368 },
369
370 #[error("invalid configuration for '{name}': {err}")]
371 InvalidBackendDefinition {
372 name: String,
373 #[source]
374 err: BackendConfigError,
375 },
376
377 #[error("invalid configuration for '{name}': {err}")]
378 InvalidDictionaryDefinition {
379 name: String,
380 #[source]
381 err: DictionaryConfigError,
382 },
383
384 #[error("invalid configuration for '{name}': {err}")]
385 InvalidObjectStoreDefinition {
386 name: String,
387 #[source]
388 err: ObjectStoreConfigError,
389 },
390
391 #[error("invalid configuration for '{name}': {err}")]
392 InvalidSecretStoreDefinition {
393 name: String,
394 #[source]
395 err: SecretStoreConfigError,
396 },
397
398 #[error("invalid configuration for '{name}': {err}")]
399 InvalidShieldingSiteDefinition {
400 name: String,
401 #[source]
402 err: ShieldingSiteConfigError,
403 },
404
405 #[error("error parsing `fastly.toml`: {0}")]
409 InvalidFastlyToml(#[from] toml::de::Error),
410
411 #[error("invalid manifest version: {0}")]
418 InvalidManifestVersion(#[from] semver::SemVerError),
419}
420
421#[derive(Debug, thiserror::Error)]
423pub enum AclConfigError {
424 #[error(transparent)]
426 IoError(std::io::Error),
427
428 #[error(transparent)]
430 JsonError(serde_json::error::Error),
431
432 #[error("acl must be a TOML table or string")]
433 InvalidType,
434
435 #[error("missing 'file' field")]
436 MissingFile,
437}
438
439#[derive(Debug, thiserror::Error)]
441pub enum BackendConfigError {
442 #[error("definition was not provided as a TOML table")]
443 InvalidEntryType,
444
445 #[error("invalid override_host: {0}")]
446 InvalidOverrideHost(#[from] http::header::InvalidHeaderValue),
447
448 #[error("'override_host' field is empty")]
449 EmptyOverrideHost,
450
451 #[error("'override_host' field was not a string")]
452 InvalidOverrideHostEntry,
453
454 #[error("'cert_host' field is empty")]
455 EmptyCertHost,
456
457 #[error("'cert_host' field was not a string")]
458 InvalidCertHostEntry,
459
460 #[error("'ca_certificate' field is empty")]
461 EmptyCACert,
462
463 #[error("'ca_certificate' field was invalid: {0}")]
464 InvalidCACertEntry(String),
465
466 #[error("'use_sni' field was not a boolean")]
467 InvalidUseSniEntry,
468
469 #[error("'grpc' field was not a boolean")]
470 InvalidGrpcEntry,
471
472 #[error("invalid url: {0}")]
473 InvalidUrl(#[from] http::uri::InvalidUri),
474
475 #[error("'url' field was not a string")]
476 InvalidUrlEntry,
477
478 #[error("no default definition provided")]
479 MissingDefault,
480
481 #[error("missing 'url' field")]
482 MissingUrl,
483
484 #[error("unrecognized key '{0}'")]
485 UnrecognizedKey(String),
486
487 #[error(transparent)]
488 ClientCertError(#[from] crate::config::ClientCertError),
489}
490
491#[derive(Debug, thiserror::Error)]
493pub enum DictionaryConfigError {
494 #[error(transparent)]
496 IoError(std::io::Error),
497
498 #[error("'contents' was not provided as a TOML table")]
499 InvalidContentsType,
500
501 #[error("inline dictionary value was not a string")]
502 InvalidInlineEntryType,
503
504 #[error("definition was not provided as a TOML table")]
505 InvalidEntryType,
506
507 #[error("'name' field was not a string")]
508 InvalidNameEntry,
509
510 #[error("'{0}' is not a valid format for the dictionary. Supported format(s) are: 'inline-toml', 'json'.")]
511 InvalidDictionaryFormat(String),
512
513 #[error("'file' field is empty")]
514 EmptyFileEntry,
515
516 #[error("'format' field is empty")]
517 EmptyFormatEntry,
518
519 #[error("'file' field was not a string")]
520 InvalidFileEntry,
521
522 #[error("'format' field was not a string")]
523 InvalidFormatEntry,
524
525 #[error("missing 'contents' field")]
526 MissingContents,
527
528 #[error("no default definition provided")]
529 MissingDefault,
530
531 #[error("missing 'name' field")]
532 MissingName,
533
534 #[error("missing 'file' field")]
535 MissingFile,
536
537 #[error("missing 'format' field")]
538 MissingFormat,
539
540 #[error("unrecognized key '{0}'")]
541 UnrecognizedKey(String),
542
543 #[error("Item key named '{key}' is too long, max size is {size}")]
544 DictionaryItemKeyTooLong { key: String, size: i32 },
545
546 #[error("too many items, max amount is {size}")]
547 DictionaryCountTooLong { size: i32 },
548
549 #[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
550 DictionaryItemValueWrongFormat { key: String },
551
552 #[error("Item value named '{key}' is too long, max size is {size}")]
553 DictionaryItemValueTooLong { key: String, size: i32 },
554
555 #[error(
556 "The file is of the wrong format. The file is expected to contain a single JSON Object"
557 )]
558 DictionaryFileWrongFormat,
559}
560
561#[derive(Debug, thiserror::Error)]
563pub enum DeviceDetectionConfigError {
564 #[error(transparent)]
566 IoError(std::io::Error),
567
568 #[error("definition was not provided as a TOML table")]
569 InvalidEntryType,
570
571 #[error("missing 'file' field")]
572 MissingFile,
573
574 #[error("'file' field is empty")]
575 EmptyFileEntry,
576
577 #[error("missing 'user_agents' field")]
578 MissingUserAgents,
579
580 #[error("inline device detection value was not a string")]
581 InvalidInlineEntryType,
582
583 #[error("'file' field was not a string")]
584 InvalidFileEntry,
585
586 #[error("'user_agents' was not provided as a TOML table")]
587 InvalidUserAgentsType,
588
589 #[error("unrecognized key '{0}'")]
590 UnrecognizedKey(String),
591
592 #[error("missing 'format' field")]
593 MissingFormat,
594
595 #[error("'format' field was not a string")]
596 InvalidFormatEntry,
597
598 #[error("'{0}' is not a valid format for the device detection mapping. Supported format(s) are: 'inline-toml', 'json'.")]
599 InvalidDeviceDetectionMappingFormat(String),
600
601 #[error(
602 "The file is of the wrong format. The file is expected to contain a single JSON Object"
603 )]
604 DeviceDetectionFileWrongFormat,
605
606 #[error("'format' field is empty")]
607 EmptyFormatEntry,
608
609 #[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
610 DeviceDetectionItemValueWrongFormat { key: String },
611}
612
613#[derive(Debug, thiserror::Error)]
615pub enum GeolocationConfigError {
616 #[error(transparent)]
618 IoError(std::io::Error),
619
620 #[error("definition was not provided as a TOML table")]
621 InvalidEntryType,
622
623 #[error("missing 'file' field")]
624 MissingFile,
625
626 #[error("'file' field is empty")]
627 EmptyFileEntry,
628
629 #[error("missing 'addresses' field")]
630 MissingAddresses,
631
632 #[error("inline geolocation value was not a string")]
633 InvalidInlineEntryType,
634
635 #[error("'file' field was not a string")]
636 InvalidFileEntry,
637
638 #[error("'addresses' was not provided as a TOML table")]
639 InvalidAddressesType,
640
641 #[error("unrecognized key '{0}'")]
642 UnrecognizedKey(String),
643
644 #[error("missing 'format' field")]
645 MissingFormat,
646
647 #[error("'format' field was not a string")]
648 InvalidFormatEntry,
649
650 #[error("IP address not valid: '{0}'")]
651 InvalidAddressEntry(String),
652
653 #[error("'{0}' is not a valid format for the geolocation mapping. Supported format(s) are: 'inline-toml', 'json'.")]
654 InvalidGeolocationMappingFormat(String),
655
656 #[error(
657 "The file is of the wrong format. The file is expected to contain a single JSON Object"
658 )]
659 GeolocationFileWrongFormat,
660
661 #[error("'format' field is empty")]
662 EmptyFormatEntry,
663
664 #[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")]
665 GeolocationItemValueWrongFormat { key: String },
666}
667
668#[derive(Debug, thiserror::Error)]
670pub enum ObjectStoreConfigError {
671 #[error(transparent)]
673 IoError(std::io::Error),
674 #[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
675 FileAndData(String),
676 #[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
677 NoFileOrData(String),
678 #[error("The `data` value for the object `{0}` is not a string.")]
679 DataNotAString(String),
680 #[error("The `metadata` value for the object `{0}` is not a string.")]
681 MetadataNotAString(String),
682 #[error("The `file` value for the object `{0}` is not a string.")]
683 FileNotAString(String),
684 #[error("The `key` key for an object is not set. It must be used.")]
685 NoKey,
686 #[error("The `key` value for an object is not a string.")]
687 KeyNotAString,
688 #[error("There is no array of objects for the given store.")]
689 NotAnArray,
690 #[error("There is an object in the given store that is not a table of keys.")]
691 NotATable,
692 #[error("There was an error when manipulating the ObjectStore: {0}.")]
693 ObjectStoreError(#[from] crate::object_store::ObjectStoreError),
694 #[error("There was an error when manipulating the KvStore: {0}.")]
695 KvStoreError(#[from] crate::object_store::KvStoreError),
696 #[error("Invalid `key` value used: {0}.")]
697 KeyValidationError(#[from] crate::object_store::KeyValidationError),
698 #[error("'{0}' is not a valid format for the config store. Supported format(s) are: 'json'.")]
699 InvalidFileFormat(String),
700 #[error("When using a top-level 'file' to load data, both 'file' and 'format' must be set.")]
701 OnlyOneFormatOrFileSet,
702 #[error(
703 "The file is of the wrong format. The file is expected to contain a single JSON Object."
704 )]
705 FileWrongFormat,
706 #[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String.")]
707 FileValueWrongFormat { key: String },
708 #[error("Item value under key named '{key}' is of the wrong format. 'data' and 'file' are mutually exclusive.")]
709 BothDataAndFilePresent { key: String },
710 #[error("Item value under key named '{key}' is of the wrong format. One of 'data' or 'file' must be present.")]
711 MissingDataOrFile { key: String },
712}
713
714#[derive(Debug, thiserror::Error)]
716pub enum SecretStoreConfigError {
717 #[error(transparent)]
719 IoError(std::io::Error),
720
721 #[error("'{0}' is not a valid format for the secret store. Supported format(s) are: 'json'.")]
722 InvalidFileFormat(String),
723 #[error("When using a top-level 'file' to load data, both 'file' and 'format' must be set.")]
724 OnlyOneFormatOrFileSet,
725 #[error(
726 "The file is of the wrong format. The file is expected to contain a single JSON object."
727 )]
728 FileWrongFormat,
729 #[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
730 FileAndData(String),
731 #[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
732 NoFileOrData(String),
733 #[error("The `data` value for the object `{0}` is not a string.")]
734 DataNotAString(String),
735 #[error("The `file` value for the object `{0}` is not a string.")]
736 FileNotAString(String),
737
738 #[error("The `key` key for an object is not set. It must be used.")]
739 NoKey,
740 #[error("The `key` value for an object is not a string.")]
741 KeyNotAString,
742
743 #[error("There is no array of objects for the given store.")]
744 NotAnArray,
745 #[error("There is an object in the given store that is not a table of keys.")]
746 NotATable,
747
748 #[error("Invalid secret store name: {0}")]
749 InvalidSecretStoreName(String),
750
751 #[error("Invalid secret name: {0}")]
752 InvalidSecretName(String),
753}
754
755#[derive(Debug, thiserror::Error)]
757pub enum ShieldingSiteConfigError {
758 #[error("Illegal TOML value for shielding site; must be either the string 'local' or a table containin an encrypted and unencrypted URL.")]
759 IllegalSiteValue,
760
761 #[error("Illegal TOML string for shielding site; must be 'local'")]
762 IllegalSiteString,
763
764 #[error("Illegal table for shielding site; must have exactly one key named 'encrypted', and one named 'unencrypted'")]
765 IllegalSiteDefinition,
766
767 #[error("Illegal URL ({url}): {error}")]
768 IllegalUrl { url: String, error: url::ParseError },
769}
770
771#[derive(Debug, thiserror::Error)]
773pub enum DownstreamRequestError {
774 #[error("Request HOST header is missing or invalid")]
775 InvalidHost,
776
777 #[error("Request URL is invalid")]
778 InvalidUrl,
779}