-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
grpc-json: handle google.api.HttpBody when building HTTP response #3793
Changes from all commits
ce46d96
aa8e4cf
ac4455b
fcd37be
da90483
59cf968
6dcd9a6
5aa346a
9f4739b
95778e8
e76880e
eff3c80
3aa68fb
4123018
085e967
d5c4ba9
784d789
c2dc9e8
e9d9b1e
c0d7c87
b45854a
d93f57e
b9a5e43
40d9b3f
9403723
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
|
||
#include "google/api/annotations.pb.h" | ||
#include "google/api/http.pb.h" | ||
#include "google/api/httpbody.pb.h" | ||
#include "grpc_transcoding/json_request_translator.h" | ||
#include "grpc_transcoding/path_matcher_utility.h" | ||
#include "grpc_transcoding/response_to_json_translator.h" | ||
|
@@ -222,6 +223,7 @@ Http::FilterHeadersStatus JsonTranscoderFilter::decodeHeaders(Http::HeaderMap& h | |
// just pass-through the request to upstream. | ||
return Http::FilterHeadersStatus::Continue; | ||
} | ||
has_http_body_output_ = !method_->server_streaming() && hasHttpBodyAsOutputType(); | ||
|
||
headers.removeContentLength(); | ||
headers.insertContentType().value().setReference(Http::Headers::get().ContentTypeValues.Grpc); | ||
|
@@ -340,6 +342,12 @@ Http::FilterDataStatus JsonTranscoderFilter::encodeData(Buffer::Instance& data, | |
return Http::FilterDataStatus::Continue; | ||
} | ||
|
||
// TODO(dio): Add support for streaming case. | ||
if (has_http_body_output_) { | ||
buildResponseFromHttpBodyOutput(*response_headers_, data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we assuming here that the entire HTTP response proto is available in a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, @htuch. It makes sense, it seems it needs to buffer the data. Will try to explore it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if we don't have enough data to decode for a single frame, we buffer it in here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, fair enough. How do we actually continue processing when we're done though? Looks like we're just always returning Http::FilterDataStatus::StopIterationAndBuffer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @htuch I think @dio already does this correctly, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dio can you add some test case to cover this? (one grpc frame split in two encodeData call) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lizan got it, makes sense, thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return Http::FilterDataStatus::StopIterationAndBuffer; | ||
} | ||
|
||
response_in_.move(data); | ||
|
||
if (end_stream) { | ||
|
@@ -415,6 +423,32 @@ bool JsonTranscoderFilter::readToBuffer(Protobuf::io::ZeroCopyInputStream& strea | |
return false; | ||
} | ||
|
||
void JsonTranscoderFilter::buildResponseFromHttpBodyOutput(Http::HeaderMap& response_headers, | ||
Buffer::Instance& data) { | ||
std::vector<Grpc::Frame> frames; | ||
decoder_.decode(data, frames); | ||
if (frames.empty()) { | ||
return; | ||
} | ||
|
||
google::api::HttpBody http_body; | ||
for (auto& frame : frames) { | ||
if (frame.length_ > 0) { | ||
Buffer::ZeroCopyInputStreamImpl stream(std::move(frame.data_)); | ||
http_body.ParseFromZeroCopyStream(&stream); | ||
const auto& body = http_body.data(); | ||
data.add(body); | ||
response_headers.insertContentType().value(http_body.content_type()); | ||
response_headers.insertContentLength().value(body.length()); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
bool JsonTranscoderFilter::hasHttpBodyAsOutputType() { | ||
return method_->output_type()->full_name() == google::api::HttpBody::descriptor()->full_name(); | ||
} | ||
|
||
} // namespace GrpcJsonTranscoder | ||
} // namespace HttpFilters | ||
} // namespace Extensions | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we keep doing this for every Google API proto we bring in, it will be quite verbose. I think it's outside the scope of this PR, but we should consider writing a Skylark macro to shrink the boilerplate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a TODO for this. I hope that's OK for now.