Skip to main content

envoy_sdk/extension/module/
start.rs

1// Copyright 2020 Tetrate
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::dispatcher::{ContextSelector, VoidContextSelector};
16use crate::extension::{Module, Result};
17
18/// Generates the [`_start`] function that will be called by `Envoy` to let
19/// WebAssembly module initialize itself.
20///
21/// [`_start`]: https://github.com/proxy-wasm/spec/blob/master/abi-versions/vNEXT/README.md#_start
22///
23/// # Examples
24///
25/// ```
26/// # use envoy_sdk as envoy;
27/// # use envoy::extension::{self, AccessLogger, NetworkFilter, HttpFilter, InstanceId, ExtensionFactory};
28/// #
29/// # struct MyHttpFilter;
30/// # impl HttpFilter for MyHttpFilter {}
31/// #
32/// # struct MyHttpFilterFactory;
33/// # impl MyHttpFilterFactory {
34/// #     fn default() -> extension::Result<Self> { Ok(MyHttpFilterFactory) }
35/// # }
36/// # impl ExtensionFactory for MyHttpFilterFactory {
37/// #     type Extension = MyHttpFilter;
38/// #
39/// #     fn name() -> &'static str { "my_http_filter" }
40/// #
41/// #     fn new_extension(&mut self, instance_id: InstanceId) -> extension::Result<Self::Extension> {
42/// #         Ok(MyHttpFilter)
43/// #     }
44/// # }
45/// #
46/// # struct MyNetworkFilter;
47/// # impl NetworkFilter for MyNetworkFilter {}
48/// #
49/// # struct MyNetworkFilterFactory;
50/// # impl MyNetworkFilterFactory {
51/// #     fn default() -> extension::Result<Self> { Ok(MyNetworkFilterFactory) }
52/// # }
53/// # impl ExtensionFactory for MyNetworkFilterFactory {
54/// #     type Extension = MyNetworkFilter;
55/// #
56/// #     fn name() -> &'static str { "my_network_filter" }
57/// #
58/// #     fn new_extension(&mut self, instance_id: InstanceId) -> extension::Result<Self::Extension> {
59/// #         Ok(MyNetworkFilter)
60/// #     }
61/// # }
62/// #
63/// # struct MyAccessLogger;
64/// # impl AccessLogger for MyAccessLogger {
65/// #     fn name() -> &'static str { "my_access_logger" }
66/// # }
67/// # impl MyAccessLogger {
68/// #     fn default() -> extension::Result<Self> { Ok(MyAccessLogger) }
69/// # }
70/// #
71/// use envoy::extension::{entrypoint, Module, Result};
72///
73/// entrypoint! { initialize } // put initialization logic into a function to make it unit testable
74///
75/// /// Does one-time initialization.
76/// ///
77/// /// Returns a registry of extensions provided by this module.
78/// fn initialize() -> Result<Module> {
79///     // arbitrary initialization steps
80///
81///     Module::new()
82///         .add_http_filter(|_instance_id| MyHttpFilterFactory::default())?
83///         .add_network_filter(|_instance_id| MyNetworkFilterFactory::default())?
84///         .add_access_logger(|_instance_id| MyAccessLogger::default())
85/// }
86/// ```
87#[macro_export]
88macro_rules! entrypoint {
89    // Apparently, Rust toolchain doesn't handle well exported name `_start`
90    // when a package is compiled to targets other than `wasm32-unknown-unknown`.
91    // Specifically, linking issues have been observed with targets `wasm32-wasi`
92    // and `x86_64-unknown-linux-gnu`, which blocks unit testing.
93    // Therefore, only use export name `_start` when in the context of target
94    // `wasm32-unknown-unknown`.
95    ($init_fn:expr) => {
96        #[cfg_attr(
97            all(
98                target_arch = "wasm32",
99                target_vendor = "unknown",
100                target_os = "unknown"
101            ),
102            export_name = "_start"
103        )]
104        #[no_mangle]
105        extern "C" fn start() {
106            use $crate::extension::{self, Module, Result};
107            use $crate::host::log;
108
109            fn init<F>(init_fn: F)
110            where
111                F: FnOnce() -> Result<Module>,
112            {
113                // Apparently, `proxy_wasm` uses `set_log_level`
114                // to set a custom panic handler that will log panics using Envoy Log API.
115                // To be sure that panics will always be set,
116                // we call `set_log_level` ourselves instead of leaving it up to a user.
117                log::set_max_level(log::LogLevel::Info);
118
119                // Call the init callback provided as an argument.
120                extension::install(init_fn());
121            }
122
123            init($init_fn);
124        }
125    };
126}
127
128#[doc(hidden)]
129pub fn install(config: Result<Module>) {
130    match config {
131        Ok(module) => ContextSelector::with_default_ops(module.into()).install(),
132        Err(err) => VoidContextSelector::new(err).install(),
133    }
134}