Skip to content

Commit 2bbb2e6

Browse files
committed
HTTP middleware
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent cd80d1a commit 2bbb2e6

21 files changed

Lines changed: 1044 additions & 95 deletions

File tree

Cargo.lock

Lines changed: 76 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/compose/src/lib.rs

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ pub use spin_capabilities::InheritConfiguration;
2929
/// dependent component. Finally, the composer will export all exports from the
3030
/// dependent component to its dependents. The composer will then encode the
3131
/// composition graph into a byte array and return it.
32-
pub async fn compose<L: ComponentSourceLoader>(
32+
pub async fn compose<
33+
L: ComponentSourceLoader,
34+
Fut: std::future::Future<Output = Result<Vec<u8>, ComposeError>>,
35+
>(
3336
loader: &L,
3437
component: &L::Component,
38+
complicator: impl Fn(Vec<u8>) -> Fut,
3539
) -> Result<Vec<u8>, ComposeError> {
36-
Composer::new(loader).compose(component).await
40+
Composer::new(loader).compose(component, complicator).await
3741
}
3842

3943
/// A Spin component dependency. This abstracts over the metadata associated with the
@@ -90,8 +94,10 @@ impl DependencyLike for spin_app::locked::LockedComponentDependency {
9094
pub trait ComponentSourceLoader {
9195
type Component: ComponentLike<Dependency = Self::Dependency>;
9296
type Dependency: DependencyLike;
97+
type Source;
9398
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>>;
9499
async fn load_dependency_source(&self, source: &Self::Dependency) -> anyhow::Result<Vec<u8>>;
100+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>>;
95101
}
96102

97103
/// A ComponentSourceLoader that loads component sources from the filesystem.
@@ -101,6 +107,7 @@ pub struct ComponentSourceLoaderFs;
101107
impl ComponentSourceLoader for ComponentSourceLoaderFs {
102108
type Component = spin_app::locked::LockedComponent;
103109
type Dependency = spin_app::locked::LockedComponentDependency;
110+
type Source = spin_app::locked::LockedComponentSource;
104111

105112
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>> {
106113
Self::load_from_locked_source(&source.source).await
@@ -109,6 +116,10 @@ impl ComponentSourceLoader for ComponentSourceLoaderFs {
109116
async fn load_dependency_source(&self, source: &Self::Dependency) -> anyhow::Result<Vec<u8>> {
110117
Self::load_from_locked_source(&source.source).await
111118
}
119+
120+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>> {
121+
Self::load_from_locked_source(source).await
122+
}
112123
}
113124

114125
impl ComponentSourceLoaderFs {
@@ -195,39 +206,47 @@ struct Composer<'a, L> {
195206
}
196207

197208
impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
198-
async fn compose(mut self, component: &L::Component) -> Result<Vec<u8>, ComposeError> {
209+
async fn compose<Fut: std::future::Future<Output = Result<Vec<u8>, ComposeError>>>(
210+
mut self,
211+
component: &L::Component,
212+
complicator: impl Fn(Vec<u8>) -> Fut,
213+
) -> Result<Vec<u8>, ComposeError> {
199214
let source = self
200215
.loader
201216
.load_component_source(component)
202217
.await
203218
.map_err(ComposeError::PrepareError)?;
204219

205-
if component.dependencies().len() == 0 {
206-
return Ok(source);
207-
}
220+
let fulfilled_source = if component.dependencies().len() == 0 {
221+
source
222+
} else {
223+
let (world_id, instantiation_id) = self
224+
.register_package(component.id(), None, source)
225+
.map_err(ComposeError::PrepareError)?;
208226

209-
let (world_id, instantiation_id) = self
210-
.register_package(component.id(), None, source)
211-
.map_err(ComposeError::PrepareError)?;
227+
let prepared = self.prepare_dependencies(world_id, component).await?;
212228

213-
let prepared = self.prepare_dependencies(world_id, component).await?;
229+
let arguments = self
230+
.build_instantiation_arguments(world_id, prepared)
231+
.await?;
214232

215-
let arguments = self
216-
.build_instantiation_arguments(world_id, prepared)
217-
.await?;
233+
for (argument_name, argument) in arguments {
234+
self.graph
235+
.set_instantiation_argument(instantiation_id, &argument_name, argument)
236+
.map_err(|e| ComposeError::PrepareError(e.into()))?;
237+
}
238+
239+
self.export_dependents_exports(world_id, instantiation_id)
240+
.map_err(ComposeError::PrepareError)?;
218241

219-
for (argument_name, argument) in arguments {
220242
self.graph
221-
.set_instantiation_argument(instantiation_id, &argument_name, argument)
222-
.map_err(|e| ComposeError::PrepareError(e.into()))?;
223-
}
243+
.encode(Default::default())
244+
.map_err(|e| ComposeError::EncodeError(e.into()))?
245+
};
224246

225-
self.export_dependents_exports(world_id, instantiation_id)
226-
.map_err(ComposeError::PrepareError)?;
247+
let with_extras = complicator(fulfilled_source).await?;
227248

228-
self.graph
229-
.encode(Default::default())
230-
.map_err(|e| ComposeError::EncodeError(e.into()))
249+
Ok(with_extras)
231250
}
232251

233252
fn new(loader: &'a L) -> Self {

crates/environments/src/loader.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl ApplicationToValidate {
147147

148148
let loader = ComponentSourceLoader::new(&self.wasm_loader);
149149

150-
let wasm = spin_compose::compose(&loader, &component).await.with_context(|| format!("Spin needed to compose dependencies for {} as part of target checking, but composition failed", component.id))?;
150+
let wasm = spin_compose::compose(&loader, &component, async |data| Ok(data)).await.with_context(|| format!("Spin needed to compose dependencies for {} as part of target checking, but composition failed", component.id))?;
151151

152152
let host_requirements = if component.requires_service_chaining {
153153
vec!["local_service_chaining".to_string()]
@@ -185,6 +185,7 @@ impl<'a> ComponentSourceLoader<'a> {
185185
impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> {
186186
type Component = ComponentSource<'a>;
187187
type Dependency = WrappedComponentDependency;
188+
type Source = spin_manifest::schema::v2::ComponentSource;
188189
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>> {
189190
let path = self
190191
.wasm_loader
@@ -210,6 +211,19 @@ impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> {
210211
.with_context(|| format!("componentizing {}", quoted_path(&path)))?;
211212
Ok(component.into())
212213
}
214+
215+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>> {
216+
let path = self
217+
.wasm_loader
218+
.load_component_source("in-memory-component", source)
219+
.await?;
220+
let bytes = tokio::fs::read(&path)
221+
.await
222+
.with_context(|| format!("reading {}", quoted_path(&path)))?;
223+
let component = spin_componentize::componentize_if_necessary(&bytes)
224+
.with_context(|| format!("componentizing {}", quoted_path(&path)))?;
225+
Ok(component.into())
226+
}
213227
}
214228

215229
// This exists only to thwart the orphan rule

crates/factors-executor/src/lib.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
5555
runtime_config: T::RuntimeConfig,
5656
component_loader: &impl ComponentLoader<T, U>,
5757
trigger_type: Option<&str>,
58+
complicator: impl Complicator,
5859
) -> anyhow::Result<FactorsExecutorApp<T, U>> {
5960
let configured_app = self
6061
.factors
@@ -77,7 +78,7 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
7778

7879
for component in components {
7980
let instance_pre = component_loader
80-
.load_instance_pre(&self.core_engine, &component)
81+
.load_instance_pre(&self.core_engine, &component, &complicator)
8182
.await?;
8283
component_instance_pres.insert(component.id().to_string(), instance_pre);
8384
}
@@ -116,19 +117,59 @@ pub trait ComponentLoader<T: RuntimeFactors, U>: Sync {
116117
&self,
117118
engine: &spin_core::wasmtime::Engine,
118119
component: &AppComponent,
120+
complicator: &impl Complicator,
119121
) -> anyhow::Result<Component>;
120122

121123
/// Loads [`InstancePre`] for the given [`AppComponent`].
122124
async fn load_instance_pre(
123125
&self,
124126
engine: &spin_core::Engine<InstanceState<T::InstanceState, U>>,
125127
component: &AppComponent,
128+
complicator: &impl Complicator,
126129
) -> anyhow::Result<spin_core::InstancePre<InstanceState<T::InstanceState, U>>> {
127-
let component = self.load_component(engine.as_ref(), component).await?;
130+
let component = self
131+
.load_component(engine.as_ref(), component, complicator)
132+
.await?;
128133
engine.instantiate_pre(&component)
129134
}
130135
}
131136

137+
#[async_trait]
138+
pub trait Complicator: Send + Sync {
139+
async fn complicate(
140+
&self,
141+
complications: &HashMap<String, Vec<Complication>>,
142+
component: Vec<u8>,
143+
) -> anyhow::Result<Vec<u8>>;
144+
}
145+
146+
#[async_trait]
147+
impl Complicator for () {
148+
async fn complicate(
149+
&self,
150+
complications: &HashMap<String, Vec<Complication>>,
151+
component: Vec<u8>,
152+
) -> anyhow::Result<Vec<u8>> {
153+
if complications.is_empty() {
154+
Ok(component)
155+
} else {
156+
Err(anyhow::anyhow!(
157+
"this trigger should not have complications"
158+
))
159+
}
160+
}
161+
}
162+
163+
pub struct Complication {
164+
pub source: spin_app::locked::LockedComponentSource,
165+
pub data: ComplicationData,
166+
}
167+
168+
pub enum ComplicationData {
169+
InMemory(Vec<u8>),
170+
OnDisk(std::path::PathBuf),
171+
}
172+
132173
type InstancePre<T, U> =
133174
spin_core::InstancePre<InstanceState<<T as RuntimeFactors>::InstanceState, U>>;
134175

@@ -437,7 +478,7 @@ mod tests {
437478
let executor = Arc::new(FactorsExecutor::new(engine_builder, env.factors)?);
438479

439480
let factors_app = executor
440-
.load_app(app, Default::default(), &DummyComponentLoader, None)
481+
.load_app(app, Default::default(), &DummyComponentLoader, None, ())
441482
.await?;
442483

443484
let mut instance_builder = factors_app.prepare("empty")?;
@@ -463,6 +504,7 @@ mod tests {
463504
&self,
464505
engine: &spin_core::wasmtime::Engine,
465506
_component: &AppComponent,
507+
_complicator: &impl Complicator,
466508
) -> anyhow::Result<Component> {
467509
Ok(Component::new(engine, "(component)")?)
468510
}

0 commit comments

Comments
 (0)