1#![allow(clippy::too_many_arguments)]
3#![allow(clippy::derive_partial_eq_without_eq)]
4
5pub use self::dictionary_impl::DictionaryError;
14pub use self::secret_store_impl::SecretStoreError;
15
16pub use self::device_detection_impl::DeviceDetectionError;
17
18use {
19 self::{
20 fastly_abi::FastlyAbi,
21 types::{FastlyStatus, UserErrorConversion},
22 },
23 crate::{error::Error, session::Session},
24 tracing::{event, Level},
25 wiggle::{GuestErrorType, GuestMemory, GuestPtr},
26};
27
28pub const ABI_VERSION: u64 = 1;
29
30macro_rules! multi_value_result {
39 ( $memory:ident, $expr:expr, $ending_cursor_out:expr ) => {{
40 let res = $expr;
41 let ec = res.as_ref().unwrap_or(&(-1));
42 if $ending_cursor_out.offset() != 0 {
44 $memory.write($ending_cursor_out, *ec)?;
45 }
46 let _ = res?;
47 Ok(())
48 }};
49}
50
51mod acl;
52mod backend_impl;
53mod body_impl;
54mod cache;
55mod compute_runtime;
56mod config_store;
57mod device_detection_impl;
58mod dictionary_impl;
59mod entity;
60mod erl_impl;
61mod fastly_purge_impl;
62mod geo_impl;
63mod headers;
64mod http_cache;
65mod http_downstream;
66mod image_optimizer;
67mod kv_store_impl;
68mod log_impl;
69mod obj_store_impl;
70mod req_impl;
71mod resp_impl;
72mod secret_store_impl;
73mod shielding;
74mod uap_impl;
75
76wiggle::from_witx!({
80 witx: ["wasm_abi/compute-at-edge-abi/compute-at-edge.witx"],
81 errors: { fastly_status => Error },
82 async: {
83 fastly_acl::lookup,
84 fastly_async_io::{select},
85 fastly_object_store::{delete_async, pending_delete_wait, insert, insert_async, pending_insert_wait, lookup_async, pending_lookup_wait, list},
86 fastly_kv_store::{lookup, lookup_wait, lookup_wait_v2, insert, insert_wait, delete, delete_wait, list, list_wait},
87 fastly_http_body::{append, read, write},
88 fastly_http_cache::{lookup, transaction_lookup, insert, transaction_insert, transaction_insert_and_stream_back, transaction_update, transaction_update_and_return_fresh, transaction_record_not_cacheable, transaction_abandon, found, close, get_suggested_backend_request, get_suggested_cache_options, prepare_response_for_storage, get_found_response, get_state, get_length, get_max_age_ns, get_stale_while_revalidate_ns, get_age_ns, get_hits, get_sensitive_data, get_surrogate_keys, get_vary_rule},
89 fastly_cache::{ cache_busy_handle_wait, close, close_busy, get_age_ns, get_body, get_hits, get_length, get_max_age_ns, get_stale_while_revalidate_ns, get_state, get_user_metadata, insert, lookup, replace, replace_get_age_ns, replace_get_body, replace_get_hits, replace_get_length, replace_get_max_age_ns, replace_get_stale_while_revalidate_ns, replace_get_state, replace_get_user_metadata, replace_insert, transaction_cancel, transaction_insert, transaction_insert_and_stream_back, transaction_lookup, transaction_lookup_async, transaction_update },
90 fastly_http_downstream::{next_request, next_request_abandon, next_request_wait},
91 fastly_http_req::{
92 pending_req_select, pending_req_select_v2, pending_req_poll, pending_req_poll_v2,
93 pending_req_wait, pending_req_wait_v2, send, send_v2, send_v3, send_async, send_async_v2, send_async_streaming,
94 },
95 fastly_image_optimizer::transform_image_optimizer_request,
96 }
97});
98
99impl From<types::ObjectStoreHandle> for types::KvStoreHandle {
100 fn from(h: types::ObjectStoreHandle) -> types::KvStoreHandle {
101 let s = unsafe { h.inner() };
102 s.into()
103 }
104}
105
106impl From<types::KvStoreHandle> for types::ObjectStoreHandle {
107 fn from(h: types::KvStoreHandle) -> types::ObjectStoreHandle {
108 let s = unsafe { h.inner() };
109 s.into()
110 }
111}
112
113impl From<types::KvStoreLookupHandle> for types::PendingKvLookupHandle {
114 fn from(h: types::KvStoreLookupHandle) -> types::PendingKvLookupHandle {
115 let s = unsafe { h.inner() };
116 s.into()
117 }
118}
119
120impl From<types::PendingKvLookupHandle> for types::KvStoreLookupHandle {
121 fn from(h: types::PendingKvLookupHandle) -> types::KvStoreLookupHandle {
122 let s = unsafe { h.inner() };
123 s.into()
124 }
125}
126
127impl From<types::KvStoreInsertHandle> for types::PendingKvInsertHandle {
128 fn from(h: types::KvStoreInsertHandle) -> types::PendingKvInsertHandle {
129 let s = unsafe { h.inner() };
130 s.into()
131 }
132}
133
134impl From<types::PendingKvInsertHandle> for types::KvStoreInsertHandle {
135 fn from(h: types::PendingKvInsertHandle) -> types::KvStoreInsertHandle {
136 let s = unsafe { h.inner() };
137 s.into()
138 }
139}
140
141impl From<types::KvStoreDeleteHandle> for types::PendingKvDeleteHandle {
142 fn from(h: types::KvStoreDeleteHandle) -> types::PendingKvDeleteHandle {
143 let s = unsafe { h.inner() };
144 s.into()
145 }
146}
147
148impl From<types::PendingKvDeleteHandle> for types::KvStoreDeleteHandle {
149 fn from(h: types::PendingKvDeleteHandle) -> types::KvStoreDeleteHandle {
150 let s = unsafe { h.inner() };
151 s.into()
152 }
153}
154
155impl From<types::KvStoreListHandle> for types::PendingKvListHandle {
156 fn from(h: types::KvStoreListHandle) -> types::PendingKvListHandle {
157 let s = unsafe { h.inner() };
158 s.into()
159 }
160}
161
162impl From<types::PendingKvListHandle> for types::KvStoreListHandle {
163 fn from(h: types::PendingKvListHandle) -> types::KvStoreListHandle {
164 let s = unsafe { h.inner() };
165 s.into()
166 }
167}
168
169impl From<types::HttpVersion> for http::version::Version {
170 fn from(v: types::HttpVersion) -> http::version::Version {
171 match v {
172 types::HttpVersion::Http09 => http::version::Version::HTTP_09,
173 types::HttpVersion::Http10 => http::version::Version::HTTP_10,
174 types::HttpVersion::Http11 => http::version::Version::HTTP_11,
175 types::HttpVersion::H2 => http::version::Version::HTTP_2,
176 types::HttpVersion::H3 => http::version::Version::HTTP_3,
177 }
178 }
179}
180
181impl TryFrom<http::version::Version> for types::HttpVersion {
184 type Error = &'static str;
185 fn try_from(v: http::version::Version) -> Result<Self, Self::Error> {
186 match v {
187 http::version::Version::HTTP_09 => Ok(types::HttpVersion::Http09),
188 http::version::Version::HTTP_10 => Ok(types::HttpVersion::Http10),
189 http::version::Version::HTTP_11 => Ok(types::HttpVersion::Http11),
190 http::version::Version::HTTP_2 => Ok(types::HttpVersion::H2),
191 http::version::Version::HTTP_3 => Ok(types::HttpVersion::H3),
192 _ => Err("unknown http::version::Version"),
193 }
194 }
195}
196
197impl FastlyAbi for Session {
198 fn init(&mut self, _memory: &mut GuestMemory<'_>, abi_version: u64) -> Result<(), Error> {
199 if abi_version != ABI_VERSION {
200 Err(Error::AbiVersionMismatch)
201 } else {
202 Ok(())
203 }
204 }
205}
206
207impl UserErrorConversion for Session {
208 fn fastly_status_from_error(&mut self, e: Error) -> Result<FastlyStatus, anyhow::Error> {
209 match e {
210 Error::UnknownBackend(ref backend) => {
211 let config_path = self.config_path();
212 let backends_buffer = itertools::join(self.backend_names(), ",");
213 let backends_len = self.backend_names().count();
214
215 match (backends_len, config_path) {
216 (_, None) => event!(
217 Level::WARN,
218 "Attempted to access backend '{}', but no manifest file was provided to define backends. \
219 Specify a file with -C <TOML_FILE>.",
220 backend,
221 ),
222 (0, Some(config_path)) => event!(
223 Level::WARN,
224 "Attempted to access backend '{}', but no backends were defined in the {} manifest file.",
225 backend,
226 config_path.display()
227 ),
228 (_, Some(config_path)) => event!(
229 Level::WARN,
230 "Backend '{}' does not exist. Currently defined backends are: {}. \
231 To define additional backends, add them to your {} file.",
232 backend,
233 backends_buffer,
234 config_path.display(),
235 ),
236 }
237 }
238 Error::DictionaryError(ref err) => match err {
239 DictionaryError::UnknownDictionaryItem(_) => {
240 event!(Level::DEBUG, "Hostcall yielded an error: {}", err);
241 }
242 DictionaryError::UnknownDictionary(_) => {
243 event!(Level::DEBUG, "Hostcall yielded an error: {}", err);
244 }
245 },
246 _ => event!(Level::DEBUG, "Hostcall yielded an error: {}", e),
247 }
248
249 match e {
250 Error::FatalError(msg) => Err(anyhow::Error::new(Error::FatalError(msg))),
252 _ => Ok(e.to_fastly_status()),
254 }
255 }
256}
257
258impl GuestErrorType for FastlyStatus {
259 fn success() -> Self {
260 FastlyStatus::Ok
261 }
262}
263
264pub(crate) trait MultiValueWriter {
265 fn write_values(
266 &mut self,
267 memory: &mut GuestMemory<'_>,
268 terminator: u8,
269 buf: GuestPtr<[u8]>,
270 cursor: types::MultiValueCursor,
271 nwritten_out: GuestPtr<u32>,
272 ) -> Result<types::MultiValueCursorResult, Error>;
273}
274
275impl<I, T> MultiValueWriter for I
276where
277 I: Iterator<Item = T>,
278 T: AsRef<[u8]>,
279{
280 #[allow(clippy::useless_conversion)] fn write_values(
282 &mut self,
283 memory: &mut GuestMemory<'_>,
284 terminator: u8,
285 buf: GuestPtr<[u8]>,
286 cursor: types::MultiValueCursor,
287 nwritten_out: GuestPtr<u32>,
288 ) -> Result<types::MultiValueCursorResult, Error> {
289 let buf = memory.as_slice_mut(buf)?.ok_or(Error::SharedMemory)?;
290
291 let mut cursor = u32::from(cursor) as usize;
298
299 let mut buf_offset = 0;
300 let mut finished = true;
301
302 for value in self.skip(cursor) {
303 let value_bytes = value.as_ref();
304 let value_len = value_bytes.len();
305 let value_len_with_term = value_len + 1;
306 match buf.get_mut(buf_offset..buf_offset + value_len_with_term) {
307 None => {
308 if buf_offset == 0 {
309 memory.write(nwritten_out, value_len_with_term.try_into().unwrap_or(0))?;
313 return Err(Error::BufferLengthError {
314 buf: "buf",
315 len: "buf.len()",
316 });
317 }
318 finished = false;
320 break;
321 }
322 Some(dest) => {
323 if dest.len() < value_len_with_term {
324 finished = false;
326 break;
327 }
328 dest[..value_len].copy_from_slice(value_bytes);
330 dest[value_len] = terminator;
332 cursor = if let Some(cursor) = cursor.checked_add(1) {
334 cursor
335 } else {
336 return Err(Error::FatalError(
337 "multi_value_writer cursor overflowed".to_owned(),
338 ));
339 };
340 buf_offset += value_len_with_term;
341 }
342 }
343 }
344
345 let ending_cursor = if finished {
346 types::MultiValueCursorResult::from(-1i64)
347 } else {
348 types::MultiValueCursorResult::from(cursor as i64)
349 };
350
351 memory.write(nwritten_out, buf_offset.try_into().unwrap_or(0))?;
352
353 Ok(ending_cursor)
354 }
355}