1
#include "source/extensions/router/cluster_specifiers/lua/lua_cluster_specifier.h"
2

            
3
#include "source/common/http/header_utility.h"
4
#include "source/common/router/delegating_route_impl.h"
5

            
6
namespace Envoy {
7
namespace Extensions {
8
namespace Router {
9
namespace Lua {
10

            
11
PerLuaCodeSetup::PerLuaCodeSetup(const std::string& lua_code, ThreadLocal::SlotAllocator& tls)
12
12
    : lua_state_(lua_code, tls) {
13
12
  lua_state_.registerType<HeaderMapWrapper>();
14
12
  lua_state_.registerType<RouteHandleWrapper>();
15
12
  lua_state_.registerType<ClusterWrapper>();
16

            
17
12
  const Filters::Common::Lua::InitializerList initializers;
18

            
19
12
  cluster_function_slot_ = lua_state_.registerGlobal("envoy_on_route", initializers);
20
12
  if (lua_state_.getGlobalRef(cluster_function_slot_) == LUA_REFNIL) {
21
1
    throw EnvoyException(
22
1
        "envoy_on_route() function not found. Lua will not hook cluster specifier.");
23
1
  }
24
12
}
25

            
26
6
int HeaderMapWrapper::luaGet(lua_State* state) {
27
6
  absl::string_view key = Filters::Common::Lua::getStringViewFromLuaString(state, 2);
28
6
  const Envoy::Http::HeaderUtility::GetAllOfHeaderAsStringResult value =
29
6
      Envoy::Http::HeaderUtility::getAllOfHeaderAsString(headers_,
30
6
                                                         Envoy::Http::LowerCaseString(key));
31
6
  if (value.result().has_value()) {
32
4
    lua_pushlstring(state, value.result().value().data(), value.result().value().size());
33
4
    return 1;
34
4
  } else {
35
2
    return 0;
36
2
  }
37
6
}
38

            
39
1
int ClusterWrapper::luaNumConnections(lua_State* state) {
40
1
  uint64_t count =
41
1
      cluster_->resourceManager(Upstream::ResourcePriority::Default).connections().count() +
42
1
      cluster_->resourceManager(Upstream::ResourcePriority::High).connections().count();
43
1
  lua_pushinteger(state, count);
44
1
  return 1;
45
1
}
46

            
47
5
int ClusterWrapper::luaNumRequests(lua_State* state) {
48
5
  uint64_t count =
49
5
      cluster_->resourceManager(Upstream::ResourcePriority::Default).requests().count() +
50
5
      cluster_->resourceManager(Upstream::ResourcePriority::High).requests().count();
51
5
  lua_pushinteger(state, count);
52
5
  return 1;
53
5
}
54

            
55
1
int ClusterWrapper::luaNumPendingRequests(lua_State* state) {
56
1
  uint64_t count =
57
1
      cluster_->resourceManager(Upstream::ResourcePriority::Default).pendingRequests().count() +
58
1
      cluster_->resourceManager(Upstream::ResourcePriority::High).pendingRequests().count();
59
1
  lua_pushinteger(state, count);
60
1
  return 1;
61
1
}
62

            
63
6
int RouteHandleWrapper::luaHeaders(lua_State* state) {
64
6
  if (headers_wrapper_.get() != nullptr) {
65
    headers_wrapper_.pushStack();
66
6
  } else {
67
6
    headers_wrapper_.reset(HeaderMapWrapper::create(state, headers_), true);
68
6
  }
69
6
  return 1;
70
6
}
71

            
72
7
int RouteHandleWrapper::luaGetCluster(lua_State* state) {
73
7
  size_t cluster_name_len = 0;
74
7
  const char* cluster_name = luaL_checklstring(state, 2, &cluster_name_len);
75
7
  Upstream::ThreadLocalCluster* cluster =
76
7
      cm_.getThreadLocalCluster(absl::string_view(cluster_name, cluster_name_len));
77
7
  if (cluster == nullptr) {
78
1
    return 0;
79
1
  }
80

            
81
6
  clusters_.emplace_back(ClusterWrapper::create(state, cluster->info()), true);
82

            
83
6
  return 1;
84
7
}
85

            
86
LuaClusterSpecifierConfig::LuaClusterSpecifierConfig(
87
    const LuaClusterSpecifierConfigProto& config,
88
    Server::Configuration::CommonFactoryContext& context)
89
12
    : cm_(context.clusterManager()), default_cluster_(config.default_cluster()) {
90
12
  const std::string code_str = THROW_OR_RETURN_VALUE(
91
12
      Config::DataSource::read(config.source_code(), true, context.api()), std::string);
92
12
  per_lua_code_setup_ptr_ = std::make_unique<PerLuaCodeSetup>(code_str, context.threadLocal());
93
12
}
94

            
95
LuaClusterSpecifierPlugin::LuaClusterSpecifierPlugin(LuaClusterSpecifierConfigSharedPtr config)
96
10
    : config_(config),
97
10
      function_ref_(config_->perLuaCodeSetup() ? config_->perLuaCodeSetup()->clusterFunctionRef()
98
10
                                               : LUA_REFNIL) {}
99

            
100
11
std::string LuaClusterSpecifierPlugin::startLua(const Http::HeaderMap& headers) const {
101
11
  if (function_ref_ == LUA_REFNIL) {
102
    return config_->defaultCluster();
103
  }
104
11
  Filters::Common::Lua::CoroutinePtr coroutine = config_->perLuaCodeSetup()->createCoroutine();
105

            
106
11
  RouteHandleRef handle;
107
11
  handle.reset(
108
11
      RouteHandleWrapper::create(coroutine->luaState(), headers, config_->clusterManager()), true);
109

            
110
11
  TRY_NEEDS_AUDIT {
111
11
    coroutine->start(function_ref_, 1, []() {});
112
11
  }
113
11
  END_TRY catch (const Filters::Common::Lua::LuaException& e) {
114
3
    ENVOY_LOG(error, "script log: {}, use default cluster", e.what());
115
3
    return config_->defaultCluster();
116
3
  }
117
8
  if (!lua_isstring(coroutine->luaState(), -1)) {
118
2
    ENVOY_LOG(error, "script log: return value is not string, use default cluster");
119
2
    return config_->defaultCluster();
120
2
  }
121
6
  return std::string(Filters::Common::Lua::getStringViewFromLuaString(coroutine->luaState(), -1));
122
8
}
123

            
124
Envoy::Router::RouteConstSharedPtr
125
LuaClusterSpecifierPlugin::route(Envoy::Router::RouteEntryAndRouteConstSharedPtr parent,
126
                                 const Http::RequestHeaderMap& headers,
127
11
                                 const StreamInfo::StreamInfo&, uint64_t) const {
128
11
  return std::make_shared<Envoy::Router::DynamicRouteEntry>(std::move(parent), startLua(headers));
129
11
}
130

            
131
} // namespace Lua
132
} // namespace Router
133
} // namespace Extensions
134
} // namespace Envoy