Skip to content

Commit 3eda758

Browse files
committed
Handle relative URIs
Signed-off-by: Ryan Levick <[email protected]>
1 parent 888e919 commit 3eda758

File tree

10 files changed

+179
-136
lines changed

10 files changed

+179
-136
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "2ffbc3
119119
"component-model",
120120
] }
121121
wasmtime-wasi-http = { git = "https://github.com/bytecodealliance/wasmtime", rev = "2ffbc36c377b98e4eabe89ae37bb334605d904cc" }
122-
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "6fa69dcac7cae44fccc5671b9908d19c3e1514c0" }
122+
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "191789170abde10cd55590466c0660dd6c7d472a" }
123123
hyper = { version = "=1.0.0-rc.3", features = ["full"] }
124124
http-body-util = "=0.1.0-rc.2"
125125

crates/core/src/lib.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crossbeam_channel::Sender;
2020
use tracing::instrument;
2121
use wasmtime::{InstanceAllocationStrategy, PoolingAllocationConfig};
2222
use wasmtime_wasi::preview2::Table;
23-
use wasmtime_wasi_http::types::{WasiHttpCtx, WasiHttpView};
23+
use wasmtime_wasi_http::types::{default_send_request, WasiHttpCtx, WasiHttpView};
2424

2525
use self::host_component::{HostComponents, HostComponentsBuilder};
2626

@@ -191,7 +191,7 @@ impl<T: Send> wasmtime_wasi::preview2::WasiView for Data<T> {
191191
}
192192
}
193193

194-
impl<T: Send> WasiHttpView for Data<T> {
194+
impl<T: Send + OutboundWasiHttpHandler> WasiHttpView for Data<T> {
195195
fn ctx(&mut self) -> &mut WasiHttpCtx {
196196
match &mut self.wasi {
197197
Wasi::Preview1(_) => panic!("using WASI Preview 1 functions with Preview 2 store"),
@@ -202,6 +202,45 @@ impl<T: Send> WasiHttpView for Data<T> {
202202
fn table(&mut self) -> &mut Table {
203203
&mut self.table
204204
}
205+
206+
fn send_request(
207+
&mut self,
208+
request: wasmtime_wasi_http::types::OutgoingRequest,
209+
) -> wasmtime::Result<
210+
wasmtime::component::Resource<wasmtime_wasi_http::types::HostFutureIncomingResponse>,
211+
>
212+
where
213+
Self: Sized,
214+
{
215+
T::send_request(self, request)
216+
}
217+
}
218+
219+
/// Handler for wasi-http based requests
220+
pub trait OutboundWasiHttpHandler {
221+
/// Send the request
222+
fn send_request(
223+
data: &mut Data<Self>,
224+
request: wasmtime_wasi_http::types::OutgoingRequest,
225+
) -> wasmtime::Result<
226+
wasmtime::component::Resource<wasmtime_wasi_http::types::HostFutureIncomingResponse>,
227+
>
228+
where
229+
Self: Sized;
230+
}
231+
232+
impl OutboundWasiHttpHandler for () {
233+
fn send_request(
234+
data: &mut Data<Self>,
235+
request: wasmtime_wasi_http::types::OutgoingRequest,
236+
) -> wasmtime::Result<
237+
wasmtime::component::Resource<wasmtime_wasi_http::types::HostFutureIncomingResponse>,
238+
>
239+
where
240+
Self: Sized,
241+
{
242+
default_send_request(data, request)
243+
}
205244
}
206245

207246
/// An alias for [`wasmtime::Linker`] specialized to [`Data`].
@@ -222,7 +261,7 @@ pub struct EngineBuilder<T> {
222261
epoch_ticker_thread: bool,
223262
}
224263

225-
impl<T: Send + Sync> EngineBuilder<T> {
264+
impl<T: Send + Sync + OutboundWasiHttpHandler> EngineBuilder<T> {
226265
fn new(config: &Config) -> Result<Self> {
227266
let engine = wasmtime::Engine::new(&config.inner)?;
228267

@@ -244,7 +283,9 @@ impl<T: Send + Sync> EngineBuilder<T> {
244283
epoch_ticker_thread: true,
245284
})
246285
}
286+
}
247287

288+
impl<T: Send + Sync> EngineBuilder<T> {
248289
/// Adds definition(s) to the built [`Engine`].
249290
///
250291
/// This method's signature is meant to be used with
@@ -345,7 +386,7 @@ pub struct Engine<T> {
345386
_epoch_ticker_signal: Option<Sender<()>>,
346387
}
347388

348-
impl<T: Send + Sync> Engine<T> {
389+
impl<T: OutboundWasiHttpHandler + Send + Sync> Engine<T> {
349390
/// Creates a new [`EngineBuilder`] with the given [`Config`].
350391
pub fn builder(config: &Config) -> Result<EngineBuilder<T>> {
351392
EngineBuilder::new(config)

crates/core/tests/integration_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ async fn run_core_wasi_test_engine<'a>(
212212
let mut exports = instance.exports(&mut store);
213213

214214
let mut instance = exports
215-
.instance("wasi:cli/run")
216-
.context("missing the expected 'wasi:cli/run' instance")?;
215+
.instance("wasi:cli/run@0.2.0-rc-2023-10-18")
216+
.context("missing the expected 'wasi:cli/run@0.2.0-rc-2023-10-18' instance")?;
217217
instance.typed_func::<(), (Result<(), ()>,)>("run")?
218218
};
219219
update_store(&mut store);

crates/loader/tests/ui/valid-manifest.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"trigger_config": {
2525
"component": "four-lights",
2626
"executor": {
27-
"type": "spin"
27+
"type": "http"
2828
},
2929
"route": "/lights"
3030
}

crates/loader/tests/ui/valid-with-files/spin.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"trigger_config": {
2222
"component": "fs",
2323
"executor": {
24-
"type": "spin"
24+
"type": "http"
2525
},
2626
"route": "/..."
2727
}

crates/trigger-http/src/handler.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ fn set_http_origin_from_request(
270270
) {
271271
if let Some(authority) = req.uri().authority() {
272272
if let Some(scheme) = req.uri().scheme_str() {
273+
let origin = format!("{}://{}", scheme, authority);
273274
if let Some(outbound_http_handle) = engine
274275
.engine
275276
.find_host_component_handle::<Arc<OutboundHttpComponent>>()
@@ -278,8 +279,9 @@ fn set_http_origin_from_request(
278279
.host_components_data()
279280
.get_or_insert(outbound_http_handle);
280281

281-
outbound_http_data.origin = format!("{}://{}", scheme, authority);
282+
outbound_http_data.origin = origin.clone();
282283
}
284+
store.as_mut().data_mut().as_mut().origin = Some(origin);
283285
}
284286
}
285287
}

crates/trigger-http/src/lib.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use hyper::{
2323
Request, Response,
2424
};
2525
use spin_app::{AppComponent, APP_DESCRIPTION_KEY};
26-
use spin_core::Engine;
26+
use spin_core::{Engine, OutboundWasiHttpHandler};
2727
use spin_http::{
2828
app_info::AppInfo,
2929
body,
@@ -43,7 +43,7 @@ use crate::{handler::HttpHandlerExecutor, wagi::WagiHttpExecutor};
4343

4444
pub use tls::TlsConfig;
4545

46-
pub(crate) type RuntimeData = ();
46+
pub(crate) type RuntimeData = HttpRuntimeData;
4747
pub(crate) type Store = spin_core::Store<RuntimeData>;
4848

4949
/// The Spin HTTP trigger.
@@ -443,6 +443,54 @@ pub(crate) trait HttpExecutor: Clone + Send + Sync + 'static {
443443
) -> Result<Response<Body>>;
444444
}
445445

446+
#[derive(Default)]
447+
pub struct HttpRuntimeData {
448+
origin: Option<String>,
449+
}
450+
451+
impl OutboundWasiHttpHandler for HttpRuntimeData {
452+
fn send_request(
453+
data: &mut spin_core::Data<Self>,
454+
mut request: wasmtime_wasi_http::types::OutgoingRequest,
455+
) -> wasmtime::Result<
456+
wasmtime::component::Resource<wasmtime_wasi_http::types::HostFutureIncomingResponse>,
457+
>
458+
where
459+
Self: Sized,
460+
{
461+
let is_relative_url = request
462+
.request
463+
.uri()
464+
.authority()
465+
.map(|a| a.host().trim() == "")
466+
.unwrap_or_default();
467+
if is_relative_url {
468+
// Origin must be set in the incoming http handler
469+
let origin = data.as_ref().origin.clone().unwrap();
470+
let path_and_query = request
471+
.request
472+
.uri()
473+
.path_and_query()
474+
.map(|p| p.as_str())
475+
.unwrap_or("/");
476+
let uri: Uri = format!("{origin}{path_and_query}")
477+
.parse()
478+
// origin together with the path and query must be a valid URI
479+
.unwrap();
480+
481+
request.use_tls = uri
482+
.scheme()
483+
.map(|s| s == &Scheme::HTTPS)
484+
.unwrap_or_default();
485+
// We know that `uri` has an authority because we set it above
486+
request.authority = uri.authority().unwrap().as_str().to_owned();
487+
*request.request.uri_mut() = uri;
488+
}
489+
490+
wasmtime_wasi_http::types::default_send_request(data, request)
491+
}
492+
}
493+
446494
#[cfg(test)]
447495
mod tests {
448496
use std::collections::BTreeMap;

crates/trigger/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use serde::de::DeserializeOwned;
1313

1414
use spin_app::{App, AppComponent, AppLoader, AppTrigger, Loader, OwnedApp, APP_NAME_KEY};
1515
use spin_core::{
16-
Config, Engine, EngineBuilder, Instance, InstancePre, ModuleInstance, ModuleInstancePre, Store,
17-
StoreBuilder, WasiVersion,
16+
Config, Engine, EngineBuilder, Instance, InstancePre, ModuleInstance, ModuleInstancePre,
17+
OutboundWasiHttpHandler, Store, StoreBuilder, WasiVersion,
1818
};
1919

2020
pub use crate::runtime_config::RuntimeConfig;
@@ -32,7 +32,7 @@ pub enum EitherInstance {
3232
#[async_trait]
3333
pub trait TriggerExecutor: Sized + Send + Sync {
3434
const TRIGGER_TYPE: &'static str;
35-
type RuntimeData: Default + Send + Sync + 'static;
35+
type RuntimeData: OutboundWasiHttpHandler + Default + Send + Sync + 'static;
3636
type TriggerConfig;
3737
type RunConfig;
3838

0 commit comments

Comments
 (0)