Skip to content
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

Add support for alternative protobuf content types #4364

Merged

Conversation

mscheong01
Copy link
Contributor

@mscheong01 mscheong01 commented Jul 22, 2022

Motivation:
Armeria UnframedGrpcService doesn't support alternative protobuf content types

Modifications:

  • add application/x-protobuf and application/x-google-protobuf to MediaType
  • add isProtobuf() function
  • identify Protobuf contentType in UnframedGrpcService using isProtobuf()
  • respond with request-given protobuf content type for deframed response

Result:

Copy link
Contributor Author

@mscheong01 mscheong01 left a comment

Choose a reason for hiding this comment

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

PR is not yet ready, but I wanted some help along the way

Comment on lines 216 to 218
if (grpcMediaType.is(GrpcSerializationFormats.PROTO.mediaType())) {
unframedHeaders.contentType(MediaType.PROTOBUF);
} else if (grpcMediaType.is(GrpcSerializationFormats.JSON.mediaType())) {
unframedHeaders.contentType(MediaType.JSON_UTF_8);
}
unframedHeaders.contentType(originalContentType);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

for requests that use x-protobuf or x-google-protobuf, my guess is that they would expect the server to respond with the same content type. Would using the passed down originalContentType be safe? Also, should accept header be checked for the response protobuf content type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, should there be an else clause here? (for when grpcMediaType is null)

Copy link
Contributor

Choose a reason for hiding this comment

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

Would using the passed down originalContentType be safe? Also, should accept header be checked for the response protobuf content type?

If a suitable accept-header is specified, it makes sense to respect the value over content-type.

Also, should there be an else clause here? (for when grpcMediaType is null)

It looks like defensive code.
A normal response including an OK status should be returned with a proper content-type.

defaultHeaders = supportedSerializationFormats
.stream()
.map(format -> {
final ResponseHeadersBuilder builder =
ResponseHeaders
.builder(HttpStatus.OK)
.contentType(format.mediaType())

@mscheong01 mscheong01 changed the title add support for additional protobuf content types Add support for alternative protobuf content types Jul 22, 2022
@@ -1052,6 +1054,20 @@ public boolean isJson() {
return is(JSON) || subtype().endsWith("+json");
}

/**
* Returns {@code true} when the subtype is in [{@link MediaType#PROTOBUF}, {@link MediaType#X_PROTOBUF}, {@link MediaType#X_GOOGLE_PROTOBUF}].
Copy link
Contributor

Choose a reason for hiding this comment

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

We prefer to list elements using and or or.

Suggested change
* Returns {@code true} when the subtype is in [{@link MediaType#PROTOBUF}, {@link MediaType#X_PROTOBUF}, {@link MediaType#X_GOOGLE_PROTOBUF}].
* Returns {@code true} when the subtype is one of {@link MediaType#PROTOBUF}, {@link MediaType#X_PROTOBUF}, and {@link MediaType#X_GOOGLE_PROTOBUF}.

@ikhoon ikhoon added the defect label Jul 27, 2022
@ikhoon ikhoon modified the milestones: 1.19.0, 1.18.0 Jul 27, 2022
@mscheong01
Copy link
Contributor Author

Some fixes are made based on review & test breaks
I wondered if there should be an accept-header checking logic in httpjsontranscodingservice too but concluded that that sort of validation should be placed where it could be used more generally on all http req/resps if needed.
If the fix looks ok, I'll start on writing tests someday soon.

@codecov
Copy link

codecov bot commented Jul 27, 2022

Codecov Report

Merging #4364 (2b82ff4) into master (e2cc62a) will decrease coverage by 0.01%.
The diff coverage is 100.00%.

@@             Coverage Diff              @@
##             master    #4364      +/-   ##
============================================
- Coverage     73.84%   73.83%   -0.02%     
- Complexity    17702    17721      +19     
============================================
  Files          1505     1509       +4     
  Lines         66105    66201      +96     
  Branches       8309     8311       +2     
============================================
+ Hits          48816    48880      +64     
- Misses        13289    13320      +31     
- Partials       4000     4001       +1     
Impacted Files Coverage Δ
...in/java/com/linecorp/armeria/common/MediaType.java 97.06% <100.00%> (+0.02%) ⬆️
...meria/server/grpc/AbstractUnframedGrpcService.java 83.90% <100.00%> (+4.59%) ⬆️
...rmeria/server/grpc/HttpJsonTranscodingService.java 80.09% <100.00%> (+0.04%) ⬆️
...ecorp/armeria/server/grpc/UnframedGrpcService.java 82.50% <100.00%> (-0.43%) ⬇️
...ria/internal/client/dns/DelegatingDnsResolver.java 81.25% <0.00%> (-18.75%) ⬇️
...ecorp/armeria/server/LengthBasedServiceNaming.java 75.00% <0.00%> (-16.67%) ⬇️
...rmeria/internal/client/dns/CachingDnsResolver.java 78.37% <0.00%> (-8.11%) ⬇️
...a/internal/client/dns/SearchDomainDnsResolver.java 87.14% <0.00%> (-5.72%) ⬇️
...eria/internal/common/stream/StreamMessageUtil.java 61.11% <0.00%> (-5.56%) ⬇️
...rnal/common/stream/AbstractFixedStreamMessage.java 89.23% <0.00%> (-3.08%) ⬇️
... and 25 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@jrhee17 jrhee17 modified the milestones: 1.18.0, 1.19.0 Jul 28, 2022
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Changes look good to me so far 👍

If the fix looks ok, I'll start on writing tests someday soon.

Please do so 🙏

@@ -210,14 +212,15 @@ static void deframeAndRespond(ServiceRequestContext ctx,
}

final MediaType grpcMediaType = grpcResponse.contentType();
requireNonNull(grpcMediaType);
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't throw exceptions in this method since the exception isn't propagated to the response.

res.completeExceptionally(new NullPointerException("MediaType is undefined"));
return;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done fixing in addition to closing pooled object before the return

frameAndServe(unwrap(), ctx, grpcHeaders.build(),
clientRequest.content(), responseFuture, null);
frameAndServe(unwrap(), ctx, grpcHeaders.build(), clientRequest.content(),
responseFuture, null, responseContentType);
Copy link
Contributor

Choose a reason for hiding this comment

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

Incorrect indentation - can you check that your IDE is correctly formatted?
https://armeria.dev/community/developer-guide#setting-up-your-ide

@CLAassistant
Copy link

CLAassistant commented Aug 22, 2022

CLA assistant check
All committers have signed the CLA.

@mscheong01 mscheong01 force-pushed the support-additional-protobuf-content-types branch from 8f9b775 to 239f63e Compare August 22, 2022 15:01
Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

Thanks, @j-min5u! 😀
Left minor comments for testing.

…m:j-min5u/armeria into support-additional-protobuf-content-types
@mscheong01
Copy link
Contributor Author

some fixes were added based on review.
Plus, I missed this

If a suitable accept-header is specified, it makes sense to respect the value over content-type.

should I do it? I'd have to pass the request acceptheaders to deframeAndRespond, then set response contentType to that included in acceptHeader if the request contentType isn't in it. Seems like a pretty extreme case though 🤔

@minwoox
Copy link
Member

minwoox commented Aug 24, 2022

should I do it? I'd have to pass the request acceptheaders to deframeAndRespond, then set response contentType to that included in acceptHeader if the request contentType isn't in it. Seems like a pretty extreme case though 🤔

Here's what I thought:

  • For HttpJsonTranscodingService, we always set GrpcSerializationFormats.JSON and I think we should do that because we generate the content regardless of the accept header.
  • For UnframedGrpcService, how about just setting the same content type from the request headers?
    This is the link from OTLP and it says that The server MUST use the same “Content-Type” in the response as it received in the request. With the current approach in this PR, if the client forgets to set the accept header, the server violates the rule.

So, we could remove these lines completely and just pass the contentType to the frameAndServe method.

@mscheong01
Copy link
Contributor Author

So, we could remove these lines completely and just pass the contentType to the frameAndServe method.

I actually forgot about writing these lines 😅 removed them in commit a498c67

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Sorry for the late review. Left minor comments. 😉

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

👍 👍 👍
Thanks a lot for fixing this bug, @j-min5u! 😄

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Looks good to me! Thanks @j-min5u ! 🙇 👍 🙇

@minwoox minwoox merged commit e7da834 into line:master Sep 5, 2022
@minwoox
Copy link
Member

minwoox commented Sep 5, 2022

Our new hero, @j-min5u! 😆
What's your next PR? 😆

@mscheong01
Copy link
Contributor Author

@minwoox
Ha ha, I'm flattered 😄 But I'm going to military training in about a week, so nothing soon I'm afraid,,

@minwoox
Copy link
Member

minwoox commented Sep 7, 2022

Please, don't worry. 😄 Take care of yourself and see you later. 😄

heowc pushed a commit to heowc/armeria that referenced this pull request Sep 24, 2022
Motivation:
Armeria `UnframedGrpcService` doesn't support alternative protobuf content types

Modifications:
- add `application/x-protobuf` and `application/x-google-protobuf` to MediaType
- add isProtobuf() function
- identify Protobuf contentType in UnframedGrpcService using isProtobuf()
- respond with request-given protobuf content type for deframed response

Result:

- Closes line#4355
- Armeria `UnframedGrpcService` now supports `application/x-protobuf` and `application/x-google-protobuf` media types.
ikhoon pushed a commit that referenced this pull request Oct 11, 2022
Motivation:

Wrong response contenttype served for http json transcoding endpoints as result of #4364 

Modifications:

- since AbstractUnframedGrpcService$frameAndServe uses the provided responseContentType parameter directly as the resulting http contenttype, application/json should be provided in HttpJsonTranscodingService instead of application/grpc+json
- alternative would be to respond with the request contenttype ex) JSON -> JSON / JSON_UTF_8 -> JSON_UTF_8 

Result:

- Closes #4473
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support protobuf Content-Type not limited to "application/protobuf" in UnframedGrpcService
5 participants