Add native support for Protobuf Lite

Lite already worked by using the protobuf project, but would bring in
extra dependencies that are not intended to work with lite. Although
protobuf is not yet providing a lite package on Maven Central, we will
be able to swap to it once it is available.

There isn't any new original code in the Java portion, except for a new
overload in ProtoUtils that accepts Message instead of MessageLite.
Depending on Message in ProtoUtils allows us to support extra features
out-of-the-box without any changes to the generated code. For example,
JSON encoding could be supported in this way if Marshaller is enhanced.

However, now codegen must be aware of Lite in order to choose with Util
class to use. That is new code.
This commit is contained in:
Eric Anderson 2016-03-22 11:31:36 -07:00
parent 1ff6ecbb21
commit 99a6d8de27
15 changed files with 817 additions and 256 deletions

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
TestService.java.txt binary
TestServiceLite.java.txt binary
TestServiceNano.java.txt binary

View File

@ -127,6 +127,11 @@ sourceSets {
}
}
compileTestJava {
// Protobuf-generated Lite produces quite a few warnings.
it.options.compilerArgs.removeAll(["-Xlint:unchecked", "-Xlint:rawtypes"])
}
protobuf {
protoc {
if (project.hasProperty('protoc')) {
@ -223,10 +228,11 @@ artifacts {
}
}
test.dependsOn('testGolden', 'testNanoGolden')
test.dependsOn('testGolden', 'testLiteGolden', 'testNanoGolden')
def configureTestTask(Task task, String suffix, String extraPackage) {
task.dependsOn "generateTest${suffix}Proto"
def configureTestTask(Task task, String suffix, String dep,
String extraPackage) {
task.dependsOn "generateTest${dep}Proto"
if (osdetector.os != 'windows') {
task.executable "diff"
} else {
@ -234,11 +240,13 @@ def configureTestTask(Task task, String suffix, String extraPackage) {
}
// File isn't found on Windows if last slash is forward-slash
def slash = System.getProperty("file.separator")
task.args "$buildDir/generated/source/proto/test${suffix}/grpc/io/grpc/testing/integration${extraPackage}${slash}TestServiceGrpc.java",
task.args "$buildDir/generated/source/proto/test${dep}/grpc/io/grpc/testing/integration${extraPackage}${slash}TestServiceGrpc.java",
"$projectDir/src/test/golden/TestService${suffix}.java.txt"
}
task testGolden(type: Exec)
task testLiteGolden(type: Exec)
task testNanoGolden(type: Exec)
configureTestTask(testGolden, '', '')
configureTestTask(testNanoGolden, 'Nano', '/nano')
configureTestTask(testGolden, '', '', '')
configureTestTask(testLiteGolden, 'Lite', '', '/lite')
configureTestTask(testNanoGolden, 'Nano', 'Nano', '/nano')

View File

@ -83,7 +83,7 @@ static inline string MessageFullJavaName(bool nano, const Descriptor* desc) {
static void PrintMethodFields(
const ServiceDescriptor* service, map<string, string>* vars, Printer* p,
bool generate_nano) {
ProtoFlavor flavor) {
p->Print("// Static method descriptors that strictly reflect the proto.\n");
(*vars)["service_name"] = service->name();
for (int i = 0; i < service->method_count(); ++i) {
@ -91,9 +91,9 @@ static void PrintMethodFields(
(*vars)["arg_in_id"] = to_string(2 * i);
(*vars)["arg_out_id"] = to_string(2 * i + 1);
(*vars)["method_name"] = method->name();
(*vars)["input_type"] = MessageFullJavaName(generate_nano,
(*vars)["input_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
method->input_type());
(*vars)["output_type"] = MessageFullJavaName(generate_nano,
(*vars)["output_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
method->output_type());
(*vars)["method_field_name"] = MethodPropertiesFieldName(method);
bool client_streaming = method->client_streaming();
@ -112,7 +112,7 @@ static void PrintMethodFields(
}
}
if (generate_nano) {
if (flavor == ProtoFlavor::NANO) {
// TODO(zsurocking): we're creating two NanoFactories for each method right now.
// We could instead create static NanoFactories and reuse them if some methods
// share the same request or response messages.
@ -133,6 +133,11 @@ static void PrintMethodFields(
" new NanoFactory<$output_type$>(ARG_OUT_$method_field_name$))\n"
" );\n");
} else {
if (flavor == ProtoFlavor::LITE) {
(*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils";
} else {
(*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
}
p->Print(
*vars,
"@$ExperimentalApi$\n"
@ -148,7 +153,7 @@ static void PrintMethodFields(
}
p->Print("\n");
if (generate_nano) {
if (flavor == ProtoFlavor::NANO) {
p->Print(
"private static final class NanoFactory<T extends com.google.protobuf.nano.MessageNano>\n"
" implements io.grpc.protobuf.nano.MessageNanoFactory<T> {\n"
@ -162,6 +167,7 @@ static void PrintMethodFields(
" public T newInstance() {\n"
" Object o;\n"
" switch (id) {\n");
bool generate_nano = true;
for (int i = 0; i < service->method_count(); ++i) {
const MethodDescriptor* method = service->method(i);
(*vars)["input_type"] = MessageFullJavaName(generate_nano,
@ -639,7 +645,7 @@ static void PrintBindServiceMethod(const ServiceDescriptor* service,
static void PrintService(const ServiceDescriptor* service,
map<string, string>* vars,
Printer* p,
bool generate_nano) {
ProtoFlavor flavor) {
(*vars)["service_name"] = service->name();
(*vars)["file_name"] = service->file()->name();
(*vars)["service_class_name"] = ServiceClassName(service);
@ -659,7 +665,7 @@ static void PrintService(const ServiceDescriptor* service,
"public static final String SERVICE_NAME = "
"\"$Package$$service_name$\";\n\n");
PrintMethodFields(service, vars, p, generate_nano);
PrintMethodFields(service, vars, p, flavor);
p->Print(
*vars,
@ -691,6 +697,7 @@ static void PrintService(const ServiceDescriptor* service,
p->Outdent();
p->Print("}\n\n");
bool generate_nano = flavor == ProtoFlavor::NANO;
PrintStub(service, vars, p, ASYNC_INTERFACE, generate_nano);
PrintStub(service, vars, p, BLOCKING_CLIENT_INTERFACE, generate_nano);
PrintStub(service, vars, p, FUTURE_CLIENT_INTERFACE, generate_nano);
@ -736,7 +743,7 @@ void PrintImports(Printer* p, bool generate_nano) {
void GenerateService(const ServiceDescriptor* service,
google::protobuf::io::ZeroCopyOutputStream* out,
bool generate_nano) {
ProtoFlavor flavor) {
// All non-generated classes must be referred by fully qualified names to
// avoid collision with generated classes.
map<string, string> vars;
@ -753,7 +760,6 @@ void GenerateService(const ServiceDescriptor* service,
vars["ImmutableList"] = "com.google.common.collect.ImmutableList";
vars["Collection"] = "java.util.Collection";
vars["MethodDescriptor"] = "io.grpc.MethodDescriptor";
vars["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
vars["NanoUtils"] = "io.grpc.protobuf.nano.NanoUtils";
vars["StreamObserver"] = "io.grpc.stub.StreamObserver";
vars["Iterator"] = "java.util.Iterator";
@ -766,20 +772,21 @@ void GenerateService(const ServiceDescriptor* service,
vars["ExperimentalApi"] = "io.grpc.ExperimentalApi";
Printer printer(out, '$');
string package_name = ServiceJavaPackage(service->file(), generate_nano);
string package_name = ServiceJavaPackage(service->file(),
flavor == ProtoFlavor::NANO);
if (!package_name.empty()) {
printer.Print(
"package $package_name$;\n\n",
"package_name", package_name);
}
PrintImports(&printer, generate_nano);
PrintImports(&printer, flavor == ProtoFlavor::NANO);
// Package string is used to fully qualify method names.
vars["Package"] = service->file()->package();
if (!vars["Package"].empty()) {
vars["Package"].append(".");
}
PrintService(service, &vars, &printer, generate_nano);
PrintService(service, &vars, &printer, flavor);
}
string ServiceJavaPackage(const FileDescriptor* file, bool nano) {

View File

@ -38,6 +38,10 @@ using namespace std;
namespace java_grpc_generator {
enum ProtoFlavor {
NORMAL, LITE, NANO
};
// Returns the package name of the gRPC services defined in the given file.
string ServiceJavaPackage(const google::protobuf::FileDescriptor* file, bool nano);
@ -48,7 +52,7 @@ string ServiceClassName(const google::protobuf::ServiceDescriptor* service);
// Writes the generated service interface into the given ZeroCopyOutputStream
void GenerateService(const google::protobuf::ServiceDescriptor* service,
google::protobuf::io::ZeroCopyOutputStream* out,
bool generate_nano);
ProtoFlavor flavor);
} // namespace java_grpc_generator

View File

@ -9,6 +9,7 @@
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/zero_copy_stream.h>
static string JavaPackageToDir(const string& package_name) {
@ -34,14 +35,20 @@ class JavaGrpcGenerator : public google::protobuf::compiler::CodeGenerator {
vector<pair<string, string> > options;
google::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
bool generate_nano = false;
java_grpc_generator::ProtoFlavor flavor =
java_grpc_generator::ProtoFlavor::NORMAL;
if (file->options().optimize_for() ==
google::protobuf::FileOptions::LITE_RUNTIME) {
flavor = java_grpc_generator::ProtoFlavor::LITE;
}
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "nano" && options[i].second == "true") {
generate_nano = true;
flavor = java_grpc_generator::ProtoFlavor::NANO;
}
}
string package_name = java_grpc_generator::ServiceJavaPackage(file, generate_nano);
string package_name = java_grpc_generator::ServiceJavaPackage(
file, flavor == java_grpc_generator::ProtoFlavor::NANO);
string package_filename = JavaPackageToDir(package_name);
for (int i = 0; i < file->service_count(); ++i) {
const google::protobuf::ServiceDescriptor* service = file->service(i);
@ -49,7 +56,7 @@ class JavaGrpcGenerator : public google::protobuf::compiler::CodeGenerator {
+ java_grpc_generator::ServiceClassName(service) + ".java";
std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output(
context->Open(filename));
java_grpc_generator::GenerateService(service, output.get(), generate_nano);
java_grpc_generator::GenerateService(service, output.get(), flavor);
}
return true;
}

View File

@ -0,0 +1,323 @@
package io.grpc.testing.integration.lite;
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
import static io.grpc.stub.ClientCalls.futureUnaryCall;
import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
@javax.annotation.Generated(
value = "by gRPC proto compiler",
comments = "Source: test_lite.proto")
public class TestServiceGrpc {
private TestServiceGrpc() {}
public static final String SERVICE_NAME = "grpc.testing.lite.TestService";
// Static method descriptors that strictly reflect the proto.
@io.grpc.ExperimentalApi
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.SimpleRequest,
io.grpc.testing.integration.lite.TestLite.SimpleResponse> METHOD_UNARY_CALL =
io.grpc.MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.UNARY,
generateFullMethodName(
"grpc.testing.lite.TestService", "UnaryCall"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.SimpleRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.SimpleResponse.getDefaultInstance()));
@io.grpc.ExperimentalApi
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_STREAMING_OUTPUT_CALL =
io.grpc.MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING,
generateFullMethodName(
"grpc.testing.lite.TestService", "StreamingOutputCall"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
@io.grpc.ExperimentalApi
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> METHOD_STREAMING_INPUT_CALL =
io.grpc.MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING,
generateFullMethodName(
"grpc.testing.lite.TestService", "StreamingInputCall"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse.getDefaultInstance()));
@io.grpc.ExperimentalApi
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_FULL_BIDI_CALL =
io.grpc.MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING,
generateFullMethodName(
"grpc.testing.lite.TestService", "FullBidiCall"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
@io.grpc.ExperimentalApi
public static final io.grpc.MethodDescriptor<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> METHOD_HALF_BIDI_CALL =
io.grpc.MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING,
generateFullMethodName(
"grpc.testing.lite.TestService", "HalfBidiCall"),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest.getDefaultInstance()),
io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse.getDefaultInstance()));
public static TestServiceStub newStub(io.grpc.Channel channel) {
return new TestServiceStub(channel);
}
public static TestServiceBlockingStub newBlockingStub(
io.grpc.Channel channel) {
return new TestServiceBlockingStub(channel);
}
public static TestServiceFutureStub newFutureStub(
io.grpc.Channel channel) {
return new TestServiceFutureStub(channel);
}
public static interface TestService {
public void unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request,
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse> responseObserver);
public void streamingOutputCall(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request,
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest> streamingInputCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> responseObserver);
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> fullBidiCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> halfBidiCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver);
}
public static interface TestServiceBlockingClient {
public io.grpc.testing.integration.lite.TestLite.SimpleResponse unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request);
public java.util.Iterator<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> streamingOutputCall(
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request);
}
public static interface TestServiceFutureClient {
public com.google.common.util.concurrent.ListenableFuture<io.grpc.testing.integration.lite.TestLite.SimpleResponse> unaryCall(
io.grpc.testing.integration.lite.TestLite.SimpleRequest request);
}
public static class TestServiceStub extends io.grpc.stub.AbstractStub<TestServiceStub>
implements TestService {
private TestServiceStub(io.grpc.Channel channel) {
super(channel);
}
private TestServiceStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected TestServiceStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new TestServiceStub(channel, callOptions);
}
@java.lang.Override
public void unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request,
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse> responseObserver) {
asyncUnaryCall(
getChannel().newCall(METHOD_UNARY_CALL, getCallOptions()), request, responseObserver);
}
@java.lang.Override
public void streamingOutputCall(io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request,
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
asyncServerStreamingCall(
getChannel().newCall(METHOD_STREAMING_OUTPUT_CALL, getCallOptions()), request, responseObserver);
}
@java.lang.Override
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest> streamingInputCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse> responseObserver) {
return asyncClientStreamingCall(
getChannel().newCall(METHOD_STREAMING_INPUT_CALL, getCallOptions()), responseObserver);
}
@java.lang.Override
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> fullBidiCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
return asyncBidiStreamingCall(
getChannel().newCall(METHOD_FULL_BIDI_CALL, getCallOptions()), responseObserver);
}
@java.lang.Override
public io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest> halfBidiCall(
io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> responseObserver) {
return asyncBidiStreamingCall(
getChannel().newCall(METHOD_HALF_BIDI_CALL, getCallOptions()), responseObserver);
}
}
public static class TestServiceBlockingStub extends io.grpc.stub.AbstractStub<TestServiceBlockingStub>
implements TestServiceBlockingClient {
private TestServiceBlockingStub(io.grpc.Channel channel) {
super(channel);
}
private TestServiceBlockingStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected TestServiceBlockingStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new TestServiceBlockingStub(channel, callOptions);
}
@java.lang.Override
public io.grpc.testing.integration.lite.TestLite.SimpleResponse unaryCall(io.grpc.testing.integration.lite.TestLite.SimpleRequest request) {
return blockingUnaryCall(
getChannel(), METHOD_UNARY_CALL, getCallOptions(), request);
}
@java.lang.Override
public java.util.Iterator<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse> streamingOutputCall(
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest request) {
return blockingServerStreamingCall(
getChannel(), METHOD_STREAMING_OUTPUT_CALL, getCallOptions(), request);
}
}
public static class TestServiceFutureStub extends io.grpc.stub.AbstractStub<TestServiceFutureStub>
implements TestServiceFutureClient {
private TestServiceFutureStub(io.grpc.Channel channel) {
super(channel);
}
private TestServiceFutureStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected TestServiceFutureStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new TestServiceFutureStub(channel, callOptions);
}
@java.lang.Override
public com.google.common.util.concurrent.ListenableFuture<io.grpc.testing.integration.lite.TestLite.SimpleResponse> unaryCall(
io.grpc.testing.integration.lite.TestLite.SimpleRequest request) {
return futureUnaryCall(
getChannel().newCall(METHOD_UNARY_CALL, getCallOptions()), request);
}
}
private static final int METHODID_UNARY_CALL = 0;
private static final int METHODID_STREAMING_OUTPUT_CALL = 1;
private static final int METHODID_STREAMING_INPUT_CALL = 2;
private static final int METHODID_FULL_BIDI_CALL = 3;
private static final int METHODID_HALF_BIDI_CALL = 4;
private static class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
private final TestService serviceImpl;
private final int methodId;
public MethodHandlers(TestService serviceImpl, int methodId) {
this.serviceImpl = serviceImpl;
this.methodId = methodId;
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_UNARY_CALL:
serviceImpl.unaryCall((io.grpc.testing.integration.lite.TestLite.SimpleRequest) request,
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.SimpleResponse>) responseObserver);
break;
case METHODID_STREAMING_OUTPUT_CALL:
serviceImpl.streamingOutputCall((io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest) request,
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
break;
default:
throw new AssertionError();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public io.grpc.stub.StreamObserver<Req> invoke(
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_STREAMING_INPUT_CALL:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.streamingInputCall(
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse>) responseObserver);
case METHODID_FULL_BIDI_CALL:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.fullBidiCall(
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
case METHODID_HALF_BIDI_CALL:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.halfBidiCall(
(io.grpc.stub.StreamObserver<io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>) responseObserver);
default:
throw new AssertionError();
}
}
}
public static io.grpc.ServerServiceDefinition bindService(
final TestService serviceImpl) {
return io.grpc.ServerServiceDefinition.builder(SERVICE_NAME)
.addMethod(
METHOD_UNARY_CALL,
asyncUnaryCall(
new MethodHandlers<
io.grpc.testing.integration.lite.TestLite.SimpleRequest,
io.grpc.testing.integration.lite.TestLite.SimpleResponse>(
serviceImpl, METHODID_UNARY_CALL)))
.addMethod(
METHOD_STREAMING_OUTPUT_CALL,
asyncServerStreamingCall(
new MethodHandlers<
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
serviceImpl, METHODID_STREAMING_OUTPUT_CALL)))
.addMethod(
METHOD_STREAMING_INPUT_CALL,
asyncClientStreamingCall(
new MethodHandlers<
io.grpc.testing.integration.lite.TestLite.StreamingInputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingInputCallResponse>(
serviceImpl, METHODID_STREAMING_INPUT_CALL)))
.addMethod(
METHOD_FULL_BIDI_CALL,
asyncBidiStreamingCall(
new MethodHandlers<
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
serviceImpl, METHODID_FULL_BIDI_CALL)))
.addMethod(
METHOD_HALF_BIDI_CALL,
asyncBidiStreamingCall(
new MethodHandlers<
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallRequest,
io.grpc.testing.integration.lite.TestLite.StreamingOutputCallResponse>(
serviceImpl, METHODID_HALF_BIDI_CALL)))
.build();
}
}

View File

@ -0,0 +1,54 @@
// A simple service definition for testing the protoc plugin.
syntax = "proto2";
package grpc.testing.lite;
option java_package = "io.grpc.testing.integration.lite";
option optimize_for = LITE_RUNTIME;
message SimpleRequest {
}
message SimpleResponse {
}
message StreamingInputCallRequest {
}
message StreamingInputCallResponse {
}
message StreamingOutputCallRequest {
}
message StreamingOutputCallResponse {
}
service TestService {
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full bidirectionality.
rpc FullBidiCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfBidiCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}

View File

@ -0,0 +1,15 @@
plugins {
id "be.insaneprogramming.gradle.animalsniffer" version "1.4.0"
}
description = 'gRPC: Protobuf Lite'
dependencies {
compile project(':grpc-core'),
libraries.protobuf,
libraries.guava
}
animalsniffer {
signature = "org.codehaus.mojo.signature:java16:+@signature"
}

View File

@ -29,7 +29,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.grpc.protobuf;
package io.grpc.protobuf.lite;
import com.google.common.io.ByteStreams;
import com.google.protobuf.CodedOutputStream;

View File

@ -0,0 +1,136 @@
/*
* Copyright 2014, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.grpc.protobuf.lite;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
import io.grpc.ExperimentalApi;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.Status;
import java.io.InputStream;
/**
* Utility methods for using protobuf with grpc.
*/
@ExperimentalApi("Experimental until Lite is stable in protobuf")
public class ProtoLiteUtils {
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
public static <T extends MessageLite> Marshaller<T> marshaller(final T defaultInstance) {
@SuppressWarnings("unchecked")
final Parser<T> parser = (Parser<T>) defaultInstance.getParserForType();
return new Marshaller<T>() {
@Override
public InputStream stream(T value) {
return new ProtoInputStream(value, parser);
}
@Override
public T parse(InputStream stream) {
if (stream instanceof ProtoInputStream) {
ProtoInputStream protoStream = (ProtoInputStream) stream;
// Optimization for in-memory transport. Returning provided object is safe since protobufs
// are immutable.
//
// However, we can't assume the types match, so we have to verify the parser matches.
// Today the parser is always the same for a given proto, but that isn't guaranteed. Even
// if not, using the same MethodDescriptor would ensure the parser matches and permit us
// to enable this optimization.
if (protoStream.parser() == parser) {
try {
@SuppressWarnings("unchecked")
T message = (T) ((ProtoInputStream) stream).message();
return message;
} catch (IllegalStateException ex) {
// Stream must have been read from, which is a strange state. Since the point of this
// optimization is to be transparent, instead of throwing an error we'll continue,
// even though it seems likely there's a bug.
}
}
}
try {
return parseFrom(stream);
} catch (InvalidProtocolBufferException ipbe) {
throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence")
.withCause(ipbe).asRuntimeException();
}
}
private T parseFrom(InputStream stream) throws InvalidProtocolBufferException {
// Pre-create the CodedInputStream so that we can remove the size limit restriction
// when parsing.
CodedInputStream codedInput = CodedInputStream.newInstance(stream);
codedInput.setSizeLimit(Integer.MAX_VALUE);
T message = parser.parseFrom(codedInput);
try {
codedInput.checkLastTagWas(0);
return message;
} catch (InvalidProtocolBufferException e) {
e.setUnfinishedMessage(message);
throw e;
}
}
};
}
/**
* Produce a metadata marshaller for a protobuf type.
*/
public static <T extends MessageLite> Metadata.BinaryMarshaller<T> metadataMarshaller(
final T instance) {
return new Metadata.BinaryMarshaller<T>() {
@Override
public byte[] toBytes(T value) {
return value.toByteArray();
}
@Override
@SuppressWarnings("unchecked")
public T parseBytes(byte[] serialized) {
try {
return (T) instance.getParserForType().parseFrom(serialized);
} catch (InvalidProtocolBufferException ipbe) {
throw new IllegalArgumentException(ipbe);
}
}
};
}
private ProtoLiteUtils() {
}
}

View File

@ -0,0 +1,214 @@
/*
* Copyright 2015, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.grpc.protobuf.lite;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
import com.google.protobuf.Empty;
import com.google.protobuf.Enum;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Type;
import io.grpc.Drainable;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/** Unit tests for {@link ProtoLiteUtils}. */
@RunWith(JUnit4.class)
public class ProtoLiteUtilsTest {
private Marshaller<Type> marshaller = ProtoLiteUtils.marshaller(Type.getDefaultInstance());
private Type proto = Type.newBuilder().setName("name").build();
@Test
public void testPassthrough() {
assertSame(proto, marshaller.parse(marshaller.stream(proto)));
}
@Test
public void testRoundtrip() throws Exception {
InputStream is = marshaller.stream(proto);
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
assertEquals(proto, marshaller.parse(is));
}
@Test
public void testInvalidatedMessage() throws Exception {
InputStream is = marshaller.stream(proto);
// Invalidates message, and drains all bytes
ByteStreams.toByteArray(is);
try {
((ProtoInputStream) is).message();
fail("Expected exception");
} catch (IllegalStateException ex) {
// expected
}
// Zero bytes is the default message
assertEquals(Type.getDefaultInstance(), marshaller.parse(is));
}
@Test
public void parseInvalid() throws Exception {
InputStream is = new ByteArrayInputStream(new byte[] {-127});
try {
marshaller.parse(is);
fail("Expected exception");
} catch (StatusRuntimeException ex) {
assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode());
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
}
}
@Test
public void testMismatch() throws Exception {
Marshaller<Enum> enumMarshaller = ProtoLiteUtils.marshaller(Enum.getDefaultInstance());
// Enum's name and Type's name are both strings with tag 1.
Enum altProto = Enum.newBuilder().setName(proto.getName()).build();
assertEquals(proto, marshaller.parse(enumMarshaller.stream(altProto)));
}
@Test
public void marshallerShouldNotLimitProtoSize() throws Exception {
// The default limit is 64MB. Using a larger proto to verify that the limit is not enforced.
byte[] bigName = new byte[70 * 1024 * 1024];
Arrays.fill(bigName, (byte) 32);
proto = Type.newBuilder().setNameBytes(ByteString.copyFrom(bigName)).build();
// Just perform a round trip to verify that it works.
testRoundtrip();
}
@Test
public void testAvailable() throws Exception {
InputStream is = marshaller.stream(proto);
assertEquals(proto.getSerializedSize(), is.available());
is.read();
assertEquals(proto.getSerializedSize() - 1, is.available());
while (is.read() != -1) {}
assertEquals(-1, is.read());
assertEquals(0, is.available());
}
@Test
public void testEmpty() throws IOException {
Marshaller<Empty> marshaller = ProtoLiteUtils.marshaller(Empty.getDefaultInstance());
InputStream is = marshaller.stream(Empty.getDefaultInstance());
assertEquals(0, is.available());
byte[] b = new byte[10];
assertEquals(-1, is.read(b));
assertArrayEquals(new byte[10], b);
// Do the same thing again, because the internal state may be different
assertEquals(-1, is.read(b));
assertArrayEquals(new byte[10], b);
assertEquals(-1, is.read());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_all() throws Exception {
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
InputStream is = marshaller.stream(proto);
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int drained = d.drainTo(baos);
assertEquals(baos.size(), drained);
assertArrayEquals(golden, baos.toByteArray());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_partial() throws Exception {
final byte[] golden;
{
InputStream is = marshaller.stream(proto);
is.read();
golden = ByteStreams.toByteArray(is);
}
InputStream is = marshaller.stream(proto);
is.read();
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int drained = d.drainTo(baos);
assertEquals(baos.size(), drained);
assertArrayEquals(golden, baos.toByteArray());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_none() throws Exception {
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
InputStream is = marshaller.stream(proto);
ByteStreams.toByteArray(is);
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertEquals(0, d.drainTo(baos));
assertArrayEquals(new byte[0], baos.toByteArray());
assertEquals(0, is.available());
}
@Test
public void metadataMarshaller_roundtrip() {
Metadata.BinaryMarshaller<Type> metadataMarshaller =
ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance());
assertEquals(proto, metadataMarshaller.parseBytes(metadataMarshaller.toBytes(proto)));
}
@Test
public void metadataMarshaller_invalid() {
Metadata.BinaryMarshaller<Type> metadataMarshaller =
ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance());
try {
metadataMarshaller.parseBytes(new byte[] {-127});
fail("Expected exception");
} catch (IllegalArgumentException ex) {
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
}
}
}

View File

@ -6,6 +6,7 @@ description = 'gRPC: Protobuf'
dependencies {
compile project(':grpc-core'),
project(':grpc-protobuf-lite'),
libraries.protobuf,
libraries.guava,
libraries.protobuf_util

View File

@ -31,13 +31,10 @@
package io.grpc.protobuf;
import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.Message.Builder;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.util.JsonFormat.Printer;
@ -45,6 +42,7 @@ import io.grpc.ExperimentalApi;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.Status;
import io.grpc.protobuf.lite.ProtoLiteUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -58,63 +56,19 @@ import java.nio.charset.Charset;
*/
public class ProtoUtils {
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
/**
* Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}.
*
* @deprecated Use ProtoLiteUtils.marshaller() or Message-based marshaller() instead
*/
@Deprecated
public static <T extends MessageLite> Marshaller<T> marshaller(final T defaultInstance) {
@SuppressWarnings("unchecked")
final Parser<T> parser = (Parser<T>) defaultInstance.getParserForType();
return new Marshaller<T>() {
@Override
public InputStream stream(T value) {
return new ProtoInputStream(value, parser);
}
return ProtoLiteUtils.marshaller(defaultInstance);
}
@Override
public T parse(InputStream stream) {
if (stream instanceof ProtoInputStream) {
ProtoInputStream protoStream = (ProtoInputStream) stream;
// Optimization for in-memory transport. Returning provided object is safe since protobufs
// are immutable.
//
// However, we can't assume the types match, so we have to verify the parser matches.
// Today the parser is always the same for a given proto, but that isn't guaranteed. Even
// if not, using the same MethodDescriptor would ensure the parser matches and permit us
// to enable this optimization.
if (protoStream.parser() == parser) {
try {
@SuppressWarnings("unchecked")
T message = (T) ((ProtoInputStream) stream).message();
return message;
} catch (IllegalStateException ex) {
// Stream must have been read from, which is a strange state. Since the point of this
// optimization is to be transparent, instead of throwing an error we'll continue,
// even though it seems likely there's a bug.
}
}
}
try {
return parseFrom(stream);
} catch (InvalidProtocolBufferException ipbe) {
throw Status.INTERNAL.withDescription("Invalid protobuf byte sequence")
.withCause(ipbe).asRuntimeException();
}
}
private T parseFrom(InputStream stream) throws InvalidProtocolBufferException {
// Pre-create the CodedInputStream so that we can remove the size limit restriction
// when parsing.
CodedInputStream codedInput = CodedInputStream.newInstance(stream);
codedInput.setSizeLimit(Integer.MAX_VALUE);
T message = parser.parseFrom(codedInput);
try {
codedInput.checkLastTagWas(0);
return message;
} catch (InvalidProtocolBufferException e) {
e.setUnfinishedMessage(message);
throw e;
}
}
};
/** Create a {@code Marshaller} for protos of the same type as {@code defaultInstance}. */
public static <T extends Message> Marshaller<T> marshaller(final T defaultInstance) {
return ProtoLiteUtils.marshaller(defaultInstance);
}
/**
@ -170,27 +124,7 @@ public class ProtoUtils {
public static <T extends Message> Metadata.Key<T> keyForProto(T instance) {
return Metadata.Key.of(
instance.getDescriptorForType().getFullName() + Metadata.BINARY_HEADER_SUFFIX,
keyMarshaller(instance));
}
@VisibleForTesting
static <T extends MessageLite> Metadata.BinaryMarshaller<T> keyMarshaller(final T instance) {
return new Metadata.BinaryMarshaller<T>() {
@Override
public byte[] toBytes(T value) {
return value.toByteArray();
}
@Override
@SuppressWarnings("unchecked")
public T parseBytes(byte[] serialized) {
try {
return (T) instance.getParserForType().parseFrom(serialized);
} catch (InvalidProtocolBufferException ipbe) {
throw new IllegalArgumentException(ipbe);
}
}
};
ProtoLiteUtils.metadataMarshaller(instance));
}
private ProtoUtils() {

View File

@ -31,166 +31,42 @@
package io.grpc.protobuf;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
import com.google.protobuf.Empty;
import com.google.protobuf.Enum;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Type;
import io.grpc.Drainable;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/** Unit tests for {@link ProtoUtils}. */
@RunWith(JUnit4.class)
public class ProtoUtilsTest {
private Marshaller<Type> marshaller = ProtoUtils.marshaller(Type.getDefaultInstance());
private Type proto = Type.newBuilder().setName("name").build();
@Test
public void testPassthrough() {
assertSame(proto, marshaller.parse(marshaller.stream(proto)));
}
@Test
public void testRoundtrip() throws Exception {
Marshaller<Type> marshaller = ProtoUtils.marshaller(Type.getDefaultInstance());
InputStream is = marshaller.stream(proto);
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
assertEquals(proto, marshaller.parse(is));
}
@Test
public void testInvalidatedMessage() throws Exception {
@Deprecated
public void testRoundtripLite() throws Exception {
Marshaller<MessageLite> marshaller
= ProtoUtils.marshaller((MessageLite) Type.getDefaultInstance());
InputStream is = marshaller.stream(proto);
// Invalidates message, and drains all bytes
ByteStreams.toByteArray(is);
try {
((ProtoInputStream) is).message();
fail("Expected exception");
} catch (IllegalStateException ex) {
// expected
}
// Zero bytes is the default message
assertEquals(Type.getDefaultInstance(), marshaller.parse(is));
}
@Test
public void parseInvalid() throws Exception {
InputStream is = new ByteArrayInputStream(new byte[] {-127});
try {
marshaller.parse(is);
fail("Expected exception");
} catch (StatusRuntimeException ex) {
assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode());
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
}
}
@Test
public void testMismatch() throws Exception {
Marshaller<Enum> enumMarshaller = ProtoUtils.marshaller(Enum.getDefaultInstance());
// Enum's name and Type's name are both strings with tag 1.
Enum altProto = Enum.newBuilder().setName(proto.getName()).build();
assertEquals(proto, marshaller.parse(enumMarshaller.stream(altProto)));
}
@Test
public void marshallerShouldNotLimitProtoSize() throws Exception {
// The default limit is 64MB. Using a larger proto to verify that the limit is not enforced.
byte[] bigName = new byte[70 * 1024 * 1024];
Arrays.fill(bigName, (byte) 32);
proto = Type.newBuilder().setNameBytes(ByteString.copyFrom(bigName)).build();
// Just perform a round trip to verify that it works.
testRoundtrip();
}
@Test
public void testAvailable() throws Exception {
InputStream is = marshaller.stream(proto);
assertEquals(proto.getSerializedSize(), is.available());
is.read();
assertEquals(proto.getSerializedSize() - 1, is.available());
while (is.read() != -1) {}
assertEquals(-1, is.read());
assertEquals(0, is.available());
}
@Test
public void testEmpty() throws IOException {
Marshaller<Empty> marshaller = ProtoUtils.marshaller(Empty.getDefaultInstance());
InputStream is = marshaller.stream(Empty.getDefaultInstance());
assertEquals(0, is.available());
byte[] b = new byte[10];
assertEquals(-1, is.read(b));
assertArrayEquals(new byte[10], b);
// Do the same thing again, because the internal state may be different
assertEquals(-1, is.read(b));
assertArrayEquals(new byte[10], b);
assertEquals(-1, is.read());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_all() throws Exception {
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
InputStream is = marshaller.stream(proto);
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int drained = d.drainTo(baos);
assertEquals(baos.size(), drained);
assertArrayEquals(golden, baos.toByteArray());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_partial() throws Exception {
final byte[] golden;
{
InputStream is = marshaller.stream(proto);
is.read();
golden = ByteStreams.toByteArray(is);
}
InputStream is = marshaller.stream(proto);
is.read();
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int drained = d.drainTo(baos);
assertEquals(baos.size(), drained);
assertArrayEquals(golden, baos.toByteArray());
assertEquals(0, is.available());
}
@Test
public void testDrainTo_none() throws Exception {
byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto));
InputStream is = marshaller.stream(proto);
ByteStreams.toByteArray(is);
Drainable d = (Drainable) is;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
assertEquals(0, d.drainTo(baos));
assertArrayEquals(new byte[0], baos.toByteArray());
assertEquals(0, is.available());
is = new ByteArrayInputStream(ByteStreams.toByteArray(is));
assertEquals(proto, marshaller.parse(is));
}
@Test
@ -198,25 +74,4 @@ public class ProtoUtilsTest {
assertEquals("google.protobuf.Type-bin",
ProtoUtils.keyForProto(Type.getDefaultInstance()).originalName());
}
@Test
public void keyMarshaller_roundtrip() {
Metadata.BinaryMarshaller<Type> keyMarshaller =
ProtoUtils.keyMarshaller(Type.getDefaultInstance());
assertEquals(proto, keyMarshaller.parseBytes(keyMarshaller.toBytes(proto)));
}
@Test
public void keyMarshaller_invalid() {
Metadata.BinaryMarshaller<Type> keyMarshaller =
ProtoUtils.keyMarshaller(Type.getDefaultInstance());
try {
keyMarshaller.parseBytes(new byte[] {-127});
fail("Expected exception");
} catch (IllegalArgumentException ex) {
assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage());
}
}
}

View File

@ -4,6 +4,7 @@ include ":grpc-stub"
include ":grpc-auth"
include ":grpc-okhttp"
include ":grpc-protobuf"
include ":grpc-protobuf-lite"
include ":grpc-protobuf-nano"
include ":grpc-netty"
include ":grpc-grpclb"
@ -18,6 +19,7 @@ project(':grpc-stub').projectDir = "$rootDir/stub" as File
project(':grpc-auth').projectDir = "$rootDir/auth" as File
project(':grpc-okhttp').projectDir = "$rootDir/okhttp" as File
project(':grpc-protobuf').projectDir = "$rootDir/protobuf" as File
project(':grpc-protobuf-lite').projectDir = "$rootDir/protobuf-lite" as File
project(':grpc-protobuf-nano').projectDir = "$rootDir/protobuf-nano" as File
project(':grpc-netty').projectDir = "$rootDir/netty" as File
project(':grpc-grpclb').projectDir = "$rootDir/grpclb" as File