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

Provide a way to encode and decode Protobuf in annotated services #3124

Merged
merged 25 commits into from
Nov 20, 2020

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Oct 21, 2020

Motivation:

Protocol Buffers is a universal serialization protocol. The usage is not limit to gRPC.
It could be used an HTTP payload for REST API.
It is also useful when building an HTTP proxy server in front of a gRPC server.

Modifications:

  • Add RequestConverterFunctionProvider that can load a custom RequestConverterFunction using SPI
  • Add @ConsumesProtobuf and @ProducesProtobuf annotations
  • Protobuf
    • Create armeria-protobuf module to use Protobuf in annotated services
    • Add ProtobufRequestConverterFunction that decodes Protocol Buffers or JSON body into Protobuf Message type
    • Add ProtobufResponseConverterFunction that encodes Protobuf Message(s) into Protocol Buffers or JSON
    • Add ProtobufRequestConverterFunctionProvider and ProtobufResponseConverterFunctionProvider to support Protobuf converters automatically via SPI
  • ScalaPB
    • Create armeria-scalapb_2.12 and armeria-scalapb_2.13 module to use ScalaPB in annotated services
    • Add ScalaPbRequestConverterFunction that decodes Protocol Buffers or JSON body into ScalaPB GenreatedMessage type
    • Add ScalaPbResponseConverterFunction that encodes ScalaPB GenerratedMesage(s) into Protocol Buffers or JSON
    • Add ScalaPbRequestConverterFunctionProvider and ScalaPbResponseConverterFunctionProvider to support ScalaPB converters automatically via SPI
  • These converters are applied only when the content-type is:
    • Protobuf - application/protobuf, application/x-protobuf or application/octet-stream
    • JSON - application/json; charset=utf-8 or subtype ends with +json

Result:

Motivation:

Protocol Buffers is an universal serialization protocol. The usage is not limit to gRPC.
It could be used an HTTP payload for REST API.
It is also useful when building an HTTP proxy server in front of a gRPC server.

Modifications:

- Add ProtobufRequestConverterFunction that decodes Protocol Buffers or JSON body into Protobuf Message type
- Add ProtobufResponseConverterFunction that encodes Protobuf Message(s) into Protocol Buffers or JSON body
- These converters are applied only when the content type is:
  - `application/protobuf`
  - `application/json; charset=utf-8`
  - Or subtype contains `protobuf` because there is no standard for protobuf content type.
    See https://stackoverflow.com/questions/30505408/what-is-the-correct-protobuf-content-type

Result:

- You can now encode and decode Protobuf Message using annotated service automatically.
- Closes line#3088
@ikhoon
Copy link
Contributor Author

ikhoon commented Oct 21, 2020

/cc @dpsoft

@codecov
Copy link

codecov bot commented Oct 21, 2020

Codecov Report

Merging #3124 (11b4702) into master (bd7fe49) will increase coverage by 0.02%.
The diff coverage is 74.80%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #3124      +/-   ##
============================================
+ Coverage     73.92%   73.95%   +0.02%     
- Complexity    12417    12587     +170     
============================================
  Files          1080     1098      +18     
  Lines         47380    47955     +575     
  Branches       5999     6094      +95     
============================================
+ Hits          35027    35463     +436     
- Misses         9269     9365      +96     
- Partials       3084     3127      +43     
Impacted Files Coverage Δ Complexity Δ
...a/com/linecorp/armeria/client/retry/RetryRule.java 69.69% <ø> (ø) 16.00 <0.00> (ø)
.../annotation/ByteArrayRequestConverterFunction.java 71.42% <ø> (ø) 3.00 <0.00> (ø)
...annotation/ByteArrayResponseConverterFunction.java 75.86% <ø> (ø) 11.00 <0.00> (ø)
...er/annotation/JacksonRequestConverterFunction.java 87.17% <ø> (ø) 15.00 <0.00> (ø)
...r/annotation/JacksonResponseConverterFunction.java 83.87% <ø> (ø) 13.00 <0.00> (ø)
...ver/annotation/StringRequestConverterFunction.java 75.00% <ø> (ø) 4.00 <0.00> (ø)
...er/annotation/StringResponseConverterFunction.java 78.12% <ø> (ø) 14.00 <0.00> (ø)
...in/example/armeria/server/annotated/kotlin/Main.kt 70.00% <ø> (ø) 0.00 <0.00> (ø)
.../example/armeria/contextpropagation/kotlin/Main.kt 0.00% <ø> (ø) 0.00 <0.00> (ø)
...2/ObservableResponseConverterFunctionProvider.java 84.00% <ø> (ø) 15.00 <0.00> (ø)
... and 91 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bd7fe49...11b4702. Read the comment docs.

@ikhoon ikhoon force-pushed the protobuf-response-converter branch from 324c2d6 to 07fca0c Compare October 23, 2020 09:58
* However a <a href="https://tools.ietf.org/html/rfc7159#section-5">JSON array</a> body
* could be converted into a {@link Collection} type. e.g, {@code List<Message>} and {@code Set<Message>}.
*/
public final class ProtobufRequestConverterFunction implements RequestConverterFunction {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we make a separate module so that the user doesn't have to bring gRPC dependencies to use this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought this feature would be interesting to some users who want to gRPC service and client already. Do we need armeria-protobuf? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure. 🤣 @anuraaga Do you have any suggestions?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry for late reply - it does seem strange to have this in gRPC package since it seems to have nothing to do with gRPC. Perhaps core with compileOnly dependency on protobuf so it's next to the other annotated service functionality? not sure

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the code is too small to create a seperate module. Let me move this code to core and add @UnstableApi in case we need armeria-protobuf later.

@minwoox minwoox added this to the 1.3.0 milestone Nov 6, 2020
Copy link
Contributor

@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.

Just nits.
Great job, @ikhoon!

}

private static boolean isProtobufMessage(Class<?> clazz) {
return Message.class.isAssignableFrom(clazz);
Copy link
Member

Choose a reason for hiding this comment

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

Message or MessageLite?

Copy link
Contributor Author

@ikhoon ikhoon Nov 9, 2020

Choose a reason for hiding this comment

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

Message class extends MessageLite. When building(deserializing) a message from protobuf, Message class is required to get Builder class. However, when serializing a message to protobuf, MessageLite is enough.
This inconsistency is able to confuse users. Let me just use Message instead of MessageLite.

Copy link
Collaborator

Choose a reason for hiding this comment

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

MessageLite is very rare in servers btw (though I remember an issue filed by someone that was actually using it don't remember the reasoning)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I remember an issue filed by someone that was actually using it don't remember the reasoning

Maybe #3085? He said it was caused by wrong dependencies.

@ikhoon ikhoon requested a review from minwoox November 11, 2020 03:13
Comment on lines 31 to 32
scalaVersions=2.13.3
defaultScalaVersions=2.13.2
Copy link
Member

Choose a reason for hiding this comment

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

Question: Why these properties have two different versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops... it was cruft. Actually, I removed gradle-scala-multiversion-plugin and add scalapb_2.12 and scalapb_2.13 because it is difficult to integrate gradle-scala-multiversion-plugin with Armeria's gradle scripts.
And we need a lot of workarounds and some of them are flaky. 😅

gradle/scripts/lib/java-publish.gradle Outdated Show resolved Hide resolved
gradle/scripts/lib/java-shade.gradle Outdated Show resolved Hide resolved
@ikhoon
Copy link
Contributor Author

ikhoon commented Nov 17, 2020

Finally, all builds are passed. 😆

Copy link
Contributor

@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.

Still LGTM 👍
(Do not know much about the Scala part 🤣 )

@@ -0,0 +1,46 @@
/*
* Copyright 2018 LINE Corporation
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😱

implementation "com.thesamet.scalapb:scalapb-runtime-grpc_2.13"
}


Copy link
Contributor

Choose a reason for hiding this comment

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

nit: two empty lines


// Run `scalafmt` to automatically format scala code from source sets
// https://github.com/alenkacz/gradle-scalafmt#tasks
project.ext.getLintTask().dependsOn tasks.checkScalafmt
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

How will we have to expose Scaladoc in our web site by the way?

@ikhoon
Copy link
Contributor Author

ikhoon commented Nov 19, 2020

How will we have to expose Scaladoc in our web site by the way?

How about adding a dedicated documentation page for ScalaPB? The page could have a simple introduction and a Scaladoc link.
I think we can add the page after migrating ScalaPB plugins from examples/examples/grpc-scala.

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

Great work, @ikhoon. ScalaPB users will love this.

@trustin
Copy link
Member

trustin commented Nov 19, 2020

@ikhoon Could you update the PR description?

@ikhoon
Copy link
Contributor Author

ikhoon commented Nov 20, 2020

@ikhoon Could you update the PR description?

Sure! The PR description is updated.

@trustin trustin merged commit 0605acd into line:master Nov 20, 2020
@ikhoon ikhoon deleted the protobuf-response-converter branch December 8, 2020 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Protocol Buffers codec for annoated service
4 participants