1
#include "source/extensions/common/wasm/wasm_vm.h"
2

            
3
#include <algorithm>
4
#include <memory>
5

            
6
#include "source/extensions/common/wasm/context.h"
7
#include "source/extensions/common/wasm/ext/envoy_null_vm_wasm_api.h"
8
#include "source/extensions/common/wasm/stats_handler.h"
9
#include "source/extensions/common/wasm/wasm_runtime_factory.h"
10

            
11
#include "include/proxy-wasm/null_plugin.h"
12

            
13
using ContextBase = proxy_wasm::ContextBase;
14
using Word = proxy_wasm::Word;
15

            
16
namespace Envoy {
17
namespace Extensions {
18
namespace Common {
19
namespace Wasm {
20

            
21
7466
proxy_wasm::LogLevel EnvoyWasmVmIntegration::getLogLevel() {
22
7466
  switch (ENVOY_LOGGER().level()) {
23
1
  case spdlog::level::trace:
24
1
    return proxy_wasm::LogLevel::trace;
25
542
  case spdlog::level::debug:
26
542
    return proxy_wasm::LogLevel::debug;
27
1
  case spdlog::level::info:
28
1
    return proxy_wasm::LogLevel::info;
29
1
  case spdlog::level::warn:
30
1
    return proxy_wasm::LogLevel::warn;
31
6860
  case spdlog::level::err:
32
6860
    return proxy_wasm::LogLevel::error;
33
61
  default:
34
61
    return proxy_wasm::LogLevel::critical;
35
7466
  }
36
7466
}
37

            
38
68
void EnvoyWasmVmIntegration::error(std::string_view message) { ENVOY_LOG(error, message); }
39
void EnvoyWasmVmIntegration::trace(std::string_view message) { ENVOY_LOG(trace, message); }
40

            
41
bool EnvoyWasmVmIntegration::getNullVmFunction(std::string_view function_name, bool returns_word,
42
                                               int number_of_arguments,
43
                                               proxy_wasm::NullPlugin* plugin,
44
527
                                               void* ptr_to_function_return) {
45
527
  if (function_name == "envoy_on_resolve_dns" && returns_word == false &&
46
527
      number_of_arguments == 3) {
47
263
    *reinterpret_cast<proxy_wasm::WasmCallVoid<3>*>(ptr_to_function_return) =
48
263
        [plugin](ContextBase* context, Word context_id, Word token, Word result_size) {
49
3
          proxy_wasm::SaveRestoreContext saved_context(context);
50
          // Need to add a new API header available to both .wasm and null vm targets.
51
3
          auto context_base = plugin->getContextBase(context_id);
52
3
          if (auto root = context_base->asRoot()) {
53
3
            static_cast<proxy_wasm::null_plugin::EnvoyRootContext*>(root)->onResolveDns(
54
3
                token, result_size);
55
3
          }
56
3
        };
57
263
    return true;
58
264
  } else if (function_name == "envoy_on_stats_update" && returns_word == false &&
59
264
             number_of_arguments == 2) {
60
263
    *reinterpret_cast<proxy_wasm::WasmCallVoid<2>*>(
61
263
        ptr_to_function_return) = [plugin](ContextBase* context, Word context_id,
62
263
                                           Word result_size) {
63
3
      proxy_wasm::SaveRestoreContext saved_context(context);
64
      // Need to add a new API header available to both .wasm and null vm targets.
65
3
      auto context_base = plugin->getContextBase(context_id);
66
3
      if (auto root = context_base->asRoot()) {
67
3
        static_cast<proxy_wasm::null_plugin::EnvoyRootContext*>(root)->onStatsUpdate(result_size);
68
3
      }
69
3
    };
70
263
    return true;
71
263
  }
72
1
  return false;
73
527
}
74

            
75
10
bool isWasmEngineAvailable(absl::string_view runtime) {
76
10
  auto runtime_factory = Registry::FactoryRegistry<WasmRuntimeFactory>::getFactory(runtime);
77
10
  return runtime_factory != nullptr;
78
10
}
79

            
80
8
absl::string_view getFirstAvailableWasmEngineName() {
81
8
  constexpr absl::string_view wasm_engines[] = {
82
8
      "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wasmtime", "envoy.wasm.runtime.wamr"};
83
10
  for (const auto wasm_engine : wasm_engines) {
84
10
    if (isWasmEngineAvailable(wasm_engine)) {
85
7
      return wasm_engine;
86
7
    }
87
10
  }
88
1
  return "";
89
8
}
90

            
91
404
WasmVmPtr createWasmVm(absl::string_view runtime) {
92
  // Set wasm runtime to built-in Wasm engine if it is not specified
93
404
  if (runtime.empty()) {
94
6
    runtime = getFirstAvailableWasmEngineName();
95
6
  }
96

            
97
404
  auto runtime_factory = Registry::FactoryRegistry<WasmRuntimeFactory>::getFactory(runtime);
98
404
  if (runtime_factory == nullptr) {
99
3
    ENVOY_LOG_TO_LOGGER(
100
3
        Envoy::Logger::Registry::getLog(Envoy::Logger::Id::wasm), warn,
101
3
        "Failed to create Wasm VM using {} runtime. Envoy was compiled without support for it",
102
3
        runtime);
103
3
    return nullptr;
104
3
  }
105

            
106
401
  auto wasm = runtime_factory->createWasmVm();
107
401
  wasm->integration() = std::make_unique<EnvoyWasmVmIntegration>();
108
401
  return wasm;
109
404
}
110

            
111
} // namespace Wasm
112
} // namespace Common
113
} // namespace Extensions
114
} // namespace Envoy