Skip to content

json: Add protobuf wrapper for streamer - #47088

Open
filipcacky wants to merge 16 commits into
envoyproxy:mainfrom
filipcacky:proto_streamer
Open

json: Add protobuf wrapper for streamer#47088
filipcacky wants to merge 16 commits into
envoyproxy:mainfrom
filipcacky:proto_streamer

Conversation

@filipcacky

Copy link
Copy Markdown
Contributor

Commit Message: json: Add protobuf wrapper for streamer
Additional Description:

Adds Envoy::Json::MessageStreamer, which wraps a BufferStreamer::Level and serializes a Protobuf::Message by walking it with reflection.

The MessageStreamer supports redacting fields annotated with [(udpa.annotations.sensitive) = true] with the same output format as MessageUtil::redact.

Risk Level: Low
Testing: Added unit tests
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Fixes commit #PR or SHA]
[Optional Deprecated:]
[Optional API Considerations:]

Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
@repokitteh-read-only

Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #47088 was opened by filipcacky.

see: more, trace.

Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
@filipcacky

filipcacky commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

#46911 (comment)

Added emitMapEntry.

#46911 (comment)

Added struct Options and renamed to preserve_proto_field_names, consistent with Protobuf::util::JsonPrintOptions.

#46911 (comment)

Added examples for all types to config and cleaned up the redactions a bit, since they were all over the place. I'm thinking the redactions should probably copy over Redact test cases from test/common/protobuf/utility_test.cc.

cc @jmarantz

@filipcacky
filipcacky marked this pull request as ready for review August 31, 2026 16:04
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>

@jmarantz jmarantz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Mostly minor nits and requests for more comments.

/wait

Comment thread source/common/json/proto_streamer.h Outdated
public:
struct Options {
// Whether to emit a leading @type naming the message.
bool emit_type_url = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not putting the "_" suffix on struct member variables is consistent with the Google style guide.

However the Envoy style guide calls this out as an explicit deviation: https://github.com/envoyproxy/envoy/blob/main/STYLE.md#deviations-from-google-c-style-guidelines

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I raised #47030 few days ago specifically because of this :D Apparently both styles are acceptable. I dislike the suffix on public interfaces, but I'll fix. I suppose its better than complaining about it in an issue and then doing the same x)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's reasonable to object to the style guide. I happen to agree with you stylistically.

However that discussion should be in the form of a PR to change the style guide, without other functional changes :)

// Whether the keys are the proto field names or the lowerCamelCase ProtoJSON defaults to.
// https://protobuf.dev/programming-guides/json/#field-names
bool preserve_proto_field_names = false;
// Whether the fields the API marks sensitive are emitted, or replaced the way

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional nit: add blank link betweeen option member variables and their doc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread source/common/json/proto_streamer.h Outdated
bool ancestor_is_sensitive);

// Emits the redacted form of `field`'s value, a replacement for text and the type's default for
// anything else. Messages are handled by walking them, not redacting as whole.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand, but maybe it's worthwhile expanding on this a little.

e.g.

Leaf messages that are redacted are handled directly here. Messages with hiearchy (repeated fields and maps) are recursed fully, and the redactions occur only at leaves.

Is that right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! Fixed.

Comment thread source/common/json/proto_streamer.h Outdated
bool field_is_sensitive_{false};
};

void nextElement(Frame& frame);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add brief doc for nextElement. I think for nextElement and startField I'd like to know from looking at this header if it's also managing a stack owned by 'this'. Maybe call that out?

I assume it is, otherwise nextElement would be 'const'.

Note that these are both non-const methods and they take a mutable Frame objects. Can you just dive into the semantics a little more?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment and renamed to emitNextElement/emitNextField, also realized that next_field_ was interpreted as "current field" during nextElement, so i cleaned that up a bit too.

Both are entry points to emit Something, which may push on the stack, emitNextElement just handles repeated fields and maps.

Comment thread source/common/json/proto_streamer.h Outdated
BufferStreamer::Level& level);

// Emits one value of `field`, or pushes a frame for it. `index` is which element of a repeated
// field to emit, or -1 for a field that is not repeated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT of making a named constant for -1 here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to std::optional, emitValue then doesn't need the same comment about the sentinel and IMO looks nicer.

field.type() != Field::TYPE_BYTES;
}

ProtobufTypes::MessagePtr redactedCopy(const Protobuf::Message& message,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to copy the entire message to redact a field from it?

Couldn't we just stream out the message and redact at the leaf level?

Maybe this is because we have MessageUtil::redactAll we don't want to mess with, or maybe you have already gotten to the leaf level by the time this is called, in which case you can add comments :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment.

Everything is redacted leaf by leaf while streaming except some well-known types (the TODO message) and TypedStructs, which need to be reified via the type_url, so they go through MessageUtil::redact.

Comment thread source/common/json/proto_streamer.cc
Comment thread source/common/json/proto_streamer.cc
Comment thread source/common/json/proto_streamer.cc
@@ -0,0 +1,311 @@
#include "source/common/buffer/buffer_impl.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is the coverage looking for this test?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

96.4 %, added a test.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's missing? This is a whole bunch of new code, replacing complex old code, which is going to be hard to hand-validate so we are utterly reliant on unit tests covering every corner.

Come to think of it, for validation purposes, can you do differential fuzzing with protobuf's protojson generator?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point just the early return for next() if stack_ is empty, which IIRC was needed for the streamed request.

Fuzzing as in generating random configurations? Probably yes although I've no idea how yet. I did run every /config_dump test side by side in the other PR, should also do the same with MessageUtil::redact.

@jmarantz

jmarantz commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

For ensuring semantic coverage of the xDS data model I'd recommend @htuch and @adisuissa take a look at the proto streamer.

Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
…ed fields instead of -1 sentinel

Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
@filipcacky

Copy link
Copy Markdown
Contributor Author

/retest

@adisuissa

Copy link
Copy Markdown
Contributor

For ensuring semantic coverage of the xDS data model I'd recommend @htuch and @adisuissa take a look at the proto streamer.

Challenging to say if the same semantic coverage is acheived by both the proto-streamer and the current proto dumper. I don't see any red-flags, and assuming that there are enough tests covering different edge cases (specifically redacted fields), then it may be ok.
If one wants to take the extra mile, a fuzzer can be added to compare different config dumps outputs.

High-level comment: please add a tracking issue that has the results (that were published in #46911), and add the issue to the PR description.

Signed-off-by: Filip Cacky <filip.cacky@cdn77.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants