Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stubs for shielding support #441

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions crates/adapter/src/fastly/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3547,3 +3547,164 @@ pub mod fastly_purge {
)
}
}

pub mod fastly_shielding {
use super::*;
use crate::bindings::fastly::api::{shielding as host, types};
use std::slice;

bitflags::bitflags! {
#[derive(Default)]
#[repr(transparent)]
pub struct ShieldBackendOptions: u32 {
const RESERVED = 1 << 0;
const CACHE_KEY = 1 << 1;
}
}

#[repr(C)]
pub struct ShieldBackendConfig {
pub cache_key: *const u8,
pub cache_key_len: u32,
}

impl Default for ShieldBackendConfig {
fn default() -> Self {
ShieldBackendConfig {
cache_key: std::ptr::null(),
cache_key_len: 0,
}
}
}

#[export_name = "fastly_shielding#shield_info"]
pub fn shield_info(
name: *const u8,
name_len: usize,
info_block: *mut u8,
info_block_len: usize,
nwritten_out: *mut u32,
) -> FastlyStatus {
let name = unsafe { slice::from_raw_parts(name, name_len) };
with_buffer!(
info_block,
info_block_len,
{ host::shield_info(name, u64::try_from(info_block_len).trapping_unwrap()) },
|res| {
match res {
Ok(res) => {
unsafe {
*nwritten_out = u32::try_from(res.len()).unwrap_or(0);
}
std::mem::forget(res);
}

Err(e) => {
if let types::Error::BufferLen(needed) = e {
unsafe {
*nwritten_out = u32::try_from(needed).unwrap_or(0);
}
}

return Err(e.into());
}
}
}
)
}

impl From<ShieldBackendOptions> for host::ShieldBackendOptionsMask {
fn from(value: ShieldBackendOptions) -> Self {
let mut flags = Self::empty();

flags.set(
Self::RESERVED,
value.contains(ShieldBackendOptions::RESERVED),
);
flags.set(
Self::CACHE_KEY,
value.contains(ShieldBackendOptions::CACHE_KEY),
);

flags
}
}

fn shield_backend_options(
mask: ShieldBackendOptions,
options: *const ShieldBackendConfig,
) -> (host::ShieldBackendOptionsMask, host::ShieldBackendOptions) {
let mask = host::ShieldBackendOptionsMask::from(mask);

// NOTE: this is only really safe because we never mutate the vectors -- we only need
// vectors to satisfy the interface produced by the DynamicBackendConfig record,
// `register_dynamic_backend` will never mutate the vectors it's given.
macro_rules! make_vec {
($ptr_field:ident, $len_field:ident) => {
unsafe {
let len = usize::try_from((*options).$len_field).trapping_unwrap();
Vec::from_raw_parts((*options).$ptr_field as *mut _, len, len)
}
};
}

let options = host::ShieldBackendOptions {
cache_key: if mask.contains(host::ShieldBackendOptionsMask::CACHE_KEY) {
make_vec!(cache_key, cache_key_len)
} else {
Vec::new()
},
};

(mask, options)
}

/// Turn a pop name into a backend that we can send requests to.
#[export_name = "fastly_shielding#backend_for_shield"]
pub fn backend_for_shield(
name: *const u8,
name_len: usize,
options_mask: ShieldBackendOptions,
options: *const ShieldBackendConfig,
backend_name: *mut u8,
backend_name_len: usize,
nwritten_out: *mut u32,
) -> FastlyStatus {
let name = unsafe { slice::from_raw_parts(name, name_len) };
let (mask, options) = shield_backend_options(options_mask, options);
with_buffer!(
backend_name,
backend_name_len,
{
let res = host::backend_for_shield(
name,
mask,
&options,
u64::try_from(backend_name_len).trapping_unwrap(),
);
std::mem::forget(options);
res
},
|res| {
match res {
Ok(res) => {
unsafe {
*nwritten_out = u32::try_from(res.len()).unwrap_or(0);
}
std::mem::forget(res);
}

Err(e) => {
if let types::Error::BufferLen(needed) = e {
unsafe {
*nwritten_out = u32::try_from(needed).unwrap_or(0);
}
}

return Err(e.into());
}
}
}
)
}
}
1 change: 1 addition & 0 deletions lib/compute-at-edge-abi/compute-at-edge.witx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(use "cache.witx")
(use "config-store.witx")
(use "http-cache.witx")
(use "shielding.witx")

(module $fastly_abi
(@interface func (export "init")
Expand Down
31 changes: 31 additions & 0 deletions lib/compute-at-edge-abi/shielding.witx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
(typename $shield_backend_options
(flags (@witx repr u32)
$reserved
$use_cache_key
))

(typename $shield_backend_config
(record
(field $cache_key (@witx pointer (@witx char8)))
(field $cache_key_len u32)
))

(module $fastly_shielding

(@interface func (export "shield_info")
(param $name string)
(param $info_block (@witx pointer (@witx char8)))
(param $info_block_max_len (@witx usize))
(result $err (expected $num_bytes (error $fastly_status)))
)

(@interface func (export "backend_for_shield")
(param $shield_name string)
(param $backend_config_mask $shield_backend_options)
(param $backend_configuration (@witx pointer $shield_backend_config))
(param $backend_name_out (@witx pointer (@witx char8)))
(param $backend_name_max_len (@witx usize))
(result $err (expected $num_bytes (error $fastly_status)))
)

)
Binary file modified lib/data/viceroy-component-adapter.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions lib/src/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub fn link_host_functions(linker: &mut component::Linker<ComponentCtx>) -> anyh
fastly::api::object_store::add_to_linker(linker, |x| x)?;
fastly::api::purge::add_to_linker(linker, |x| x)?;
fastly::api::secret_store::add_to_linker(linker, |x| x)?;
fastly::api::shielding::add_to_linker(linker, |x| x)?;
fastly::api::types::add_to_linker(linker, |x| x)?;
fastly::api::uap::add_to_linker(linker, |x| x)?;

Expand Down Expand Up @@ -91,5 +92,6 @@ pub mod log;
pub mod object_store;
pub mod purge;
pub mod secret_store;
pub mod shielding;
pub mod types;
pub mod uap;
29 changes: 29 additions & 0 deletions lib/src/component/shielding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use super::fastly::api::{shielding, types};
use crate::linking::ComponentCtx;

#[async_trait::async_trait]
impl shielding::Host for ComponentCtx {
async fn shield_info(&mut self, name: Vec<u8>, _max_len: u64) -> Result<Vec<u8>, types::Error> {
// Validate input name and return the unsupported error.
let _name = String::from_utf8(name)?;

Err(types::Error::Unsupported)
}

async fn backend_for_shield(
&mut self,
name: Vec<u8>,
options_mask: shielding::ShieldBackendOptionsMask,
options: shielding::ShieldBackendOptions,
_max_len: u64,
) -> Result<Vec<u8>, types::Error> {
// Validate our inputs and return the unsupported error.
let _target_shield = String::from_utf8(name)?;

if options_mask.contains(shielding::ShieldBackendOptionsMask::CACHE_KEY) {
let _ = String::from_utf8(options.cache_key)?;
}

Err(types::Error::Unsupported)
}
}
1 change: 1 addition & 0 deletions lib/src/linking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ pub fn link_host_functions(
wiggle_abi::fastly_object_store::add_to_linker(linker, WasmCtx::session)?;
wiggle_abi::fastly_purge::add_to_linker(linker, WasmCtx::session)?;
wiggle_abi::fastly_secret_store::add_to_linker(linker, WasmCtx::session)?;
wiggle_abi::fastly_shielding::add_to_linker(linker, WasmCtx::session)?;
wiggle_abi::fastly_uap::add_to_linker(linker, WasmCtx::session)?;
link_legacy_aliases(linker)?;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions lib/src/wiggle_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ mod obj_store_impl;
mod req_impl;
mod resp_impl;
mod secret_store_impl;
mod shielding;
mod uap_impl;

// Expand the `.witx` interface definition into a collection of modules. The `types` module will
Expand Down
53 changes: 53 additions & 0 deletions lib/src/wiggle_abi/shielding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::error::Error;
use crate::session::Session;
use crate::wiggle_abi::{fastly_shielding, types};

impl fastly_shielding::FastlyShielding for Session {
fn shield_info(
&mut self,
memory: &mut wiggle::GuestMemory<'_>,
name: wiggle::GuestPtr<str>,
_out_buffer: wiggle::GuestPtr<u8>,
_out_buffer_max_len: u32,
) -> Result<u32, Error> {
// Validate the input name and then return the unsupported error.
let name_bytes = memory.to_vec(name.as_bytes())?;
let _name = String::from_utf8(name_bytes).map_err(|_| Error::InvalidArgument)?;

Err(Error::Unsupported {
msg: "shielding hostcalls are not supported",
})
}

fn backend_for_shield(
&mut self,
memory: &mut wiggle::GuestMemory<'_>,
shield_name: wiggle::GuestPtr<str>,
shield_backend_options: types::ShieldBackendOptions,
shield_backend_config: wiggle::GuestPtr<types::ShieldBackendConfig>,
_out_buffer: wiggle::GuestPtr<u8>,
_out_buffer_max_len: u32,
) -> Result<u32, Error> {
// Validate our inputs and then return the unsupported error.
let Some(_) = memory.as_str(shield_name)?.map(str::to_string) else {
return Err(Error::ValueAbsent);
};

if shield_backend_options.contains(types::ShieldBackendOptions::RESERVED) {
return Err(Error::InvalidArgument);
}

let config = memory.read(shield_backend_config)?;

if shield_backend_options.contains(types::ShieldBackendOptions::USE_CACHE_KEY) {
let field_string = config.cache_key.as_array(config.cache_key_len).cast();
if memory.as_str(field_string)?.is_none() {
return Err(Error::InvalidArgument);
}
}

Err(Error::Unsupported {
msg: "shielding hostcalls are not supported",
})
}
}
26 changes: 26 additions & 0 deletions lib/wit/deps/fastly/compute.wit
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,31 @@ interface config-store {
) -> result<option<list<u8>>, error>;
}

interface shielding {
use types.{error};

shield-info: func(
name: list<u8>,
max-len: u64,
) -> result<list<u8>, error>;

flags shield-backend-options-mask {
reserved,
cache-key,
}

record shield-backend-options {
cache-key: list<u8>,
}

backend-for-shield: func(
name: list<u8>,
options-mask: shield-backend-options-mask,
options: shield-backend-options,
max-len: u64,
) -> result<list<u8>, error>;
}

interface reactor {
use http-types.{request-handle, body-handle};

Expand Down Expand Up @@ -1381,6 +1406,7 @@ world compute {
import object-store;
import purge;
import secret-store;
import shielding;
import config-store;
import uap;

Expand Down
Loading