wasmer_wasix/runners/dcgi/
runner.rs

1use std::{net::SocketAddr, sync::Arc};
2
3use anyhow::Error;
4use wasmer_journal::FilteredJournalBuilder;
5use wcgi_host::CgiDialect;
6use webc::metadata::Command;
7
8use crate::{
9    Runtime,
10    bin_factory::BinaryPackage,
11    capabilities::Capabilities,
12    journal::DynJournal,
13    runners::{
14        MappedDirectory,
15        dcgi::handler::Handler,
16        wcgi::{self, NoOpWcgiCallbacks, WcgiRunner},
17    },
18    runtime::{DynRuntime, OverriddenRuntime},
19};
20
21use super::{DcgiCallbacks, DcgiInstanceFactory};
22
23#[derive(Debug)]
24pub struct DcgiRunner {
25    config: Config,
26    inner: wcgi::WcgiRunner,
27}
28
29impl DcgiRunner {
30    pub fn new(factory: DcgiInstanceFactory) -> Self {
31        let callbacks = DcgiCallbacks::new(factory, NoOpWcgiCallbacks);
32        DcgiRunner {
33            config: Config {
34                inner: wcgi::Config::new(callbacks.clone()),
35            },
36            inner: WcgiRunner::new(callbacks),
37        }
38    }
39
40    pub fn config(&mut self) -> &mut Config {
41        &mut self.config
42    }
43
44    #[tracing::instrument(skip_all)]
45    fn prepare_handler(
46        &mut self,
47        command_name: &str,
48        pkg: &BinaryPackage,
49        runtime: Arc<dyn Runtime + Send + Sync>,
50    ) -> Result<Handler, Error> {
51        let inner: wcgi::Handler =
52            self.inner
53                .prepare_handler(command_name, pkg, true, CgiDialect::Rfc3875, runtime)?;
54        Ok(Handler::new(inner))
55    }
56}
57
58/// The base URI used by a [`Dcgi`] runner.
59pub const DCGI_RUNNER_URI: &str = "https://webc.org/runner/dcgi";
60
61impl crate::runners::Runner for DcgiRunner {
62    fn can_run_command(command: &Command) -> Result<bool, Error> {
63        Ok(command.runner.starts_with(DCGI_RUNNER_URI))
64    }
65
66    fn run_command(
67        &mut self,
68        command_name: &str,
69        pkg: &BinaryPackage,
70        runtime: Arc<DynRuntime>,
71    ) -> Result<(), Error> {
72        // We use a filter in front of the journals supplied to the runtime.
73        // The reason for this is that DCGI currently only supports persisting the
74        // file system changes as it is unable to run the main function more than
75        // once due to limitations in the runtime
76        let journals = runtime
77            .writable_journals()
78            .map(|journal| {
79                let journal = FilteredJournalBuilder::new()
80                    .with_ignore_memory(true)
81                    .with_ignore_threads(true)
82                    .with_ignore_core(true)
83                    .with_ignore_snapshots(true)
84                    .with_ignore_networking(true)
85                    .with_ignore_stdio(true)
86                    .build(journal);
87                Arc::new(journal) as Arc<DynJournal>
88            })
89            .collect::<Vec<_>>();
90        let runtime = OverriddenRuntime::new(runtime).with_writable_journals(journals);
91        let runtime = Arc::new(runtime) as Arc<DynRuntime>;
92
93        //We now pass the runtime to the handlers
94        let handler = self.prepare_handler(command_name, pkg, Arc::clone(&runtime))?;
95        self.inner.run_command_with_handler(handler, runtime)
96    }
97}
98
99#[derive(Debug)]
100pub struct Config {
101    inner: wcgi::Config,
102}
103
104impl Config {
105    pub fn inner(&mut self) -> &mut wcgi::Config {
106        &mut self.inner
107    }
108
109    pub fn addr(&mut self, addr: SocketAddr) -> &mut Self {
110        self.inner.addr(addr);
111        self
112    }
113
114    /// Add an argument to the WASI executable's command-line arguments.
115    pub fn arg(&mut self, arg: impl Into<String>) -> &mut Self {
116        self.inner.arg(arg);
117        self
118    }
119
120    /// Add multiple arguments to the WASI executable's command-line arguments.
121    pub fn args<A, S>(&mut self, args: A) -> &mut Self
122    where
123        A: IntoIterator<Item = S>,
124        S: Into<String>,
125    {
126        self.inner.args(args);
127        self
128    }
129
130    /// Expose an environment variable to the guest.
131    pub fn env(&mut self, name: impl Into<String>, value: impl Into<String>) -> &mut Self {
132        self.inner.env(name, value);
133        self
134    }
135
136    /// Expose multiple environment variables to the guest.
137    pub fn envs<I, K, V>(&mut self, variables: I) -> &mut Self
138    where
139        I: IntoIterator<Item = (K, V)>,
140        K: Into<String>,
141        V: Into<String>,
142    {
143        self.inner.envs(variables);
144        self
145    }
146
147    /// Forward all of the host's environment variables to the guest.
148    pub fn forward_host_env(&mut self) -> &mut Self {
149        self.inner.forward_host_env();
150        self
151    }
152
153    pub fn map_directory(&mut self, dir: MappedDirectory) -> &mut Self {
154        self.inner.map_directory(dir);
155        self
156    }
157
158    pub fn map_directories(
159        &mut self,
160        mappings: impl IntoIterator<Item = MappedDirectory>,
161    ) -> &mut Self {
162        self.inner.map_directories(mappings);
163        self
164    }
165
166    /// Set callbacks that will be triggered at various points in the runner's
167    /// lifecycle.
168    pub fn callbacks(&mut self, callbacks: impl wcgi::Callbacks + 'static) -> &mut Self {
169        self.inner.callbacks(callbacks);
170        self
171    }
172
173    /// Add a package that should be available to the instance at runtime.
174    pub fn inject_package(&mut self, pkg: BinaryPackage) -> &mut Self {
175        self.inner.inject_package(pkg);
176        self
177    }
178
179    /// Add packages that should be available to the instance at runtime.
180    pub fn inject_packages(
181        &mut self,
182        packages: impl IntoIterator<Item = BinaryPackage>,
183    ) -> &mut Self {
184        self.inner.inject_packages(packages);
185        self
186    }
187
188    pub fn capabilities(&mut self) -> &mut Capabilities {
189        self.inner.capabilities()
190    }
191
192    #[cfg(feature = "journal")]
193    pub fn add_snapshot_trigger(&mut self, on: crate::journal::SnapshotTrigger) {
194        self.inner.add_snapshot_trigger(on);
195    }
196
197    #[cfg(feature = "journal")]
198    pub fn add_default_snapshot_triggers(&mut self) -> &mut Self {
199        self.inner.add_default_snapshot_triggers();
200        self
201    }
202
203    #[cfg(feature = "journal")]
204    pub fn has_snapshot_trigger(&self, on: crate::journal::SnapshotTrigger) -> bool {
205        self.inner.has_snapshot_trigger(on)
206    }
207
208    #[cfg(feature = "journal")]
209    pub fn with_snapshot_interval(&mut self, period: std::time::Duration) -> &mut Self {
210        self.inner.with_snapshot_interval(period);
211        self
212    }
213
214    #[cfg(feature = "journal")]
215    pub fn with_stop_running_after_snapshot(&mut self, stop_running: bool) {
216        self.inner.with_stop_running_after_snapshot(stop_running);
217    }
218
219    #[cfg(feature = "journal")]
220    pub fn add_read_only_journal(
221        &mut self,
222        journal: Arc<crate::journal::DynReadableJournal>,
223    ) -> &mut Self {
224        self.inner.add_read_only_journal(journal);
225        self
226    }
227
228    #[cfg(feature = "journal")]
229    pub fn add_writable_journal(&mut self, journal: Arc<crate::journal::DynJournal>) -> &mut Self {
230        self.inner.add_writable_journal(journal);
231        self
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn send_and_sync() {
241        fn assert_send<T: Send>() {}
242        fn assert_sync<T: Sync>() {}
243
244        assert_send::<DcgiRunner>();
245        assert_sync::<DcgiRunner>();
246    }
247}