Skip to content

Commit

Permalink
json_to_metadata: apply on_missing for all rules for non-object json …
Browse files Browse the repository at this point in the history
…payload (envoyproxy#28935)

Signed-off-by: kuochunghsu <kuochunghsu@pinterest.com>
  • Loading branch information
JuniorHsu committed Aug 11, 2023
1 parent 473cb07 commit a4d1c85
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ comes from the owning HTTP connection manager.
:header: Name, Type, Description
:widths: 1, 1, 2

rq_success, Counter, Total requests that succeed to parse the json body
rq_success, Counter, Total requests that succeed to parse the json body. Note that a pure string or number body is treated as a successful json body which increases this counter.
rq_mismatched_content_type, Counter, Total requests that mismatch the content type
rq_no_body, Counter, Total requests without content body
rq_invalid_json_body, Counter, Total requests with invalid json body
13 changes: 13 additions & 0 deletions source/extensions/filters/http/json_to_metadata/filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,19 @@ void Filter::processBody(const Buffer::Instance* body, const Rules& rules,
}

Json::ObjectSharedPtr body_json = std::move(result.value());
// A pure string or number is considered a valid application/json body, but it is not a JSON
// object. Therefore, we treat this case as 'on_missing' for all rules in the absence of any
// key-value pairs to match.
if (!body_json) {
ENVOY_LOG(
debug,
"Apply on_missing for all rules on a valid application/json body but not a json object.");
handleAllOnMissing(rules, request_processing_finished_);
// This JSON body is valid and successfully parsed.
success.inc();
return;
}

StructMap struct_map;
for (const auto& rule : rules) {
const auto& keys = rule.keys_;
Expand Down
86 changes: 86 additions & 0 deletions test/extensions/filters/http/json_to_metadata/filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,40 @@ TEST_F(FilterTest, OnMissingForArray) {
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 0);
}

TEST_F(FilterTest, OnMissingSecondLayerString) {
initializeFilter(R"EOF(
request_rules:
rules:
- selectors:
- key: foo
- key: bar
on_present:
metadata_namespace: envoy.lb
key: baz
on_missing:
metadata_namespace: envoy.lb
key: version
value: "missing"
)EOF");
const std::string request_body =
R"delimiter(
{"foo": "James, James, Morrison, Morrison, weather-beaten, off-key"})delimiter";

const std::map<std::string, std::string> expected = {{"version", "missing"}};

EXPECT_EQ(Http::FilterHeadersStatus::StopIteration,
filter_->decodeHeaders(incoming_headers_, false));

EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_));
EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected)));
testRequestWithBody(request_body);

EXPECT_EQ(getCounterValue("json_to_metadata.rq_success"), 1);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_mismatched_content_type"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_no_body"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 0);
}

TEST_F(FilterTest, NoRequestContentType) {
initializeFilter(request_config_yaml_);

Expand Down Expand Up @@ -838,6 +872,58 @@ TEST_F(FilterTest, InvalidJsonPayload) {
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 1);
}

TEST_F(FilterTest, OnMissingQuotedString) {
initializeFilter(request_config_yaml_);
const std::string request_body = R"delimiter("")delimiter";
const std::map<std::string, std::string> expected = {{"version", "unknown"}};

EXPECT_EQ(Http::FilterHeadersStatus::StopIteration,
filter_->decodeHeaders(incoming_headers_, false));
EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_));
EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected)));
testRequestWithBody(request_body);

EXPECT_EQ(getCounterValue("json_to_metadata.rq_success"), 1);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_mismatched_content_type"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_no_body"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 0);
}

TEST_F(FilterTest, OnMissingQuotedJsonObject) {
initializeFilter(request_config_yaml_);
const std::string request_body =
R"delimiter("{\"model\": \"gpt-3.5-turbo\",\"temperature\": 0.2,\"stream\": false}")delimiter";
const std::map<std::string, std::string> expected = {{"version", "unknown"}};

EXPECT_EQ(Http::FilterHeadersStatus::StopIteration,
filter_->decodeHeaders(incoming_headers_, false));
EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_));
EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected)));
testRequestWithBody(request_body);

EXPECT_EQ(getCounterValue("json_to_metadata.rq_success"), 1);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_mismatched_content_type"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_no_body"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 0);
}

TEST_F(FilterTest, OnMissingPureNumber) {
initializeFilter(request_config_yaml_);
const std::string request_body = R"delimiter(5566)delimiter";
const std::map<std::string, std::string> expected = {{"version", "unknown"}};

EXPECT_EQ(Http::FilterHeadersStatus::StopIteration,
filter_->decodeHeaders(incoming_headers_, false));
EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_));
EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected)));
testRequestWithBody(request_body);

EXPECT_EQ(getCounterValue("json_to_metadata.rq_success"), 1);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_mismatched_content_type"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_no_body"), 0);
EXPECT_EQ(getCounterValue("json_to_metadata.rq_invalid_json_body"), 0);
}

// TODO(kuochunghsu): Planned to support trimming.
TEST_F(FilterTest, InvalidJsonForAdditionalPrefixSuffix) {
initializeFilter(request_config_yaml_);
Expand Down

0 comments on commit a4d1c85

Please sign in to comment.