mirror of
https://github.com/alibaba/higress.git
synced 2026-04-23 21:17:30 +08:00
Add plugins (#27)
This commit is contained in:
194
plugins/wasm-cpp/extensions/custom_response/plugin.cc
Normal file
194
plugins/wasm-cpp/extensions/custom_response/plugin.cc
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "extensions/custom_response/plugin.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "common/json_util.h"
|
||||
|
||||
using ::nlohmann::json;
|
||||
using ::Wasm::Common::JsonArrayIterate;
|
||||
using ::Wasm::Common::JsonGetField;
|
||||
using ::Wasm::Common::JsonObjectIterate;
|
||||
using ::Wasm::Common::JsonValueAs;
|
||||
|
||||
#ifdef NULL_PLUGIN
|
||||
|
||||
namespace proxy_wasm {
|
||||
namespace null_plugin {
|
||||
namespace custom_response {
|
||||
|
||||
PROXY_WASM_NULL_PLUGIN_REGISTRY
|
||||
|
||||
#endif
|
||||
|
||||
static RegisterContextFactory register_CustomResponse(
|
||||
CONTEXT_FACTORY(PluginContext), ROOT_FACTORY(PluginRootContext));
|
||||
|
||||
bool PluginRootContext::parsePluginConfig(const json& configuration,
|
||||
CustomResponseConfigRule& rule) {
|
||||
if (!JsonArrayIterate(
|
||||
configuration, "enable_on_status", [&](const json& item) -> bool {
|
||||
auto status = JsonValueAs<int64_t>(item);
|
||||
if (status.second != Wasm::Common::JsonParserResultDetail::OK) {
|
||||
LOG_WARN("cannot parse enable_on_status");
|
||||
return false;
|
||||
}
|
||||
rule.enable_on_status.push_back(
|
||||
std::to_string(status.first.value()));
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse configuration for enable_on_status.");
|
||||
return false;
|
||||
}
|
||||
bool has_content_type = false;
|
||||
if (!JsonArrayIterate(
|
||||
configuration, "headers", [&](const json& item) -> bool {
|
||||
auto header = JsonValueAs<std::string>(item);
|
||||
if (header.second != Wasm::Common::JsonParserResultDetail::OK) {
|
||||
LOG_WARN("cannot parse header");
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> pair =
|
||||
absl::StrSplit(header.first.value(), absl::MaxSplits("=", 2));
|
||||
if (pair.size() != 2) {
|
||||
LOG_WARN("invalid header pair format");
|
||||
}
|
||||
if (absl::AsciiStrToLower(pair[0]) ==
|
||||
Wasm::Common::Http::Header::ContentLength) {
|
||||
return true;
|
||||
}
|
||||
if (absl::AsciiStrToLower(pair[0]) ==
|
||||
Wasm::Common::Http::Header::ContentType) {
|
||||
rule.content_type = pair[1];
|
||||
has_content_type = true;
|
||||
return true;
|
||||
}
|
||||
rule.headers.emplace_back(pair[0], pair[1]);
|
||||
return true;
|
||||
})) {
|
||||
LOG_WARN("failed to parse configuration for headers.");
|
||||
return false;
|
||||
}
|
||||
auto it = configuration.find("status_code");
|
||||
if (it != configuration.end()) {
|
||||
auto status_code = JsonValueAs<int64_t>(it.value());
|
||||
if (status_code.second != Wasm::Common::JsonParserResultDetail::OK) {
|
||||
LOG_WARN("cannot parse status code");
|
||||
return false;
|
||||
}
|
||||
rule.status_code = status_code.first.value();
|
||||
}
|
||||
it = configuration.find("body");
|
||||
if (it != configuration.end()) {
|
||||
auto body_string = JsonValueAs<std::string>(it.value());
|
||||
if (body_string.second != Wasm::Common::JsonParserResultDetail::OK) {
|
||||
LOG_WARN("cannot parse body");
|
||||
return false;
|
||||
}
|
||||
rule.body = body_string.first.value();
|
||||
}
|
||||
if (!rule.body.empty() && !has_content_type) {
|
||||
auto try_decode_json = Wasm::Common::JsonParse(rule.body);
|
||||
if (try_decode_json.has_value()) {
|
||||
rule.content_type = "application/json; charset=utf-8";
|
||||
// rule.headers.emplace_back(Wasm::Common::Http::Header::ContentType,
|
||||
// "application/json; charset=utf-8");
|
||||
} else {
|
||||
rule.content_type = "text/plain; charset=utf-8";
|
||||
// rule.headers.emplace_back(Wasm::Common::Http::Header::ContentType,
|
||||
// "text/plain; charset=utf-8");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FilterHeadersStatus PluginRootContext::onRequest(
|
||||
const CustomResponseConfigRule& rule) {
|
||||
if (!rule.enable_on_status.empty()) {
|
||||
return FilterHeadersStatus::Continue;
|
||||
}
|
||||
sendLocalResponse(rule.status_code, "", rule.body, rule.headers);
|
||||
return FilterHeadersStatus::StopIteration;
|
||||
}
|
||||
|
||||
FilterHeadersStatus PluginRootContext::onResponse(
|
||||
const CustomResponseConfigRule& rule) {
|
||||
GET_RESPONSE_HEADER_VIEW(":status", status_code);
|
||||
bool hit = false;
|
||||
for (const auto& status : rule.enable_on_status) {
|
||||
if (status_code == status) {
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hit) {
|
||||
return FilterHeadersStatus::Continue;
|
||||
}
|
||||
replaceResponseHeader(Wasm::Common::Http::Header::ContentType,
|
||||
rule.content_type);
|
||||
sendLocalResponse(rule.status_code, "", rule.body, rule.headers);
|
||||
return FilterHeadersStatus::StopIteration;
|
||||
}
|
||||
|
||||
bool PluginRootContext::onConfigure(size_t size) {
|
||||
// Parse configuration JSON string.
|
||||
if (size > 0 && !configure(size)) {
|
||||
LOG_WARN("configuration has errors initialization will not continue.");
|
||||
setInvalidConfig();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginRootContext::configure(size_t configuration_size) {
|
||||
auto configuration_data = getBufferBytes(WasmBufferType::PluginConfiguration,
|
||||
0, configuration_size);
|
||||
// Parse configuration JSON string.
|
||||
auto result = ::Wasm::Common::JsonParse(configuration_data->view());
|
||||
if (!result.has_value()) {
|
||||
LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ",
|
||||
configuration_data->view()));
|
||||
return false;
|
||||
}
|
||||
if (!parseRuleConfig(result.value())) {
|
||||
LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ",
|
||||
configuration_data->view()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) {
|
||||
auto* rootCtx = rootContext();
|
||||
return rootCtx->onHeaders(
|
||||
[rootCtx](const auto& config) { return rootCtx->onRequest(config); });
|
||||
}
|
||||
|
||||
FilterHeadersStatus PluginContext::onResponseHeaders(uint32_t, bool) {
|
||||
auto* rootCtx = rootContext();
|
||||
return rootCtx->onHeaders(
|
||||
[rootCtx](const auto& config) { return rootCtx->onResponse(config); });
|
||||
}
|
||||
|
||||
#ifdef NULL_PLUGIN
|
||||
|
||||
} // namespace custom_response
|
||||
} // namespace null_plugin
|
||||
} // namespace proxy_wasm
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user