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}