Merge branch 'master' into server_channel_affinity
This commit is contained in:
commit
a2b549578f
|
|
@ -32,7 +32,7 @@
|
|||
CXX = g++
|
||||
CPPFLAGS += -I/usr/local/include -pthread
|
||||
CXXFLAGS += -std=c++11
|
||||
LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lprotobuf -lpthread -ldl
|
||||
LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++` -lprotobuf -lpthread -ldl
|
||||
PROTOC = protoc
|
||||
GRPC_CPP_PLUGIN = grpc_cpp_plugin
|
||||
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
CXX = g++
|
||||
CPPFLAGS += -I/usr/local/include -pthread
|
||||
CXXFLAGS += -std=c++11
|
||||
LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lprotobuf -lpthread -ldl
|
||||
LDFLAGS += -L/usr/local/lib `pkg-config --libs grpc++` -lprotobuf -lpthread -ldl
|
||||
PROTOC = protoc
|
||||
GRPC_CPP_PLUGIN = grpc_cpp_plugin
|
||||
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
|
||||
|
|
|
|||
|
|
@ -8,12 +8,19 @@ For this sample, we've already generated the server and client stubs from
|
|||
|
||||
|
||||
Install gRPC:
|
||||
```sh
|
||||
```sh
|
||||
$ pip install grpcio
|
||||
```
|
||||
Or, to install it system wide:
|
||||
```sh
|
||||
$ sudo pip install grpcio
|
||||
$ sudo pip install grpcio
|
||||
```
|
||||
|
||||
If you're on Windows, make sure you installed the `pip.exe` component when you
|
||||
installed Python. Invoke as above but with `pip.exe` instead of `pip` (you may
|
||||
also need to invoke from a `cmd.exe` ran as administrator):
|
||||
```sh
|
||||
$ pip.exe install grpcio
|
||||
```
|
||||
|
||||
Download the example
|
||||
|
|
|
|||
1
grpc.def
1
grpc.def
|
|
@ -87,6 +87,7 @@ EXPORTS
|
|||
grpc_header_key_is_legal
|
||||
grpc_header_nonbin_value_is_legal
|
||||
grpc_is_binary_header
|
||||
grpc_call_error_to_string
|
||||
grpc_auth_property_iterator_next
|
||||
grpc_auth_context_property_iterator
|
||||
grpc_auth_context_peer_identity
|
||||
|
|
|
|||
|
|
@ -393,6 +393,9 @@ GRPCAPI int grpc_header_nonbin_value_is_legal(const char *value, size_t length);
|
|||
/** Check whether a metadata key corresponds to a binary value */
|
||||
GRPCAPI int grpc_is_binary_header(const char *key, size_t length);
|
||||
|
||||
/** Convert grpc_call_error values to a string */
|
||||
GRPCAPI const char *grpc_call_error_to_string(grpc_call_error error);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@
|
|||
#define GRPC_CUSTOM_PLUGINMAIN ::google::protobuf::compiler::PluginMain
|
||||
#endif
|
||||
|
||||
#ifndef GRPC_CUSTOM_PARSEGENERATORPARAMETER
|
||||
#include <google/protobuf/compiler/code_generator.h>
|
||||
#define GRPC_CUSTOM_PARSEGENERATORPARAMETER ::google::protobuf::compiler::ParseGeneratorParameter
|
||||
#endif
|
||||
|
||||
namespace grpc {
|
||||
namespace protobuf {
|
||||
typedef GRPC_CUSTOM_DESCRIPTOR Descriptor;
|
||||
|
|
@ -85,6 +90,11 @@ static inline int PluginMain(int argc, char* argv[],
|
|||
const CodeGenerator* generator) {
|
||||
return GRPC_CUSTOM_PLUGINMAIN(argc, argv, generator);
|
||||
}
|
||||
static inline void ParseGeneratorParameter(const string& parameter,
|
||||
std::vector<std::pair<string, string> >* options) {
|
||||
GRPC_CUSTOM_PARSEGENERATORPARAMETER(parameter, options);
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
namespace io {
|
||||
typedef GRPC_CUSTOM_PRINTER Printer;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ using grpc::protobuf::MethodDescriptor;
|
|||
using grpc::protobuf::io::Printer;
|
||||
using grpc::protobuf::io::StringOutputStream;
|
||||
using grpc_generator::MethodType;
|
||||
using grpc_generator::GetCppComments;
|
||||
using grpc_generator::GetMethodType;
|
||||
using grpc_generator::METHODTYPE_NO_STREAMING;
|
||||
using grpc_generator::METHODTYPE_CLIENT_STREAMING;
|
||||
|
|
@ -65,6 +66,56 @@ using std::vector;
|
|||
namespace grpc_csharp_generator {
|
||||
namespace {
|
||||
|
||||
// This function is a massaged version of
|
||||
// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
|
||||
// Currently, we cannot easily reuse the functionality as
|
||||
// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
|
||||
// TODO(jtattermusch): reuse the functionality from google/protobuf.
|
||||
void GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, grpc::protobuf::SourceLocation location) {
|
||||
grpc::string comments = location.leading_comments.empty() ?
|
||||
location.trailing_comments : location.leading_comments;
|
||||
if (comments.empty()) {
|
||||
return;
|
||||
}
|
||||
// XML escaping... no need for apostrophes etc as the whole text is going to be a child
|
||||
// node of a summary element, not part of an attribute.
|
||||
comments = grpc_generator::StringReplace(comments, "&", "&", true);
|
||||
comments = grpc_generator::StringReplace(comments, "<", "<", true);
|
||||
|
||||
std::vector<grpc::string> lines;
|
||||
grpc_generator::Split(comments, '\n', &lines);
|
||||
// TODO: We really should work out which part to put in the summary and which to put in the remarks...
|
||||
// but that needs to be part of a bigger effort to understand the markdown better anyway.
|
||||
printer->Print("/// <summary>\n");
|
||||
bool last_was_empty = false;
|
||||
// We squash multiple blank lines down to one, and remove any trailing blank lines. We need
|
||||
// to preserve the blank lines themselves, as this is relevant in the markdown.
|
||||
// Note that we can't remove leading or trailing whitespace as *that's* relevant in markdown too.
|
||||
// (We don't skip "just whitespace" lines, either.)
|
||||
for (std::vector<grpc::string>::iterator it = lines.begin(); it != lines.end(); ++it) {
|
||||
grpc::string line = *it;
|
||||
if (line.empty()) {
|
||||
last_was_empty = true;
|
||||
} else {
|
||||
if (last_was_empty) {
|
||||
printer->Print("///\n");
|
||||
}
|
||||
last_was_empty = false;
|
||||
printer->Print("/// $line$\n", "line", *it);
|
||||
}
|
||||
}
|
||||
printer->Print("/// </summary>\n");
|
||||
}
|
||||
|
||||
template <typename DescriptorType>
|
||||
void GenerateDocCommentBody(
|
||||
grpc::protobuf::io::Printer* printer, const DescriptorType* descriptor) {
|
||||
grpc::protobuf::SourceLocation location;
|
||||
if (descriptor->GetSourceLocation(&location)) {
|
||||
GenerateDocCommentBodyImpl(printer, location);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetServiceClassName(const ServiceDescriptor* service) {
|
||||
return service->name();
|
||||
}
|
||||
|
|
@ -123,6 +174,10 @@ std::string GetMethodRequestParamMaybe(const MethodDescriptor *method,
|
|||
return GetClassName(method->input_type()) + " request, ";
|
||||
}
|
||||
|
||||
std::string GetAccessLevel(bool internal_access) {
|
||||
return internal_access ? "internal" : "public";
|
||||
}
|
||||
|
||||
std::string GetMethodReturnTypeClient(const MethodDescriptor *method) {
|
||||
switch (GetMethodType(method)) {
|
||||
case METHODTYPE_NO_STREAMING:
|
||||
|
|
@ -238,7 +293,7 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) {
|
|||
void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *service) {
|
||||
std::ostringstream index;
|
||||
index << service->index();
|
||||
out->Print("// service descriptor\n");
|
||||
out->Print("/// <summary>Service descriptor</summary>\n");
|
||||
out->Print("public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor\n");
|
||||
out->Print("{\n");
|
||||
out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n",
|
||||
|
|
@ -249,7 +304,8 @@ void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *se
|
|||
}
|
||||
|
||||
void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("// client interface\n");
|
||||
out->Print("/// <summary>Client for $servicename$</summary>\n",
|
||||
"servicename", GetServiceClassName(service));
|
||||
out->Print("[System.Obsolete(\"Client side interfaced will be removed "
|
||||
"in the next release. Use client class directly.\")]\n");
|
||||
out->Print("public interface $name$\n", "name",
|
||||
|
|
@ -262,6 +318,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
|
||||
if (method_type == METHODTYPE_NO_STREAMING) {
|
||||
// unary calls have an extra synchronous stub method
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"$response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n",
|
||||
"methodname", method->name(), "request",
|
||||
|
|
@ -269,6 +326,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
GetClassName(method->output_type()));
|
||||
|
||||
// overload taking CallOptions as a param
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"$response$ $methodname$($request$ request, CallOptions options);\n",
|
||||
"methodname", method->name(), "request",
|
||||
|
|
@ -280,6 +338,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
if (method_type == METHODTYPE_NO_STREAMING) {
|
||||
method_name += "Async"; // prevent name clash with synchronous method.
|
||||
}
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"$returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n",
|
||||
"methodname", method_name, "request_maybe",
|
||||
|
|
@ -287,6 +346,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
GetMethodReturnTypeClient(method));
|
||||
|
||||
// overload taking CallOptions as a param
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"$returntype$ $methodname$($request_maybe$CallOptions options);\n",
|
||||
"methodname", method_name, "request_maybe",
|
||||
|
|
@ -299,7 +359,8 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
}
|
||||
|
||||
void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("// server-side interface\n");
|
||||
out->Print("/// <summary>Interface of server-side implementations of $servicename$</summary>\n",
|
||||
"servicename", GetServiceClassName(service));
|
||||
out->Print("[System.Obsolete(\"Service implementations should inherit"
|
||||
" from the generated abstract base class instead.\")]\n");
|
||||
out->Print("public interface $name$\n", "name",
|
||||
|
|
@ -308,6 +369,7 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
out->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
const MethodDescriptor *method = service->method(i);
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"$returntype$ $methodname$($request$$response_stream_maybe$, "
|
||||
"ServerCallContext context);\n",
|
||||
|
|
@ -322,13 +384,15 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
|
|||
}
|
||||
|
||||
void GenerateServerClass(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("// server-side abstract class\n");
|
||||
out->Print("/// <summary>Base class for server-side implementations of $servicename$</summary>\n",
|
||||
"servicename", GetServiceClassName(service));
|
||||
out->Print("public abstract class $name$\n", "name",
|
||||
GetServerClassName(service));
|
||||
out->Print("{\n");
|
||||
out->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
const MethodDescriptor *method = service->method(i);
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"public virtual $returntype$ $methodname$($request$$response_stream_maybe$, "
|
||||
"ServerCallContext context)\n",
|
||||
|
|
@ -349,7 +413,8 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor *service) {
|
|||
}
|
||||
|
||||
void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("// client stub\n");
|
||||
out->Print("/// <summary>Client for $servicename$</summary>\n",
|
||||
"servicename", GetServiceClassName(service));
|
||||
out->Print("#pragma warning disable 0618\n");
|
||||
out->Print(
|
||||
"public class $name$ : ClientBase<$name$>, $interface$\n",
|
||||
|
|
@ -388,6 +453,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
|||
|
||||
if (method_type == METHODTYPE_NO_STREAMING) {
|
||||
// unary calls have an extra synchronous stub method
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print("public virtual $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
|
||||
"methodname", method->name(), "request",
|
||||
GetClassName(method->input_type()), "response",
|
||||
|
|
@ -400,6 +466,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
|||
out->Print("}\n");
|
||||
|
||||
// overload taking CallOptions as a param
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print("public virtual $response$ $methodname$($request$ request, CallOptions options)\n",
|
||||
"methodname", method->name(), "request",
|
||||
GetClassName(method->input_type()), "response",
|
||||
|
|
@ -416,6 +483,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
|||
if (method_type == METHODTYPE_NO_STREAMING) {
|
||||
method_name += "Async"; // prevent name clash with synchronous method.
|
||||
}
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"public virtual $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
|
||||
"methodname", method_name, "request_maybe",
|
||||
|
|
@ -431,6 +499,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
|||
out->Print("}\n");
|
||||
|
||||
// overload taking CallOptions as a param
|
||||
GenerateDocCommentBody(out, method);
|
||||
out->Print(
|
||||
"public virtual $returntype$ $methodname$($request_maybe$CallOptions options)\n",
|
||||
"methodname", method_name, "request_maybe",
|
||||
|
|
@ -481,7 +550,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
|
|||
void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service,
|
||||
bool use_server_class) {
|
||||
out->Print(
|
||||
"// creates service definition that can be registered with a server\n");
|
||||
"/// <summary>Creates service definition that can be registered with a server</summary>\n");
|
||||
out->Print("#pragma warning disable 0618\n");
|
||||
out->Print(
|
||||
"public static ServerServiceDefinition BindService($interface$ serviceImpl)\n",
|
||||
|
|
@ -515,7 +584,8 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service,
|
|||
}
|
||||
|
||||
void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("// creates a new client\n");
|
||||
out->Print("/// <summary>Creates a new client for $servicename$</summary>\n",
|
||||
"servicename", GetServiceClassName(service));
|
||||
out->Print("public static $classname$ NewClient(Channel channel)\n",
|
||||
"classname", GetClientClassName(service));
|
||||
out->Print("{\n");
|
||||
|
|
@ -527,8 +597,12 @@ void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
|
|||
out->Print("\n");
|
||||
}
|
||||
|
||||
void GenerateService(Printer* out, const ServiceDescriptor *service) {
|
||||
out->Print("public static class $classname$\n", "classname",
|
||||
void GenerateService(Printer* out, const ServiceDescriptor *service,
|
||||
bool generate_client, bool generate_server,
|
||||
bool internal_access) {
|
||||
GenerateDocCommentBody(out, service);
|
||||
out->Print("$access_level$ static class $classname$\n", "access_level",
|
||||
GetAccessLevel(internal_access), "classname",
|
||||
GetServiceClassName(service));
|
||||
out->Print("{\n");
|
||||
out->Indent();
|
||||
|
|
@ -542,13 +616,22 @@ void GenerateService(Printer* out, const ServiceDescriptor *service) {
|
|||
GenerateStaticMethodField(out, service->method(i));
|
||||
}
|
||||
GenerateServiceDescriptorProperty(out, service);
|
||||
GenerateClientInterface(out, service);
|
||||
GenerateServerInterface(out, service);
|
||||
GenerateServerClass(out, service);
|
||||
GenerateClientStub(out, service);
|
||||
GenerateBindServiceMethod(out, service, false);
|
||||
GenerateBindServiceMethod(out, service, true);
|
||||
GenerateNewStubMethods(out, service);
|
||||
|
||||
if (generate_client) {
|
||||
GenerateClientInterface(out, service);
|
||||
}
|
||||
if (generate_server) {
|
||||
GenerateServerInterface(out, service);
|
||||
GenerateServerClass(out, service);
|
||||
}
|
||||
if (generate_client) {
|
||||
GenerateClientStub(out, service);
|
||||
GenerateNewStubMethods(out, service);
|
||||
}
|
||||
if (generate_server) {
|
||||
GenerateBindServiceMethod(out, service, false);
|
||||
GenerateBindServiceMethod(out, service, true);
|
||||
}
|
||||
|
||||
out->Outdent();
|
||||
out->Print("}\n");
|
||||
|
|
@ -556,7 +639,8 @@ void GenerateService(Printer* out, const ServiceDescriptor *service) {
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
grpc::string GetServices(const FileDescriptor *file) {
|
||||
grpc::string GetServices(const FileDescriptor *file, bool generate_client,
|
||||
bool generate_server, bool internal_access) {
|
||||
grpc::string output;
|
||||
{
|
||||
// Scope the output stream so it closes and finalizes output to the string.
|
||||
|
|
@ -573,6 +657,14 @@ grpc::string GetServices(const FileDescriptor *file) {
|
|||
// Write out a file header.
|
||||
out.Print("// Generated by the protocol buffer compiler. DO NOT EDIT!\n");
|
||||
out.Print("// source: $filename$\n", "filename", file->name());
|
||||
|
||||
// use C++ style as there are no file-level XML comments in .NET
|
||||
grpc::string leading_comments = GetCppComments(file, true);
|
||||
if (!leading_comments.empty()) {
|
||||
out.Print("// Original file comments:\n");
|
||||
out.Print(leading_comments.c_str());
|
||||
}
|
||||
|
||||
out.Print("#region Designer generated code\n");
|
||||
out.Print("\n");
|
||||
out.Print("using System;\n");
|
||||
|
|
@ -584,7 +676,8 @@ grpc::string GetServices(const FileDescriptor *file) {
|
|||
out.Print("namespace $namespace$ {\n", "namespace", GetFileNamespace(file));
|
||||
out.Indent();
|
||||
for (int i = 0; i < file->service_count(); i++) {
|
||||
GenerateService(&out, file->service(i));
|
||||
GenerateService(&out, file->service(i), generate_client, generate_server,
|
||||
internal_access);
|
||||
}
|
||||
out.Outdent();
|
||||
out.Print("}\n");
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@
|
|||
|
||||
namespace grpc_csharp_generator {
|
||||
|
||||
grpc::string GetServices(const grpc::protobuf::FileDescriptor *file);
|
||||
grpc::string GetServices(const grpc::protobuf::FileDescriptor *file,
|
||||
bool generate_client, bool generate_server,
|
||||
bool internal_access);
|
||||
|
||||
} // namespace grpc_csharp_generator
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,29 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
|
|||
const grpc::string ¶meter,
|
||||
grpc::protobuf::compiler::GeneratorContext *context,
|
||||
grpc::string *error) const {
|
||||
grpc::string code = grpc_csharp_generator::GetServices(file);
|
||||
std::vector<std::pair<grpc::string, grpc::string> > options;
|
||||
grpc::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
|
||||
|
||||
bool generate_client = true;
|
||||
bool generate_server = true;
|
||||
bool internal_access = false;
|
||||
for (size_t i = 0; i < options.size(); i++) {
|
||||
if (options[i].first == "no_client") {
|
||||
generate_client = false;
|
||||
} else if (options[i].first == "no_server") {
|
||||
generate_server = false;
|
||||
} else if (options[i].first == "internal_access") {
|
||||
internal_access = true;
|
||||
} else {
|
||||
*error = "Unknown generator option: " + options[i].first;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
grpc::string code = grpc_csharp_generator::GetServices(file,
|
||||
generate_client,
|
||||
generate_server,
|
||||
internal_access);
|
||||
if (code.size() == 0) {
|
||||
return true; // don't generate a file if there are no services
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ void PrintMethodSignature(Printer *printer, const MethodDescriptor *method,
|
|||
if (method->server_streaming()) {
|
||||
printer->Print(vars,
|
||||
" eventHandler:(void(^)(BOOL done, "
|
||||
"$response_class$ *response, NSError *error))eventHandler");
|
||||
"$response_class$ *_Nullable response, NSError *_Nullable error))eventHandler");
|
||||
} else {
|
||||
printer->Print(vars,
|
||||
" handler:(void(^)($response_class$ *response, "
|
||||
"NSError *error))handler");
|
||||
" handler:(void(^)($response_class$ *_Nullable response, "
|
||||
"NSError *_Nullable error))handler");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,12 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
|
|||
declarations += grpc_objective_c_generator::GetHeader(service);
|
||||
}
|
||||
|
||||
static const ::grpc::string kNonNullBegin = "\nNS_ASSUME_NONNULL_BEGIN\n\n";
|
||||
static const ::grpc::string kNonNullEnd = "\nNS_ASSUME_NONNULL_END\n";
|
||||
|
||||
Write(context, file_name + ".pbrpc.h",
|
||||
imports + '\n' + proto_imports + '\n' + declarations);
|
||||
imports + '\n' + proto_imports + '\n' + kNonNullBegin +
|
||||
declarations + kNonNullEnd);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ bool PrintBetaServicer(const ServiceDescriptor* service,
|
|||
"Documentation", doc,
|
||||
});
|
||||
out->Print("\n");
|
||||
out->Print(dict, "class Beta$Service$Servicer(six.with_metaclass(abc.ABCMeta, object)):\n");
|
||||
out->Print(dict, "class Beta$Service$Servicer(object):\n");
|
||||
{
|
||||
IndentScope raii_class_indent(out);
|
||||
out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
|
||||
|
|
@ -198,12 +198,11 @@ bool PrintBetaServicer(const ServiceDescriptor* service,
|
|||
auto meth = service->method(i);
|
||||
grpc::string arg_name = meth->client_streaming() ?
|
||||
"request_iterator" : "request";
|
||||
out->Print("@abc.abstractmethod\n");
|
||||
out->Print("def $Method$(self, $ArgName$, context):\n",
|
||||
"Method", meth->name(), "ArgName", arg_name);
|
||||
{
|
||||
IndentScope raii_method_indent(out);
|
||||
out->Print("raise NotImplementedError()\n");
|
||||
out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,7 +217,7 @@ bool PrintBetaStub(const ServiceDescriptor* service,
|
|||
"Documentation", doc,
|
||||
});
|
||||
out->Print("\n");
|
||||
out->Print(dict, "class Beta$Service$Stub(six.with_metaclass(abc.ABCMeta, object)):\n");
|
||||
out->Print(dict, "class Beta$Service$Stub(object):\n");
|
||||
{
|
||||
IndentScope raii_class_indent(out);
|
||||
out->Print(dict, "\"\"\"$Documentation$\"\"\"\n");
|
||||
|
|
@ -227,7 +226,6 @@ bool PrintBetaStub(const ServiceDescriptor* service,
|
|||
grpc::string arg_name = meth->client_streaming() ?
|
||||
"request_iterator" : "request";
|
||||
auto methdict = ListToDict({"Method", meth->name(), "ArgName", arg_name});
|
||||
out->Print("@abc.abstractmethod\n");
|
||||
out->Print(methdict, "def $Method$(self, $ArgName$, timeout):\n");
|
||||
{
|
||||
IndentScope raii_method_indent(out);
|
||||
|
|
@ -450,6 +448,8 @@ bool PrintPreamble(const FileDescriptor* file,
|
|||
out->Print("import six\n");
|
||||
out->Print("from $Package$ import implementations as beta_implementations\n",
|
||||
"Package", config.beta_package_root);
|
||||
out->Print("from $Package$ import interfaces as beta_interfaces\n",
|
||||
"Package", config.beta_package_root);
|
||||
out->Print("from grpc.framework.common import cardinality\n");
|
||||
out->Print("from grpc.framework.interfaces.face import utilities as face_utilities\n");
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ static void client_init_call_elem(grpc_exec_ctx *exec_ctx,
|
|||
}
|
||||
|
||||
static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
grpc_call_element *elem, void *ignored) {
|
||||
call_data *d = elem->call_data;
|
||||
GPR_ASSERT(d != NULL);
|
||||
/* TODO(hongyu): record rpc client stats and census_rpc_end_op here */
|
||||
|
|
@ -152,7 +152,7 @@ static void server_init_call_elem(grpc_exec_ctx *exec_ctx,
|
|||
}
|
||||
|
||||
static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
grpc_call_element *elem, void *ignored) {
|
||||
call_data *d = elem->call_data;
|
||||
GPR_ASSERT(d != NULL);
|
||||
/* TODO(hongyu): record rpc server stats and census_tracing_end_op here */
|
||||
|
|
|
|||
|
|
@ -415,9 +415,10 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *and_free_memory) {
|
||||
grpc_subchannel_call_holder_destroy(exec_ctx, elem->call_data);
|
||||
gpr_free(and_free_memory);
|
||||
}
|
||||
|
||||
/* Constructor for channel_data */
|
||||
|
|
|
|||
|
|
@ -644,9 +644,9 @@ static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call,
|
|||
bool success) {
|
||||
grpc_subchannel_call *c = call;
|
||||
GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0);
|
||||
grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c));
|
||||
GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, c->connection, "subchannel_call");
|
||||
gpr_free(c);
|
||||
grpc_connected_subchannel *connection = c->connection;
|
||||
grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), c);
|
||||
GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, connection, "subchannel_call");
|
||||
GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -291,27 +291,44 @@ struct grpc_chttp2_transport_parsing {
|
|||
int64_t outgoing_window;
|
||||
};
|
||||
|
||||
typedef void (*grpc_chttp2_locked_action)(grpc_exec_ctx *ctx,
|
||||
grpc_chttp2_transport *t,
|
||||
grpc_chttp2_stream *s, void *arg);
|
||||
|
||||
typedef struct grpc_chttp2_executor_action_header {
|
||||
grpc_chttp2_stream *stream;
|
||||
grpc_chttp2_locked_action action;
|
||||
struct grpc_chttp2_executor_action_header *next;
|
||||
void *arg;
|
||||
} grpc_chttp2_executor_action_header;
|
||||
|
||||
struct grpc_chttp2_transport {
|
||||
grpc_transport base; /* must be first */
|
||||
grpc_endpoint *ep;
|
||||
gpr_refcount refs;
|
||||
grpc_endpoint *ep;
|
||||
char *peer_string;
|
||||
|
||||
/** when this drops to zero it's safe to shutdown the endpoint */
|
||||
gpr_refcount shutdown_ep_refs;
|
||||
|
||||
gpr_mu mu;
|
||||
struct {
|
||||
gpr_mu mu;
|
||||
|
||||
/** is a thread currently in the global lock */
|
||||
bool global_active;
|
||||
/** is a thread currently writing */
|
||||
bool writing_active;
|
||||
/** is a thread currently parsing */
|
||||
bool parsing_active;
|
||||
|
||||
grpc_chttp2_executor_action_header *pending_actions;
|
||||
} executor;
|
||||
|
||||
/** is the transport destroying itself? */
|
||||
uint8_t destroying;
|
||||
/** has the upper layer closed the transport? */
|
||||
uint8_t closed;
|
||||
|
||||
/** is a thread currently writing */
|
||||
uint8_t writing_active;
|
||||
/** is a thread currently parsing */
|
||||
uint8_t parsing_active;
|
||||
|
||||
/** is there a read request to the endpoint outstanding? */
|
||||
uint8_t endpoint_reading;
|
||||
|
||||
|
|
@ -338,8 +355,10 @@ struct grpc_chttp2_transport {
|
|||
|
||||
/** closure to execute writing */
|
||||
grpc_closure writing_action;
|
||||
/** closure to finish reading from the endpoint */
|
||||
grpc_closure recv_data;
|
||||
/** closure to start reading from the endpoint */
|
||||
grpc_closure reading_action;
|
||||
/** closure to actually do parsing */
|
||||
grpc_closure parsing_action;
|
||||
|
||||
/** incoming read bytes */
|
||||
gpr_slice_buffer read_buffer;
|
||||
|
|
@ -397,21 +416,26 @@ typedef struct {
|
|||
grpc_transport_stream_stats *collecting_stats;
|
||||
grpc_transport_stream_stats stats;
|
||||
|
||||
/** number of streams that are currently being read */
|
||||
gpr_refcount active_streams;
|
||||
|
||||
/** when the application requests writes be closed, the write_closed is
|
||||
'queued'; when the close is flow controlled into the send path, we are
|
||||
'sending' it; when the write has been performed it is 'sent' */
|
||||
uint8_t write_closed;
|
||||
bool write_closed;
|
||||
/** is this stream reading half-closed (boolean) */
|
||||
uint8_t read_closed;
|
||||
bool read_closed;
|
||||
/** are all published incoming byte streams closed */
|
||||
bool all_incoming_byte_streams_finished;
|
||||
/** is this stream in the stream map? (boolean) */
|
||||
uint8_t in_stream_map;
|
||||
bool in_stream_map;
|
||||
/** has this stream seen an error? if 1, then pending incoming frames
|
||||
can be thrown away */
|
||||
uint8_t seen_error;
|
||||
bool seen_error;
|
||||
|
||||
uint8_t published_initial_metadata;
|
||||
uint8_t published_trailing_metadata;
|
||||
uint8_t faked_trailing_metadata;
|
||||
bool published_initial_metadata;
|
||||
bool published_trailing_metadata;
|
||||
bool faked_trailing_metadata;
|
||||
|
||||
grpc_chttp2_incoming_metadata_buffer received_initial_metadata;
|
||||
grpc_chttp2_incoming_metadata_buffer received_trailing_metadata;
|
||||
|
|
@ -570,6 +594,9 @@ int grpc_chttp2_list_pop_waiting_for_concurrency(
|
|||
void grpc_chttp2_list_add_check_read_ops(
|
||||
grpc_chttp2_transport_global *transport_global,
|
||||
grpc_chttp2_stream_global *stream_global);
|
||||
bool grpc_chttp2_list_remove_check_read_ops(
|
||||
grpc_chttp2_transport_global *transport_global,
|
||||
grpc_chttp2_stream_global *stream_global);
|
||||
int grpc_chttp2_list_pop_check_read_ops(
|
||||
grpc_chttp2_transport_global *transport_global,
|
||||
grpc_chttp2_stream_global **stream_global);
|
||||
|
|
@ -645,6 +672,12 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
|
|||
grpc_chttp2_stream_global *stream_global,
|
||||
grpc_closure **pclosure, int success);
|
||||
|
||||
void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
|
||||
grpc_chttp2_transport *transport,
|
||||
grpc_chttp2_stream *optional_stream,
|
||||
grpc_chttp2_locked_action action,
|
||||
void *arg, size_t sizeof_arg);
|
||||
|
||||
#define GRPC_CHTTP2_CLIENT_CONNECT_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
#define GRPC_CHTTP2_CLIENT_CONNECT_STRLEN \
|
||||
(sizeof(GRPC_CHTTP2_CLIENT_CONNECT_STRING) - 1)
|
||||
|
|
|
|||
|
|
@ -305,6 +305,14 @@ void grpc_chttp2_list_add_check_read_ops(
|
|||
GRPC_CHTTP2_LIST_CHECK_READ_OPS);
|
||||
}
|
||||
|
||||
bool grpc_chttp2_list_remove_check_read_ops(
|
||||
grpc_chttp2_transport_global *transport_global,
|
||||
grpc_chttp2_stream_global *stream_global) {
|
||||
return stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global),
|
||||
STREAM_FROM_GLOBAL(stream_global),
|
||||
GRPC_CHTTP2_LIST_CHECK_READ_OPS);
|
||||
}
|
||||
|
||||
int grpc_chttp2_list_pop_check_read_ops(
|
||||
grpc_chttp2_transport_global *transport_global,
|
||||
grpc_chttp2_stream_global **stream_global) {
|
||||
|
|
|
|||
|
|
@ -213,14 +213,16 @@ void grpc_call_stack_ignore_set_pollset(grpc_exec_ctx *exec_ctx,
|
|||
grpc_call_element *elem,
|
||||
grpc_pollset *pollset) {}
|
||||
|
||||
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack) {
|
||||
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
|
||||
void *and_free_memory) {
|
||||
grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack);
|
||||
size_t count = stack->count;
|
||||
size_t i;
|
||||
|
||||
/* destroy per-filter data */
|
||||
for (i = 0; i < count; i++) {
|
||||
elems[i].filter->destroy_call_elem(exec_ctx, &elems[i]);
|
||||
elems[i].filter->destroy_call_elem(exec_ctx, &elems[i],
|
||||
i == count - 1 ? and_free_memory : NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,8 +104,12 @@ typedef struct {
|
|||
void (*set_pollset)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
grpc_pollset *pollset);
|
||||
/* Destroy per call data.
|
||||
The filter does not need to do any chaining */
|
||||
void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem);
|
||||
The filter does not need to do any chaining.
|
||||
The bottom filter of a stack will be passed a non-NULL pointer to
|
||||
\a and_free_memory that should be passed to gpr_free when destruction
|
||||
is complete. */
|
||||
void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *and_free_memory);
|
||||
|
||||
/* sizeof(per channel data) */
|
||||
size_t sizeof_channel_data;
|
||||
|
|
@ -223,7 +227,8 @@ void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
|
|||
#endif
|
||||
|
||||
/* Destroy a call stack */
|
||||
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack);
|
||||
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
|
||||
void *and_free_memory);
|
||||
|
||||
/* Ignore set pollset - used by filters to implement the set_pollset method
|
||||
if they don't care about pollsets at all. Does nothing. */
|
||||
|
|
|
|||
|
|
@ -246,8 +246,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {
|
||||
/* grab pointers to our data from the call element */
|
||||
call_data *calld = elem->call_data;
|
||||
gpr_slice_buffer_destroy(&calld->slices);
|
||||
|
|
|
|||
|
|
@ -102,12 +102,13 @@ static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *and_free_memory) {
|
||||
call_data *calld = elem->call_data;
|
||||
channel_data *chand = elem->channel_data;
|
||||
grpc_transport_destroy_stream(exec_ctx, chand->transport,
|
||||
TRANSPORT_STREAM_FROM_CALL_DATA(calld));
|
||||
TRANSPORT_STREAM_FROM_CALL_DATA(calld),
|
||||
and_free_memory);
|
||||
}
|
||||
|
||||
/* Constructor for channel_data */
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {}
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {}
|
||||
|
||||
static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
|
||||
unsigned i;
|
||||
|
|
|
|||
|
|
@ -225,8 +225,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {}
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {}
|
||||
|
||||
/* Constructor for channel_data */
|
||||
static void init_channel_elem(grpc_exec_ctx *exec_ctx,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
static const grpc_event_engine_vtable *g_event_engine;
|
||||
|
||||
grpc_poll_function_type grpc_poll_function = poll;
|
||||
grpc_wakeup_fd grpc_global_wakeup_fd;
|
||||
|
||||
void grpc_event_engine_init(void) {
|
||||
if ((g_event_engine = grpc_init_poll_and_epoll_posix())) {
|
||||
|
|
|
|||
|
|
@ -166,8 +166,10 @@ bool grpc_iomgr_abort_on_leaks(void) {
|
|||
if (env == NULL) return false;
|
||||
static const char *truthy[] = {"yes", "Yes", "YES", "true",
|
||||
"True", "TRUE", "1"};
|
||||
bool should_we = false;
|
||||
for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
|
||||
if (0 == strcmp(env, truthy[i])) return true;
|
||||
if (0 == strcmp(env, truthy[i])) should_we = true;
|
||||
}
|
||||
return false;
|
||||
gpr_free(env);
|
||||
return should_we;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,8 +277,8 @@ static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {
|
||||
call_data *calld = elem->call_data;
|
||||
grpc_call_credentials_unref(calld->creds);
|
||||
if (calld->host != NULL) {
|
||||
|
|
|
|||
|
|
@ -224,8 +224,8 @@ static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
grpc_pollset *pollset) {}
|
||||
|
||||
/* Destructor for call_data */
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {}
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {}
|
||||
|
||||
/* Constructor for channel_data */
|
||||
static void init_channel_elem(grpc_exec_ctx *exec_ctx,
|
||||
|
|
|
|||
|
|
@ -373,8 +373,6 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
|
|||
if (c->receiving_stream != NULL) {
|
||||
grpc_byte_stream_destroy(exec_ctx, c->receiving_stream);
|
||||
}
|
||||
grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c));
|
||||
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->channel, "call");
|
||||
gpr_mu_destroy(&c->mu);
|
||||
for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
|
||||
if (c->status[i].details) {
|
||||
|
|
@ -392,7 +390,9 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
|
|||
if (c->cq) {
|
||||
GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
|
||||
}
|
||||
gpr_free(c);
|
||||
grpc_channel *channel = c->channel;
|
||||
grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), c);
|
||||
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call");
|
||||
GPR_TIMER_END("destroy_call", 0);
|
||||
}
|
||||
|
||||
|
|
@ -1517,3 +1517,39 @@ grpc_compression_algorithm grpc_call_compression_for_level(
|
|||
gpr_mu_unlock(&call->mu);
|
||||
return grpc_compression_algorithm_for_level(level, accepted_encodings);
|
||||
}
|
||||
|
||||
const char *grpc_call_error_to_string(grpc_call_error error) {
|
||||
switch (error) {
|
||||
case GRPC_CALL_ERROR:
|
||||
return "GRPC_CALL_ERROR";
|
||||
case GRPC_CALL_ERROR_ALREADY_ACCEPTED:
|
||||
return "GRPC_CALL_ERROR_ALREADY_ACCEPTED";
|
||||
case GRPC_CALL_ERROR_ALREADY_FINISHED:
|
||||
return "GRPC_CALL_ERROR_ALREADY_FINISHED";
|
||||
case GRPC_CALL_ERROR_ALREADY_INVOKED:
|
||||
return "GRPC_CALL_ERROR_ALREADY_INVOKED";
|
||||
case GRPC_CALL_ERROR_BATCH_TOO_BIG:
|
||||
return "GRPC_CALL_ERROR_BATCH_TOO_BIG";
|
||||
case GRPC_CALL_ERROR_INVALID_FLAGS:
|
||||
return "GRPC_CALL_ERROR_INVALID_FLAGS";
|
||||
case GRPC_CALL_ERROR_INVALID_MESSAGE:
|
||||
return "GRPC_CALL_ERROR_INVALID_MESSAGE";
|
||||
case GRPC_CALL_ERROR_INVALID_METADATA:
|
||||
return "GRPC_CALL_ERROR_INVALID_METADATA";
|
||||
case GRPC_CALL_ERROR_NOT_INVOKED:
|
||||
return "GRPC_CALL_ERROR_NOT_INVOKED";
|
||||
case GRPC_CALL_ERROR_NOT_ON_CLIENT:
|
||||
return "GRPC_CALL_ERROR_NOT_ON_CLIENT";
|
||||
case GRPC_CALL_ERROR_NOT_ON_SERVER:
|
||||
return "GRPC_CALL_ERROR_NOT_ON_SERVER";
|
||||
case GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE:
|
||||
return "GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE";
|
||||
case GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH:
|
||||
return "GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH";
|
||||
case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS:
|
||||
return "GRPC_CALL_ERROR_TOO_MANY_OPERATIONS";
|
||||
case GRPC_CALL_OK:
|
||||
return "GRPC_CALL_OK";
|
||||
}
|
||||
GPR_UNREACHABLE_CODE(return "GRPC_CALL_ERROR_UNKNOW");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,10 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
|
|||
#endif
|
||||
|
||||
GPR_TIMER_BEGIN("grpc_cq_end_op", 0);
|
||||
GRPC_API_TRACE(
|
||||
"grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, success=%d, done=%p, "
|
||||
"done_arg=%p, storage=%p)",
|
||||
7, (exec_ctx, cc, tag, success, done, done_arg, storage));
|
||||
|
||||
storage->tag = tag;
|
||||
storage->done = done;
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ void grpc_init(void) {
|
|||
grpc_security_pre_init();
|
||||
grpc_iomgr_init();
|
||||
grpc_executor_init();
|
||||
grpc_tracer_init("GRPC_TRACE");
|
||||
gpr_timers_global_init();
|
||||
grpc_cq_global_init();
|
||||
for (i = 0; i < g_number_of_plugins; i++) {
|
||||
|
|
@ -179,6 +178,7 @@ void grpc_init(void) {
|
|||
* at the appropriate time */
|
||||
grpc_register_security_filters();
|
||||
register_builtin_channel_init();
|
||||
grpc_tracer_init("GRPC_TRACE");
|
||||
/* no more changes to channel init pipelines */
|
||||
grpc_channel_init_finalize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,8 +107,10 @@ static void lame_start_transport_op(grpc_exec_ctx *exec_ctx,
|
|||
static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
grpc_call_element_args *args) {}
|
||||
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {}
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *and_free_memory) {
|
||||
gpr_free(and_free_memory);
|
||||
}
|
||||
|
||||
static void init_channel_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_channel_element *elem,
|
||||
|
|
|
|||
|
|
@ -820,8 +820,8 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
|||
server_ref(chand->server);
|
||||
}
|
||||
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
|
||||
grpc_call_element *elem) {
|
||||
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
|
||||
void *ignored) {
|
||||
channel_data *chand = elem->channel_data;
|
||||
call_data *calld = elem->call_data;
|
||||
|
||||
|
|
|
|||
|
|
@ -133,8 +133,9 @@ void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
|
|||
|
||||
void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
|
||||
grpc_transport *transport,
|
||||
grpc_stream *stream) {
|
||||
transport->vtable->destroy_stream(exec_ctx, transport, stream);
|
||||
grpc_stream *stream, void *and_free_memory) {
|
||||
transport->vtable->destroy_stream(exec_ctx, transport, stream,
|
||||
and_free_memory);
|
||||
}
|
||||
|
||||
char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx,
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@ void grpc_transport_move_stats(grpc_transport_stream_stats *from,
|
|||
/* Transport stream op: a set of operations to perform on a transport
|
||||
against a single stream */
|
||||
typedef struct grpc_transport_stream_op {
|
||||
/** Should be enqueued when all requested operations (excluding recv_message
|
||||
and recv_initial_metadata which have their own closures) in a given batch
|
||||
have been completed. */
|
||||
grpc_closure *on_complete;
|
||||
|
||||
/** Send initial metadata to the peer, from the provided metadata batch.
|
||||
idempotent_request MUST be set if this is non-null */
|
||||
grpc_metadata_batch *send_initial_metadata;
|
||||
|
|
@ -129,11 +134,6 @@ typedef struct grpc_transport_stream_op {
|
|||
/** Collect any stats into provided buffer, zero internal stat counters */
|
||||
grpc_transport_stream_stats *collect_stats;
|
||||
|
||||
/** Should be enqueued when all requested operations (excluding recv_message
|
||||
and recv_initial_metadata which have their own closures) in a given batch
|
||||
have been completed. */
|
||||
grpc_closure *on_complete;
|
||||
|
||||
/** If != GRPC_STATUS_OK, cancel this stream */
|
||||
grpc_status_code cancel_with_status;
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
|
|||
caller, but any child memory must be cleaned up) */
|
||||
void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
|
||||
grpc_transport *transport,
|
||||
grpc_stream *stream);
|
||||
grpc_stream *stream, void *and_free_memory);
|
||||
|
||||
void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
|
||||
grpc_transport_stream_op *op);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ typedef struct grpc_transport_vtable {
|
|||
|
||||
/* implementation of grpc_transport_destroy_stream */
|
||||
void (*destroy_stream)(grpc_exec_ctx *exec_ctx, grpc_transport *self,
|
||||
grpc_stream *stream);
|
||||
grpc_stream *stream, void *and_free_memory);
|
||||
|
||||
/* implementation of grpc_transport_destroy */
|
||||
void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_transport *self);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,35 @@
|
|||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: math.proto
|
||||
// Original file comments:
|
||||
// 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.
|
||||
//
|
||||
#region Designer generated code
|
||||
|
||||
using System;
|
||||
|
|
@ -45,56 +75,140 @@ namespace Math {
|
|||
__Marshaller_Num,
|
||||
__Marshaller_Num);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Math.MathReflection.Descriptor.Services[0]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for Math</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IMathClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
global::Math.DivReply Div(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
global::Math.DivReply Div(global::Math.DivArgs request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, CallOptions options);
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(CallOptions options);
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of Math</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IMath
|
||||
{
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
Task<global::Math.DivReply> Div(global::Math.DivArgs request, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
Task DivMany(IAsyncStreamReader<global::Math.DivArgs> requestStream, IServerStreamWriter<global::Math.DivReply> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
Task Fib(global::Math.FibArgs request, IServerStreamWriter<global::Math.Num> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
Task<global::Math.Num> Sum(IAsyncStreamReader<global::Math.Num> requestStream, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of Math</summary>
|
||||
public abstract class MathBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
public virtual Task<global::Math.DivReply> Div(global::Math.DivArgs request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
public virtual Task DivMany(IAsyncStreamReader<global::Math.DivArgs> requestStream, IServerStreamWriter<global::Math.DivReply> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
public virtual Task Fib(global::Math.FibArgs request, IServerStreamWriter<global::Math.Num> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
public virtual Task<global::Math.Num> Sum(IAsyncStreamReader<global::Math.Num> requestStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -102,7 +216,7 @@ namespace Math {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for Math</summary>
|
||||
#pragma warning disable 0618
|
||||
public class MathClient : ClientBase<MathClient>, IMathClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -122,42 +236,88 @@ namespace Math {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
public virtual global::Math.DivReply Div(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Div(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
public virtual global::Math.DivReply Div(global::Math.DivArgs request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_Div, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return DivAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Div divides args.dividend by args.divisor and returns the quotient and
|
||||
/// remainder.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_Div, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return DivMany(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// DivMany accepts an arbitrary number of division args from the client stream
|
||||
/// and sends back the results in the reply stream. The stream continues until
|
||||
/// the client closes its end; the server does the same after sending all the
|
||||
/// replies. The stream ends immediately if either end aborts.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_DivMany, null, options);
|
||||
}
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Fib(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
|
||||
/// generates up to limit numbers; otherwise it continues until the call is
|
||||
/// canceled. Unlike Fib above, Fib has no final FibReply.
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncServerStreamingCall(__Method_Fib, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
public virtual AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Sum(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Sum sums a stream of numbers, returning the final result once the stream
|
||||
/// is closed.
|
||||
/// </summary>
|
||||
public virtual AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncClientStreamingCall(__Method_Sum, null, options);
|
||||
|
|
@ -168,7 +328,13 @@ namespace Math {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for Math</summary>
|
||||
public static MathClient NewClient(Channel channel)
|
||||
{
|
||||
return new MathClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IMath serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -180,7 +346,7 @@ namespace Math {
|
|||
.AddMethod(__Method_Sum, serviceImpl.Sum).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(MathBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -192,12 +358,6 @@ namespace Math {
|
|||
.AddMethod(__Method_Sum, serviceImpl.Sum).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static MathClient NewClient(Channel channel)
|
||||
{
|
||||
return new MathClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,5 +1,35 @@
|
|||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: health.proto
|
||||
// Original file comments:
|
||||
// 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.
|
||||
//
|
||||
#region Designer generated code
|
||||
|
||||
using System;
|
||||
|
|
@ -22,13 +52,13 @@ namespace Grpc.Health.V1 {
|
|||
__Marshaller_HealthCheckRequest,
|
||||
__Marshaller_HealthCheckResponse);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Health.V1.HealthReflection.Descriptor.Services[0]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for Health</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IHealthClient
|
||||
{
|
||||
|
|
@ -38,14 +68,14 @@ namespace Grpc.Health.V1 {
|
|||
AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of Health</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IHealth
|
||||
{
|
||||
Task<global::Grpc.Health.V1.HealthCheckResponse> Check(global::Grpc.Health.V1.HealthCheckRequest request, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of Health</summary>
|
||||
public abstract class HealthBase
|
||||
{
|
||||
public virtual Task<global::Grpc.Health.V1.HealthCheckResponse> Check(global::Grpc.Health.V1.HealthCheckRequest request, ServerCallContext context)
|
||||
|
|
@ -55,7 +85,7 @@ namespace Grpc.Health.V1 {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for Health</summary>
|
||||
#pragma warning disable 0618
|
||||
public class HealthClient : ClientBase<HealthClient>, IHealthClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -97,7 +127,13 @@ namespace Grpc.Health.V1 {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for Health</summary>
|
||||
public static HealthClient NewClient(Channel channel)
|
||||
{
|
||||
return new HealthClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IHealth serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -106,7 +142,7 @@ namespace Grpc.Health.V1 {
|
|||
.AddMethod(__Method_Check, serviceImpl.Check).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(HealthBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -115,12 +151,6 @@ namespace Grpc.Health.V1 {
|
|||
.AddMethod(__Method_Check, serviceImpl.Check).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static HealthClient NewClient(Channel channel)
|
||||
{
|
||||
return new HealthClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,5 +1,41 @@
|
|||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: src/proto/grpc/testing/metrics.proto
|
||||
// Original file comments:
|
||||
// Copyright 2015-2016, 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.
|
||||
//
|
||||
// Contains the definitions for a metrics service and the type of metrics
|
||||
// exposed by the service.
|
||||
//
|
||||
// Currently, 'Gauge' (i.e a metric that represents the measured value of
|
||||
// something at an instant of time) is the only metric type supported by the
|
||||
// service.
|
||||
#region Designer generated code
|
||||
|
||||
using System;
|
||||
|
|
@ -30,40 +66,74 @@ namespace Grpc.Testing {
|
|||
__Marshaller_GaugeRequest,
|
||||
__Marshaller_GaugeResponse);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.MetricsReflection.Descriptor.Services[0]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for MetricsService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IMetricsServiceClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of MetricsService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IMetricsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
Task GetAllGauges(global::Grpc.Testing.EmptyMessage request, IServerStreamWriter<global::Grpc.Testing.GaugeResponse> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.GaugeResponse> GetGauge(global::Grpc.Testing.GaugeRequest request, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of MetricsService</summary>
|
||||
public abstract class MetricsServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
public virtual Task GetAllGauges(global::Grpc.Testing.EmptyMessage request, IServerStreamWriter<global::Grpc.Testing.GaugeResponse> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.GaugeResponse> GetGauge(global::Grpc.Testing.GaugeRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -71,7 +141,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for MetricsService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class MetricsServiceClient : ClientBase<MetricsServiceClient>, IMetricsServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -91,26 +161,46 @@ namespace Grpc.Testing {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return GetAllGauges(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the values of all the gauges that are currently being maintained by
|
||||
/// the service
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncServerStreamingCall(__Method_GetAllGauges, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return GetGauge(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_GetGauge, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return GetGaugeAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the value of one gauge
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_GetGauge, null, options, request);
|
||||
|
|
@ -121,7 +211,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for MetricsService</summary>
|
||||
public static MetricsServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new MetricsServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IMetricsService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -131,7 +227,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_GetGauge, serviceImpl.GetGauge).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(MetricsServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -141,12 +237,6 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_GetGauge, serviceImpl.GetGauge).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static MetricsServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new MetricsServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,5 +1,37 @@
|
|||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: src/proto/grpc/testing/services.proto
|
||||
// Original file comments:
|
||||
// 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.
|
||||
//
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
#region Designer generated code
|
||||
|
||||
using System;
|
||||
|
|
@ -29,40 +61,80 @@ namespace Grpc.Testing {
|
|||
__Marshaller_SimpleRequest,
|
||||
__Marshaller_SimpleResponse);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.ServicesReflection.Descriptor.Services[0]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for BenchmarkService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IBenchmarkServiceClient
|
||||
{
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of BenchmarkService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IBenchmarkService
|
||||
{
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
Task StreamingCall(IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of BenchmarkService</summary>
|
||||
public abstract class BenchmarkServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual Task StreamingCall(IAsyncStreamReader<global::Grpc.Testing.SimpleRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.SimpleResponse> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -70,7 +142,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for BenchmarkService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class BenchmarkServiceClient : ClientBase<BenchmarkServiceClient>, IBenchmarkServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -90,26 +162,50 @@ namespace Grpc.Testing {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnaryCall(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return StreamingCall(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// The server returns the client payload as-is.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_StreamingCall, null, options);
|
||||
|
|
@ -120,7 +216,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for BenchmarkService</summary>
|
||||
public static BenchmarkServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new BenchmarkServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IBenchmarkService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -130,7 +232,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_StreamingCall, serviceImpl.StreamingCall).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(BenchmarkServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -140,12 +242,6 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_StreamingCall, serviceImpl.StreamingCall).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static BenchmarkServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new BenchmarkServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
public static class WorkerService
|
||||
{
|
||||
|
|
@ -187,58 +283,158 @@ namespace Grpc.Testing {
|
|||
__Marshaller_Void,
|
||||
__Marshaller_Void);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.ServicesReflection.Descriptor.Services[1]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for WorkerService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IWorkerServiceClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(CallOptions options);
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(CallOptions options);
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, CallOptions options);
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of WorkerService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IWorkerService
|
||||
{
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
Task RunServer(IAsyncStreamReader<global::Grpc.Testing.ServerArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ServerStatus> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
Task RunClient(IAsyncStreamReader<global::Grpc.Testing.ClientArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ClientStatus> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.CoreResponse> CoreCount(global::Grpc.Testing.CoreRequest request, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.Void> QuitWorker(global::Grpc.Testing.Void request, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of WorkerService</summary>
|
||||
public abstract class WorkerServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual Task RunServer(IAsyncStreamReader<global::Grpc.Testing.ServerArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ServerStatus> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual Task RunClient(IAsyncStreamReader<global::Grpc.Testing.ClientArgs> requestStream, IServerStreamWriter<global::Grpc.Testing.ClientStatus> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.CoreResponse> CoreCount(global::Grpc.Testing.CoreRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.Void> QuitWorker(global::Grpc.Testing.Void request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -246,7 +442,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for WorkerService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class WorkerServiceClient : ClientBase<WorkerServiceClient>, IWorkerServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -266,50 +462,106 @@ namespace Grpc.Testing {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return RunServer(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Start server with specified workload.
|
||||
/// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test server
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_RunServer, null, options);
|
||||
}
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return RunClient(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Start client with specified workload.
|
||||
/// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
/// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
/// stats. Closing the stream will initiate shutdown of the test client
|
||||
/// and once the shutdown has finished, the OK status is sent to terminate
|
||||
/// this RPC.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_RunClient, null, options);
|
||||
}
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return CoreCount(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_CoreCount, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return CoreCountAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Just return the core count - unary call
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_CoreCount, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return QuitWorker(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_QuitWorker, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return QuitWorkerAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// Quit this worker
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_QuitWorker, null, options, request);
|
||||
|
|
@ -320,7 +572,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for WorkerService</summary>
|
||||
public static WorkerServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new WorkerServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IWorkerService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -332,7 +590,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_QuitWorker, serviceImpl.QuitWorker).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(WorkerServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -344,12 +602,6 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_QuitWorker, serviceImpl.QuitWorker).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static WorkerServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new WorkerServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,5 +1,38 @@
|
|||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: src/proto/grpc/testing/test.proto
|
||||
// Original file comments:
|
||||
// Copyright 2015-2016, 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.
|
||||
//
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
//
|
||||
#region Designer generated code
|
||||
|
||||
using System;
|
||||
|
|
@ -8,6 +41,10 @@ using System.Threading.Tasks;
|
|||
using Grpc.Core;
|
||||
|
||||
namespace Grpc.Testing {
|
||||
/// <summary>
|
||||
/// A simple service to test the various types of RPCs and experiment with
|
||||
/// performance with various types of payload.
|
||||
/// </summary>
|
||||
public static class TestService
|
||||
{
|
||||
static readonly string __ServiceName = "grpc.testing.TestService";
|
||||
|
|
@ -62,74 +99,186 @@ namespace Grpc.Testing {
|
|||
__Marshaller_StreamingOutputCallRequest,
|
||||
__Marshaller_StreamingOutputCallResponse);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.TestReflection.Descriptor.Services[0]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for TestService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface ITestServiceClient
|
||||
{
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, CallOptions options);
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options);
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options);
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of TestService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface ITestService
|
||||
{
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.Empty> EmptyCall(global::Grpc.Testing.Empty request, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
Task StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<global::Grpc.Testing.StreamingInputCallRequest> requestStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
Task FullDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context);
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
Task HalfDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of TestService</summary>
|
||||
public abstract class TestServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.Empty> EmptyCall(global::Grpc.Testing.Empty request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.SimpleResponse> UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
public virtual Task StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<global::Grpc.Testing.StreamingInputCallRequest> requestStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
public virtual Task FullDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual Task HalfDuplexCall(IAsyncStreamReader<global::Grpc.Testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::Grpc.Testing.StreamingOutputCallResponse> responseStream, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -137,7 +286,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for TestService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class TestServiceClient : ClientBase<TestServiceClient>, ITestServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -157,66 +306,128 @@ namespace Grpc.Testing {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return EmptyCall(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_EmptyCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return EmptyCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One empty request followed by one empty response.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_EmptyCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnaryCall(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_UnaryCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by one response.
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return StreamingOutputCall(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// One request followed by a sequence of responses (streamed download).
|
||||
/// The server returns the payload with client desired type and sizes.
|
||||
/// </summary>
|
||||
public virtual AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncServerStreamingCall(__Method_StreamingOutputCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
public virtual AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return StreamingInputCall(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// A sequence of requests followed by one response (streamed upload).
|
||||
/// The server returns the aggregated size of client payload as the result.
|
||||
/// </summary>
|
||||
public virtual AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncClientStreamingCall(__Method_StreamingInputCall, null, options);
|
||||
}
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return FullDuplexCall(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// 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 duplexing.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_FullDuplexCall, null, options);
|
||||
}
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return HalfDuplexCall(new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncDuplexStreamingCall(__Method_HalfDuplexCall, null, options);
|
||||
|
|
@ -227,7 +438,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for TestService</summary>
|
||||
public static TestServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new TestServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(ITestService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -241,7 +458,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(TestServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -255,13 +472,11 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static TestServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new TestServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// A simple service NOT implemented at servers so clients can test for
|
||||
/// that case.
|
||||
/// </summary>
|
||||
public static class UnimplementedService
|
||||
{
|
||||
static readonly string __ServiceName = "grpc.testing.UnimplementedService";
|
||||
|
|
@ -275,32 +490,50 @@ namespace Grpc.Testing {
|
|||
__Marshaller_Empty,
|
||||
__Marshaller_Empty);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.TestReflection.Descriptor.Services[1]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for UnimplementedService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IUnimplementedServiceClient
|
||||
{
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options);
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of UnimplementedService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IUnimplementedService
|
||||
{
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of UnimplementedService</summary>
|
||||
public abstract class UnimplementedServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
public virtual Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.Unimplemented, ""));
|
||||
|
|
@ -308,7 +541,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for UnimplementedService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class UnimplementedServiceClient : ClientBase<UnimplementedServiceClient>, IUnimplementedServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -328,18 +561,30 @@ namespace Grpc.Testing {
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnimplementedCall(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request);
|
||||
}
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return UnimplementedCallAsync(request, new CallOptions(headers, deadline, cancellationToken));
|
||||
}
|
||||
/// <summary>
|
||||
/// A call that no server should implement
|
||||
/// </summary>
|
||||
public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
|
||||
{
|
||||
return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request);
|
||||
|
|
@ -350,7 +595,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for UnimplementedService</summary>
|
||||
public static UnimplementedServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new UnimplementedServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IUnimplementedService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -359,7 +610,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(UnimplementedServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -368,13 +619,10 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static UnimplementedServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new UnimplementedServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// A service used to control reconnect server.
|
||||
/// </summary>
|
||||
public static class ReconnectService
|
||||
{
|
||||
static readonly string __ServiceName = "grpc.testing.ReconnectService";
|
||||
|
|
@ -397,13 +645,13 @@ namespace Grpc.Testing {
|
|||
__Marshaller_Empty,
|
||||
__Marshaller_ReconnectInfo);
|
||||
|
||||
// service descriptor
|
||||
/// <summary>Service descriptor</summary>
|
||||
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
|
||||
{
|
||||
get { return global::Grpc.Testing.TestReflection.Descriptor.Services[2]; }
|
||||
}
|
||||
|
||||
// client interface
|
||||
/// <summary>Client for ReconnectService</summary>
|
||||
[System.Obsolete("Client side interfaced will be removed in the next release. Use client class directly.")]
|
||||
public interface IReconnectServiceClient
|
||||
{
|
||||
|
|
@ -417,7 +665,7 @@ namespace Grpc.Testing {
|
|||
AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, CallOptions options);
|
||||
}
|
||||
|
||||
// server-side interface
|
||||
/// <summary>Interface of server-side implementations of ReconnectService</summary>
|
||||
[System.Obsolete("Service implementations should inherit from the generated abstract base class instead.")]
|
||||
public interface IReconnectService
|
||||
{
|
||||
|
|
@ -425,7 +673,7 @@ namespace Grpc.Testing {
|
|||
Task<global::Grpc.Testing.ReconnectInfo> Stop(global::Grpc.Testing.Empty request, ServerCallContext context);
|
||||
}
|
||||
|
||||
// server-side abstract class
|
||||
/// <summary>Base class for server-side implementations of ReconnectService</summary>
|
||||
public abstract class ReconnectServiceBase
|
||||
{
|
||||
public virtual Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.ReconnectParams request, ServerCallContext context)
|
||||
|
|
@ -440,7 +688,7 @@ namespace Grpc.Testing {
|
|||
|
||||
}
|
||||
|
||||
// client stub
|
||||
/// <summary>Client for ReconnectService</summary>
|
||||
#pragma warning disable 0618
|
||||
public class ReconnectServiceClient : ClientBase<ReconnectServiceClient>, IReconnectServiceClient
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -498,7 +746,13 @@ namespace Grpc.Testing {
|
|||
}
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates a new client for ReconnectService</summary>
|
||||
public static ReconnectServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new ReconnectServiceClient(channel);
|
||||
}
|
||||
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(IReconnectService serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -508,7 +762,7 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_Stop, serviceImpl.Stop).Build();
|
||||
}
|
||||
|
||||
// creates service definition that can be registered with a server
|
||||
/// <summary>Creates service definition that can be registered with a server</summary>
|
||||
#pragma warning disable 0618
|
||||
public static ServerServiceDefinition BindService(ReconnectServiceBase serviceImpl)
|
||||
#pragma warning restore 0618
|
||||
|
|
@ -518,12 +772,6 @@ namespace Grpc.Testing {
|
|||
.AddMethod(__Method_Stop, serviceImpl.Stop).Build();
|
||||
}
|
||||
|
||||
// creates a new client
|
||||
public static ReconnectServiceClient NewClient(Channel channel)
|
||||
{
|
||||
return new ReconnectServiceClient(channel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include <nan.h>
|
||||
#include <v8.h>
|
||||
#include "grpc/grpc.h"
|
||||
#include "grpc/grpc_security.h"
|
||||
#include "grpc/support/alloc.h"
|
||||
|
||||
#include "call.h"
|
||||
#include "call_credentials.h"
|
||||
|
|
@ -51,6 +53,8 @@ using v8::Object;
|
|||
using v8::Uint32;
|
||||
using v8::String;
|
||||
|
||||
static char *pem_root_certs = NULL;
|
||||
|
||||
void InitStatusConstants(Local<Object> exports) {
|
||||
Nan::HandleScope scope;
|
||||
Local<Object> status = Nan::New<Object>();
|
||||
|
|
@ -268,9 +272,36 @@ NAN_METHOD(MetadataKeyIsBinary) {
|
|||
grpc_is_binary_header(key_str, static_cast<size_t>(key->Length()))));
|
||||
}
|
||||
|
||||
static grpc_ssl_roots_override_result get_ssl_roots_override(
|
||||
char **pem_root_certs_ptr) {
|
||||
*pem_root_certs_ptr = pem_root_certs;
|
||||
if (pem_root_certs == NULL) {
|
||||
return GRPC_SSL_ROOTS_OVERRIDE_FAIL;
|
||||
} else {
|
||||
return GRPC_SSL_ROOTS_OVERRIDE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* This should only be called once, and only before creating any
|
||||
*ServerCredentials */
|
||||
NAN_METHOD(SetDefaultRootsPem) {
|
||||
if (!info[0]->IsString()) {
|
||||
return Nan::ThrowTypeError(
|
||||
"setDefaultRootsPem's argument must be a string");
|
||||
}
|
||||
Nan::Utf8String utf8_roots(info[0]);
|
||||
size_t length = static_cast<size_t>(utf8_roots.length());
|
||||
if (length > 0) {
|
||||
const char *data = *utf8_roots;
|
||||
pem_root_certs = (char *)gpr_malloc((length + 1) * sizeof(char));
|
||||
memcpy(pem_root_certs, data, length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void init(Local<Object> exports) {
|
||||
Nan::HandleScope scope;
|
||||
grpc_init();
|
||||
grpc_set_ssl_roots_override_callback(get_ssl_roots_override);
|
||||
InitStatusConstants(exports);
|
||||
InitCallErrorConstants(exports);
|
||||
InitOpTypeConstants(exports);
|
||||
|
|
@ -298,6 +329,10 @@ void init(Local<Object> exports) {
|
|||
Nan::GetFunction(
|
||||
Nan::New<FunctionTemplate>(MetadataKeyIsBinary)
|
||||
).ToLocalChecked());
|
||||
Nan::Set(exports, Nan::New("setDefaultRootsPem").ToLocalChecked(),
|
||||
Nan::GetFunction(
|
||||
Nan::New<FunctionTemplate>(SetDefaultRootsPem)
|
||||
).ToLocalChecked());
|
||||
}
|
||||
|
||||
NODE_MODULE(grpc_node, init)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ NAN_METHOD(ServerCredentials::CreateSsl) {
|
|||
"createSsl's second argument must be a list of objects");
|
||||
}
|
||||
|
||||
grpc_ssl_client_certificate_request_type client_certificate_request;
|
||||
// Default to not requesting the client certificate
|
||||
grpc_ssl_client_certificate_request_type client_certificate_request =
|
||||
GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
|
||||
if (info[2]->IsBoolean()) {
|
||||
client_certificate_request =
|
||||
Nan::To<bool>(info[2]).FromJust()
|
||||
|
|
|
|||
|
|
@ -34,13 +34,10 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var SSL_ROOTS_PATH = path.resolve(__dirname, '..', '..', 'etc', 'roots.pem');
|
||||
|
||||
if (!process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH) {
|
||||
process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH = SSL_ROOTS_PATH;
|
||||
}
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
var ProtoBuf = require('protobufjs');
|
||||
|
|
@ -53,6 +50,8 @@ var Metadata = require('./src/metadata.js');
|
|||
|
||||
var grpc = require('./src/grpc_extension');
|
||||
|
||||
grpc.setDefaultRootsPem(fs.readFileSync(SSL_ROOTS_PATH, 'ascii'));
|
||||
|
||||
/**
|
||||
* Load a gRPC object from an existing ProtoBuf.Reflect object.
|
||||
* @param {ProtoBuf.Reflect.Namespace} value The ProtoBuf object to load.
|
||||
|
|
|
|||
|
|
@ -684,6 +684,26 @@ Server.prototype.register = function(name, handler, serialize, deserialize,
|
|||
return true;
|
||||
};
|
||||
|
||||
var unimplementedStatusResponse = {
|
||||
code: grpc.status.UNIMPLEMENTED,
|
||||
details: 'The server does not implement this method'
|
||||
};
|
||||
|
||||
var defaultHandler = {
|
||||
unary: function(call, callback) {
|
||||
callback(unimplementedStatusResponse);
|
||||
},
|
||||
client_stream: function(call, callback) {
|
||||
callback(unimplementedStatusResponse);
|
||||
},
|
||||
server_stream: function(call) {
|
||||
call.emit('error', unimplementedStatusResponse);
|
||||
},
|
||||
bidi: function(call) {
|
||||
call.emit('error', unimplementedStatusResponse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a service to the server, with a corresponding implementation. If you are
|
||||
* generating this from a proto file, you should instead use
|
||||
|
|
@ -713,16 +733,18 @@ Server.prototype.addService = function(service, implementation) {
|
|||
method_type = 'unary';
|
||||
}
|
||||
}
|
||||
var impl;
|
||||
if (implementation[name] === undefined) {
|
||||
throw new Error('Method handler for ' + attrs.path +
|
||||
' not provided.');
|
||||
console.warn('Method handler for %s expected but not provided',
|
||||
attrs.path);
|
||||
impl = defaultHandler[method_type];
|
||||
} else {
|
||||
impl = _.bind(implementation[name], implementation);
|
||||
}
|
||||
var serialize = attrs.responseSerialize;
|
||||
var deserialize = attrs.requestDeserialize;
|
||||
var register_success = self.register(attrs.path,
|
||||
_.bind(implementation[name],
|
||||
implementation),
|
||||
serialize, deserialize, method_type);
|
||||
var register_success = self.register(attrs.path, impl, serialize,
|
||||
deserialize, method_type);
|
||||
if (!register_success) {
|
||||
throw new Error('Method handler for ' + attrs.path +
|
||||
' already provided.');
|
||||
|
|
|
|||
|
|
@ -143,21 +143,59 @@ describe('Server.prototype.addProtoService', function() {
|
|||
server.addProtoService(mathService, dummyImpls);
|
||||
});
|
||||
});
|
||||
it('Should fail with missing handlers', function() {
|
||||
assert.throws(function() {
|
||||
server.addProtoService(mathService, {
|
||||
'div': function() {},
|
||||
'divMany': function() {},
|
||||
'fib': function() {}
|
||||
});
|
||||
}, /math.Math.Sum/);
|
||||
});
|
||||
it('Should fail if the server has been started', function() {
|
||||
server.start();
|
||||
assert.throws(function() {
|
||||
server.addProtoService(mathService, dummyImpls);
|
||||
});
|
||||
});
|
||||
describe('Default handlers', function() {
|
||||
var client;
|
||||
beforeEach(function() {
|
||||
server.addProtoService(mathService, {});
|
||||
var port = server.bind('localhost:0', server_insecure_creds);
|
||||
var Client = surface_client.makeProtobufClientConstructor(mathService);
|
||||
client = new Client('localhost:' + port,
|
||||
grpc.credentials.createInsecure());
|
||||
server.start();
|
||||
});
|
||||
it('should respond to a unary call with UNIMPLEMENTED', function(done) {
|
||||
client.div({divisor: 4, dividend: 3}, function(error, response) {
|
||||
assert(error);
|
||||
assert.strictEqual(error.code, grpc.status.UNIMPLEMENTED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should respond to a client stream with UNIMPLEMENTED', function(done) {
|
||||
var call = client.sum(function(error, respones) {
|
||||
assert(error);
|
||||
assert.strictEqual(error.code, grpc.status.UNIMPLEMENTED);
|
||||
done();
|
||||
});
|
||||
call.end();
|
||||
});
|
||||
it('should respond to a server stream with UNIMPLEMENTED', function(done) {
|
||||
var call = client.fib({limit: 5});
|
||||
call.on('data', function(value) {
|
||||
assert.fail('No messages expected');
|
||||
});
|
||||
call.on('status', function(status) {
|
||||
assert.strictEqual(status.code, grpc.status.UNIMPLEMENTED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should respond to a bidi call with UNIMPLEMENTED', function(done) {
|
||||
var call = client.divMany();
|
||||
call.on('data', function(value) {
|
||||
assert.fail('No messages expected');
|
||||
});
|
||||
call.on('status', function(status) {
|
||||
assert.strictEqual(status.code, grpc.status.UNIMPLEMENTED);
|
||||
done();
|
||||
});
|
||||
call.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Client constructor building', function() {
|
||||
var illegal_service_attrs = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env node
|
||||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is required because package.json cannot reference a file that
|
||||
* is not distributed with the package, and we use node-pre-gyp to distribute
|
||||
* the plugin binary
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var execFile = require('child_process').execFile;
|
||||
|
||||
var protoc = path.resolve(__dirname, 'grpc_node_plugin');
|
||||
|
||||
execFile(protoc, process.argv.slice(2), function(error, stdout, stderr) {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
console.log(stdout);
|
||||
console.log(stderr);
|
||||
});
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
}
|
||||
],
|
||||
"bin": {
|
||||
"grpc-tools-protoc": "./bin/protoc.js"
|
||||
"grpc-tools-protoc": "./bin/protoc.js",
|
||||
"grpc-tools-plugin": "./bin/protoc_plugin.js"
|
||||
},
|
||||
"scripts": {
|
||||
"install": "./node_modules/.bin/node-pre-gyp install"
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
"files": [
|
||||
"index.js",
|
||||
"bin/protoc.js",
|
||||
"bin/protoc_plugin.js",
|
||||
"LICENSE"
|
||||
],
|
||||
"main": "index.js"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2016, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#import "GRPCCall.h"
|
||||
|
||||
/** Helpers for setting TLS Trusted Roots, Client Certificates, and Private Key */
|
||||
@interface GRPCCall (ChannelCredentials)
|
||||
|
||||
/**
|
||||
* Use the provided @c pemRootCert as the set of trusted root Certificate Authorities for @c host.
|
||||
*/
|
||||
+ (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCert
|
||||
forHost:(nonnull NSString *)host
|
||||
error:(NSError **)errorPtr;
|
||||
/**
|
||||
* Configures @c host with TLS/SSL Client Credentials and optionally trusted root Certificate
|
||||
* Authorities. If @c pemRootCerts is nil, the default CA Certificates bundled with gRPC will be
|
||||
* used.
|
||||
*/
|
||||
+ (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts
|
||||
withPrivateKey:(nullable NSString *)pemPrivateKey
|
||||
withCertChain:(nullable NSString *)pemCertChain
|
||||
forHost:(nonnull NSString *)host
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2016, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#import "GRPCCall+ChannelCredentials.h"
|
||||
|
||||
#import "private/GRPCHost.h"
|
||||
|
||||
@implementation GRPCCall (ChannelCredentials)
|
||||
|
||||
+ (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts
|
||||
withPrivateKey:(nullable NSString *)pemPrivateKey
|
||||
withCertChain:(nullable NSString *)pemCertChain
|
||||
forHost:(nonnull NSString *)host
|
||||
error:(NSError **)errorPtr {
|
||||
if (!host) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"host must be provided."];
|
||||
}
|
||||
GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
|
||||
return [hostConfig setTLSPEMRootCerts:pemRootCerts
|
||||
withPrivateKey:pemPrivateKey
|
||||
withCertChain:pemCertChain
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
+ (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts
|
||||
forHost:(nonnull NSString *)host
|
||||
error:(NSError **)errorPtr {
|
||||
return [GRPCCall setTLSPEMRootCerts:pemRootCerts
|
||||
withPrivateKey:nil
|
||||
withCertChain:nil
|
||||
forHost:host
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015, Google Inc.
|
||||
* Copyright 2015-2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -43,8 +43,16 @@
|
|||
if (!host || !certsPath || !testName) {
|
||||
[NSException raise:NSInvalidArgumentException format:@"host, path and name must be provided."];
|
||||
}
|
||||
NSError *error = nil;
|
||||
NSString *certs = [NSString stringWithContentsOfFile:certsPath
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&error];
|
||||
if (error != nil) {
|
||||
[NSException raise:[error localizedDescription] format:@"failed to load certs"];
|
||||
}
|
||||
|
||||
GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
|
||||
hostConfig.pathToCertificates = certsPath;
|
||||
[hostConfig setTLSPEMRootCerts:certs withPrivateKey:nil withCertChain:nil error:nil];
|
||||
hostConfig.hostNameOverride = testName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,18 +55,6 @@ struct grpc_channel_credentials;
|
|||
*/
|
||||
+ (nullable GRPCChannel *)secureChannelWithHost:(nonnull NSString *)host;
|
||||
|
||||
/**
|
||||
* Creates a secure channel to the specified @c host using the specified @c pathToCertificates and
|
||||
* @c channelArgs. Only in tests should @c pathToCertificates be nil or
|
||||
* @c GRPC_SSL_TARGET_NAME_OVERRIDE_ARG channel arg be set. Passing nil for @c pathToCertificates
|
||||
* results in using the default root certificates distributed with the library. If certificates
|
||||
* could not be found in any case, then @c nil is returned.
|
||||
*/
|
||||
+ (nullable GRPCChannel *)secureChannelWithHost:(nonnull NSString *)host
|
||||
pathToCertificates:(nullable NSString *)pathToCertificates
|
||||
channelArgs:(nullable NSDictionary *)channelArgs;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a secure channel to the specified @c host using the specified @c credentials and
|
||||
* @c channelArgs. Only in tests should @c GRPC_SSL_TARGET_NAME_OVERRIDE_ARG channel arg be set.
|
||||
|
|
|
|||
|
|
@ -40,26 +40,6 @@
|
|||
|
||||
#import "GRPCCompletionQueue.h"
|
||||
|
||||
/**
|
||||
* Returns @c grpc_channel_credentials from the specified @c path. If the file at the path could not
|
||||
* be read then NULL is returned. If NULL is returned, @c errorPtr may not be NULL if there are
|
||||
* details available describing what went wrong.
|
||||
*/
|
||||
static grpc_channel_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) {
|
||||
// Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the
|
||||
// issuer). Load them as UTF8 and produce an ASCII equivalent.
|
||||
NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:errorPtr];
|
||||
NSData *contentInASCII = [contentInUTF8 dataUsingEncoding:NSASCIIStringEncoding
|
||||
allowLossyConversion:YES];
|
||||
if (!contentInASCII.bytes) {
|
||||
// Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return.
|
||||
return NULL;
|
||||
}
|
||||
return grpc_ssl_credentials_create(contentInASCII.bytes, NULL, NULL);
|
||||
}
|
||||
|
||||
void freeChannelArgs(grpc_channel_args *channel_args) {
|
||||
for (size_t i = 0; i < channel_args->num_args; ++i) {
|
||||
grpc_arg *arg = &channel_args->args[i];
|
||||
|
|
@ -157,38 +137,6 @@ grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
|
|||
return [[GRPCChannel alloc] initWithHost:host secure:YES credentials:NULL channelArgs:NULL];
|
||||
}
|
||||
|
||||
+ (GRPCChannel *)secureChannelWithHost:(NSString *)host
|
||||
pathToCertificates:(NSString *)path
|
||||
channelArgs:(NSDictionary *)channelArgs {
|
||||
// Load default SSL certificates once.
|
||||
static grpc_channel_credentials *kDefaultCertificates;
|
||||
static dispatch_once_t loading;
|
||||
dispatch_once(&loading, ^{
|
||||
NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem
|
||||
// Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
|
||||
NSBundle *bundle = [NSBundle bundleForClass:self.class];
|
||||
NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
|
||||
NSError *error;
|
||||
kDefaultCertificates = CertificatesAtPath(path, &error);
|
||||
NSAssert(kDefaultCertificates, @"Could not read %@/%@.pem. This file, with the root "
|
||||
"certificates, is needed to establish secure (TLS) connections. Because the file is "
|
||||
"distributed with the gRPC library, this error is usually a sign that the library "
|
||||
"wasn't configured correctly for your project. Error: %@",
|
||||
bundle.bundlePath, defaultPath, error);
|
||||
});
|
||||
|
||||
//TODO(jcanizales): Add NSError** parameter to the initializer.
|
||||
grpc_channel_credentials *certificates = path
|
||||
? CertificatesAtPath(path, NULL)
|
||||
: kDefaultCertificates;
|
||||
|
||||
return [[GRPCChannel alloc] initWithHost:host
|
||||
secure:YES
|
||||
credentials:certificates
|
||||
channelArgs:channelArgs];
|
||||
}
|
||||
|
||||
|
||||
+ (GRPCChannel *)secureChannelWithHost:(NSString *)host
|
||||
credentials:(struct grpc_channel_credentials *)credentials
|
||||
channelArgs:(NSDictionary *)channelArgs {
|
||||
|
|
|
|||
|
|
@ -37,23 +37,28 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@class GRPCCompletionQueue;
|
||||
struct grpc_call;
|
||||
struct grpc_channel_credentials;
|
||||
|
||||
@interface GRPCHost : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *address;
|
||||
@property(nonatomic, copy, nullable) NSString *userAgentPrefix;
|
||||
@property(nonatomic, nullable) struct grpc_channel_credentials *channelCreds;
|
||||
|
||||
/** The following properties should only be modified for testing: */
|
||||
|
||||
@property(nonatomic, getter=isSecure) BOOL secure;
|
||||
|
||||
@property(nonatomic, copy, nullable) NSString *pathToCertificates;
|
||||
@property(nonatomic, copy, nullable) NSString *hostNameOverride;
|
||||
|
||||
- (nullable instancetype)init NS_UNAVAILABLE;
|
||||
/** Host objects initialized with the same address are the same. */
|
||||
+ (nullable instancetype)hostWithAddress:(NSString *)address;
|
||||
- (nullable instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
|
||||
- (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts
|
||||
withPrivateKey:(nullable NSString *)pemPrivateKey
|
||||
withCertChain:(nullable NSString *)pemCertChain
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/** Create a grpc_call object to the provided path on this host. */
|
||||
- (nullable struct grpc_call *)unmanagedCallWithPath:(NSString *)path
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#import "GRPCHost.h"
|
||||
|
||||
#include <grpc/grpc.h>
|
||||
#include <grpc/grpc_security.h>
|
||||
#import <GRPCClient/GRPCCall.h>
|
||||
#import <GRPCClient/GRPCCall+ChannelArg.h>
|
||||
|
||||
|
|
@ -56,6 +57,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [[self alloc] initWithAddress:address];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_channelCreds != nil) {
|
||||
grpc_channel_credentials_release(_channelCreds);
|
||||
}
|
||||
}
|
||||
|
||||
// Default initializer.
|
||||
- (nullable instancetype)initWithAddress:(NSString *)address {
|
||||
if (!address) {
|
||||
|
|
@ -105,6 +112,75 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [channel unmanagedCallWithPath:path completionQueue:queue];
|
||||
}
|
||||
|
||||
- (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts
|
||||
withPrivateKey:(nullable NSString *)pemPrivateKey
|
||||
withCertChain:(nullable NSString *)pemCertChain
|
||||
error:(NSError **)errorPtr {
|
||||
static NSData *kDefaultRootsASCII;
|
||||
static NSError *kDefaultRootsError;
|
||||
static dispatch_once_t loading;
|
||||
dispatch_once(&loading, ^{
|
||||
NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem
|
||||
// Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
|
||||
NSBundle *bundle = [NSBundle bundleForClass:self.class];
|
||||
NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
|
||||
NSError *error;
|
||||
// Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the
|
||||
// issuer). Load them as UTF8 and produce an ASCII equivalent.
|
||||
NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&error];
|
||||
if (contentInUTF8 == nil) {
|
||||
kDefaultRootsError = error;
|
||||
return;
|
||||
}
|
||||
kDefaultRootsASCII = [contentInUTF8 dataUsingEncoding:NSASCIIStringEncoding
|
||||
allowLossyConversion:YES];
|
||||
});
|
||||
|
||||
NSData *rootsASCII;
|
||||
if (pemRootCerts != nil) {
|
||||
rootsASCII = [pemRootCerts dataUsingEncoding:NSASCIIStringEncoding
|
||||
allowLossyConversion:YES];
|
||||
} else {
|
||||
if (kDefaultRootsASCII == nil) {
|
||||
if (errorPtr) {
|
||||
*errorPtr = kDefaultRootsError;
|
||||
}
|
||||
NSAssert(kDefaultRootsASCII, @"Could not read gRPCCertificates.bundle/roots.pem. This file, "
|
||||
"with the root certificates, is needed to establish secure (TLS) connections. "
|
||||
"Because the file is distributed with the gRPC library, this error is usually a sign "
|
||||
"that the library wasn't configured correctly for your project. Error: %@",
|
||||
kDefaultRootsError);
|
||||
return NO;
|
||||
}
|
||||
rootsASCII = kDefaultRootsASCII;
|
||||
}
|
||||
|
||||
grpc_channel_credentials *creds;
|
||||
if (pemPrivateKey == nil && pemCertChain == nil) {
|
||||
creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL);
|
||||
} else {
|
||||
grpc_ssl_pem_key_cert_pair key_cert_pair;
|
||||
NSData *privateKeyASCII = [pemPrivateKey dataUsingEncoding:NSASCIIStringEncoding
|
||||
allowLossyConversion:YES];
|
||||
NSData *certChainASCII = [pemCertChain dataUsingEncoding:NSASCIIStringEncoding
|
||||
allowLossyConversion:YES];
|
||||
key_cert_pair.private_key = privateKeyASCII.bytes;
|
||||
key_cert_pair.cert_chain = certChainASCII.bytes;
|
||||
creds = grpc_ssl_credentials_create(rootsASCII.bytes, &key_cert_pair, NULL);
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
if (_channelCreds != nil) {
|
||||
grpc_channel_credentials_release(_channelCreds);
|
||||
}
|
||||
_channelCreds = creds;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSDictionary *)channelArgs {
|
||||
NSMutableDictionary *args = [NSMutableDictionary dictionary];
|
||||
|
||||
|
|
@ -125,9 +201,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (GRPCChannel *)newChannel {
|
||||
NSDictionary *args = [self channelArgs];
|
||||
if (_secure) {
|
||||
return [GRPCChannel secureChannelWithHost:_address
|
||||
pathToCertificates:_pathToCertificates
|
||||
channelArgs:args];
|
||||
GRPCChannel *channel;
|
||||
@synchronized(self) {
|
||||
if (_channelCreds == nil) {
|
||||
[self setTLSPEMRootCerts:nil withPrivateKey:nil withCertChain:nil error:nil];
|
||||
}
|
||||
channel = [GRPCChannel secureChannelWithHost:_address
|
||||
credentials:_channelCreds
|
||||
channelArgs:args];
|
||||
}
|
||||
return channel;
|
||||
} else {
|
||||
return [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
|
||||
}
|
||||
|
|
@ -145,9 +228,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(jcanizales): Don't let set |secure| to |NO| if |pathToCertificates| or |hostNameOverride|
|
||||
// have been set. Don't let set either of the latter if |secure| has been set to |NO|.
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -23,6 +23,16 @@ Else system wide (on Ubuntu)...
|
|||
|
||||
$ sudo pip install grpcio
|
||||
|
||||
If you're on Windows make sure that you installed the :code:`pip.exe` component
|
||||
when you installed Python (if not go back and install it!) then invoke:
|
||||
|
||||
::
|
||||
|
||||
$ pip.exe install grpcio
|
||||
|
||||
Windows users may need to invoke :code:`pip.exe` from a command line ran as
|
||||
administrator.
|
||||
|
||||
n.b. On Windows and on Mac OS X one *must* have a recent release of :code:`pip`
|
||||
to retrieve the proper wheel from PyPI. Be sure to upgrade to the latest
|
||||
version!
|
||||
|
|
@ -43,6 +53,9 @@ package named :code:`python-dev`).
|
|||
$ pip install -rrequirements.txt
|
||||
$ GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install .
|
||||
|
||||
You cannot currently install Python from source on Windows. Things might work
|
||||
out for you in MSYS2 (follow the Linux instructions), but it isn't officially
|
||||
supported at the moment.
|
||||
|
||||
Troubleshooting
|
||||
~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -1,363 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire."""
|
||||
|
||||
import enum
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
from grpc._adapter import _common
|
||||
from grpc._adapter import _intermediary_low as _low
|
||||
from grpc.framework.base import interfaces as base_interfaces
|
||||
from grpc.framework.base import null
|
||||
from grpc.framework.foundation import activated
|
||||
from grpc.framework.foundation import logging_pool
|
||||
|
||||
_THREAD_POOL_SIZE = 10
|
||||
|
||||
|
||||
@enum.unique
|
||||
class _LowWrite(enum.Enum):
|
||||
"""The possible categories of low-level write state."""
|
||||
|
||||
OPEN = 'OPEN'
|
||||
ACTIVE = 'ACTIVE'
|
||||
CLOSED = 'CLOSED'
|
||||
|
||||
|
||||
def _write(call, rpc_state, payload):
|
||||
serialized_payload = rpc_state.serializer(payload)
|
||||
if rpc_state.write.low is _LowWrite.OPEN:
|
||||
call.write(serialized_payload, call, 0)
|
||||
rpc_state.write.low = _LowWrite.ACTIVE
|
||||
else:
|
||||
rpc_state.write.pending.append(serialized_payload)
|
||||
|
||||
|
||||
def _status(call, rpc_state):
|
||||
call.status(_low.Status(_low.Code.OK, ''), call)
|
||||
rpc_state.write.low = _LowWrite.CLOSED
|
||||
|
||||
|
||||
class ForeLink(base_interfaces.ForeLink, activated.Activated):
|
||||
"""A service-side bridge between RPC Framework and the C-ish _low code."""
|
||||
|
||||
def __init__(
|
||||
self, pool, request_deserializers, response_serializers,
|
||||
root_certificates, key_chain_pairs, port=None):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
pool: A thread pool.
|
||||
request_deserializers: A dict from RPC method names to request object
|
||||
deserializer behaviors.
|
||||
response_serializers: A dict from RPC method names to response object
|
||||
serializer behaviors.
|
||||
root_certificates: The PEM-encoded client root certificates as a
|
||||
bytestring or None.
|
||||
key_chain_pairs: A sequence of PEM-encoded private key-certificate chain
|
||||
pairs.
|
||||
port: The port on which to serve, or None to have a port selected
|
||||
automatically.
|
||||
"""
|
||||
self._condition = threading.Condition()
|
||||
self._pool = pool
|
||||
self._request_deserializers = request_deserializers
|
||||
self._response_serializers = response_serializers
|
||||
self._root_certificates = root_certificates
|
||||
self._key_chain_pairs = key_chain_pairs
|
||||
self._requested_port = port
|
||||
|
||||
self._rear_link = null.NULL_REAR_LINK
|
||||
self._completion_queue = None
|
||||
self._server = None
|
||||
self._rpc_states = {}
|
||||
self._spinning = False
|
||||
self._port = None
|
||||
|
||||
def _on_stop_event(self):
|
||||
self._spinning = False
|
||||
self._condition.notify_all()
|
||||
|
||||
def _on_service_acceptance_event(self, event, server):
|
||||
"""Handle a service invocation event."""
|
||||
service_acceptance = event.service_acceptance
|
||||
if service_acceptance is None:
|
||||
return
|
||||
|
||||
call = service_acceptance.call
|
||||
call.accept(self._completion_queue, call)
|
||||
# TODO(nathaniel): Metadata support.
|
||||
call.premetadata()
|
||||
call.read(call)
|
||||
method = service_acceptance.method
|
||||
|
||||
self._rpc_states[call] = _common.CommonRPCState(
|
||||
_common.WriteState(_LowWrite.OPEN, _common.HighWrite.OPEN, []), 1,
|
||||
self._request_deserializers[method],
|
||||
self._response_serializers[method])
|
||||
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, 0, base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT, method,
|
||||
base_interfaces.ServicedSubscription.Kind.FULL, None, None,
|
||||
service_acceptance.deadline - time.time())
|
||||
self._rear_link.accept_front_to_back_ticket(ticket)
|
||||
|
||||
server.service(None)
|
||||
|
||||
def _on_read_event(self, event):
|
||||
"""Handle data arriving during an RPC."""
|
||||
call = event.tag
|
||||
rpc_state = self._rpc_states.get(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
sequence_number = rpc_state.sequence_number
|
||||
rpc_state.sequence_number += 1
|
||||
if event.bytes is None:
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None,
|
||||
None, None)
|
||||
else:
|
||||
call.read(call)
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None,
|
||||
None, rpc_state.deserializer(event.bytes), None)
|
||||
|
||||
self._rear_link.accept_front_to_back_ticket(ticket)
|
||||
|
||||
def _on_write_event(self, event):
|
||||
call = event.tag
|
||||
rpc_state = self._rpc_states.get(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
if rpc_state.write.pending:
|
||||
serialized_payload = rpc_state.write.pending.pop(0)
|
||||
call.write(serialized_payload, call, 0)
|
||||
elif rpc_state.write.high is _common.HighWrite.CLOSED:
|
||||
_status(call, rpc_state)
|
||||
else:
|
||||
rpc_state.write.low = _LowWrite.OPEN
|
||||
|
||||
def _on_complete_event(self, event):
|
||||
if not event.complete_accepted:
|
||||
logging.error('Complete not accepted! %s', (event,))
|
||||
call = event.tag
|
||||
rpc_state = self._rpc_states.pop(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
sequence_number = rpc_state.sequence_number
|
||||
rpc_state.sequence_number += 1
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None,
|
||||
None, None, None, None)
|
||||
self._rear_link.accept_front_to_back_ticket(ticket)
|
||||
|
||||
def _on_finish_event(self, event):
|
||||
"""Handle termination of an RPC."""
|
||||
call = event.tag
|
||||
rpc_state = self._rpc_states.pop(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
code = event.status.code
|
||||
if code is _low.Code.OK:
|
||||
return
|
||||
|
||||
sequence_number = rpc_state.sequence_number
|
||||
rpc_state.sequence_number += 1
|
||||
if code is _low.Code.CANCELLED:
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None,
|
||||
None, None, None)
|
||||
elif code is _low.Code.DEADLINE_EXCEEDED:
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None,
|
||||
None, None)
|
||||
else:
|
||||
# TODO(nathaniel): Better mapping of codes to ticket-categories
|
||||
ticket = base_interfaces.FrontToBackTicket(
|
||||
call, sequence_number,
|
||||
base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None,
|
||||
None, None, None, None)
|
||||
self._rear_link.accept_front_to_back_ticket(ticket)
|
||||
|
||||
def _spin(self, completion_queue, server):
|
||||
while True:
|
||||
event = completion_queue.get(None)
|
||||
|
||||
with self._condition:
|
||||
if event.kind is _low.Event.Kind.STOP:
|
||||
self._on_stop_event()
|
||||
return
|
||||
elif self._server is None:
|
||||
continue
|
||||
elif event.kind is _low.Event.Kind.SERVICE_ACCEPTED:
|
||||
self._on_service_acceptance_event(event, server)
|
||||
elif event.kind is _low.Event.Kind.READ_ACCEPTED:
|
||||
self._on_read_event(event)
|
||||
elif event.kind is _low.Event.Kind.WRITE_ACCEPTED:
|
||||
self._on_write_event(event)
|
||||
elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED:
|
||||
self._on_complete_event(event)
|
||||
elif event.kind is _low.Event.Kind.FINISH:
|
||||
self._on_finish_event(event)
|
||||
else:
|
||||
logging.error('Illegal event! %s', (event,))
|
||||
|
||||
def _continue(self, call, payload):
|
||||
rpc_state = self._rpc_states.get(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
_write(call, rpc_state, payload)
|
||||
|
||||
def _complete(self, call, payload):
|
||||
"""Handle completion of the writes of an RPC."""
|
||||
rpc_state = self._rpc_states.get(call, None)
|
||||
if rpc_state is None:
|
||||
return
|
||||
|
||||
if rpc_state.write.low is _LowWrite.OPEN:
|
||||
if payload is None:
|
||||
_status(call, rpc_state)
|
||||
else:
|
||||
_write(call, rpc_state, payload)
|
||||
elif rpc_state.write.low is _LowWrite.ACTIVE:
|
||||
if payload is not None:
|
||||
rpc_state.write.pending.append(rpc_state.serializer(payload))
|
||||
else:
|
||||
raise ValueError('Called to complete after having already completed!')
|
||||
rpc_state.write.high = _common.HighWrite.CLOSED
|
||||
|
||||
def _cancel(self, call):
|
||||
call.cancel()
|
||||
self._rpc_states.pop(call, None)
|
||||
|
||||
def join_rear_link(self, rear_link):
|
||||
"""See base_interfaces.ForeLink.join_rear_link for specification."""
|
||||
self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link
|
||||
|
||||
def _start(self):
|
||||
"""Starts this ForeLink.
|
||||
|
||||
This method must be called before attempting to exchange tickets with this
|
||||
object.
|
||||
"""
|
||||
with self._condition:
|
||||
address = '[::]:%d' % (
|
||||
0 if self._requested_port is None else self._requested_port)
|
||||
self._completion_queue = _low.CompletionQueue()
|
||||
if self._root_certificates is None and not self._key_chain_pairs:
|
||||
self._server = _low.Server(self._completion_queue)
|
||||
self._port = self._server.add_http2_addr(address)
|
||||
else:
|
||||
server_credentials = _low.ServerCredentials(
|
||||
self._root_certificates, self._key_chain_pairs, False)
|
||||
self._server = _low.Server(self._completion_queue)
|
||||
self._port = self._server.add_secure_http2_addr(
|
||||
address, server_credentials)
|
||||
self._server.start()
|
||||
|
||||
self._server.service(None)
|
||||
|
||||
self._pool.submit(self._spin, self._completion_queue, self._server)
|
||||
self._spinning = True
|
||||
|
||||
return self
|
||||
|
||||
# TODO(nathaniel): Expose graceful-shutdown semantics in which this object
|
||||
# enters a state in which it finishes ongoing RPCs but refuses new ones.
|
||||
def _stop(self):
|
||||
"""Stops this ForeLink.
|
||||
|
||||
This method must be called for proper termination of this object, and no
|
||||
attempts to exchange tickets with this object may be made after this method
|
||||
has been called.
|
||||
"""
|
||||
with self._condition:
|
||||
self._server.stop()
|
||||
# TODO(nathaniel): Yep, this is weird. Deleting a server shouldn't have a
|
||||
# behaviorally significant side-effect.
|
||||
self._server = None
|
||||
self._completion_queue.stop()
|
||||
|
||||
while self._spinning:
|
||||
self._condition.wait()
|
||||
|
||||
self._port = None
|
||||
|
||||
def __enter__(self):
|
||||
"""See activated.Activated.__enter__ for specification."""
|
||||
return self._start()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""See activated.Activated.__exit__ for specification."""
|
||||
self._stop()
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""See activated.Activated.start for specification."""
|
||||
return self._start()
|
||||
|
||||
def stop(self):
|
||||
"""See activated.Activated.stop for specification."""
|
||||
self._stop()
|
||||
|
||||
def port(self):
|
||||
"""Identifies the port on which this ForeLink is servicing RPCs.
|
||||
|
||||
Returns:
|
||||
The number of the port on which this ForeLink is servicing RPCs, or None
|
||||
if this ForeLink is not currently activated and servicing RPCs.
|
||||
"""
|
||||
with self._condition:
|
||||
return self._port
|
||||
|
||||
def accept_back_to_front_ticket(self, ticket):
|
||||
"""See base_interfaces.ForeLink.accept_back_to_front_ticket for spec."""
|
||||
with self._condition:
|
||||
if self._server is None:
|
||||
return
|
||||
|
||||
if ticket.kind is base_interfaces.BackToFrontTicket.Kind.CONTINUATION:
|
||||
self._continue(ticket.operation_id, ticket.payload)
|
||||
elif ticket.kind is base_interfaces.BackToFrontTicket.Kind.COMPLETION:
|
||||
self._complete(ticket.operation_id, ticket.payload)
|
||||
else:
|
||||
self._cancel(ticket.operation_id)
|
||||
|
|
@ -1,395 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire."""
|
||||
|
||||
import enum
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
from grpc._adapter import _common
|
||||
from grpc._adapter import _intermediary_low as _low
|
||||
from grpc.framework.base import interfaces as base_interfaces
|
||||
from grpc.framework.base import null
|
||||
from grpc.framework.foundation import activated
|
||||
from grpc.framework.foundation import logging_pool
|
||||
|
||||
_THREAD_POOL_SIZE = 10
|
||||
|
||||
_INVOCATION_EVENT_KINDS = (
|
||||
_low.Event.Kind.METADATA_ACCEPTED,
|
||||
_low.Event.Kind.FINISH
|
||||
)
|
||||
|
||||
|
||||
@enum.unique
|
||||
class _LowWrite(enum.Enum):
|
||||
"""The possible categories of low-level write state."""
|
||||
|
||||
OPEN = 'OPEN'
|
||||
ACTIVE = 'ACTIVE'
|
||||
CLOSED = 'CLOSED'
|
||||
|
||||
|
||||
class _RPCState(object):
|
||||
"""The full state of any tracked RPC.
|
||||
|
||||
Attributes:
|
||||
call: The _low.Call object for the RPC.
|
||||
outstanding: The set of Event.Kind values describing expected future events
|
||||
for the RPC.
|
||||
active: A boolean indicating whether or not the RPC is active.
|
||||
common: An _common.RPCState describing additional state for the RPC.
|
||||
"""
|
||||
|
||||
def __init__(self, call, outstanding, active, common):
|
||||
self.call = call
|
||||
self.outstanding = outstanding
|
||||
self.active = active
|
||||
self.common = common
|
||||
|
||||
|
||||
def _write(operation_id, call, outstanding, write_state, serialized_payload):
|
||||
if write_state.low is _LowWrite.OPEN:
|
||||
call.write(serialized_payload, operation_id, 0)
|
||||
outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
|
||||
write_state.low = _LowWrite.ACTIVE
|
||||
elif write_state.low is _LowWrite.ACTIVE:
|
||||
write_state.pending.append(serialized_payload)
|
||||
else:
|
||||
raise ValueError('Write attempted after writes completed!')
|
||||
|
||||
|
||||
class RearLink(base_interfaces.RearLink, activated.Activated):
|
||||
"""An invocation-side bridge between RPC Framework and the C-ish _low code."""
|
||||
|
||||
def __init__(
|
||||
self, host, port, pool, request_serializers, response_deserializers,
|
||||
secure, root_certificates, private_key, certificate_chain,
|
||||
metadata_transformer=None, server_host_override=None):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
host: The host to which to connect for RPC service.
|
||||
port: The port to which to connect for RPC service.
|
||||
pool: A thread pool.
|
||||
request_serializers: A dict from RPC method names to request object
|
||||
serializer behaviors.
|
||||
response_deserializers: A dict from RPC method names to response object
|
||||
deserializer behaviors.
|
||||
secure: A boolean indicating whether or not to use a secure connection.
|
||||
root_certificates: The PEM-encoded root certificates or None to ask for
|
||||
them to be retrieved from a default location.
|
||||
private_key: The PEM-encoded private key to use or None if no private
|
||||
key should be used.
|
||||
certificate_chain: The PEM-encoded certificate chain to use or None if
|
||||
no certificate chain should be used.
|
||||
metadata_transformer: A function that given a metadata object produces
|
||||
another metadata to be used in the underlying communication on the
|
||||
wire.
|
||||
server_host_override: (For testing only) the target name used for SSL
|
||||
host name checking.
|
||||
"""
|
||||
self._condition = threading.Condition()
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._pool = pool
|
||||
self._request_serializers = request_serializers
|
||||
self._response_deserializers = response_deserializers
|
||||
|
||||
self._fore_link = null.NULL_FORE_LINK
|
||||
self._completion_queue = None
|
||||
self._channel = None
|
||||
self._rpc_states = {}
|
||||
self._spinning = False
|
||||
if secure:
|
||||
self._client_credentials = _low.ClientCredentials(
|
||||
root_certificates, private_key, certificate_chain)
|
||||
else:
|
||||
self._client_credentials = None
|
||||
self._root_certificates = root_certificates
|
||||
self._private_key = private_key
|
||||
self._certificate_chain = certificate_chain
|
||||
self._metadata_transformer = metadata_transformer
|
||||
self._server_host_override = server_host_override
|
||||
|
||||
def _on_write_event(self, operation_id, event, rpc_state):
|
||||
if event.write_accepted:
|
||||
if rpc_state.common.write.pending:
|
||||
rpc_state.call.write(
|
||||
rpc_state.common.write.pending.pop(0), operation_id, 0)
|
||||
rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
|
||||
elif rpc_state.common.write.high is _common.HighWrite.CLOSED:
|
||||
rpc_state.call.complete(operation_id)
|
||||
rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED)
|
||||
rpc_state.common.write.low = _LowWrite.CLOSED
|
||||
else:
|
||||
rpc_state.common.write.low = _LowWrite.OPEN
|
||||
else:
|
||||
logging.error('RPC write not accepted! Event: %s', (event,))
|
||||
rpc_state.active = False
|
||||
ticket = base_interfaces.BackToFrontTicket(
|
||||
operation_id, rpc_state.common.sequence_number,
|
||||
base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None)
|
||||
rpc_state.common.sequence_number += 1
|
||||
self._fore_link.accept_back_to_front_ticket(ticket)
|
||||
|
||||
def _on_read_event(self, operation_id, event, rpc_state):
|
||||
if event.bytes is not None:
|
||||
rpc_state.call.read(operation_id)
|
||||
rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED)
|
||||
|
||||
ticket = base_interfaces.BackToFrontTicket(
|
||||
operation_id, rpc_state.common.sequence_number,
|
||||
base_interfaces.BackToFrontTicket.Kind.CONTINUATION,
|
||||
rpc_state.common.deserializer(event.bytes))
|
||||
rpc_state.common.sequence_number += 1
|
||||
self._fore_link.accept_back_to_front_ticket(ticket)
|
||||
|
||||
def _on_complete_event(self, operation_id, event, rpc_state):
|
||||
if not event.complete_accepted:
|
||||
logging.error('RPC complete not accepted! Event: %s', (event,))
|
||||
rpc_state.active = False
|
||||
ticket = base_interfaces.BackToFrontTicket(
|
||||
operation_id, rpc_state.common.sequence_number,
|
||||
base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None)
|
||||
rpc_state.common.sequence_number += 1
|
||||
self._fore_link.accept_back_to_front_ticket(ticket)
|
||||
|
||||
# TODO(nathaniel): Metadata support.
|
||||
def _on_metadata_event(self, operation_id, event, rpc_state): # pylint: disable=unused-argument
|
||||
rpc_state.call.read(operation_id)
|
||||
rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED)
|
||||
|
||||
def _on_finish_event(self, operation_id, event, rpc_state):
|
||||
"""Handle termination of an RPC."""
|
||||
# TODO(nathaniel): Cover all statuses.
|
||||
if event.status.code is _low.Code.OK:
|
||||
kind = base_interfaces.BackToFrontTicket.Kind.COMPLETION
|
||||
elif event.status.code is _low.Code.CANCELLED:
|
||||
kind = base_interfaces.BackToFrontTicket.Kind.CANCELLATION
|
||||
elif event.status.code is _low.Code.DEADLINE_EXCEEDED:
|
||||
kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION
|
||||
else:
|
||||
kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE
|
||||
ticket = base_interfaces.BackToFrontTicket(
|
||||
operation_id, rpc_state.common.sequence_number, kind, None)
|
||||
rpc_state.common.sequence_number += 1
|
||||
self._fore_link.accept_back_to_front_ticket(ticket)
|
||||
|
||||
def _spin(self, completion_queue):
|
||||
while True:
|
||||
event = completion_queue.get(None)
|
||||
operation_id = event.tag
|
||||
|
||||
with self._condition:
|
||||
rpc_state = self._rpc_states[operation_id]
|
||||
rpc_state.outstanding.remove(event.kind)
|
||||
if rpc_state.active and self._completion_queue is not None:
|
||||
if event.kind is _low.Event.Kind.WRITE_ACCEPTED:
|
||||
self._on_write_event(operation_id, event, rpc_state)
|
||||
elif event.kind is _low.Event.Kind.METADATA_ACCEPTED:
|
||||
self._on_metadata_event(operation_id, event, rpc_state)
|
||||
elif event.kind is _low.Event.Kind.READ_ACCEPTED:
|
||||
self._on_read_event(operation_id, event, rpc_state)
|
||||
elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED:
|
||||
self._on_complete_event(operation_id, event, rpc_state)
|
||||
elif event.kind is _low.Event.Kind.FINISH:
|
||||
self._on_finish_event(operation_id, event, rpc_state)
|
||||
else:
|
||||
logging.error('Illegal RPC event! %s', (event,))
|
||||
|
||||
if not rpc_state.outstanding:
|
||||
self._rpc_states.pop(operation_id)
|
||||
if not self._rpc_states:
|
||||
self._spinning = False
|
||||
self._condition.notify_all()
|
||||
return
|
||||
|
||||
def _invoke(self, operation_id, name, high_state, payload, timeout):
|
||||
"""Invoke an RPC.
|
||||
|
||||
Args:
|
||||
operation_id: Any object to be used as an operation ID for the RPC.
|
||||
name: The RPC method name.
|
||||
high_state: A _common.HighWrite value representing the "high write state"
|
||||
of the RPC.
|
||||
payload: A payload object for the RPC or None if no payload was given at
|
||||
invocation-time.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
"""
|
||||
request_serializer = self._request_serializers[name]
|
||||
call = _low.Call(self._channel, self._completion_queue, name, self._host, time.time() + timeout)
|
||||
if self._metadata_transformer is not None:
|
||||
metadata = self._metadata_transformer([])
|
||||
for metadata_key, metadata_value in metadata:
|
||||
call.add_metadata(metadata_key, metadata_value)
|
||||
call.invoke(self._completion_queue, operation_id, operation_id)
|
||||
outstanding = set(_INVOCATION_EVENT_KINDS)
|
||||
|
||||
if payload is None:
|
||||
if high_state is _common.HighWrite.CLOSED:
|
||||
call.complete(operation_id)
|
||||
low_state = _LowWrite.CLOSED
|
||||
outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED)
|
||||
else:
|
||||
low_state = _LowWrite.OPEN
|
||||
else:
|
||||
serialized_payload = request_serializer(payload)
|
||||
call.write(serialized_payload, operation_id, 0)
|
||||
outstanding.add(_low.Event.Kind.WRITE_ACCEPTED)
|
||||
low_state = _LowWrite.ACTIVE
|
||||
|
||||
write_state = _common.WriteState(low_state, high_state, [])
|
||||
common_state = _common.CommonRPCState(
|
||||
write_state, 0, self._response_deserializers[name], request_serializer)
|
||||
self._rpc_states[operation_id] = _RPCState(
|
||||
call, outstanding, True, common_state)
|
||||
|
||||
if not self._spinning:
|
||||
self._pool.submit(self._spin, self._completion_queue)
|
||||
self._spinning = True
|
||||
|
||||
def _commence(self, operation_id, name, payload, timeout):
|
||||
self._invoke(operation_id, name, _common.HighWrite.OPEN, payload, timeout)
|
||||
|
||||
def _continue(self, operation_id, payload):
|
||||
rpc_state = self._rpc_states.get(operation_id, None)
|
||||
if rpc_state is None or not rpc_state.active:
|
||||
return
|
||||
|
||||
_write(
|
||||
operation_id, rpc_state.call, rpc_state.outstanding,
|
||||
rpc_state.common.write, rpc_state.common.serializer(payload))
|
||||
|
||||
def _complete(self, operation_id, payload):
|
||||
"""Close writes associated with an ongoing RPC.
|
||||
|
||||
Args:
|
||||
operation_id: Any object being use as an operation ID for the RPC.
|
||||
payload: A payload object for the RPC (and thus the last payload object
|
||||
for the RPC) or None if no payload was given along with the instruction
|
||||
to indicate the end of writes for the RPC.
|
||||
"""
|
||||
rpc_state = self._rpc_states.get(operation_id, None)
|
||||
if rpc_state is None or not rpc_state.active:
|
||||
return
|
||||
|
||||
write_state = rpc_state.common.write
|
||||
if payload is None:
|
||||
if write_state.low is _LowWrite.OPEN:
|
||||
rpc_state.call.complete(operation_id)
|
||||
rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED)
|
||||
write_state.low = _LowWrite.CLOSED
|
||||
else:
|
||||
_write(
|
||||
operation_id, rpc_state.call, rpc_state.outstanding, write_state,
|
||||
rpc_state.common.serializer(payload))
|
||||
write_state.high = _common.HighWrite.CLOSED
|
||||
|
||||
def _entire(self, operation_id, name, payload, timeout):
|
||||
self._invoke(operation_id, name, _common.HighWrite.CLOSED, payload, timeout)
|
||||
|
||||
def _cancel(self, operation_id):
|
||||
rpc_state = self._rpc_states.get(operation_id, None)
|
||||
if rpc_state is not None and rpc_state.active:
|
||||
rpc_state.call.cancel()
|
||||
rpc_state.active = False
|
||||
|
||||
def join_fore_link(self, fore_link):
|
||||
"""See base_interfaces.RearLink.join_fore_link for specification."""
|
||||
with self._condition:
|
||||
self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link
|
||||
|
||||
def _start(self):
|
||||
"""Starts this RearLink.
|
||||
|
||||
This method must be called before attempting to exchange tickets with this
|
||||
object.
|
||||
"""
|
||||
with self._condition:
|
||||
self._completion_queue = _low.CompletionQueue()
|
||||
self._channel = _low.Channel(
|
||||
'%s:%d' % (self._host, self._port), self._client_credentials,
|
||||
server_host_override=self._server_host_override)
|
||||
return self
|
||||
|
||||
def _stop(self):
|
||||
"""Stops this RearLink.
|
||||
|
||||
This method must be called for proper termination of this object, and no
|
||||
attempts to exchange tickets with this object may be made after this method
|
||||
has been called.
|
||||
"""
|
||||
with self._condition:
|
||||
self._completion_queue.stop()
|
||||
self._completion_queue = None
|
||||
|
||||
while self._spinning:
|
||||
self._condition.wait()
|
||||
|
||||
def __enter__(self):
|
||||
"""See activated.Activated.__enter__ for specification."""
|
||||
return self._start()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""See activated.Activated.__exit__ for specification."""
|
||||
self._stop()
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""See activated.Activated.start for specification."""
|
||||
return self._start()
|
||||
|
||||
def stop(self):
|
||||
"""See activated.Activated.stop for specification."""
|
||||
self._stop()
|
||||
|
||||
def accept_front_to_back_ticket(self, ticket):
|
||||
"""See base_interfaces.RearLink.accept_front_to_back_ticket for spec."""
|
||||
with self._condition:
|
||||
if self._completion_queue is None:
|
||||
return
|
||||
|
||||
if ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT:
|
||||
self._commence(
|
||||
ticket.operation_id, ticket.name, ticket.payload, ticket.timeout)
|
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CONTINUATION:
|
||||
self._continue(ticket.operation_id, ticket.payload)
|
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMPLETION:
|
||||
self._complete(ticket.operation_id, ticket.payload)
|
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.ENTIRE:
|
||||
self._entire(
|
||||
ticket.operation_id, ticket.name, ticket.payload, ticket.timeout)
|
||||
elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CANCELLATION:
|
||||
self._cancel(ticket.operation_id)
|
||||
else:
|
||||
# NOTE(nathaniel): All other categories are treated as cancellation.
|
||||
self._cancel(ticket.operation_id)
|
||||
|
|
@ -125,6 +125,7 @@ grpc_tracer_set_enabled_type grpc_tracer_set_enabled_import;
|
|||
grpc_header_key_is_legal_type grpc_header_key_is_legal_import;
|
||||
grpc_header_nonbin_value_is_legal_type grpc_header_nonbin_value_is_legal_import;
|
||||
grpc_is_binary_header_type grpc_is_binary_header_import;
|
||||
grpc_call_error_to_string_type grpc_call_error_to_string_import;
|
||||
grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
|
||||
grpc_auth_context_property_iterator_type grpc_auth_context_property_iterator_import;
|
||||
grpc_auth_context_peer_identity_type grpc_auth_context_peer_identity_import;
|
||||
|
|
@ -395,6 +396,7 @@ void pygrpc_load_imports(HMODULE library) {
|
|||
grpc_header_key_is_legal_import = (grpc_header_key_is_legal_type) GetProcAddress(library, "grpc_header_key_is_legal");
|
||||
grpc_header_nonbin_value_is_legal_import = (grpc_header_nonbin_value_is_legal_type) GetProcAddress(library, "grpc_header_nonbin_value_is_legal");
|
||||
grpc_is_binary_header_import = (grpc_is_binary_header_type) GetProcAddress(library, "grpc_is_binary_header");
|
||||
grpc_call_error_to_string_import = (grpc_call_error_to_string_type) GetProcAddress(library, "grpc_call_error_to_string");
|
||||
grpc_auth_property_iterator_next_import = (grpc_auth_property_iterator_next_type) GetProcAddress(library, "grpc_auth_property_iterator_next");
|
||||
grpc_auth_context_property_iterator_import = (grpc_auth_context_property_iterator_type) GetProcAddress(library, "grpc_auth_context_property_iterator");
|
||||
grpc_auth_context_peer_identity_import = (grpc_auth_context_peer_identity_type) GetProcAddress(library, "grpc_auth_context_peer_identity");
|
||||
|
|
|
|||
|
|
@ -325,6 +325,9 @@ extern grpc_header_nonbin_value_is_legal_type grpc_header_nonbin_value_is_legal_
|
|||
typedef int(*grpc_is_binary_header_type)(const char *key, size_t length);
|
||||
extern grpc_is_binary_header_type grpc_is_binary_header_import;
|
||||
#define grpc_is_binary_header grpc_is_binary_header_import
|
||||
typedef const char *(*grpc_call_error_to_string_type)(grpc_call_error error);
|
||||
extern grpc_call_error_to_string_type grpc_call_error_to_string_import;
|
||||
#define grpc_call_error_to_string grpc_call_error_to_string_import
|
||||
typedef const grpc_auth_property *(*grpc_auth_property_iterator_next_type)(grpc_auth_property_iterator *it);
|
||||
extern grpc_auth_property_iterator_next_type grpc_auth_property_iterator_next_import;
|
||||
#define grpc_auth_property_iterator_next grpc_auth_property_iterator_next_import
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn('the alpha API (includes this package) is deprecated, '
|
||||
'unmaintained, and no longer tested. Please migrate to the beta '
|
||||
'API.', DeprecationWarning, stacklevel=2)
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Entry points into GRPC."""
|
||||
|
||||
import threading
|
||||
|
||||
from grpc._adapter import fore as _fore
|
||||
from grpc._adapter import rear as _rear
|
||||
from grpc.framework.alpha import _face_utilities
|
||||
from grpc.framework.alpha import _reexport
|
||||
from grpc.framework.alpha import interfaces
|
||||
from grpc.framework.base import implementations as _base_implementations
|
||||
from grpc.framework.base import util as _base_utilities
|
||||
from grpc.framework.face import implementations as _face_implementations
|
||||
from grpc.framework.foundation import logging_pool
|
||||
|
||||
_DEFAULT_THREAD_POOL_SIZE = 8
|
||||
_ONE_DAY_IN_SECONDS = 24 * 60 * 60
|
||||
|
||||
|
||||
class _Server(interfaces.Server):
|
||||
|
||||
def __init__(
|
||||
self, breakdown, port, private_key, certificate_chain,
|
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE):
|
||||
self._lock = threading.Lock()
|
||||
self._breakdown = breakdown
|
||||
self._port = port
|
||||
if private_key is None or certificate_chain is None:
|
||||
self._key_chain_pairs = ()
|
||||
else:
|
||||
self._key_chain_pairs = ((private_key, certificate_chain),)
|
||||
|
||||
self._pool_size = thread_pool_size
|
||||
self._pool = None
|
||||
self._back = None
|
||||
self._fore_link = None
|
||||
|
||||
def _start(self):
|
||||
with self._lock:
|
||||
if self._pool is None:
|
||||
self._pool = logging_pool.pool(self._pool_size)
|
||||
servicer = _face_implementations.servicer(
|
||||
self._pool, self._breakdown.implementations, None)
|
||||
self._back = _base_implementations.back_link(
|
||||
servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS,
|
||||
_ONE_DAY_IN_SECONDS)
|
||||
self._fore_link = _fore.ForeLink(
|
||||
self._pool, self._breakdown.request_deserializers,
|
||||
self._breakdown.response_serializers, None, self._key_chain_pairs,
|
||||
port=self._port)
|
||||
self._back.join_fore_link(self._fore_link)
|
||||
self._fore_link.join_rear_link(self._back)
|
||||
self._fore_link.start()
|
||||
else:
|
||||
raise ValueError('Server currently running!')
|
||||
|
||||
def _stop(self):
|
||||
with self._lock:
|
||||
if self._pool is None:
|
||||
raise ValueError('Server not running!')
|
||||
else:
|
||||
self._fore_link.stop()
|
||||
_base_utilities.wait_for_idle(self._back)
|
||||
self._pool.shutdown(wait=True)
|
||||
self._fore_link = None
|
||||
self._back = None
|
||||
self._pool = None
|
||||
|
||||
def __enter__(self):
|
||||
self._start()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._stop()
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
self._start()
|
||||
|
||||
def stop(self):
|
||||
self._stop()
|
||||
|
||||
def port(self):
|
||||
with self._lock:
|
||||
return self._fore_link.port()
|
||||
|
||||
|
||||
class _Stub(interfaces.Stub):
|
||||
|
||||
def __init__(
|
||||
self, breakdown, host, port, secure, root_certificates, private_key,
|
||||
certificate_chain, metadata_transformer=None, server_host_override=None,
|
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE):
|
||||
self._lock = threading.Lock()
|
||||
self._breakdown = breakdown
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._secure = secure
|
||||
self._root_certificates = root_certificates
|
||||
self._private_key = private_key
|
||||
self._certificate_chain = certificate_chain
|
||||
self._metadata_transformer = metadata_transformer
|
||||
self._server_host_override = server_host_override
|
||||
|
||||
self._pool_size = thread_pool_size
|
||||
self._pool = None
|
||||
self._front = None
|
||||
self._rear_link = None
|
||||
self._understub = None
|
||||
|
||||
def __enter__(self):
|
||||
with self._lock:
|
||||
if self._pool is None:
|
||||
self._pool = logging_pool.pool(self._pool_size)
|
||||
self._front = _base_implementations.front_link(
|
||||
self._pool, self._pool, self._pool)
|
||||
self._rear_link = _rear.RearLink(
|
||||
self._host, self._port, self._pool,
|
||||
self._breakdown.request_serializers,
|
||||
self._breakdown.response_deserializers, self._secure,
|
||||
self._root_certificates, self._private_key, self._certificate_chain,
|
||||
metadata_transformer=self._metadata_transformer,
|
||||
server_host_override=self._server_host_override)
|
||||
self._front.join_rear_link(self._rear_link)
|
||||
self._rear_link.join_fore_link(self._front)
|
||||
self._rear_link.start()
|
||||
self._understub = _face_implementations.dynamic_stub(
|
||||
self._breakdown.face_cardinalities, self._front, self._pool, '')
|
||||
else:
|
||||
raise ValueError('Tried to __enter__ already-__enter__ed Stub!')
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
with self._lock:
|
||||
if self._pool is None:
|
||||
raise ValueError('Tried to __exit__ non-__enter__ed Stub!')
|
||||
else:
|
||||
self._rear_link.stop()
|
||||
_base_utilities.wait_for_idle(self._front)
|
||||
self._pool.shutdown(wait=True)
|
||||
self._rear_link = None
|
||||
self._front = None
|
||||
self._pool = None
|
||||
self._understub = None
|
||||
return False
|
||||
|
||||
def __getattr__(self, attr):
|
||||
with self._lock:
|
||||
if self._pool is None:
|
||||
raise ValueError('Tried to __getattr__ non-__enter__ed Stub!')
|
||||
else:
|
||||
method_cardinality = self._breakdown.cardinalities.get(attr)
|
||||
underlying_attr = getattr(
|
||||
self._understub, self._breakdown.qualified_names.get(attr), None)
|
||||
if method_cardinality is interfaces.Cardinality.UNARY_UNARY:
|
||||
return _reexport.unary_unary_sync_async(underlying_attr)
|
||||
elif method_cardinality is interfaces.Cardinality.UNARY_STREAM:
|
||||
return lambda request, timeout: _reexport.cancellable_iterator(
|
||||
underlying_attr(request, timeout))
|
||||
elif method_cardinality is interfaces.Cardinality.STREAM_UNARY:
|
||||
return _reexport.stream_unary_sync_async(underlying_attr)
|
||||
elif method_cardinality is interfaces.Cardinality.STREAM_STREAM:
|
||||
return lambda request_iterator, timeout: (
|
||||
_reexport.cancellable_iterator(underlying_attr(
|
||||
request_iterator, timeout)))
|
||||
else:
|
||||
raise AttributeError(attr)
|
||||
|
||||
|
||||
def stub(
|
||||
service_name, methods, host, port, metadata_transformer=None, secure=False,
|
||||
root_certificates=None, private_key=None, certificate_chain=None,
|
||||
server_host_override=None, thread_pool_size=_DEFAULT_THREAD_POOL_SIZE):
|
||||
"""Constructs an interfaces.Stub.
|
||||
|
||||
Args:
|
||||
service_name: The package-qualified full name of the service.
|
||||
methods: A dictionary from RPC method name to
|
||||
interfaces.RpcMethodInvocationDescription describing the RPCs to be
|
||||
supported by the created stub. The RPC method names in the dictionary are
|
||||
not qualified by the service name or decorated in any other way.
|
||||
host: The host to which to connect for RPC service.
|
||||
port: The port to which to connect for RPC service.
|
||||
metadata_transformer: A callable that given a metadata object produces
|
||||
another metadata object to be used in the underlying communication on the
|
||||
wire.
|
||||
secure: Whether or not to construct the stub with a secure connection.
|
||||
root_certificates: The PEM-encoded root certificates or None to ask for
|
||||
them to be retrieved from a default location.
|
||||
private_key: The PEM-encoded private key to use or None if no private key
|
||||
should be used.
|
||||
certificate_chain: The PEM-encoded certificate chain to use or None if no
|
||||
certificate chain should be used.
|
||||
server_host_override: (For testing only) the target name used for SSL
|
||||
host name checking.
|
||||
thread_pool_size: The maximum number of threads to allow in the backing
|
||||
thread pool.
|
||||
|
||||
Returns:
|
||||
An interfaces.Stub affording RPC invocation.
|
||||
"""
|
||||
breakdown = _face_utilities.break_down_invocation(service_name, methods)
|
||||
return _Stub(
|
||||
breakdown, host, port, secure, root_certificates, private_key,
|
||||
certificate_chain, server_host_override=server_host_override,
|
||||
metadata_transformer=metadata_transformer,
|
||||
thread_pool_size=thread_pool_size)
|
||||
|
||||
|
||||
def server(
|
||||
service_name, methods, port, private_key=None, certificate_chain=None,
|
||||
thread_pool_size=_DEFAULT_THREAD_POOL_SIZE):
|
||||
"""Constructs an interfaces.Server.
|
||||
|
||||
Args:
|
||||
service_name: The package-qualified full name of the service.
|
||||
methods: A dictionary from RPC method name to
|
||||
interfaces.RpcMethodServiceDescription describing the RPCs to
|
||||
be serviced by the created server. The RPC method names in the dictionary
|
||||
are not qualified by the service name or decorated in any other way.
|
||||
port: The port on which to serve or zero to ask for a port to be
|
||||
automatically selected.
|
||||
private_key: A pem-encoded private key, or None for an insecure server.
|
||||
certificate_chain: A pem-encoded certificate chain, or None for an insecure
|
||||
server.
|
||||
thread_pool_size: The maximum number of threads to allow in the backing
|
||||
thread pool.
|
||||
|
||||
Returns:
|
||||
An interfaces.Server that will serve secure traffic.
|
||||
"""
|
||||
breakdown = _face_utilities.break_down_service(service_name, methods)
|
||||
return _Server(breakdown, port, private_key, certificate_chain,
|
||||
thread_pool_size=thread_pool_size)
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn('the alpha API (includes this package) is deprecated, '
|
||||
'unmaintained, and no longer tested. Please migrate to the beta '
|
||||
'API.', DeprecationWarning, stacklevel=2)
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import abc
|
||||
import collections
|
||||
|
||||
import six
|
||||
|
||||
# face_interfaces is referenced from specification in this module.
|
||||
from grpc.framework.common import cardinality
|
||||
from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import
|
||||
from grpc.framework.face import utilities as face_utilities
|
||||
from grpc.framework.alpha import _reexport
|
||||
from grpc.framework.alpha import interfaces
|
||||
|
||||
|
||||
def _qualified_name(service_name, method_name):
|
||||
return '/%s/%s' % (service_name, method_name)
|
||||
|
||||
|
||||
# TODO(nathaniel): This structure is getting bloated; it could be shrunk if
|
||||
# implementations._Stub used a generic rather than a dynamic underlying
|
||||
# face-layer stub.
|
||||
class InvocationBreakdown(six.with_metaclass(abc.ABCMeta)):
|
||||
"""An intermediate representation of invocation-side views of RPC methods.
|
||||
|
||||
Attributes:
|
||||
cardinalities: A dictionary from RPC method name to interfaces.Cardinality
|
||||
value.
|
||||
qualified_names: A dictionary from unqualified RPC method name to
|
||||
service-qualified RPC method name.
|
||||
face_cardinalities: A dictionary from service-qualified RPC method name to
|
||||
to cardinality.Cardinality value.
|
||||
request_serializers: A dictionary from service-qualified RPC method name to
|
||||
callable behavior to be used serializing request values for the RPC.
|
||||
response_deserializers: A dictionary from service-qualified RPC method name
|
||||
to callable behavior to be used deserializing response values for the
|
||||
RPC.
|
||||
"""
|
||||
|
||||
|
||||
class _EasyInvocationBreakdown(
|
||||
InvocationBreakdown,
|
||||
collections.namedtuple(
|
||||
'_EasyInvocationBreakdown',
|
||||
('cardinalities', 'qualified_names', 'face_cardinalities',
|
||||
'request_serializers', 'response_deserializers'))):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceBreakdown(six.with_metaclass(abc.ABCMeta)):
|
||||
"""An intermediate representation of service-side views of RPC methods.
|
||||
|
||||
Attributes:
|
||||
implementations: A dictionary from service-qualified RPC method name to
|
||||
face_interfaces.MethodImplementation implementing the RPC method.
|
||||
request_deserializers: A dictionary from service-qualified RPC method name
|
||||
to callable behavior to be used deserializing request values for the RPC.
|
||||
response_serializers: A dictionary from service-qualified RPC method name
|
||||
to callable behavior to be used serializing response values for the RPC.
|
||||
"""
|
||||
|
||||
|
||||
class _EasyServiceBreakdown(
|
||||
ServiceBreakdown,
|
||||
collections.namedtuple(
|
||||
'_EasyServiceBreakdown',
|
||||
('implementations', 'request_deserializers', 'response_serializers'))):
|
||||
pass
|
||||
|
||||
|
||||
def break_down_invocation(service_name, method_descriptions):
|
||||
"""Derives an InvocationBreakdown from several RPC method descriptions.
|
||||
|
||||
Args:
|
||||
service_name: The package-qualified full name of the service.
|
||||
method_descriptions: A dictionary from RPC method name to
|
||||
interfaces.RpcMethodInvocationDescription describing the RPCs.
|
||||
|
||||
Returns:
|
||||
An InvocationBreakdown corresponding to the given method descriptions.
|
||||
"""
|
||||
cardinalities = {}
|
||||
qualified_names = {}
|
||||
face_cardinalities = {}
|
||||
request_serializers = {}
|
||||
response_deserializers = {}
|
||||
for name, method_description in six.iteritems(method_descriptions):
|
||||
qualified_name = _qualified_name(service_name, name)
|
||||
method_cardinality = method_description.cardinality()
|
||||
cardinalities[name] = method_description.cardinality()
|
||||
qualified_names[name] = qualified_name
|
||||
face_cardinalities[qualified_name] = _reexport.common_cardinality(
|
||||
method_cardinality)
|
||||
request_serializers[qualified_name] = method_description.serialize_request
|
||||
response_deserializers[qualified_name] = (
|
||||
method_description.deserialize_response)
|
||||
return _EasyInvocationBreakdown(
|
||||
cardinalities, qualified_names, face_cardinalities, request_serializers,
|
||||
response_deserializers)
|
||||
|
||||
|
||||
def break_down_service(service_name, method_descriptions):
|
||||
"""Derives a ServiceBreakdown from several RPC method descriptions.
|
||||
|
||||
Args:
|
||||
method_descriptions: A dictionary from RPC method name to
|
||||
interfaces.RpcMethodServiceDescription describing the RPCs.
|
||||
|
||||
Returns:
|
||||
A ServiceBreakdown corresponding to the given method descriptions.
|
||||
"""
|
||||
implementations = {}
|
||||
request_deserializers = {}
|
||||
response_serializers = {}
|
||||
for name, method_description in six.iteritems(method_descriptions):
|
||||
qualified_name = _qualified_name(service_name, name)
|
||||
method_cardinality = method_description.cardinality()
|
||||
if method_cardinality is interfaces.Cardinality.UNARY_UNARY:
|
||||
def service(
|
||||
request, face_rpc_context,
|
||||
service_behavior=method_description.service_unary_unary):
|
||||
return service_behavior(
|
||||
request, _reexport.rpc_context(face_rpc_context))
|
||||
implementations[qualified_name] = face_utilities.unary_unary_inline(
|
||||
service)
|
||||
elif method_cardinality is interfaces.Cardinality.UNARY_STREAM:
|
||||
def service(
|
||||
request, face_rpc_context,
|
||||
service_behavior=method_description.service_unary_stream):
|
||||
return service_behavior(
|
||||
request, _reexport.rpc_context(face_rpc_context))
|
||||
implementations[qualified_name] = face_utilities.unary_stream_inline(
|
||||
service)
|
||||
elif method_cardinality is interfaces.Cardinality.STREAM_UNARY:
|
||||
def service(
|
||||
request_iterator, face_rpc_context,
|
||||
service_behavior=method_description.service_stream_unary):
|
||||
return service_behavior(
|
||||
request_iterator, _reexport.rpc_context(face_rpc_context))
|
||||
implementations[qualified_name] = face_utilities.stream_unary_inline(
|
||||
service)
|
||||
elif method_cardinality is interfaces.Cardinality.STREAM_STREAM:
|
||||
def service(
|
||||
request_iterator, face_rpc_context,
|
||||
service_behavior=method_description.service_stream_stream):
|
||||
return service_behavior(
|
||||
request_iterator, _reexport.rpc_context(face_rpc_context))
|
||||
implementations[qualified_name] = face_utilities.stream_stream_inline(
|
||||
service)
|
||||
request_deserializers[qualified_name] = (
|
||||
method_description.deserialize_request)
|
||||
response_serializers[qualified_name] = (
|
||||
method_description.serialize_response)
|
||||
|
||||
return _EasyServiceBreakdown(
|
||||
implementations, request_deserializers, response_serializers)
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from grpc.framework.common import cardinality
|
||||
from grpc.framework.face import exceptions as face_exceptions
|
||||
from grpc.framework.face import interfaces as face_interfaces
|
||||
from grpc.framework.foundation import future
|
||||
from grpc.framework.alpha import exceptions
|
||||
from grpc.framework.alpha import interfaces
|
||||
|
||||
_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = {
|
||||
interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY,
|
||||
interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM,
|
||||
interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY,
|
||||
interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM,
|
||||
}
|
||||
|
||||
_ABORTION_REEXPORT = {
|
||||
face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED,
|
||||
face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED,
|
||||
face_interfaces.Abortion.NETWORK_FAILURE:
|
||||
interfaces.Abortion.NETWORK_FAILURE,
|
||||
face_interfaces.Abortion.SERVICED_FAILURE:
|
||||
interfaces.Abortion.SERVICED_FAILURE,
|
||||
face_interfaces.Abortion.SERVICER_FAILURE:
|
||||
interfaces.Abortion.SERVICER_FAILURE,
|
||||
}
|
||||
|
||||
|
||||
class _RpcError(exceptions.RpcError):
|
||||
pass
|
||||
|
||||
|
||||
def _reexport_error(face_rpc_error):
|
||||
if isinstance(face_rpc_error, face_exceptions.CancellationError):
|
||||
return exceptions.CancellationError()
|
||||
elif isinstance(face_rpc_error, face_exceptions.ExpirationError):
|
||||
return exceptions.ExpirationError()
|
||||
else:
|
||||
return _RpcError()
|
||||
|
||||
|
||||
def _as_face_abortion_callback(abortion_callback):
|
||||
def face_abortion_callback(face_abortion):
|
||||
abortion_callback(_ABORTION_REEXPORT[face_abortion])
|
||||
return face_abortion_callback
|
||||
|
||||
|
||||
class _ReexportedFuture(future.Future):
|
||||
|
||||
def __init__(self, face_future):
|
||||
self._face_future = face_future
|
||||
|
||||
def cancel(self):
|
||||
return self._face_future.cancel()
|
||||
|
||||
def cancelled(self):
|
||||
return self._face_future.cancelled()
|
||||
|
||||
def running(self):
|
||||
return self._face_future.running()
|
||||
|
||||
def done(self):
|
||||
return self._face_future.done()
|
||||
|
||||
def result(self, timeout=None):
|
||||
try:
|
||||
return self._face_future.result(timeout=timeout)
|
||||
except face_exceptions.RpcError as e:
|
||||
raise _reexport_error(e)
|
||||
|
||||
def exception(self, timeout=None):
|
||||
face_error = self._face_future.exception(timeout=timeout)
|
||||
return None if face_error is None else _reexport_error(face_error)
|
||||
|
||||
def traceback(self, timeout=None):
|
||||
return self._face_future.traceback(timeout=timeout)
|
||||
|
||||
def add_done_callback(self, fn):
|
||||
self._face_future.add_done_callback(lambda unused_face_future: fn(self))
|
||||
|
||||
|
||||
def _call_reexporting_errors(behavior, *args, **kwargs):
|
||||
try:
|
||||
return behavior(*args, **kwargs)
|
||||
except face_exceptions.RpcError as e:
|
||||
raise _reexport_error(e)
|
||||
|
||||
|
||||
def _reexported_future(face_future):
|
||||
return _ReexportedFuture(face_future)
|
||||
|
||||
|
||||
class _CancellableIterator(interfaces.CancellableIterator):
|
||||
|
||||
def __init__(self, face_cancellable_iterator):
|
||||
self._face_cancellable_iterator = face_cancellable_iterator
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
return _call_reexporting_errors(self._face_cancellable_iterator.next)
|
||||
|
||||
def cancel(self):
|
||||
self._face_cancellable_iterator.cancel()
|
||||
|
||||
|
||||
class _RpcContext(interfaces.RpcContext):
|
||||
|
||||
def __init__(self, face_rpc_context):
|
||||
self._face_rpc_context = face_rpc_context
|
||||
|
||||
def is_active(self):
|
||||
return self._face_rpc_context.is_active()
|
||||
|
||||
def time_remaining(self):
|
||||
return self._face_rpc_context.time_remaining()
|
||||
|
||||
def add_abortion_callback(self, abortion_callback):
|
||||
self._face_rpc_context.add_abortion_callback(
|
||||
_as_face_abortion_callback(abortion_callback))
|
||||
|
||||
|
||||
class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync):
|
||||
|
||||
def __init__(self, face_unary_unary_multi_callable):
|
||||
self._underlying = face_unary_unary_multi_callable
|
||||
|
||||
def __call__(self, request, timeout):
|
||||
return _call_reexporting_errors(
|
||||
self._underlying, request, timeout)
|
||||
|
||||
def async(self, request, timeout):
|
||||
return _ReexportedFuture(self._underlying.future(request, timeout))
|
||||
|
||||
|
||||
class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync):
|
||||
|
||||
def __init__(self, face_stream_unary_multi_callable):
|
||||
self._underlying = face_stream_unary_multi_callable
|
||||
|
||||
def __call__(self, request_iterator, timeout):
|
||||
return _call_reexporting_errors(
|
||||
self._underlying, request_iterator, timeout)
|
||||
|
||||
def async(self, request_iterator, timeout):
|
||||
return _ReexportedFuture(self._underlying.future(request_iterator, timeout))
|
||||
|
||||
|
||||
def common_cardinality(early_adopter_cardinality):
|
||||
return _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[
|
||||
early_adopter_cardinality]
|
||||
|
||||
|
||||
def common_cardinalities(early_adopter_cardinalities):
|
||||
common_cardinalities = {}
|
||||
for name, early_adopter_cardinality in six.iteritems(early_adopter_cardinalities):
|
||||
common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[
|
||||
early_adopter_cardinality]
|
||||
return common_cardinalities
|
||||
|
||||
|
||||
def rpc_context(face_rpc_context):
|
||||
return _RpcContext(face_rpc_context)
|
||||
|
||||
|
||||
def cancellable_iterator(face_cancellable_iterator):
|
||||
return _CancellableIterator(face_cancellable_iterator)
|
||||
|
||||
|
||||
def unary_unary_sync_async(face_unary_unary_multi_callable):
|
||||
return _UnaryUnarySyncAsync(face_unary_unary_multi_callable)
|
||||
|
||||
|
||||
def stream_unary_sync_async(face_stream_unary_multi_callable):
|
||||
return _StreamUnarySyncAsync(face_stream_unary_multi_callable)
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Interfaces of GRPC."""
|
||||
|
||||
import abc
|
||||
import enum
|
||||
|
||||
import six
|
||||
|
||||
# exceptions is referenced from specification in this module.
|
||||
from grpc.framework.alpha import exceptions # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import activated
|
||||
from grpc.framework.foundation import future
|
||||
|
||||
|
||||
@enum.unique
|
||||
class Cardinality(enum.Enum):
|
||||
"""Constants for the four cardinalities of RPC."""
|
||||
|
||||
UNARY_UNARY = 'request-unary/response-unary'
|
||||
UNARY_STREAM = 'request-unary/response-streaming'
|
||||
STREAM_UNARY = 'request-streaming/response-unary'
|
||||
STREAM_STREAM = 'request-streaming/response-streaming'
|
||||
|
||||
|
||||
@enum.unique
|
||||
class Abortion(enum.Enum):
|
||||
"""Categories of RPC abortion."""
|
||||
|
||||
CANCELLED = 'cancelled'
|
||||
EXPIRED = 'expired'
|
||||
NETWORK_FAILURE = 'network failure'
|
||||
SERVICED_FAILURE = 'serviced failure'
|
||||
SERVICER_FAILURE = 'servicer failure'
|
||||
|
||||
|
||||
class CancellableIterator(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Implements the Iterator protocol and affords a cancel method."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __iter__(self):
|
||||
"""Returns the self object in accordance with the Iterator protocol."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
|
||||
@abc.abstractmethod
|
||||
def next(self):
|
||||
"""Returns a value or raises StopIteration per the Iterator protocol."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self):
|
||||
"""Requests cancellation of whatever computation underlies this iterator."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RpcContext(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Provides RPC-related information and action."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_active(self):
|
||||
"""Describes whether the RPC is active or has terminated."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def time_remaining(self):
|
||||
"""Describes the length of allowed time remaining for the RPC.
|
||||
Returns:
|
||||
A nonnegative float indicating the length of allowed time in seconds
|
||||
remaining for the RPC to complete before it is considered to have timed
|
||||
out.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_abortion_callback(self, abortion_callback):
|
||||
"""Registers a callback to be called if the RPC is aborted.
|
||||
Args:
|
||||
abortion_callback: A callable to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class UnaryUnarySyncAsync(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a unary-unary RPC synchronously or asynchronously.
|
||||
Values implementing this interface are directly callable and present an
|
||||
"async" method. Both calls take a request value and a numeric timeout.
|
||||
Direct invocation of a value of this type invokes its associated RPC and
|
||||
blocks until the RPC's response is available. Calling the "async" method
|
||||
of a value of this type invokes its associated RPC and immediately returns a
|
||||
future.Future bound to the asynchronous execution of the RPC.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def async(self, request, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future's result value will be the response value of the RPC.
|
||||
In the event of RPC abortion, the returned Future's exception value
|
||||
will be an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class StreamUnarySyncAsync(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a stream-unary RPC synchronously or asynchronously.
|
||||
Values implementing this interface are directly callable and present an
|
||||
"async" method. Both calls take an iterator of request values and a numeric
|
||||
timeout. Direct invocation of a value of this type invokes its associated RPC
|
||||
and blocks until the RPC's response is available. Calling the "async" method
|
||||
of a value of this type invokes its associated RPC and immediately returns a
|
||||
future.Future bound to the asynchronous execution of the RPC.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request_iterator, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator that yields request values for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def async(self, request_iterator, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator that yields request values for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future's result value will be the response value of the RPC.
|
||||
In the event of RPC abortion, the returned Future's exception value
|
||||
will be an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RpcMethodDescription(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A type for the common aspects of RPC method descriptions."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def cardinality(self):
|
||||
"""Identifies the cardinality of this RpcMethodDescription.
|
||||
|
||||
Returns:
|
||||
A Cardinality value identifying whether or not this
|
||||
RpcMethodDescription is request-unary or request-streaming and
|
||||
whether or not it is response-unary or response-streaming.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RpcMethodInvocationDescription(six.with_metaclass(abc.ABCMeta, RpcMethodDescription)):
|
||||
"""Invocation-side description of an RPC method."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def serialize_request(self, request):
|
||||
"""Serializes a request value.
|
||||
|
||||
Args:
|
||||
request: A request value appropriate for the RPC method described by this
|
||||
RpcMethodInvocationDescription.
|
||||
|
||||
Returns:
|
||||
The serialization of the given request value as a
|
||||
bytestring.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def deserialize_response(self, serialized_response):
|
||||
"""Deserializes a response value.
|
||||
|
||||
Args:
|
||||
serialized_response: A bytestring that is the serialization of a response
|
||||
value appropriate for the RPC method described by this
|
||||
RpcMethodInvocationDescription.
|
||||
|
||||
Returns:
|
||||
A response value corresponding to the given bytestring.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RpcMethodServiceDescription(six.with_metaclass(abc.ABCMeta, RpcMethodDescription)):
|
||||
"""Service-side description of an RPC method."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def deserialize_request(self, serialized_request):
|
||||
"""Deserializes a request value.
|
||||
|
||||
Args:
|
||||
serialized_request: A bytestring that is the serialization of a request
|
||||
value appropriate for the RPC method described by this
|
||||
RpcMethodServiceDescription.
|
||||
|
||||
Returns:
|
||||
A request value corresponding to the given bytestring.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def serialize_response(self, response):
|
||||
"""Serializes a response value.
|
||||
|
||||
Args:
|
||||
response: A response value appropriate for the RPC method described by
|
||||
this RpcMethodServiceDescription.
|
||||
|
||||
Returns:
|
||||
The serialization of the given response value as a
|
||||
bytestring.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def service_unary_unary(self, request, context):
|
||||
"""Carries out this RPC.
|
||||
|
||||
This method may only be called if the cardinality of this
|
||||
RpcMethodServiceDescription is Cardinality.UNARY_UNARY.
|
||||
|
||||
Args:
|
||||
request: A request value appropriate for the RPC method described by this
|
||||
RpcMethodServiceDescription.
|
||||
context: An RpcContext object for the RPC.
|
||||
|
||||
Returns:
|
||||
A response value appropriate for the RPC method described by this
|
||||
RpcMethodServiceDescription.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def service_unary_stream(self, request, context):
|
||||
"""Carries out this RPC.
|
||||
|
||||
This method may only be called if the cardinality of this
|
||||
RpcMethodServiceDescription is Cardinality.UNARY_STREAM.
|
||||
|
||||
Args:
|
||||
request: A request value appropriate for the RPC method described by this
|
||||
RpcMethodServiceDescription.
|
||||
context: An RpcContext object for the RPC.
|
||||
|
||||
Yields:
|
||||
Zero or more response values appropriate for the RPC method described by
|
||||
this RpcMethodServiceDescription.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def service_stream_unary(self, request_iterator, context):
|
||||
"""Carries out this RPC.
|
||||
|
||||
This method may only be called if the cardinality of this
|
||||
RpcMethodServiceDescription is Cardinality.STREAM_UNARY.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator of request values appropriate for the RPC
|
||||
method described by this RpcMethodServiceDescription.
|
||||
context: An RpcContext object for the RPC.
|
||||
|
||||
Returns:
|
||||
A response value appropriate for the RPC method described by this
|
||||
RpcMethodServiceDescription.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def service_stream_stream(self, request_iterator, context):
|
||||
"""Carries out this RPC.
|
||||
|
||||
This method may only be called if the cardinality of this
|
||||
RpcMethodServiceDescription is Cardinality.STREAM_STREAM.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator of request values appropriate for the RPC
|
||||
method described by this RpcMethodServiceDescription.
|
||||
context: An RpcContext object for the RPC.
|
||||
|
||||
Yields:
|
||||
Zero or more response values appropriate for the RPC method described by
|
||||
this RpcMethodServiceDescription.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Stub(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A stub with callable RPC method names for attributes.
|
||||
|
||||
Instances of this type are context managers and only afford RPC invocation
|
||||
when used in context.
|
||||
|
||||
Instances of this type, when used in context, respond to attribute access
|
||||
as follows: if the requested attribute is the name of a unary-unary RPC
|
||||
method, the value of the attribute will be a UnaryUnarySyncAsync with which
|
||||
to invoke the RPC method. If the requested attribute is the name of a
|
||||
unary-stream RPC method, the value of the attribute will be a callable taking
|
||||
a request object and a timeout parameter and returning a CancellableIterator
|
||||
that yields the response values of the RPC. If the requested attribute is the
|
||||
name of a stream-unary RPC method, the value of the attribute will be a
|
||||
StreamUnarySyncAsync with which to invoke the RPC method. If the requested
|
||||
attribute is the name of a stream-stream RPC method, the value of the
|
||||
attribute will be a callable taking an iterator of request objects and a
|
||||
timeout and returning a CancellableIterator that yields the response values
|
||||
of the RPC.
|
||||
|
||||
In all cases indication of abortion is indicated by raising of
|
||||
exceptions.RpcError, exceptions.CancellationError,
|
||||
and exceptions.ExpirationError.
|
||||
"""
|
||||
|
||||
|
||||
class Server(six.with_metaclass(abc.ABCMeta, activated.Activated)):
|
||||
"""A GRPC Server."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def port(self):
|
||||
"""Reports the port on which the server is serving.
|
||||
|
||||
This method may only be called while the server is activated.
|
||||
|
||||
Returns:
|
||||
The port on which the server is serving.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Utilities for use with GRPC."""
|
||||
|
||||
from grpc.framework.alpha import interfaces
|
||||
|
||||
|
||||
class _RpcMethodDescription(
|
||||
interfaces.RpcMethodInvocationDescription,
|
||||
interfaces.RpcMethodServiceDescription):
|
||||
|
||||
def __init__(
|
||||
self, cardinality, unary_unary, unary_stream, stream_unary,
|
||||
stream_stream, request_serializer, request_deserializer,
|
||||
response_serializer, response_deserializer):
|
||||
self._cardinality = cardinality
|
||||
self._unary_unary = unary_unary
|
||||
self._unary_stream = unary_stream
|
||||
self._stream_unary = stream_unary
|
||||
self._stream_stream = stream_stream
|
||||
self._request_serializer = request_serializer
|
||||
self._request_deserializer = request_deserializer
|
||||
self._response_serializer = response_serializer
|
||||
self._response_deserializer = response_deserializer
|
||||
|
||||
def cardinality(self):
|
||||
"""See interfaces.RpcMethodDescription.cardinality for specification."""
|
||||
return self._cardinality
|
||||
|
||||
def serialize_request(self, request):
|
||||
"""See interfaces.RpcMethodInvocationDescription.serialize_request."""
|
||||
return self._request_serializer(request)
|
||||
|
||||
def deserialize_request(self, serialized_request):
|
||||
"""See interfaces.RpcMethodServiceDescription.deserialize_request."""
|
||||
return self._request_deserializer(serialized_request)
|
||||
|
||||
def serialize_response(self, response):
|
||||
"""See interfaces.RpcMethodServiceDescription.serialize_response."""
|
||||
return self._response_serializer(response)
|
||||
|
||||
def deserialize_response(self, serialized_response):
|
||||
"""See interfaces.RpcMethodInvocationDescription.deserialize_response."""
|
||||
return self._response_deserializer(serialized_response)
|
||||
|
||||
def service_unary_unary(self, request, context):
|
||||
"""See interfaces.RpcMethodServiceDescription.service_unary_unary."""
|
||||
return self._unary_unary(request, context)
|
||||
|
||||
def service_unary_stream(self, request, context):
|
||||
"""See interfaces.RpcMethodServiceDescription.service_unary_stream."""
|
||||
return self._unary_stream(request, context)
|
||||
|
||||
def service_stream_unary(self, request_iterator, context):
|
||||
"""See interfaces.RpcMethodServiceDescription.service_stream_unary."""
|
||||
return self._stream_unary(request_iterator, context)
|
||||
|
||||
def service_stream_stream(self, request_iterator, context):
|
||||
"""See interfaces.RpcMethodServiceDescription.service_stream_stream."""
|
||||
return self._stream_stream(request_iterator, context)
|
||||
|
||||
|
||||
def unary_unary_invocation_description(
|
||||
request_serializer, response_deserializer):
|
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method.
|
||||
|
||||
Args:
|
||||
request_serializer: A callable that when called on a request
|
||||
value returns a bytestring corresponding to that value.
|
||||
response_deserializer: A callable that when called on a
|
||||
bytestring returns the response value corresponding to
|
||||
that bytestring.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodInvocationDescription constructed from the given
|
||||
arguments representing a unary-request/unary-response RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.UNARY_UNARY, None, None, None, None,
|
||||
request_serializer, None, None, response_deserializer)
|
||||
|
||||
|
||||
def unary_stream_invocation_description(
|
||||
request_serializer, response_deserializer):
|
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method.
|
||||
|
||||
Args:
|
||||
request_serializer: A callable that when called on a request
|
||||
value returns a bytestring corresponding to that value.
|
||||
response_deserializer: A callable that when called on a
|
||||
bytestring returns the response value corresponding to
|
||||
that bytestring.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodInvocationDescription constructed from the given
|
||||
arguments representing a unary-request/streaming-response RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.UNARY_STREAM, None, None, None, None,
|
||||
request_serializer, None, None, response_deserializer)
|
||||
|
||||
|
||||
def stream_unary_invocation_description(
|
||||
request_serializer, response_deserializer):
|
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method.
|
||||
|
||||
Args:
|
||||
request_serializer: A callable that when called on a request
|
||||
value returns a bytestring corresponding to that value.
|
||||
response_deserializer: A callable that when called on a
|
||||
bytestring returns the response value corresponding to
|
||||
that bytestring.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodInvocationDescription constructed from the given
|
||||
arguments representing a streaming-request/unary-response RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.STREAM_UNARY, None, None, None, None,
|
||||
request_serializer, None, None, response_deserializer)
|
||||
|
||||
|
||||
def stream_stream_invocation_description(
|
||||
request_serializer, response_deserializer):
|
||||
"""Creates an interfaces.RpcMethodInvocationDescription for an RPC method.
|
||||
|
||||
Args:
|
||||
request_serializer: A callable that when called on a request
|
||||
value returns a bytestring corresponding to that value.
|
||||
response_deserializer: A callable that when called on a
|
||||
bytestring returns the response value corresponding to
|
||||
that bytestring.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodInvocationDescription constructed from the given
|
||||
arguments representing a streaming-request/streaming-response RPC
|
||||
method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.STREAM_STREAM, None, None, None, None,
|
||||
request_serializer, None, None, response_deserializer)
|
||||
|
||||
|
||||
def unary_unary_service_description(
|
||||
behavior, request_deserializer, response_serializer):
|
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: A callable that implements a unary-unary RPC
|
||||
method that accepts a single request and an interfaces.RpcContext and
|
||||
returns a single response.
|
||||
request_deserializer: A callable that when called on a
|
||||
bytestring returns the request value corresponding to that
|
||||
bytestring.
|
||||
response_serializer: A callable that when called on a
|
||||
response value returns the bytestring corresponding to
|
||||
that value.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodServiceDescription constructed from the given
|
||||
arguments representing a unary-request/unary-response RPC
|
||||
method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None,
|
||||
None, request_deserializer, response_serializer, None)
|
||||
|
||||
|
||||
def unary_stream_service_description(
|
||||
behavior, request_deserializer, response_serializer):
|
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: A callable that implements a unary-stream RPC
|
||||
method that accepts a single request and an interfaces.RpcContext
|
||||
and returns an iterator of zero or more responses.
|
||||
request_deserializer: A callable that when called on a
|
||||
bytestring returns the request value corresponding to that
|
||||
bytestring.
|
||||
response_serializer: A callable that when called on a
|
||||
response value returns the bytestring corresponding to
|
||||
that value.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodServiceDescription constructed from the given
|
||||
arguments representing a unary-request/streaming-response
|
||||
RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None,
|
||||
None, request_deserializer, response_serializer, None)
|
||||
|
||||
|
||||
def stream_unary_service_description(
|
||||
behavior, request_deserializer, response_serializer):
|
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: A callable that implements a stream-unary RPC
|
||||
method that accepts an iterator of zero or more requests
|
||||
and an interfaces.RpcContext and returns a single response.
|
||||
request_deserializer: A callable that when called on a
|
||||
bytestring returns the request value corresponding to that
|
||||
bytestring.
|
||||
response_serializer: A callable that when called on a
|
||||
response value returns the bytestring corresponding to
|
||||
that value.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodServiceDescription constructed from the given
|
||||
arguments representing a streaming-request/unary-response
|
||||
RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None,
|
||||
None, request_deserializer, response_serializer, None)
|
||||
|
||||
|
||||
def stream_stream_service_description(
|
||||
behavior, request_deserializer, response_serializer):
|
||||
"""Creates an interfaces.RpcMethodServiceDescription for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: A callable that implements a stream-stream RPC
|
||||
method that accepts an iterator of zero or more requests
|
||||
and an interfaces.RpcContext and returns an iterator of
|
||||
zero or more responses.
|
||||
request_deserializer: A callable that when called on a
|
||||
bytestring returns the request value corresponding to that
|
||||
bytestring.
|
||||
response_serializer: A callable that when called on a
|
||||
response value returns the bytestring corresponding to
|
||||
that value.
|
||||
|
||||
Returns:
|
||||
An interfaces.RpcMethodServiceDescription constructed from the given
|
||||
arguments representing a
|
||||
streaming-request/streaming-response RPC method.
|
||||
"""
|
||||
return _RpcMethodDescription(
|
||||
interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior,
|
||||
None, request_deserializer, response_serializer, None)
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn('the alpha API (includes this package) is deprecated, '
|
||||
'unmaintained, and no longer tested. Please migrate to the beta '
|
||||
'API.', DeprecationWarning, stacklevel=2)
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for operation context."""
|
||||
|
||||
import time
|
||||
|
||||
# _interfaces is referenced from specification in this module.
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.base import _interfaces # pylint: disable=unused-import
|
||||
|
||||
|
||||
class OperationContext(interfaces.OperationContext):
|
||||
"""An implementation of interfaces.OperationContext."""
|
||||
|
||||
def __init__(
|
||||
self, lock, operation_id, local_failure, termination_manager,
|
||||
transmission_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
operation_id: An object identifying the operation.
|
||||
local_failure: Whichever one of interfaces.Outcome.SERVICED_FAILURE or
|
||||
interfaces.Outcome.SERVICER_FAILURE describes local failure of
|
||||
customer code.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._local_failure = local_failure
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = None
|
||||
self._expiration_manager = None
|
||||
|
||||
self.operation_id = operation_id
|
||||
|
||||
def set_ingestion_and_expiration_managers(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
"""Sets managers with which this OperationContext cooperates.
|
||||
|
||||
Args:
|
||||
ingestion_manager: The _interfaces.IngestionManager for the operation.
|
||||
expiration_manager: The _interfaces.ExpirationManager for the operation.
|
||||
"""
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
def is_active(self):
|
||||
"""See interfaces.OperationContext.is_active for specification."""
|
||||
with self._lock:
|
||||
return self._termination_manager.is_active()
|
||||
|
||||
def add_termination_callback(self, callback):
|
||||
"""See interfaces.OperationContext.add_termination_callback."""
|
||||
with self._lock:
|
||||
self._termination_manager.add_callback(callback)
|
||||
|
||||
def time_remaining(self):
|
||||
"""See interfaces.OperationContext.time_remaining for specification."""
|
||||
with self._lock:
|
||||
deadline = self._expiration_manager.deadline()
|
||||
return max(0.0, deadline - time.time())
|
||||
|
||||
def fail(self, exception):
|
||||
"""See interfaces.OperationContext.fail for specification."""
|
||||
with self._lock:
|
||||
self._termination_manager.abort(self._local_failure)
|
||||
self._transmission_manager.abort(self._local_failure)
|
||||
self._ingestion_manager.abort()
|
||||
self._expiration_manager.abort()
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for handling emitted values."""
|
||||
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.base import _interfaces
|
||||
|
||||
|
||||
class _EmissionManager(_interfaces.EmissionManager):
|
||||
"""An implementation of _interfaces.EmissionManager."""
|
||||
|
||||
def __init__(
|
||||
self, lock, failure_outcome, termination_manager, transmission_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
failure_outcome: Whichever one of interfaces.Outcome.SERVICED_FAILURE or
|
||||
interfaces.Outcome.SERVICER_FAILURE describes this object's methods
|
||||
being called inappropriately by customer code.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._failure_outcome = failure_outcome
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = None
|
||||
self._expiration_manager = None
|
||||
|
||||
self._emission_complete = False
|
||||
|
||||
def set_ingestion_manager_and_expiration_manager(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
def _abort(self):
|
||||
self._termination_manager.abort(self._failure_outcome)
|
||||
self._transmission_manager.abort(self._failure_outcome)
|
||||
self._ingestion_manager.abort()
|
||||
self._expiration_manager.abort()
|
||||
|
||||
def consume(self, value):
|
||||
with self._lock:
|
||||
if self._emission_complete:
|
||||
self._abort()
|
||||
else:
|
||||
self._transmission_manager.inmit(value, False)
|
||||
|
||||
def terminate(self):
|
||||
with self._lock:
|
||||
if not self._emission_complete:
|
||||
self._termination_manager.emission_complete()
|
||||
self._transmission_manager.inmit(None, True)
|
||||
self._emission_complete = True
|
||||
|
||||
def consume_and_terminate(self, value):
|
||||
with self._lock:
|
||||
if self._emission_complete:
|
||||
self._abort()
|
||||
else:
|
||||
self._termination_manager.emission_complete()
|
||||
self._transmission_manager.inmit(value, True)
|
||||
self._emission_complete = True
|
||||
|
||||
|
||||
def front_emission_manager(lock, termination_manager, transmission_manager):
|
||||
"""Creates an _interfaces.EmissionManager appropriate for front-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the operation.
|
||||
|
||||
Returns:
|
||||
An _interfaces.EmissionManager appropriate for front-side use.
|
||||
"""
|
||||
return _EmissionManager(
|
||||
lock, interfaces.Outcome.SERVICED_FAILURE, termination_manager,
|
||||
transmission_manager)
|
||||
|
||||
|
||||
def back_emission_manager(lock, termination_manager, transmission_manager):
|
||||
"""Creates an _interfaces.EmissionManager appropriate for back-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the operation.
|
||||
|
||||
Returns:
|
||||
An _interfaces.EmissionManager appropriate for back-side use.
|
||||
"""
|
||||
return _EmissionManager(
|
||||
lock, interfaces.Outcome.SERVICER_FAILURE, termination_manager,
|
||||
transmission_manager)
|
||||
|
|
@ -1,399 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Implementations of FrontLinks and BackLinks."""
|
||||
|
||||
import collections
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
# _interfaces is referenced from specification in this module.
|
||||
from grpc.framework.base import _cancellation
|
||||
from grpc.framework.base import _context
|
||||
from grpc.framework.base import _emission
|
||||
from grpc.framework.base import _expiration
|
||||
from grpc.framework.base import _ingestion
|
||||
from grpc.framework.base import _interfaces # pylint: disable=unused-import
|
||||
from grpc.framework.base import _reception
|
||||
from grpc.framework.base import _termination
|
||||
from grpc.framework.base import _transmission
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import callable_util
|
||||
|
||||
_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!'
|
||||
|
||||
|
||||
class _EasyOperation(interfaces.Operation):
|
||||
"""A trivial implementation of interfaces.Operation."""
|
||||
|
||||
def __init__(self, emission_manager, context, cancellation_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
emission_manager: The _interfaces.EmissionManager for the operation that
|
||||
will accept values emitted by customer code.
|
||||
context: The interfaces.OperationContext for use by the customer
|
||||
during the operation.
|
||||
cancellation_manager: The _interfaces.CancellationManager for the
|
||||
operation.
|
||||
"""
|
||||
self.consumer = emission_manager
|
||||
self.context = context
|
||||
self._cancellation_manager = cancellation_manager
|
||||
|
||||
def cancel(self):
|
||||
self._cancellation_manager.cancel()
|
||||
|
||||
|
||||
class _Endlette(object):
|
||||
"""Utility for stateful behavior common to Fronts and Backs."""
|
||||
|
||||
def __init__(self, pool):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
pool: A thread pool to use when calling registered idle actions.
|
||||
"""
|
||||
self._lock = threading.Lock()
|
||||
self._pool = pool
|
||||
# Dictionary from operation IDs to ReceptionManager-or-None. A None value
|
||||
# indicates an in-progress fire-and-forget operation for which the customer
|
||||
# has chosen to ignore results.
|
||||
self._operations = {}
|
||||
self._stats = {outcome: 0 for outcome in interfaces.Outcome}
|
||||
self._idle_actions = []
|
||||
|
||||
def terminal_action(self, operation_id):
|
||||
"""Constructs the termination action for a single operation.
|
||||
|
||||
Args:
|
||||
operation_id: An operation ID.
|
||||
|
||||
Returns:
|
||||
A callable that takes an operation outcome for an argument to be used as
|
||||
the termination action for the operation associated with the given
|
||||
operation ID.
|
||||
"""
|
||||
def termination_action(outcome):
|
||||
with self._lock:
|
||||
self._stats[outcome] += 1
|
||||
self._operations.pop(operation_id, None)
|
||||
if not self._operations:
|
||||
for action in self._idle_actions:
|
||||
self._pool.submit(callable_util.with_exceptions_logged(
|
||||
action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE))
|
||||
self._idle_actions = []
|
||||
return termination_action
|
||||
|
||||
def __enter__(self):
|
||||
self._lock.acquire()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._lock.release()
|
||||
|
||||
def get_operation(self, operation_id):
|
||||
return self._operations.get(operation_id, None)
|
||||
|
||||
def add_operation(self, operation_id, operation_reception_manager):
|
||||
self._operations[operation_id] = operation_reception_manager
|
||||
|
||||
def operation_stats(self):
|
||||
with self._lock:
|
||||
return dict(self._stats)
|
||||
|
||||
def add_idle_action(self, action):
|
||||
with self._lock:
|
||||
if self._operations:
|
||||
self._idle_actions.append(action)
|
||||
else:
|
||||
self._pool.submit(callable_util.with_exceptions_logged(
|
||||
action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE))
|
||||
|
||||
|
||||
class _FrontManagement(
|
||||
collections.namedtuple(
|
||||
'_FrontManagement',
|
||||
('reception', 'emission', 'operation', 'cancellation'))):
|
||||
"""Just a trivial helper class to bundle four fellow-traveling objects."""
|
||||
|
||||
|
||||
def _front_operate(
|
||||
callback, work_pool, transmission_pool, utility_pool,
|
||||
termination_action, operation_id, name, payload, complete, timeout,
|
||||
subscription, trace_id):
|
||||
"""Constructs objects necessary for front-side operation management.
|
||||
|
||||
Args:
|
||||
callback: A callable that accepts interfaces.FrontToBackTickets and
|
||||
delivers them to the other side of the operation. Execution of this
|
||||
callable may take any arbitrary length of time.
|
||||
work_pool: A thread pool in which to execute customer code.
|
||||
transmission_pool: A thread pool to use for transmitting to the other side
|
||||
of the operation.
|
||||
utility_pool: A thread pool for utility tasks.
|
||||
termination_action: A no-arg behavior to be called upon operation
|
||||
completion.
|
||||
operation_id: An object identifying the operation.
|
||||
name: The name of the method being called during the operation.
|
||||
payload: The first customer-significant value to be transmitted to the other
|
||||
side. May be None if there is no such value or if the customer chose not
|
||||
to pass it at operation invocation.
|
||||
complete: A boolean indicating whether or not additional payloads will be
|
||||
supplied by the customer.
|
||||
timeout: A length of time in seconds to allow for the operation.
|
||||
subscription: A interfaces.ServicedSubscription describing the
|
||||
customer's interest in the results of the operation.
|
||||
trace_id: A uuid.UUID identifying a set of related operations to which this
|
||||
operation belongs. May be None.
|
||||
|
||||
Returns:
|
||||
A _FrontManagement object bundling together the
|
||||
_interfaces.ReceptionManager, _interfaces.EmissionManager,
|
||||
_context.OperationContext, and _interfaces.CancellationManager for the
|
||||
operation.
|
||||
"""
|
||||
lock = threading.Lock()
|
||||
with lock:
|
||||
termination_manager = _termination.front_termination_manager(
|
||||
work_pool, utility_pool, termination_action, subscription.kind)
|
||||
transmission_manager = _transmission.front_transmission_manager(
|
||||
lock, transmission_pool, callback, operation_id, name,
|
||||
subscription.kind, trace_id, timeout, termination_manager)
|
||||
operation_context = _context.OperationContext(
|
||||
lock, operation_id, interfaces.Outcome.SERVICED_FAILURE,
|
||||
termination_manager, transmission_manager)
|
||||
emission_manager = _emission.front_emission_manager(
|
||||
lock, termination_manager, transmission_manager)
|
||||
ingestion_manager = _ingestion.front_ingestion_manager(
|
||||
lock, work_pool, subscription, termination_manager,
|
||||
transmission_manager, operation_context)
|
||||
expiration_manager = _expiration.front_expiration_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
timeout)
|
||||
reception_manager = _reception.front_reception_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager)
|
||||
cancellation_manager = _cancellation.CancellationManager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager)
|
||||
|
||||
termination_manager.set_expiration_manager(expiration_manager)
|
||||
transmission_manager.set_ingestion_and_expiration_managers(
|
||||
ingestion_manager, expiration_manager)
|
||||
operation_context.set_ingestion_and_expiration_managers(
|
||||
ingestion_manager, expiration_manager)
|
||||
emission_manager.set_ingestion_manager_and_expiration_manager(
|
||||
ingestion_manager, expiration_manager)
|
||||
ingestion_manager.set_expiration_manager(expiration_manager)
|
||||
|
||||
transmission_manager.inmit(payload, complete)
|
||||
|
||||
if subscription.kind is interfaces.ServicedSubscription.Kind.NONE:
|
||||
returned_reception_manager = None
|
||||
else:
|
||||
returned_reception_manager = reception_manager
|
||||
|
||||
return _FrontManagement(
|
||||
returned_reception_manager, emission_manager, operation_context,
|
||||
cancellation_manager)
|
||||
|
||||
|
||||
class FrontLink(interfaces.FrontLink):
|
||||
"""An implementation of interfaces.FrontLink."""
|
||||
|
||||
def __init__(self, work_pool, transmission_pool, utility_pool):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
work_pool: A thread pool to be used for executing customer code.
|
||||
transmission_pool: A thread pool to be used for transmitting values to
|
||||
the other side of the operation.
|
||||
utility_pool: A thread pool to be used for utility tasks.
|
||||
"""
|
||||
self._endlette = _Endlette(utility_pool)
|
||||
self._work_pool = work_pool
|
||||
self._transmission_pool = transmission_pool
|
||||
self._utility_pool = utility_pool
|
||||
self._callback = None
|
||||
|
||||
self._operations = {}
|
||||
|
||||
def join_rear_link(self, rear_link):
|
||||
"""See interfaces.ForeLink.join_rear_link for specification."""
|
||||
with self._endlette:
|
||||
self._callback = rear_link.accept_front_to_back_ticket
|
||||
|
||||
def operation_stats(self):
|
||||
"""See interfaces.End.operation_stats for specification."""
|
||||
return self._endlette.operation_stats()
|
||||
|
||||
def add_idle_action(self, action):
|
||||
"""See interfaces.End.add_idle_action for specification."""
|
||||
self._endlette.add_idle_action(action)
|
||||
|
||||
def operate(
|
||||
self, name, payload, complete, timeout, subscription, trace_id):
|
||||
"""See interfaces.Front.operate for specification."""
|
||||
operation_id = uuid.uuid4()
|
||||
with self._endlette:
|
||||
management = _front_operate(
|
||||
self._callback, self._work_pool, self._transmission_pool,
|
||||
self._utility_pool, self._endlette.terminal_action(operation_id),
|
||||
operation_id, name, payload, complete, timeout, subscription,
|
||||
trace_id)
|
||||
self._endlette.add_operation(operation_id, management.reception)
|
||||
return _EasyOperation(
|
||||
management.emission, management.operation, management.cancellation)
|
||||
|
||||
def accept_back_to_front_ticket(self, ticket):
|
||||
"""See interfaces.End.act for specification."""
|
||||
with self._endlette:
|
||||
reception_manager = self._endlette.get_operation(ticket.operation_id)
|
||||
if reception_manager:
|
||||
reception_manager.receive_ticket(ticket)
|
||||
|
||||
|
||||
def _back_operate(
|
||||
servicer, callback, work_pool, transmission_pool, utility_pool,
|
||||
termination_action, ticket, default_timeout, maximum_timeout):
|
||||
"""Constructs objects necessary for back-side operation management.
|
||||
|
||||
Also begins back-side operation by feeding the first received ticket into the
|
||||
constructed _interfaces.ReceptionManager.
|
||||
|
||||
Args:
|
||||
servicer: An interfaces.Servicer for servicing operations.
|
||||
callback: A callable that accepts interfaces.BackToFrontTickets and
|
||||
delivers them to the other side of the operation. Execution of this
|
||||
callable may take any arbitrary length of time.
|
||||
work_pool: A thread pool in which to execute customer code.
|
||||
transmission_pool: A thread pool to use for transmitting to the other side
|
||||
of the operation.
|
||||
utility_pool: A thread pool for utility tasks.
|
||||
termination_action: A no-arg behavior to be called upon operation
|
||||
completion.
|
||||
ticket: The first interfaces.FrontToBackTicket received for the operation.
|
||||
default_timeout: A length of time in seconds to be used as the default
|
||||
time alloted for a single operation.
|
||||
maximum_timeout: A length of time in seconds to be used as the maximum
|
||||
time alloted for a single operation.
|
||||
|
||||
Returns:
|
||||
The _interfaces.ReceptionManager to be used for the operation.
|
||||
"""
|
||||
lock = threading.Lock()
|
||||
with lock:
|
||||
termination_manager = _termination.back_termination_manager(
|
||||
work_pool, utility_pool, termination_action, ticket.subscription)
|
||||
transmission_manager = _transmission.back_transmission_manager(
|
||||
lock, transmission_pool, callback, ticket.operation_id,
|
||||
termination_manager, ticket.subscription)
|
||||
operation_context = _context.OperationContext(
|
||||
lock, ticket.operation_id, interfaces.Outcome.SERVICER_FAILURE,
|
||||
termination_manager, transmission_manager)
|
||||
emission_manager = _emission.back_emission_manager(
|
||||
lock, termination_manager, transmission_manager)
|
||||
ingestion_manager = _ingestion.back_ingestion_manager(
|
||||
lock, work_pool, servicer, termination_manager,
|
||||
transmission_manager, operation_context, emission_manager)
|
||||
expiration_manager = _expiration.back_expiration_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
ticket.timeout, default_timeout, maximum_timeout)
|
||||
reception_manager = _reception.back_reception_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager)
|
||||
|
||||
termination_manager.set_expiration_manager(expiration_manager)
|
||||
transmission_manager.set_ingestion_and_expiration_managers(
|
||||
ingestion_manager, expiration_manager)
|
||||
operation_context.set_ingestion_and_expiration_managers(
|
||||
ingestion_manager, expiration_manager)
|
||||
emission_manager.set_ingestion_manager_and_expiration_manager(
|
||||
ingestion_manager, expiration_manager)
|
||||
ingestion_manager.set_expiration_manager(expiration_manager)
|
||||
|
||||
reception_manager.receive_ticket(ticket)
|
||||
|
||||
return reception_manager
|
||||
|
||||
|
||||
class BackLink(interfaces.BackLink):
|
||||
"""An implementation of interfaces.BackLink."""
|
||||
|
||||
def __init__(
|
||||
self, servicer, work_pool, transmission_pool, utility_pool,
|
||||
default_timeout, maximum_timeout):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
servicer: An interfaces.Servicer for servicing operations.
|
||||
work_pool: A thread pool in which to execute customer code.
|
||||
transmission_pool: A thread pool to use for transmitting to the other side
|
||||
of the operation.
|
||||
utility_pool: A thread pool for utility tasks.
|
||||
default_timeout: A length of time in seconds to be used as the default
|
||||
time alloted for a single operation.
|
||||
maximum_timeout: A length of time in seconds to be used as the maximum
|
||||
time alloted for a single operation.
|
||||
"""
|
||||
self._endlette = _Endlette(utility_pool)
|
||||
self._servicer = servicer
|
||||
self._work_pool = work_pool
|
||||
self._transmission_pool = transmission_pool
|
||||
self._utility_pool = utility_pool
|
||||
self._default_timeout = default_timeout
|
||||
self._maximum_timeout = maximum_timeout
|
||||
self._callback = None
|
||||
|
||||
def join_fore_link(self, fore_link):
|
||||
"""See interfaces.RearLink.join_fore_link for specification."""
|
||||
with self._endlette:
|
||||
self._callback = fore_link.accept_back_to_front_ticket
|
||||
|
||||
def accept_front_to_back_ticket(self, ticket):
|
||||
"""See interfaces.RearLink.accept_front_to_back_ticket for specification."""
|
||||
with self._endlette:
|
||||
reception_manager = self._endlette.get_operation(ticket.operation_id)
|
||||
if reception_manager is None:
|
||||
reception_manager = _back_operate(
|
||||
self._servicer, self._callback, self._work_pool,
|
||||
self._transmission_pool, self._utility_pool,
|
||||
self._endlette.terminal_action(ticket.operation_id), ticket,
|
||||
self._default_timeout, self._maximum_timeout)
|
||||
self._endlette.add_operation(ticket.operation_id, reception_manager)
|
||||
else:
|
||||
reception_manager.receive_ticket(ticket)
|
||||
|
||||
def operation_stats(self):
|
||||
"""See interfaces.End.operation_stats for specification."""
|
||||
return self._endlette.operation_stats()
|
||||
|
||||
def add_idle_action(self, action):
|
||||
"""See interfaces.End.add_idle_action for specification."""
|
||||
self._endlette.add_idle_action(action)
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for operation expiration."""
|
||||
|
||||
import time
|
||||
|
||||
from grpc.framework.base import _interfaces
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import later
|
||||
|
||||
|
||||
class _ExpirationManager(_interfaces.ExpirationManager):
|
||||
"""An implementation of _interfaces.ExpirationManager."""
|
||||
|
||||
def __init__(
|
||||
self, lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
commencement, timeout, maximum_timeout):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
ingestion_manager: The _interfaces.IngestionManager for the operation.
|
||||
commencement: The time in seconds since the epoch at which the operation
|
||||
began.
|
||||
timeout: A length of time in seconds to allow for the operation to run.
|
||||
maximum_timeout: The maximum length of time in seconds to allow for the
|
||||
operation to run despite what is requested via this object's
|
||||
change_timout method.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._commencement = commencement
|
||||
self._maximum_timeout = maximum_timeout
|
||||
|
||||
self._timeout = timeout
|
||||
self._deadline = commencement + timeout
|
||||
self._index = None
|
||||
self._future = None
|
||||
|
||||
def _expire(self, index):
|
||||
with self._lock:
|
||||
if self._future is not None and index == self._index:
|
||||
self._future = None
|
||||
self._termination_manager.abort(interfaces.Outcome.EXPIRED)
|
||||
self._transmission_manager.abort(interfaces.Outcome.EXPIRED)
|
||||
self._ingestion_manager.abort()
|
||||
|
||||
def start(self):
|
||||
self._index = 0
|
||||
self._future = later.later(self._timeout, lambda: self._expire(0))
|
||||
|
||||
def change_timeout(self, timeout):
|
||||
if self._future is not None and timeout != self._timeout:
|
||||
self._future.cancel()
|
||||
new_timeout = min(timeout, self._maximum_timeout)
|
||||
new_index = self._index + 1
|
||||
self._timeout = new_timeout
|
||||
self._deadline = self._commencement + new_timeout
|
||||
self._index = new_index
|
||||
delay = self._deadline - time.time()
|
||||
self._future = later.later(
|
||||
delay, lambda: self._expire(new_index))
|
||||
|
||||
def deadline(self):
|
||||
return self._deadline
|
||||
|
||||
def abort(self):
|
||||
if self._future:
|
||||
self._future.cancel()
|
||||
self._future = None
|
||||
self._deadline_index = None
|
||||
|
||||
|
||||
def front_expiration_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
timeout):
|
||||
"""Creates an _interfaces.ExpirationManager appropriate for front-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
ingestion_manager: The _interfaces.IngestionManager for the operation.
|
||||
timeout: A length of time in seconds to allow for the operation to run.
|
||||
|
||||
Returns:
|
||||
An _interfaces.ExpirationManager appropriate for front-side use.
|
||||
"""
|
||||
commencement = time.time()
|
||||
expiration_manager = _ExpirationManager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
commencement, timeout, timeout)
|
||||
expiration_manager.start()
|
||||
return expiration_manager
|
||||
|
||||
|
||||
def back_expiration_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
timeout, default_timeout, maximum_timeout):
|
||||
"""Creates an _interfaces.ExpirationManager appropriate for back-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
ingestion_manager: The _interfaces.IngestionManager for the operation.
|
||||
timeout: A length of time in seconds to allow for the operation to run. May
|
||||
be None in which case default_timeout will be used.
|
||||
default_timeout: The default length of time in seconds to allow for the
|
||||
operation to run if the front-side customer has not specified such a value
|
||||
(or if the value they specified is not yet known).
|
||||
maximum_timeout: The maximum length of time in seconds to allow for the
|
||||
operation to run.
|
||||
|
||||
Returns:
|
||||
An _interfaces.ExpirationManager appropriate for back-side use.
|
||||
"""
|
||||
commencement = time.time()
|
||||
expiration_manager = _ExpirationManager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
commencement, default_timeout if timeout is None else timeout,
|
||||
maximum_timeout)
|
||||
expiration_manager.start()
|
||||
return expiration_manager
|
||||
|
|
@ -1,443 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for ingestion during an operation."""
|
||||
|
||||
import abc
|
||||
import collections
|
||||
|
||||
import six
|
||||
|
||||
from grpc.framework.base import _constants
|
||||
from grpc.framework.base import _interfaces
|
||||
from grpc.framework.base import exceptions
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import abandonment
|
||||
from grpc.framework.foundation import callable_util
|
||||
from grpc.framework.foundation import stream
|
||||
|
||||
_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!'
|
||||
_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!'
|
||||
|
||||
|
||||
class _ConsumerCreation(collections.namedtuple(
|
||||
'_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))):
|
||||
"""A sum type for the outcome of ingestion initialization.
|
||||
|
||||
Either consumer will be non-None, remote_error will be True, or abandoned will
|
||||
be True.
|
||||
|
||||
Attributes:
|
||||
consumer: A stream.Consumer for ingesting payloads.
|
||||
remote_error: A boolean indicating that the consumer could not be created
|
||||
due to an error on the remote side of the operation.
|
||||
abandoned: A boolean indicating that the consumer creation was abandoned.
|
||||
"""
|
||||
|
||||
|
||||
class _EmptyConsumer(stream.Consumer):
|
||||
"""A no-operative stream.Consumer that ignores all inputs and calls."""
|
||||
|
||||
def consume(self, value):
|
||||
"""See stream.Consumer.consume for specification."""
|
||||
|
||||
def terminate(self):
|
||||
"""See stream.Consumer.terminate for specification."""
|
||||
|
||||
def consume_and_terminate(self, value):
|
||||
"""See stream.Consumer.consume_and_terminate for specification."""
|
||||
|
||||
|
||||
class _ConsumerCreator(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Common specification of different consumer-creating behavior."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_consumer(self, requirement):
|
||||
"""Creates the stream.Consumer to which customer payloads will be delivered.
|
||||
|
||||
Any exceptions raised by this method should be attributed to and treated as
|
||||
defects in the serviced or servicer code called by this method.
|
||||
|
||||
Args:
|
||||
requirement: A value required by this _ConsumerCreator for consumer
|
||||
creation.
|
||||
|
||||
Returns:
|
||||
A _ConsumerCreation describing the result of consumer creation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class _FrontConsumerCreator(_ConsumerCreator):
|
||||
"""A _ConsumerCreator appropriate for front-side use."""
|
||||
|
||||
def __init__(self, subscription, operation_context):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
subscription: The serviced's interfaces.ServicedSubscription for the
|
||||
operation.
|
||||
operation_context: The interfaces.OperationContext object for the
|
||||
operation.
|
||||
"""
|
||||
self._subscription = subscription
|
||||
self._operation_context = operation_context
|
||||
|
||||
def create_consumer(self, requirement):
|
||||
"""See _ConsumerCreator.create_consumer for specification."""
|
||||
if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL:
|
||||
try:
|
||||
return _ConsumerCreation(
|
||||
self._subscription.ingestor.consumer(self._operation_context),
|
||||
False, False)
|
||||
except abandonment.Abandoned:
|
||||
return _ConsumerCreation(None, False, True)
|
||||
else:
|
||||
return _ConsumerCreation(_EmptyConsumer(), False, False)
|
||||
|
||||
|
||||
class _BackConsumerCreator(_ConsumerCreator):
|
||||
"""A _ConsumerCreator appropriate for back-side use."""
|
||||
|
||||
def __init__(self, servicer, operation_context, emission_consumer):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
servicer: The interfaces.Servicer that will service the operation.
|
||||
operation_context: The interfaces.OperationContext object for the
|
||||
operation.
|
||||
emission_consumer: The stream.Consumer object to which payloads emitted
|
||||
from the operation will be passed.
|
||||
"""
|
||||
self._servicer = servicer
|
||||
self._operation_context = operation_context
|
||||
self._emission_consumer = emission_consumer
|
||||
|
||||
def create_consumer(self, requirement):
|
||||
"""See _ConsumerCreator.create_consumer for full specification.
|
||||
|
||||
Args:
|
||||
requirement: The name of the Servicer method to be called during this
|
||||
operation.
|
||||
|
||||
Returns:
|
||||
A _ConsumerCreation describing the result of consumer creation.
|
||||
"""
|
||||
try:
|
||||
return _ConsumerCreation(
|
||||
self._servicer.service(
|
||||
requirement, self._operation_context, self._emission_consumer),
|
||||
False, False)
|
||||
except exceptions.NoSuchMethodError:
|
||||
return _ConsumerCreation(None, True, False)
|
||||
except abandonment.Abandoned:
|
||||
return _ConsumerCreation(None, False, True)
|
||||
|
||||
|
||||
class _WrappedConsumer(object):
|
||||
"""Wraps a consumer to catch the exceptions that it is allowed to throw."""
|
||||
|
||||
def __init__(self, consumer):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
consumer: A stream.Consumer that may raise abandonment.Abandoned from any
|
||||
of its methods.
|
||||
"""
|
||||
self._consumer = consumer
|
||||
|
||||
def moar(self, payload, complete):
|
||||
"""Makes progress with the wrapped consumer.
|
||||
|
||||
This method catches all exceptions allowed to be thrown by the wrapped
|
||||
consumer. Any exceptions raised by this method should be blamed on the
|
||||
customer-supplied consumer.
|
||||
|
||||
Args:
|
||||
payload: A customer-significant payload object. May be None only if
|
||||
complete is True.
|
||||
complete: Whether or not the end of the payload sequence has been reached.
|
||||
Must be True if payload is None.
|
||||
|
||||
Returns:
|
||||
True if the wrapped consumer made progress or False if the wrapped
|
||||
consumer raised abandonment.Abandoned to indicate its abandonment of
|
||||
progress.
|
||||
"""
|
||||
try:
|
||||
if payload is None:
|
||||
self._consumer.terminate()
|
||||
elif complete:
|
||||
self._consumer.consume_and_terminate(payload)
|
||||
else:
|
||||
self._consumer.consume(payload)
|
||||
return True
|
||||
except abandonment.Abandoned:
|
||||
return False
|
||||
|
||||
|
||||
class _IngestionManager(_interfaces.IngestionManager):
|
||||
"""An implementation of _interfaces.IngestionManager."""
|
||||
|
||||
def __init__(
|
||||
self, lock, pool, consumer_creator, failure_outcome, termination_manager,
|
||||
transmission_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
pool: A thread pool in which to execute customer code.
|
||||
consumer_creator: A _ConsumerCreator wrapping the portion of customer code
|
||||
that when called returns the stream.Consumer with which the customer
|
||||
code will ingest payload values.
|
||||
failure_outcome: Whichever one of
|
||||
interfaces.Outcome.SERVICED_FAILURE or
|
||||
interfaces.Outcome.SERVICER_FAILURE describes local failure of
|
||||
customer code.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._pool = pool
|
||||
self._consumer_creator = consumer_creator
|
||||
self._failure_outcome = failure_outcome
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._expiration_manager = None
|
||||
|
||||
self._wrapped_ingestion_consumer = None
|
||||
self._pending_ingestion = []
|
||||
self._ingestion_complete = False
|
||||
self._processing = False
|
||||
|
||||
def set_expiration_manager(self, expiration_manager):
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
def _abort_internal_only(self):
|
||||
self._wrapped_ingestion_consumer = None
|
||||
self._pending_ingestion = None
|
||||
|
||||
def _abort_and_notify(self, outcome):
|
||||
self._abort_internal_only()
|
||||
self._termination_manager.abort(outcome)
|
||||
self._transmission_manager.abort(outcome)
|
||||
self._expiration_manager.abort()
|
||||
|
||||
def _next(self):
|
||||
"""Computes the next step for ingestion.
|
||||
|
||||
Returns:
|
||||
A payload, complete, continue triplet indicating what payload (if any) is
|
||||
available to feed into customer code, whether or not the sequence of
|
||||
payloads has terminated, and whether or not there is anything
|
||||
immediately actionable to call customer code to do.
|
||||
"""
|
||||
if self._pending_ingestion is None:
|
||||
return None, False, False
|
||||
elif self._pending_ingestion:
|
||||
payload = self._pending_ingestion.pop(0)
|
||||
complete = self._ingestion_complete and not self._pending_ingestion
|
||||
return payload, complete, True
|
||||
elif self._ingestion_complete:
|
||||
return None, True, True
|
||||
else:
|
||||
return None, False, False
|
||||
|
||||
def _process(self, wrapped_ingestion_consumer, payload, complete):
|
||||
"""A method to call to execute customer code.
|
||||
|
||||
This object's lock must *not* be held when calling this method.
|
||||
|
||||
Args:
|
||||
wrapped_ingestion_consumer: The _WrappedConsumer with which to pass
|
||||
payloads to customer code.
|
||||
payload: A customer payload. May be None only if complete is True.
|
||||
complete: Whether or not the sequence of payloads to pass to the customer
|
||||
has concluded.
|
||||
"""
|
||||
while True:
|
||||
consumption_outcome = callable_util.call_logging_exceptions(
|
||||
wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE,
|
||||
payload, complete)
|
||||
if consumption_outcome.exception is None:
|
||||
if consumption_outcome.return_value:
|
||||
with self._lock:
|
||||
if complete:
|
||||
self._pending_ingestion = None
|
||||
self._termination_manager.ingestion_complete()
|
||||
return
|
||||
else:
|
||||
payload, complete, moar = self._next()
|
||||
if not moar:
|
||||
self._processing = False
|
||||
return
|
||||
else:
|
||||
with self._lock:
|
||||
if self._pending_ingestion is not None:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
self._processing = False
|
||||
return
|
||||
else:
|
||||
with self._lock:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
self._processing = False
|
||||
return
|
||||
|
||||
def start(self, requirement):
|
||||
if self._pending_ingestion is not None:
|
||||
def initialize():
|
||||
consumer_creation_outcome = callable_util.call_logging_exceptions(
|
||||
self._consumer_creator.create_consumer,
|
||||
_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement)
|
||||
if consumer_creation_outcome.return_value is None:
|
||||
with self._lock:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
self._processing = False
|
||||
elif consumer_creation_outcome.return_value.remote_error:
|
||||
with self._lock:
|
||||
self._abort_and_notify(interfaces.Outcome.RECEPTION_FAILURE)
|
||||
self._processing = False
|
||||
elif consumer_creation_outcome.return_value.abandoned:
|
||||
with self._lock:
|
||||
if self._pending_ingestion is not None:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
self._processing = False
|
||||
else:
|
||||
wrapped_ingestion_consumer = _WrappedConsumer(
|
||||
consumer_creation_outcome.return_value.consumer)
|
||||
with self._lock:
|
||||
self._wrapped_ingestion_consumer = wrapped_ingestion_consumer
|
||||
payload, complete, moar = self._next()
|
||||
if not moar:
|
||||
self._processing = False
|
||||
return
|
||||
|
||||
self._process(wrapped_ingestion_consumer, payload, complete)
|
||||
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE))
|
||||
self._processing = True
|
||||
|
||||
def consume(self, payload):
|
||||
if self._ingestion_complete:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
elif self._pending_ingestion is not None:
|
||||
if self._processing:
|
||||
self._pending_ingestion.append(payload)
|
||||
else:
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
self._wrapped_ingestion_consumer, payload, False)
|
||||
self._processing = True
|
||||
|
||||
def terminate(self):
|
||||
if self._ingestion_complete:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
else:
|
||||
self._ingestion_complete = True
|
||||
if self._pending_ingestion is not None and not self._processing:
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
self._wrapped_ingestion_consumer, None, True)
|
||||
self._processing = True
|
||||
|
||||
def consume_and_terminate(self, payload):
|
||||
if self._ingestion_complete:
|
||||
self._abort_and_notify(self._failure_outcome)
|
||||
else:
|
||||
self._ingestion_complete = True
|
||||
if self._pending_ingestion is not None:
|
||||
if self._processing:
|
||||
self._pending_ingestion.append(payload)
|
||||
else:
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
self._wrapped_ingestion_consumer, payload, True)
|
||||
self._processing = True
|
||||
|
||||
def abort(self):
|
||||
"""See _interfaces.IngestionManager.abort for specification."""
|
||||
self._abort_internal_only()
|
||||
|
||||
|
||||
def front_ingestion_manager(
|
||||
lock, pool, subscription, termination_manager, transmission_manager,
|
||||
operation_context):
|
||||
"""Creates an IngestionManager appropriate for front-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
pool: A thread pool in which to execute customer code.
|
||||
subscription: A interfaces.ServicedSubscription indicating the
|
||||
customer's interest in the results of the operation.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
operation_context: A interfaces.OperationContext for the operation.
|
||||
|
||||
Returns:
|
||||
An IngestionManager appropriate for front-side use.
|
||||
"""
|
||||
ingestion_manager = _IngestionManager(
|
||||
lock, pool, _FrontConsumerCreator(subscription, operation_context),
|
||||
interfaces.Outcome.SERVICED_FAILURE, termination_manager,
|
||||
transmission_manager)
|
||||
ingestion_manager.start(None)
|
||||
return ingestion_manager
|
||||
|
||||
|
||||
def back_ingestion_manager(
|
||||
lock, pool, servicer, termination_manager, transmission_manager,
|
||||
operation_context, emission_consumer):
|
||||
"""Creates an IngestionManager appropriate for back-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
pool: A thread pool in which to execute customer code.
|
||||
servicer: A interfaces.Servicer for servicing the operation.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
operation_context: A interfaces.OperationContext for the operation.
|
||||
emission_consumer: The _interfaces.EmissionConsumer for the operation.
|
||||
|
||||
Returns:
|
||||
An IngestionManager appropriate for back-side use.
|
||||
"""
|
||||
ingestion_manager = _IngestionManager(
|
||||
lock, pool, _BackConsumerCreator(
|
||||
servicer, operation_context, emission_consumer),
|
||||
interfaces.Outcome.SERVICER_FAILURE, termination_manager,
|
||||
transmission_manager)
|
||||
return ingestion_manager
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
# 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-internal interfaces."""
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
# interfaces is referenced from specification in this module.
|
||||
from grpc.framework.base import interfaces # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import stream
|
||||
|
||||
|
||||
class TerminationManager(six.with_metaclass(abc.ABCMeta)):
|
||||
"""An object responsible for handling the termination of an operation."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_expiration_manager(self, expiration_manager):
|
||||
"""Sets the ExpirationManager with which this object will cooperate."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_active(self):
|
||||
"""Reports whether or not the operation is active.
|
||||
|
||||
Returns:
|
||||
True if the operation is active or False if the operation has terminated.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_callback(self, callback):
|
||||
"""Registers a callback to be called on operation termination.
|
||||
|
||||
If the operation has already terminated, the callback will be called
|
||||
immediately.
|
||||
|
||||
Args:
|
||||
callback: A callable that will be passed an interfaces.Outcome value.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def emission_complete(self):
|
||||
"""Indicates that emissions from customer code have completed."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def transmission_complete(self):
|
||||
"""Indicates that transmissions to the remote end are complete."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def ingestion_complete(self):
|
||||
"""Indicates that customer code ingestion of received values is complete."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def abort(self, outcome):
|
||||
"""Indicates that the operation must abort for the indicated reason.
|
||||
|
||||
Args:
|
||||
outcome: An interfaces.Outcome indicating operation abortion.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class TransmissionManager(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A manager responsible for transmitting to the other end of an operation."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def inmit(self, emission, complete):
|
||||
"""Accepts a value for transmission to the other end of the operation.
|
||||
|
||||
Args:
|
||||
emission: A value of some significance to the customer to be transmitted
|
||||
to the other end of the operation. May be None only if complete is True.
|
||||
complete: A boolean that if True indicates that customer code has emitted
|
||||
all values it intends to emit.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def abort(self, outcome):
|
||||
"""Indicates that the operation has aborted for the indicated reason.
|
||||
|
||||
Args:
|
||||
outcome: An interfaces.Outcome indicating operation abortion.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class EmissionManager(six.with_metaclass(abc.ABCMeta, stream.Consumer)):
|
||||
"""A manager of values emitted by customer code."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_ingestion_manager_and_expiration_manager(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
"""Sets two other objects with which this EmissionManager will cooperate.
|
||||
|
||||
Args:
|
||||
ingestion_manager: The IngestionManager for the operation.
|
||||
expiration_manager: The ExpirationManager for the operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def consume(self, value):
|
||||
"""Accepts a value emitted by customer code.
|
||||
|
||||
This method should only be called by customer code.
|
||||
|
||||
Args:
|
||||
value: Any value of significance to the customer.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def terminate(self):
|
||||
"""Indicates that no more values will be emitted by customer code.
|
||||
|
||||
This method should only be called by customer code.
|
||||
|
||||
Implementations of this method may be idempotent and forgive customer code
|
||||
calling this method more than once.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def consume_and_terminate(self, value):
|
||||
"""Accepts the last value emitted by customer code.
|
||||
|
||||
This method should only be called by customer code.
|
||||
|
||||
Args:
|
||||
value: Any value of significance to the customer.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class IngestionManager(six.with_metaclass(abc.ABCMeta, stream.Consumer)):
|
||||
"""A manager responsible for executing customer code."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_expiration_manager(self, expiration_manager):
|
||||
"""Sets the ExpirationManager with which this object will cooperate."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def start(self, requirement):
|
||||
"""Commences execution of customer code.
|
||||
|
||||
Args:
|
||||
requirement: Some value unavailable at the time of this object's
|
||||
construction that is required to begin executing customer code.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def consume(self, payload):
|
||||
"""Accepts a customer-significant value to be supplied to customer code.
|
||||
|
||||
Args:
|
||||
payload: Some customer-significant value.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def terminate(self):
|
||||
"""Indicates the end of values to be supplied to customer code."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def consume_and_terminate(self, payload):
|
||||
"""Accepts the last value to be supplied to customer code.
|
||||
|
||||
Args:
|
||||
payload: Some customer-significant value (and the last such value).
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def abort(self):
|
||||
"""Indicates to this manager that the operation has aborted."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ExpirationManager(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A manager responsible for aborting the operation if it runs out of time."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def change_timeout(self, timeout):
|
||||
"""Changes the timeout allotted for the operation.
|
||||
|
||||
Operation duration is always measure from the beginning of the operation;
|
||||
calling this method changes the operation's allotted time to timeout total
|
||||
seconds, not timeout seconds from the time of this method call.
|
||||
|
||||
Args:
|
||||
timeout: A length of time in seconds to allow for the operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def deadline(self):
|
||||
"""Returns the time until which the operation is allowed to run.
|
||||
|
||||
Returns:
|
||||
The time (seconds since the epoch) at which the operation will expire.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def abort(self):
|
||||
"""Indicates to this manager that the operation has aborted."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ReceptionManager(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A manager responsible for receiving tickets from the other end."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def receive_ticket(self, ticket):
|
||||
"""Handle a ticket from the other side of the operation.
|
||||
|
||||
Args:
|
||||
ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket
|
||||
appropriate to this end of the operation and this object.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class CancellationManager(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A manager of operation cancellation."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self):
|
||||
"""Cancels the operation."""
|
||||
raise NotImplementedError()
|
||||
|
|
@ -1,400 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for ticket reception."""
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.base import _interfaces
|
||||
|
||||
_INITIAL_FRONT_TO_BACK_TICKET_KINDS = (
|
||||
interfaces.FrontToBackTicket.Kind.COMMENCEMENT,
|
||||
interfaces.FrontToBackTicket.Kind.ENTIRE,
|
||||
)
|
||||
|
||||
|
||||
class _Receiver(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Common specification of different ticket-handling behavior."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def abort_if_abortive(self, ticket):
|
||||
"""Aborts the operation if the ticket is abortive.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
|
||||
Returns:
|
||||
A boolean indicating whether or not this Receiver aborted the operation
|
||||
based on the ticket.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def receive(self, ticket):
|
||||
"""Handles a just-arrived ticket.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
|
||||
Returns:
|
||||
A boolean indicating whether or not the ticket was terminal (i.e. whether
|
||||
or not non-abortive tickets are legal after this one).
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def reception_failure(self):
|
||||
"""Aborts the operation with an indication of reception failure."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _abort(
|
||||
outcome, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Indicates abortion with the given outcome to the given managers."""
|
||||
termination_manager.abort(outcome)
|
||||
transmission_manager.abort(outcome)
|
||||
ingestion_manager.abort()
|
||||
expiration_manager.abort()
|
||||
|
||||
|
||||
def _abort_if_abortive(
|
||||
ticket, abortive, termination_manager, transmission_manager,
|
||||
ingestion_manager, expiration_manager):
|
||||
"""Determines a ticket's being abortive and if so aborts the operation.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
abortive: A callable that takes a ticket and returns an interfaces.Outcome
|
||||
indicating that the operation should be aborted or None indicating that
|
||||
the operation should not be aborted.
|
||||
termination_manager: The operation's _interfaces.TerminationManager.
|
||||
transmission_manager: The operation's _interfaces.TransmissionManager.
|
||||
ingestion_manager: The operation's _interfaces.IngestionManager.
|
||||
expiration_manager: The operation's _interfaces.ExpirationManager.
|
||||
|
||||
Returns:
|
||||
True if the operation was aborted; False otherwise.
|
||||
"""
|
||||
abortion_outcome = abortive(ticket)
|
||||
if abortion_outcome is None:
|
||||
return False
|
||||
else:
|
||||
_abort(
|
||||
abortion_outcome, termination_manager, transmission_manager,
|
||||
ingestion_manager, expiration_manager)
|
||||
return True
|
||||
|
||||
|
||||
def _reception_failure(
|
||||
termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Aborts the operation with an indication of reception failure."""
|
||||
_abort(
|
||||
interfaces.Outcome.RECEPTION_FAILURE, termination_manager,
|
||||
transmission_manager, ingestion_manager, expiration_manager)
|
||||
|
||||
|
||||
class _BackReceiver(_Receiver):
|
||||
"""Ticket-handling specific to the back side of an operation."""
|
||||
|
||||
def __init__(
|
||||
self, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
termination_manager: The operation's _interfaces.TerminationManager.
|
||||
transmission_manager: The operation's _interfaces.TransmissionManager.
|
||||
ingestion_manager: The operation's _interfaces.IngestionManager.
|
||||
expiration_manager: The operation's _interfaces.ExpirationManager.
|
||||
"""
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
self._first_ticket_seen = False
|
||||
self._last_ticket_seen = False
|
||||
|
||||
def _abortive(self, ticket):
|
||||
"""Determines whether or not (and if so, how) a ticket is abortive.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
|
||||
Returns:
|
||||
An interfaces.Outcome value describing operation abortion if the
|
||||
ticket is abortive or None if the ticket is not abortive.
|
||||
"""
|
||||
if ticket.kind is interfaces.FrontToBackTicket.Kind.CANCELLATION:
|
||||
return interfaces.Outcome.CANCELLED
|
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.EXPIRATION:
|
||||
return interfaces.Outcome.EXPIRED
|
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE:
|
||||
return interfaces.Outcome.SERVICED_FAILURE
|
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE:
|
||||
return interfaces.Outcome.SERVICED_FAILURE
|
||||
elif (ticket.kind in _INITIAL_FRONT_TO_BACK_TICKET_KINDS and
|
||||
self._first_ticket_seen):
|
||||
return interfaces.Outcome.RECEPTION_FAILURE
|
||||
elif self._last_ticket_seen:
|
||||
return interfaces.Outcome.RECEPTION_FAILURE
|
||||
else:
|
||||
return None
|
||||
|
||||
def abort_if_abortive(self, ticket):
|
||||
"""See _Receiver.abort_if_abortive for specification."""
|
||||
return _abort_if_abortive(
|
||||
ticket, self._abortive, self._termination_manager,
|
||||
self._transmission_manager, self._ingestion_manager,
|
||||
self._expiration_manager)
|
||||
|
||||
def receive(self, ticket):
|
||||
"""See _Receiver.receive for specification."""
|
||||
if ticket.timeout is not None:
|
||||
self._expiration_manager.change_timeout(ticket.timeout)
|
||||
|
||||
if ticket.kind is interfaces.FrontToBackTicket.Kind.COMMENCEMENT:
|
||||
self._first_ticket_seen = True
|
||||
self._ingestion_manager.start(ticket.name)
|
||||
if ticket.payload is not None:
|
||||
self._ingestion_manager.consume(ticket.payload)
|
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.CONTINUATION:
|
||||
self._ingestion_manager.consume(ticket.payload)
|
||||
elif ticket.kind is interfaces.FrontToBackTicket.Kind.COMPLETION:
|
||||
self._last_ticket_seen = True
|
||||
if ticket.payload is None:
|
||||
self._ingestion_manager.terminate()
|
||||
else:
|
||||
self._ingestion_manager.consume_and_terminate(ticket.payload)
|
||||
else:
|
||||
self._first_ticket_seen = True
|
||||
self._last_ticket_seen = True
|
||||
self._ingestion_manager.start(ticket.name)
|
||||
if ticket.payload is None:
|
||||
self._ingestion_manager.terminate()
|
||||
else:
|
||||
self._ingestion_manager.consume_and_terminate(ticket.payload)
|
||||
|
||||
def reception_failure(self):
|
||||
"""See _Receiver.reception_failure for specification."""
|
||||
_reception_failure(
|
||||
self._termination_manager, self._transmission_manager,
|
||||
self._ingestion_manager, self._expiration_manager)
|
||||
|
||||
|
||||
class _FrontReceiver(_Receiver):
|
||||
"""Ticket-handling specific to the front side of an operation."""
|
||||
|
||||
def __init__(
|
||||
self, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
termination_manager: The operation's _interfaces.TerminationManager.
|
||||
transmission_manager: The operation's _interfaces.TransmissionManager.
|
||||
ingestion_manager: The operation's _interfaces.IngestionManager.
|
||||
expiration_manager: The operation's _interfaces.ExpirationManager.
|
||||
"""
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
self._last_ticket_seen = False
|
||||
|
||||
def _abortive(self, ticket):
|
||||
"""Determines whether or not (and if so, how) a ticket is abortive.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
|
||||
Returns:
|
||||
An interfaces.Outcome value describing operation abortion if the ticket
|
||||
is abortive or None if the ticket is not abortive.
|
||||
"""
|
||||
if ticket.kind is interfaces.BackToFrontTicket.Kind.CANCELLATION:
|
||||
return interfaces.Outcome.CANCELLED
|
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.EXPIRATION:
|
||||
return interfaces.Outcome.EXPIRED
|
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE:
|
||||
return interfaces.Outcome.SERVICER_FAILURE
|
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE:
|
||||
return interfaces.Outcome.SERVICER_FAILURE
|
||||
elif self._last_ticket_seen:
|
||||
return interfaces.Outcome.RECEPTION_FAILURE
|
||||
else:
|
||||
return None
|
||||
|
||||
def abort_if_abortive(self, ticket):
|
||||
"""See _Receiver.abort_if_abortive for specification."""
|
||||
return _abort_if_abortive(
|
||||
ticket, self._abortive, self._termination_manager,
|
||||
self._transmission_manager, self._ingestion_manager,
|
||||
self._expiration_manager)
|
||||
|
||||
def receive(self, ticket):
|
||||
"""See _Receiver.receive for specification."""
|
||||
if ticket.kind is interfaces.BackToFrontTicket.Kind.CONTINUATION:
|
||||
self._ingestion_manager.consume(ticket.payload)
|
||||
elif ticket.kind is interfaces.BackToFrontTicket.Kind.COMPLETION:
|
||||
self._last_ticket_seen = True
|
||||
if ticket.payload is None:
|
||||
self._ingestion_manager.terminate()
|
||||
else:
|
||||
self._ingestion_manager.consume_and_terminate(ticket.payload)
|
||||
|
||||
def reception_failure(self):
|
||||
"""See _Receiver.reception_failure for specification."""
|
||||
_reception_failure(
|
||||
self._termination_manager, self._transmission_manager,
|
||||
self._ingestion_manager, self._expiration_manager)
|
||||
|
||||
|
||||
class _ReceptionManager(_interfaces.ReceptionManager):
|
||||
"""A ReceptionManager based around a _Receiver passed to it."""
|
||||
|
||||
def __init__(self, lock, receiver):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
receiver: A _Receiver responsible for handling received tickets.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._receiver = receiver
|
||||
|
||||
self._lowest_unseen_sequence_number = 0
|
||||
self._out_of_sequence_tickets = {}
|
||||
self._completed_sequence_number = None
|
||||
self._aborted = False
|
||||
|
||||
def _sequence_failure(self, ticket):
|
||||
"""Determines a just-arrived ticket's sequential legitimacy.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket.
|
||||
|
||||
Returns:
|
||||
True if the ticket is sequentially legitimate; False otherwise.
|
||||
"""
|
||||
if ticket.sequence_number < self._lowest_unseen_sequence_number:
|
||||
return True
|
||||
elif ticket.sequence_number in self._out_of_sequence_tickets:
|
||||
return True
|
||||
elif (self._completed_sequence_number is not None and
|
||||
self._completed_sequence_number <= ticket.sequence_number):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _process(self, ticket):
|
||||
"""Process those tickets ready to be processed.
|
||||
|
||||
Args:
|
||||
ticket: A just-arrived ticket the sequence number of which matches this
|
||||
_ReceptionManager's _lowest_unseen_sequence_number field.
|
||||
"""
|
||||
while True:
|
||||
completed = self._receiver.receive(ticket)
|
||||
if completed:
|
||||
self._out_of_sequence_tickets.clear()
|
||||
self._completed_sequence_number = ticket.sequence_number
|
||||
self._lowest_unseen_sequence_number = ticket.sequence_number + 1
|
||||
return
|
||||
else:
|
||||
next_ticket = self._out_of_sequence_tickets.pop(
|
||||
ticket.sequence_number + 1, None)
|
||||
if next_ticket is None:
|
||||
self._lowest_unseen_sequence_number = ticket.sequence_number + 1
|
||||
return
|
||||
else:
|
||||
ticket = next_ticket
|
||||
|
||||
def receive_ticket(self, ticket):
|
||||
"""See _interfaces.ReceptionManager.receive_ticket for specification."""
|
||||
with self._lock:
|
||||
if self._aborted:
|
||||
return
|
||||
elif self._sequence_failure(ticket):
|
||||
self._receiver.reception_failure()
|
||||
self._aborted = True
|
||||
elif self._receiver.abort_if_abortive(ticket):
|
||||
self._aborted = True
|
||||
elif ticket.sequence_number == self._lowest_unseen_sequence_number:
|
||||
self._process(ticket)
|
||||
else:
|
||||
self._out_of_sequence_tickets[ticket.sequence_number] = ticket
|
||||
|
||||
|
||||
def front_reception_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Creates a _interfaces.ReceptionManager for front-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
termination_manager: The operation's _interfaces.TerminationManager.
|
||||
transmission_manager: The operation's _interfaces.TransmissionManager.
|
||||
ingestion_manager: The operation's _interfaces.IngestionManager.
|
||||
expiration_manager: The operation's _interfaces.ExpirationManager.
|
||||
|
||||
Returns:
|
||||
A _interfaces.ReceptionManager appropriate for front-side use.
|
||||
"""
|
||||
return _ReceptionManager(
|
||||
lock, _FrontReceiver(
|
||||
termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager))
|
||||
|
||||
|
||||
def back_reception_manager(
|
||||
lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Creates a _interfaces.ReceptionManager for back-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
termination_manager: The operation's _interfaces.TerminationManager.
|
||||
transmission_manager: The operation's _interfaces.TransmissionManager.
|
||||
ingestion_manager: The operation's _interfaces.IngestionManager.
|
||||
expiration_manager: The operation's _interfaces.ExpirationManager.
|
||||
|
||||
Returns:
|
||||
A _interfaces.ReceptionManager appropriate for back-side use.
|
||||
"""
|
||||
return _ReceptionManager(
|
||||
lock, _BackReceiver(
|
||||
termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager))
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for operation termination."""
|
||||
|
||||
import enum
|
||||
|
||||
from grpc.framework.base import _constants
|
||||
from grpc.framework.base import _interfaces
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import callable_util
|
||||
|
||||
_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!'
|
||||
|
||||
|
||||
@enum.unique
|
||||
class _Requirement(enum.Enum):
|
||||
"""Symbols indicating events required for termination."""
|
||||
|
||||
EMISSION = 'emission'
|
||||
TRANSMISSION = 'transmission'
|
||||
INGESTION = 'ingestion'
|
||||
|
||||
_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,)
|
||||
_BACK_NOT_LISTENING_REQUIREMENTS = (
|
||||
_Requirement.EMISSION, _Requirement.INGESTION,)
|
||||
_LISTENING_REQUIREMENTS = (
|
||||
_Requirement.TRANSMISSION, _Requirement.INGESTION,)
|
||||
|
||||
|
||||
class _TerminationManager(_interfaces.TerminationManager):
|
||||
"""An implementation of _interfaces.TerminationManager."""
|
||||
|
||||
def __init__(
|
||||
self, work_pool, utility_pool, action, requirements, local_failure):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
work_pool: A thread pool in which customer work will be done.
|
||||
utility_pool: A thread pool in which work utility work will be done.
|
||||
action: An action to call on operation termination.
|
||||
requirements: A combination of _Requirement values identifying what
|
||||
must finish for the operation to be considered completed.
|
||||
local_failure: An interfaces.Outcome specifying what constitutes local
|
||||
failure of customer work.
|
||||
"""
|
||||
self._work_pool = work_pool
|
||||
self._utility_pool = utility_pool
|
||||
self._action = action
|
||||
self._local_failure = local_failure
|
||||
self._has_locally_failed = False
|
||||
self._expiration_manager = None
|
||||
|
||||
self._outstanding_requirements = set(requirements)
|
||||
self._outcome = None
|
||||
self._callbacks = []
|
||||
|
||||
def set_expiration_manager(self, expiration_manager):
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
def _terminate(self, outcome):
|
||||
"""Terminates the operation.
|
||||
|
||||
Args:
|
||||
outcome: An interfaces.Outcome describing the outcome of the operation.
|
||||
"""
|
||||
self._expiration_manager.abort()
|
||||
self._outstanding_requirements = None
|
||||
callbacks = list(self._callbacks)
|
||||
self._callbacks = None
|
||||
self._outcome = outcome
|
||||
|
||||
act = callable_util.with_exceptions_logged(
|
||||
self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE)
|
||||
|
||||
if self._has_locally_failed:
|
||||
self._utility_pool.submit(act, outcome)
|
||||
else:
|
||||
def call_callbacks_and_act(callbacks, outcome):
|
||||
for callback in callbacks:
|
||||
callback_outcome = callable_util.call_logging_exceptions(
|
||||
callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome)
|
||||
if callback_outcome.exception is not None:
|
||||
outcome = self._local_failure
|
||||
break
|
||||
self._utility_pool.submit(act, outcome)
|
||||
|
||||
self._work_pool.submit(callable_util.with_exceptions_logged(
|
||||
call_callbacks_and_act,
|
||||
_constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
callbacks, outcome)
|
||||
|
||||
def is_active(self):
|
||||
"""See _interfaces.TerminationManager.is_active for specification."""
|
||||
return self._outstanding_requirements is not None
|
||||
|
||||
def add_callback(self, callback):
|
||||
"""See _interfaces.TerminationManager.add_callback for specification."""
|
||||
if not self._has_locally_failed:
|
||||
if self._outstanding_requirements is None:
|
||||
self._work_pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), self._outcome)
|
||||
else:
|
||||
self._callbacks.append(callback)
|
||||
|
||||
def emission_complete(self):
|
||||
"""See superclass method for specification."""
|
||||
if self._outstanding_requirements is not None:
|
||||
self._outstanding_requirements.discard(_Requirement.EMISSION)
|
||||
if not self._outstanding_requirements:
|
||||
self._terminate(interfaces.Outcome.COMPLETED)
|
||||
|
||||
def transmission_complete(self):
|
||||
"""See superclass method for specification."""
|
||||
if self._outstanding_requirements is not None:
|
||||
self._outstanding_requirements.discard(_Requirement.TRANSMISSION)
|
||||
if not self._outstanding_requirements:
|
||||
self._terminate(interfaces.Outcome.COMPLETED)
|
||||
|
||||
def ingestion_complete(self):
|
||||
"""See superclass method for specification."""
|
||||
if self._outstanding_requirements is not None:
|
||||
self._outstanding_requirements.discard(_Requirement.INGESTION)
|
||||
if not self._outstanding_requirements:
|
||||
self._terminate(interfaces.Outcome.COMPLETED)
|
||||
|
||||
def abort(self, outcome):
|
||||
"""See _interfaces.TerminationManager.abort for specification."""
|
||||
if outcome is self._local_failure:
|
||||
self._has_failed_locally = True
|
||||
if self._outstanding_requirements is not None:
|
||||
self._terminate(outcome)
|
||||
|
||||
|
||||
def front_termination_manager(
|
||||
work_pool, utility_pool, action, subscription_kind):
|
||||
"""Creates a TerminationManager appropriate for front-side use.
|
||||
|
||||
Args:
|
||||
work_pool: A thread pool in which customer work will be done.
|
||||
utility_pool: A thread pool in which work utility work will be done.
|
||||
action: An action to call on operation termination.
|
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value.
|
||||
|
||||
Returns:
|
||||
A TerminationManager appropriate for front-side use.
|
||||
"""
|
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
|
||||
requirements = _FRONT_NOT_LISTENING_REQUIREMENTS
|
||||
else:
|
||||
requirements = _LISTENING_REQUIREMENTS
|
||||
|
||||
return _TerminationManager(
|
||||
work_pool, utility_pool, action, requirements,
|
||||
interfaces.Outcome.SERVICED_FAILURE)
|
||||
|
||||
|
||||
def back_termination_manager(work_pool, utility_pool, action, subscription_kind):
|
||||
"""Creates a TerminationManager appropriate for back-side use.
|
||||
|
||||
Args:
|
||||
work_pool: A thread pool in which customer work will be done.
|
||||
utility_pool: A thread pool in which work utility work will be done.
|
||||
action: An action to call on operation termination.
|
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value.
|
||||
|
||||
Returns:
|
||||
A TerminationManager appropriate for back-side use.
|
||||
"""
|
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
|
||||
requirements = _BACK_NOT_LISTENING_REQUIREMENTS
|
||||
else:
|
||||
requirements = _LISTENING_REQUIREMENTS
|
||||
|
||||
return _TerminationManager(
|
||||
work_pool, utility_pool, action, requirements,
|
||||
interfaces.Outcome.SERVICER_FAILURE)
|
||||
|
|
@ -1,429 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for ticket transmission during an operation."""
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
from grpc.framework.base import _constants
|
||||
from grpc.framework.base import _interfaces
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import callable_util
|
||||
|
||||
_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!'
|
||||
|
||||
_FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES = (
|
||||
interfaces.Outcome.SERVICER_FAILURE,
|
||||
)
|
||||
_BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES = (
|
||||
interfaces.Outcome.CANCELLED,
|
||||
interfaces.Outcome.SERVICED_FAILURE,
|
||||
)
|
||||
|
||||
_ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND = {
|
||||
interfaces.Outcome.CANCELLED:
|
||||
interfaces.FrontToBackTicket.Kind.CANCELLATION,
|
||||
interfaces.Outcome.EXPIRED:
|
||||
interfaces.FrontToBackTicket.Kind.EXPIRATION,
|
||||
interfaces.Outcome.RECEPTION_FAILURE:
|
||||
interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE,
|
||||
interfaces.Outcome.TRANSMISSION_FAILURE:
|
||||
interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE,
|
||||
interfaces.Outcome.SERVICED_FAILURE:
|
||||
interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE,
|
||||
interfaces.Outcome.SERVICER_FAILURE:
|
||||
interfaces.FrontToBackTicket.Kind.SERVICER_FAILURE,
|
||||
}
|
||||
|
||||
_ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND = {
|
||||
interfaces.Outcome.CANCELLED:
|
||||
interfaces.BackToFrontTicket.Kind.CANCELLATION,
|
||||
interfaces.Outcome.EXPIRED:
|
||||
interfaces.BackToFrontTicket.Kind.EXPIRATION,
|
||||
interfaces.Outcome.RECEPTION_FAILURE:
|
||||
interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE,
|
||||
interfaces.Outcome.TRANSMISSION_FAILURE:
|
||||
interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE,
|
||||
interfaces.Outcome.SERVICED_FAILURE:
|
||||
interfaces.BackToFrontTicket.Kind.SERVICED_FAILURE,
|
||||
interfaces.Outcome.SERVICER_FAILURE:
|
||||
interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE,
|
||||
}
|
||||
|
||||
|
||||
class _Ticketizer(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Common specification of different ticket-creating behavior."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ticketize(self, operation_id, sequence_number, payload, complete):
|
||||
"""Creates a ticket indicating ordinary operation progress.
|
||||
|
||||
Args:
|
||||
operation_id: The operation ID for the current operation.
|
||||
sequence_number: A sequence number for the ticket.
|
||||
payload: A customer payload object. May be None if sequence_number is
|
||||
zero or complete is true.
|
||||
complete: A boolean indicating whether or not the ticket should describe
|
||||
itself as (but for a later indication of operation abortion) the last
|
||||
ticket to be sent.
|
||||
|
||||
Returns:
|
||||
An object of an appropriate type suitable for transmission to the other
|
||||
side of the operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome):
|
||||
"""Creates a ticket indicating that the operation is aborted.
|
||||
|
||||
Args:
|
||||
operation_id: The operation ID for the current operation.
|
||||
sequence_number: A sequence number for the ticket.
|
||||
outcome: An interfaces.Outcome value describing the operation abortion.
|
||||
|
||||
Returns:
|
||||
An object of an appropriate type suitable for transmission to the other
|
||||
side of the operation, or None if transmission is not appropriate for
|
||||
the given outcome.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class _FrontTicketizer(_Ticketizer):
|
||||
"""Front-side ticket-creating behavior."""
|
||||
|
||||
def __init__(self, name, subscription_kind, trace_id, timeout):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
name: The name of the operation.
|
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value
|
||||
describing the interest the front has in tickets sent from the back.
|
||||
trace_id: A uuid.UUID identifying a set of related operations to which
|
||||
this operation belongs.
|
||||
timeout: A length of time in seconds to allow for the entire operation.
|
||||
"""
|
||||
self._name = name
|
||||
self._subscription_kind = subscription_kind
|
||||
self._trace_id = trace_id
|
||||
self._timeout = timeout
|
||||
|
||||
def ticketize(self, operation_id, sequence_number, payload, complete):
|
||||
"""See _Ticketizer.ticketize for specification."""
|
||||
if sequence_number:
|
||||
if complete:
|
||||
kind = interfaces.FrontToBackTicket.Kind.COMPLETION
|
||||
else:
|
||||
kind = interfaces.FrontToBackTicket.Kind.CONTINUATION
|
||||
return interfaces.FrontToBackTicket(
|
||||
operation_id, sequence_number, kind, self._name,
|
||||
self._subscription_kind, self._trace_id, payload, self._timeout)
|
||||
else:
|
||||
if complete:
|
||||
kind = interfaces.FrontToBackTicket.Kind.ENTIRE
|
||||
else:
|
||||
kind = interfaces.FrontToBackTicket.Kind.COMMENCEMENT
|
||||
return interfaces.FrontToBackTicket(
|
||||
operation_id, 0, kind, self._name, self._subscription_kind,
|
||||
self._trace_id, payload, self._timeout)
|
||||
|
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome):
|
||||
"""See _Ticketizer.ticketize_abortion for specification."""
|
||||
if outcome in _FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES:
|
||||
return None
|
||||
else:
|
||||
kind = _ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND[outcome]
|
||||
return interfaces.FrontToBackTicket(
|
||||
operation_id, sequence_number, kind, None, None, None, None, None)
|
||||
|
||||
|
||||
class _BackTicketizer(_Ticketizer):
|
||||
"""Back-side ticket-creating behavior."""
|
||||
|
||||
def ticketize(self, operation_id, sequence_number, payload, complete):
|
||||
"""See _Ticketizer.ticketize for specification."""
|
||||
if complete:
|
||||
kind = interfaces.BackToFrontTicket.Kind.COMPLETION
|
||||
else:
|
||||
kind = interfaces.BackToFrontTicket.Kind.CONTINUATION
|
||||
return interfaces.BackToFrontTicket(
|
||||
operation_id, sequence_number, kind, payload)
|
||||
|
||||
def ticketize_abortion(self, operation_id, sequence_number, outcome):
|
||||
"""See _Ticketizer.ticketize_abortion for specification."""
|
||||
if outcome in _BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES:
|
||||
return None
|
||||
else:
|
||||
kind = _ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND[outcome]
|
||||
return interfaces.BackToFrontTicket(
|
||||
operation_id, sequence_number, kind, None)
|
||||
|
||||
|
||||
class TransmissionManager(six.with_metaclass(abc.ABCMeta, _interfaces.TransmissionManager)):
|
||||
"""A _interfaces.TransmissionManager on which other managers may be set."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_ingestion_and_expiration_managers(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
"""Sets two of the other managers with which this manager may interact.
|
||||
|
||||
Args:
|
||||
ingestion_manager: The _interfaces.IngestionManager associated with the
|
||||
current operation.
|
||||
expiration_manager: The _interfaces.ExpirationManager associated with the
|
||||
current operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class _EmptyTransmissionManager(TransmissionManager):
|
||||
"""A completely no-operative _interfaces.TransmissionManager."""
|
||||
|
||||
def set_ingestion_and_expiration_managers(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
"""See overriden method for specification."""
|
||||
|
||||
def inmit(self, emission, complete):
|
||||
"""See _interfaces.TransmissionManager.inmit for specification."""
|
||||
|
||||
def abort(self, outcome):
|
||||
"""See _interfaces.TransmissionManager.abort for specification."""
|
||||
|
||||
|
||||
class _TransmittingTransmissionManager(TransmissionManager):
|
||||
"""A TransmissionManager implementation that sends tickets."""
|
||||
|
||||
def __init__(
|
||||
self, lock, pool, callback, operation_id, ticketizer,
|
||||
termination_manager):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
pool: A thread pool in which the work of transmitting tickets will be
|
||||
performed.
|
||||
callback: A callable that accepts tickets and sends them to the other side
|
||||
of the operation.
|
||||
operation_id: The operation's ID.
|
||||
ticketizer: A _Ticketizer for ticket creation.
|
||||
termination_manager: The _interfaces.TerminationManager associated with
|
||||
this operation.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._pool = pool
|
||||
self._callback = callback
|
||||
self._operation_id = operation_id
|
||||
self._ticketizer = ticketizer
|
||||
self._termination_manager = termination_manager
|
||||
self._ingestion_manager = None
|
||||
self._expiration_manager = None
|
||||
|
||||
self._emissions = []
|
||||
self._emission_complete = False
|
||||
self._outcome = None
|
||||
self._lowest_unused_sequence_number = 0
|
||||
self._transmitting = False
|
||||
|
||||
def set_ingestion_and_expiration_managers(
|
||||
self, ingestion_manager, expiration_manager):
|
||||
"""See overridden method for specification."""
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
|
||||
def _lead_ticket(self, emission, complete):
|
||||
"""Creates a ticket suitable for leading off the transmission loop.
|
||||
|
||||
Args:
|
||||
emission: A customer payload object to be sent to the other side of the
|
||||
operation.
|
||||
complete: Whether or not the sequence of customer payloads ends with
|
||||
the passed object.
|
||||
|
||||
Returns:
|
||||
A ticket with which to lead off the transmission loop.
|
||||
"""
|
||||
sequence_number = self._lowest_unused_sequence_number
|
||||
self._lowest_unused_sequence_number += 1
|
||||
return self._ticketizer.ticketize(
|
||||
self._operation_id, sequence_number, emission, complete)
|
||||
|
||||
def _abortive_response_ticket(self, outcome):
|
||||
"""Creates a ticket indicating operation abortion.
|
||||
|
||||
Args:
|
||||
outcome: An interfaces.Outcome value describing operation abortion.
|
||||
|
||||
Returns:
|
||||
A ticket indicating operation abortion.
|
||||
"""
|
||||
ticket = self._ticketizer.ticketize_abortion(
|
||||
self._operation_id, self._lowest_unused_sequence_number, outcome)
|
||||
if ticket is None:
|
||||
return None
|
||||
else:
|
||||
self._lowest_unused_sequence_number += 1
|
||||
return ticket
|
||||
|
||||
def _next_ticket(self):
|
||||
"""Creates the next ticket to be sent to the other side of the operation.
|
||||
|
||||
Returns:
|
||||
A (completed, ticket) tuple comprised of a boolean indicating whether or
|
||||
not the sequence of tickets has completed normally and a ticket to send
|
||||
to the other side if the sequence of tickets hasn't completed. The tuple
|
||||
will never have both a True first element and a non-None second element.
|
||||
"""
|
||||
if self._emissions is None:
|
||||
return False, None
|
||||
elif self._outcome is None:
|
||||
if self._emissions:
|
||||
payload = self._emissions.pop(0)
|
||||
complete = self._emission_complete and not self._emissions
|
||||
sequence_number = self._lowest_unused_sequence_number
|
||||
self._lowest_unused_sequence_number += 1
|
||||
return complete, self._ticketizer.ticketize(
|
||||
self._operation_id, sequence_number, payload, complete)
|
||||
else:
|
||||
return self._emission_complete, None
|
||||
else:
|
||||
ticket = self._abortive_response_ticket(self._outcome)
|
||||
self._emissions = None
|
||||
return False, None if ticket is None else ticket
|
||||
|
||||
def _transmit(self, ticket):
|
||||
"""Commences the transmission loop sending tickets.
|
||||
|
||||
Args:
|
||||
ticket: A ticket to be sent to the other side of the operation.
|
||||
"""
|
||||
def transmit(ticket):
|
||||
while True:
|
||||
transmission_outcome = callable_util.call_logging_exceptions(
|
||||
self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket)
|
||||
if transmission_outcome.exception is None:
|
||||
with self._lock:
|
||||
complete, ticket = self._next_ticket()
|
||||
if ticket is None:
|
||||
if complete:
|
||||
self._termination_manager.transmission_complete()
|
||||
self._transmitting = False
|
||||
return
|
||||
else:
|
||||
with self._lock:
|
||||
self._emissions = None
|
||||
self._termination_manager.abort(
|
||||
interfaces.Outcome.TRANSMISSION_FAILURE)
|
||||
self._ingestion_manager.abort()
|
||||
self._expiration_manager.abort()
|
||||
self._transmitting = False
|
||||
return
|
||||
|
||||
self._pool.submit(callable_util.with_exceptions_logged(
|
||||
transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket)
|
||||
self._transmitting = True
|
||||
|
||||
def inmit(self, emission, complete):
|
||||
"""See _interfaces.TransmissionManager.inmit for specification."""
|
||||
if self._emissions is not None and self._outcome is None:
|
||||
self._emission_complete = complete
|
||||
if self._transmitting:
|
||||
self._emissions.append(emission)
|
||||
else:
|
||||
self._transmit(self._lead_ticket(emission, complete))
|
||||
|
||||
def abort(self, outcome):
|
||||
"""See _interfaces.TransmissionManager.abort for specification."""
|
||||
if self._emissions is not None and self._outcome is None:
|
||||
self._outcome = outcome
|
||||
if not self._transmitting:
|
||||
ticket = self._abortive_response_ticket(outcome)
|
||||
self._emissions = None
|
||||
if ticket is not None:
|
||||
self._transmit(ticket)
|
||||
|
||||
|
||||
def front_transmission_manager(
|
||||
lock, pool, callback, operation_id, name, subscription_kind, trace_id,
|
||||
timeout, termination_manager):
|
||||
"""Creates a TransmissionManager appropriate for front-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
pool: A thread pool in which the work of transmitting tickets will be
|
||||
performed.
|
||||
callback: A callable that accepts tickets and sends them to the other side
|
||||
of the operation.
|
||||
operation_id: The operation's ID.
|
||||
name: The name of the operation.
|
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value
|
||||
describing the interest the front has in tickets sent from the back.
|
||||
trace_id: A uuid.UUID identifying a set of related operations to which
|
||||
this operation belongs.
|
||||
timeout: A length of time in seconds to allow for the entire operation.
|
||||
termination_manager: The _interfaces.TerminationManager associated with
|
||||
this operation.
|
||||
|
||||
Returns:
|
||||
A TransmissionManager appropriate for front-side use.
|
||||
"""
|
||||
return _TransmittingTransmissionManager(
|
||||
lock, pool, callback, operation_id, _FrontTicketizer(
|
||||
name, subscription_kind, trace_id, timeout),
|
||||
termination_manager)
|
||||
|
||||
|
||||
def back_transmission_manager(
|
||||
lock, pool, callback, operation_id, termination_manager,
|
||||
subscription_kind):
|
||||
"""Creates a TransmissionManager appropriate for back-side use.
|
||||
|
||||
Args:
|
||||
lock: The operation-servicing-wide lock object.
|
||||
pool: A thread pool in which the work of transmitting tickets will be
|
||||
performed.
|
||||
callback: A callable that accepts tickets and sends them to the other side
|
||||
of the operation.
|
||||
operation_id: The operation's ID.
|
||||
termination_manager: The _interfaces.TerminationManager associated with
|
||||
this operation.
|
||||
subscription_kind: An interfaces.ServicedSubscription.Kind value
|
||||
describing the interest the front has in tickets sent from the back.
|
||||
|
||||
Returns:
|
||||
A TransmissionManager appropriate for back-side use.
|
||||
"""
|
||||
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
|
||||
return _EmptyTransmissionManager()
|
||||
else:
|
||||
return _TransmittingTransmissionManager(
|
||||
lock, pool, callback, operation_id, _BackTicketizer(),
|
||||
termination_manager)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Exceptions defined and used by the base layer of RPC Framework."""
|
||||
|
||||
|
||||
class NoSuchMethodError(Exception):
|
||||
"""Indicates that an operation with an unrecognized name has been called."""
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Entry points into the ticket-exchange-based base layer implementation."""
|
||||
|
||||
# interfaces is referenced from specification in this module.
|
||||
from grpc.framework.base import _ends
|
||||
from grpc.framework.base import interfaces # pylint: disable=unused-import
|
||||
|
||||
|
||||
def front_link(work_pool, transmission_pool, utility_pool):
|
||||
"""Factory function for creating interfaces.FrontLinks.
|
||||
|
||||
Args:
|
||||
work_pool: A thread pool to be used for doing work within the created
|
||||
FrontLink object.
|
||||
transmission_pool: A thread pool to be used within the created FrontLink
|
||||
object for transmitting values to a joined RearLink object.
|
||||
utility_pool: A thread pool to be used within the created FrontLink object
|
||||
for utility tasks.
|
||||
|
||||
Returns:
|
||||
An interfaces.FrontLink.
|
||||
"""
|
||||
return _ends.FrontLink(work_pool, transmission_pool, utility_pool)
|
||||
|
||||
|
||||
def back_link(
|
||||
servicer, work_pool, transmission_pool, utility_pool, default_timeout,
|
||||
maximum_timeout):
|
||||
"""Factory function for creating interfaces.BackLinks.
|
||||
|
||||
Args:
|
||||
servicer: An interfaces.Servicer for servicing operations.
|
||||
work_pool: A thread pool to be used for doing work within the created
|
||||
BackLink object.
|
||||
transmission_pool: A thread pool to be used within the created BackLink
|
||||
object for transmitting values to a joined ForeLink object.
|
||||
utility_pool: A thread pool to be used within the created BackLink object
|
||||
for utility tasks.
|
||||
default_timeout: A length of time in seconds to be used as the default
|
||||
time alloted for a single operation.
|
||||
maximum_timeout: A length of time in seconds to be used as the maximum
|
||||
time alloted for a single operation.
|
||||
|
||||
Returns:
|
||||
An interfaces.BackLink.
|
||||
"""
|
||||
return _ends.BackLink(
|
||||
servicer, work_pool, transmission_pool, utility_pool, default_timeout,
|
||||
maximum_timeout)
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""In-memory implementations of base layer interfaces."""
|
||||
|
||||
import threading
|
||||
|
||||
from grpc.framework.base import _constants
|
||||
from grpc.framework.base import interfaces
|
||||
from grpc.framework.foundation import callable_util
|
||||
|
||||
|
||||
class _Serializer(object):
|
||||
"""A utility for serializing values that may arrive concurrently."""
|
||||
|
||||
def __init__(self, pool):
|
||||
self._lock = threading.Lock()
|
||||
self._pool = pool
|
||||
self._sink = None
|
||||
self._spinning = False
|
||||
self._values = []
|
||||
|
||||
def _spin(self, sink, value):
|
||||
while True:
|
||||
sink(value)
|
||||
with self._lock:
|
||||
if self._sink is None or not self._values:
|
||||
self._spinning = False
|
||||
return
|
||||
else:
|
||||
sink, value = self._sink, self._values.pop(0)
|
||||
|
||||
def set_sink(self, sink):
|
||||
with self._lock:
|
||||
self._sink = sink
|
||||
if sink is not None and self._values and not self._spinning:
|
||||
self._spinning = True
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
sink, self._values.pop(0))
|
||||
|
||||
def add_value(self, value):
|
||||
with self._lock:
|
||||
if self._sink and not self._spinning:
|
||||
self._spinning = True
|
||||
self._pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE),
|
||||
self._sink, value)
|
||||
else:
|
||||
self._values.append(value)
|
||||
|
||||
|
||||
class Link(interfaces.ForeLink, interfaces.RearLink):
|
||||
"""A trivial implementation of interfaces.ForeLink and interfaces.RearLink."""
|
||||
|
||||
def __init__(self, pool):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
pool: A thread pool to be used for serializing ticket exchange in each
|
||||
direction.
|
||||
"""
|
||||
self._front_to_back = _Serializer(pool)
|
||||
self._back_to_front = _Serializer(pool)
|
||||
|
||||
def join_fore_link(self, fore_link):
|
||||
"""See interfaces.RearLink.join_fore_link for specification."""
|
||||
self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket)
|
||||
|
||||
def join_rear_link(self, rear_link):
|
||||
"""See interfaces.ForeLink.join_rear_link for specification."""
|
||||
self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket)
|
||||
|
||||
def accept_front_to_back_ticket(self, ticket):
|
||||
"""See interfaces.ForeLink.accept_front_to_back_ticket for specification."""
|
||||
self._front_to_back.add_value(ticket)
|
||||
|
||||
def accept_back_to_front_ticket(self, ticket):
|
||||
"""See interfaces.RearLink.accept_back_to_front_ticket for specification."""
|
||||
self._back_to_front.add_value(ticket)
|
||||
|
|
@ -1,353 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Interfaces defined and used by the base layer of RPC Framework."""
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import enum
|
||||
|
||||
import six
|
||||
|
||||
# stream is referenced from specification in this module.
|
||||
from grpc.framework.foundation import stream # pylint: disable=unused-import
|
||||
|
||||
|
||||
@enum.unique
|
||||
class Outcome(enum.Enum):
|
||||
"""Operation outcomes."""
|
||||
|
||||
COMPLETED = 'completed'
|
||||
CANCELLED = 'cancelled'
|
||||
EXPIRED = 'expired'
|
||||
RECEPTION_FAILURE = 'reception failure'
|
||||
TRANSMISSION_FAILURE = 'transmission failure'
|
||||
SERVICER_FAILURE = 'servicer failure'
|
||||
SERVICED_FAILURE = 'serviced failure'
|
||||
|
||||
|
||||
class OperationContext(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Provides operation-related information and action.
|
||||
|
||||
Attributes:
|
||||
trace_id: A uuid.UUID identifying a particular set of related operations.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_active(self):
|
||||
"""Describes whether the operation is active or has terminated."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_termination_callback(self, callback):
|
||||
"""Adds a function to be called upon operation termination.
|
||||
|
||||
Args:
|
||||
callback: A callable that will be passed an Outcome value.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def time_remaining(self):
|
||||
"""Describes the length of allowed time remaining for the operation.
|
||||
|
||||
Returns:
|
||||
A nonnegative float indicating the length of allowed time in seconds
|
||||
remaining for the operation to complete before it is considered to have
|
||||
timed out.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def fail(self, exception):
|
||||
"""Indicates that the operation has failed.
|
||||
|
||||
Args:
|
||||
exception: An exception germane to the operation failure. May be None.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Servicer(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Interface for service implementations."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def service(self, name, context, output_consumer):
|
||||
"""Services an operation.
|
||||
|
||||
Args:
|
||||
name: The name of the operation.
|
||||
context: A ServicerContext object affording contextual information and
|
||||
actions.
|
||||
output_consumer: A stream.Consumer that will accept output values of
|
||||
the operation.
|
||||
|
||||
Returns:
|
||||
A stream.Consumer that will accept input values for the operation.
|
||||
|
||||
Raises:
|
||||
exceptions.NoSuchMethodError: If this Servicer affords no method with the
|
||||
given name.
|
||||
abandonment.Abandoned: If the operation has been aborted and there no
|
||||
longer is any reason to service the operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Operation(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Representation of an in-progress operation.
|
||||
|
||||
Attributes:
|
||||
consumer: A stream.Consumer into which payloads constituting the operation's
|
||||
input may be passed.
|
||||
context: An OperationContext affording information and action about the
|
||||
operation.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self):
|
||||
"""Cancels this operation."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ServicedIngestor(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Responsible for accepting the result of an operation."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def consumer(self, operation_context):
|
||||
"""Affords a consumer to which operation results will be passed.
|
||||
|
||||
Args:
|
||||
operation_context: An OperationContext object for the current operation.
|
||||
|
||||
Returns:
|
||||
A stream.Consumer to which the results of the current operation will be
|
||||
passed.
|
||||
|
||||
Raises:
|
||||
abandonment.Abandoned: If the operation has been aborted and there no
|
||||
longer is any reason to service the operation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ServicedSubscription(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A sum type representing a serviced's interest in an operation.
|
||||
|
||||
Attributes:
|
||||
kind: A Kind value.
|
||||
ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must
|
||||
be None if kind is Kind.TERMINATION_ONLY or Kind.NONE.
|
||||
"""
|
||||
|
||||
@enum.unique
|
||||
class Kind(enum.Enum):
|
||||
"""Kinds of subscription."""
|
||||
|
||||
FULL = 'full'
|
||||
TERMINATION_ONLY = 'termination only'
|
||||
NONE = 'none'
|
||||
|
||||
|
||||
class End(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Common type for entry-point objects on both sides of an operation."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def operation_stats(self):
|
||||
"""Reports the number of terminated operations broken down by outcome.
|
||||
|
||||
Returns:
|
||||
A dictionary from Outcome value to an integer identifying the number
|
||||
of operations that terminated with that outcome.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_idle_action(self, action):
|
||||
"""Adds an action to be called when this End has no ongoing operations.
|
||||
|
||||
Args:
|
||||
action: A callable that accepts no arguments.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Front(six.with_metaclass(abc.ABCMeta, End)):
|
||||
"""Clientish objects that afford the invocation of operations."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def operate(
|
||||
self, name, payload, complete, timeout, subscription, trace_id):
|
||||
"""Commences an operation.
|
||||
|
||||
Args:
|
||||
name: The name of the method invoked for the operation.
|
||||
payload: An initial payload for the operation. May be None.
|
||||
complete: A boolean indicating whether or not additional payloads to be
|
||||
sent to the servicer may be supplied after this call.
|
||||
timeout: A length of time in seconds to allow for the operation.
|
||||
subscription: A ServicedSubscription for the operation.
|
||||
trace_id: A uuid.UUID identifying a set of related operations to which
|
||||
this operation belongs.
|
||||
|
||||
Returns:
|
||||
An Operation object affording information and action about the operation
|
||||
in progress.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Back(six.with_metaclass(abc.ABCMeta, End)):
|
||||
"""Serverish objects that perform the work of operations."""
|
||||
|
||||
|
||||
class FrontToBackTicket(
|
||||
collections.namedtuple(
|
||||
'FrontToBackTicket',
|
||||
['operation_id', 'sequence_number', 'kind', 'name', 'subscription',
|
||||
'trace_id', 'payload', 'timeout'])):
|
||||
"""A sum type for all values sent from a front to a back.
|
||||
|
||||
Attributes:
|
||||
operation_id: A unique-with-respect-to-equality hashable object identifying
|
||||
a particular operation.
|
||||
sequence_number: A zero-indexed integer sequence number identifying the
|
||||
ticket's place among all the tickets sent from front to back for this
|
||||
particular operation. Must be zero if kind is Kind.COMMENCEMENT or
|
||||
Kind.ENTIRE. Must be positive for any other kind.
|
||||
kind: A Kind value describing the overall kind of ticket.
|
||||
name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT
|
||||
or Kind.ENTIRE. Must be None for any other kind.
|
||||
subscription: An ServicedSubscription.Kind value describing the interest
|
||||
the front has in tickets sent from the back. Must be present if
|
||||
kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind.
|
||||
trace_id: A uuid.UUID identifying a set of related operations to which this
|
||||
operation belongs. May be None.
|
||||
payload: A customer payload object. Must be present if kind is
|
||||
Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None
|
||||
for any other kind.
|
||||
timeout: An optional length of time (measured from the beginning of the
|
||||
operation) to allow for the entire operation. If None, a default value on
|
||||
the back will be used. If present and excessively large, the back may
|
||||
limit the operation to a smaller duration of its choice. May be present
|
||||
for any ticket kind; setting a value on a later ticket allows fronts
|
||||
to request time extensions (or even time reductions!) on in-progress
|
||||
operations.
|
||||
"""
|
||||
|
||||
@enum.unique
|
||||
class Kind(enum.Enum):
|
||||
"""Identifies the overall kind of a FrontToBackTicket."""
|
||||
|
||||
COMMENCEMENT = 'commencement'
|
||||
CONTINUATION = 'continuation'
|
||||
COMPLETION = 'completion'
|
||||
ENTIRE = 'entire'
|
||||
CANCELLATION = 'cancellation'
|
||||
EXPIRATION = 'expiration'
|
||||
SERVICER_FAILURE = 'servicer failure'
|
||||
SERVICED_FAILURE = 'serviced failure'
|
||||
RECEPTION_FAILURE = 'reception failure'
|
||||
TRANSMISSION_FAILURE = 'transmission failure'
|
||||
|
||||
|
||||
class BackToFrontTicket(
|
||||
collections.namedtuple(
|
||||
'BackToFrontTicket',
|
||||
['operation_id', 'sequence_number', 'kind', 'payload'])):
|
||||
"""A sum type for all values sent from a back to a front.
|
||||
|
||||
Attributes:
|
||||
operation_id: A unique-with-respect-to-equality hashable object identifying
|
||||
a particular operation.
|
||||
sequence_number: A zero-indexed integer sequence number identifying the
|
||||
ticket's place among all the tickets sent from back to front for this
|
||||
particular operation.
|
||||
kind: A Kind value describing the overall kind of ticket.
|
||||
payload: A customer payload object. Must be present if kind is
|
||||
Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None
|
||||
otherwise.
|
||||
"""
|
||||
|
||||
@enum.unique
|
||||
class Kind(enum.Enum):
|
||||
"""Identifies the overall kind of a BackToFrontTicket."""
|
||||
|
||||
CONTINUATION = 'continuation'
|
||||
COMPLETION = 'completion'
|
||||
CANCELLATION = 'cancellation'
|
||||
EXPIRATION = 'expiration'
|
||||
SERVICER_FAILURE = 'servicer failure'
|
||||
SERVICED_FAILURE = 'serviced failure'
|
||||
RECEPTION_FAILURE = 'reception failure'
|
||||
TRANSMISSION_FAILURE = 'transmission failure'
|
||||
|
||||
|
||||
class ForeLink(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Accepts back-to-front tickets and emits front-to-back tickets."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def accept_back_to_front_ticket(self, ticket):
|
||||
"""Accept a BackToFrontTicket.
|
||||
|
||||
Args:
|
||||
ticket: Any BackToFrontTicket.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def join_rear_link(self, rear_link):
|
||||
"""Mates this object with a peer with which it will exchange tickets."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RearLink(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Accepts front-to-back tickets and emits back-to-front tickets."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def accept_front_to_back_ticket(self, ticket):
|
||||
"""Accepts a FrontToBackTicket.
|
||||
|
||||
Args:
|
||||
ticket: Any FrontToBackTicket.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def join_fore_link(self, fore_link):
|
||||
"""Mates this object with a peer with which it will exchange tickets."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class FrontLink(six.with_metaclass(abc.ABCMeta, Front, ForeLink)):
|
||||
"""Clientish objects that operate by sending and receiving tickets."""
|
||||
|
||||
|
||||
class BackLink(six.with_metaclass(abc.ABCMeta, Back, RearLink)):
|
||||
"""Serverish objects that operate by sending and receiving tickets."""
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Utilities helpful for working with the base layer of RPC Framework."""
|
||||
|
||||
import collections
|
||||
import threading
|
||||
|
||||
from grpc.framework.base import interfaces
|
||||
|
||||
|
||||
class _ServicedSubscription(
|
||||
collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']),
|
||||
interfaces.ServicedSubscription):
|
||||
"""See interfaces.ServicedSubscription for specification."""
|
||||
|
||||
_NONE_SUBSCRIPTION = _ServicedSubscription(
|
||||
interfaces.ServicedSubscription.Kind.NONE, None)
|
||||
_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription(
|
||||
interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None)
|
||||
|
||||
|
||||
def none_serviced_subscription():
|
||||
"""Creates a "none" interfaces.ServicedSubscription object.
|
||||
|
||||
Returns:
|
||||
An interfaces.ServicedSubscription indicating no subscription to an
|
||||
operation's results (such as would be the case for a fire-and-forget
|
||||
operation invocation).
|
||||
"""
|
||||
return _NONE_SUBSCRIPTION
|
||||
|
||||
|
||||
def termination_only_serviced_subscription():
|
||||
"""Creates a "termination only" interfaces.ServicedSubscription object.
|
||||
|
||||
Returns:
|
||||
An interfaces.ServicedSubscription indicating that the front-side customer
|
||||
is interested only in the overall termination outcome of the operation
|
||||
(such as completion or expiration) and would ignore the actual results of
|
||||
the operation.
|
||||
"""
|
||||
return _TERMINATION_ONLY_SUBSCRIPTION
|
||||
|
||||
|
||||
def full_serviced_subscription(ingestor):
|
||||
"""Creates a "full" interfaces.ServicedSubscription object.
|
||||
|
||||
Args:
|
||||
ingestor: An interfaces.ServicedIngestor.
|
||||
|
||||
Returns:
|
||||
An interfaces.ServicedSubscription object indicating a full
|
||||
subscription.
|
||||
"""
|
||||
return _ServicedSubscription(
|
||||
interfaces.ServicedSubscription.Kind.FULL, ingestor)
|
||||
|
||||
|
||||
def wait_for_idle(end):
|
||||
"""Waits for an interfaces.End to complete all operations.
|
||||
|
||||
Args:
|
||||
end: Any interfaces.End.
|
||||
"""
|
||||
event = threading.Event()
|
||||
end.add_idle_action(event.set)
|
||||
event.wait()
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn('the alpha API (includes this package) is deprecated, '
|
||||
'unmaintained, and no longer tested. Please migrate to the beta '
|
||||
'API.', DeprecationWarning, stacklevel=2)
|
||||
|
|
@ -1,422 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Utility functions for invoking RPCs."""
|
||||
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from grpc.framework.base import interfaces as base_interfaces
|
||||
from grpc.framework.base import util as base_util
|
||||
from grpc.framework.face import _control
|
||||
from grpc.framework.face import interfaces
|
||||
from grpc.framework.foundation import callable_util
|
||||
from grpc.framework.foundation import future
|
||||
|
||||
_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!'
|
||||
_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!'
|
||||
|
||||
|
||||
class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor):
|
||||
|
||||
def __init__(self, rendezvous):
|
||||
self._rendezvous = rendezvous
|
||||
|
||||
def consumer(self, operation_context):
|
||||
return self._rendezvous
|
||||
|
||||
|
||||
class _EventServicedIngestor(base_interfaces.ServicedIngestor):
|
||||
|
||||
def __init__(self, result_consumer, abortion_callback):
|
||||
self._result_consumer = result_consumer
|
||||
self._abortion_callback = abortion_callback
|
||||
|
||||
def consumer(self, operation_context):
|
||||
operation_context.add_termination_callback(
|
||||
_control.as_operation_termination_callback(self._abortion_callback))
|
||||
return self._result_consumer
|
||||
|
||||
|
||||
def _rendezvous_subscription(rendezvous):
|
||||
return base_util.full_serviced_subscription(
|
||||
_RendezvousServicedIngestor(rendezvous))
|
||||
|
||||
|
||||
def _unary_event_subscription(completion_callback, abortion_callback):
|
||||
return base_util.full_serviced_subscription(
|
||||
_EventServicedIngestor(
|
||||
_control.UnaryConsumer(completion_callback), abortion_callback))
|
||||
|
||||
|
||||
def _stream_event_subscription(result_consumer, abortion_callback):
|
||||
return base_util.full_serviced_subscription(
|
||||
_EventServicedIngestor(result_consumer, abortion_callback))
|
||||
|
||||
|
||||
# NOTE(nathaniel): This class has some extremely special semantics around
|
||||
# cancellation that allow it to be used by both "blocking" APIs and "futures"
|
||||
# APIs.
|
||||
#
|
||||
# Since futures.Future defines its own exception for cancellation, we want these
|
||||
# objects, when returned by methods of a returning-Futures-from-other-methods
|
||||
# object, to raise the same exception for cancellation. But that's weird in a
|
||||
# blocking API - why should this object, also returned by methods of blocking
|
||||
# APIs, raise exceptions from the "future" module? Should we do something like
|
||||
# have this class be parameterized by the type of exception that it raises in
|
||||
# cancellation circumstances?
|
||||
#
|
||||
# We don't have to take such a dramatic step: since blocking APIs define no
|
||||
# cancellation semantics whatsoever, there is no supported way for
|
||||
# blocking-API-users of these objects to cancel RPCs, and thus no supported way
|
||||
# for them to see an exception the type of which would be weird to them.
|
||||
#
|
||||
# Bonus: in both blocking and futures APIs, this object still properly raises
|
||||
# exceptions.CancellationError for any *server-side cancellation* of an RPC.
|
||||
class _OperationCancellableIterator(interfaces.CancellableIterator):
|
||||
"""An interfaces.CancellableIterator for response-streaming operations."""
|
||||
|
||||
def __init__(self, rendezvous, operation):
|
||||
self._lock = threading.Lock()
|
||||
self._rendezvous = rendezvous
|
||||
self._operation = operation
|
||||
self._cancelled = False
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
with self._lock:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
return next(self._rendezvous)
|
||||
|
||||
def cancel(self):
|
||||
with self._lock:
|
||||
self._cancelled = True
|
||||
self._operation.cancel()
|
||||
self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED)
|
||||
|
||||
|
||||
class _OperationFuture(future.Future):
|
||||
"""A future.Future interface to an operation."""
|
||||
|
||||
def __init__(self, rendezvous, operation):
|
||||
self._condition = threading.Condition()
|
||||
self._rendezvous = rendezvous
|
||||
self._operation = operation
|
||||
|
||||
self._cancelled = False
|
||||
self._computed = False
|
||||
self._payload = None
|
||||
self._exception = None
|
||||
self._traceback = None
|
||||
self._callbacks = []
|
||||
|
||||
def cancel(self):
|
||||
"""See future.Future.cancel for specification."""
|
||||
with self._condition:
|
||||
if not self._cancelled and not self._computed:
|
||||
self._operation.cancel()
|
||||
self._cancelled = True
|
||||
self._condition.notify_all()
|
||||
return False
|
||||
|
||||
def cancelled(self):
|
||||
"""See future.Future.cancelled for specification."""
|
||||
with self._condition:
|
||||
return self._cancelled
|
||||
|
||||
def running(self):
|
||||
"""See future.Future.running for specification."""
|
||||
with self._condition:
|
||||
return not self._cancelled and not self._computed
|
||||
|
||||
def done(self):
|
||||
"""See future.Future.done for specification."""
|
||||
with self._condition:
|
||||
return self._cancelled or self._computed
|
||||
|
||||
def result(self, timeout=None):
|
||||
"""See future.Future.result for specification."""
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
if self._computed:
|
||||
if self._payload is None:
|
||||
raise self._exception # pylint: disable=raising-bad-type
|
||||
else:
|
||||
return self._payload
|
||||
|
||||
condition = threading.Condition()
|
||||
def notify_condition(unused_future):
|
||||
with condition:
|
||||
condition.notify()
|
||||
self._callbacks.append(notify_condition)
|
||||
|
||||
with condition:
|
||||
condition.wait(timeout=timeout)
|
||||
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
elif self._computed:
|
||||
if self._payload is None:
|
||||
raise self._exception # pylint: disable=raising-bad-type
|
||||
else:
|
||||
return self._payload
|
||||
else:
|
||||
raise future.TimeoutError()
|
||||
|
||||
def exception(self, timeout=None):
|
||||
"""See future.Future.exception for specification."""
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
if self._computed:
|
||||
return self._exception
|
||||
|
||||
condition = threading.Condition()
|
||||
def notify_condition(unused_future):
|
||||
with condition:
|
||||
condition.notify()
|
||||
self._callbacks.append(notify_condition)
|
||||
|
||||
with condition:
|
||||
condition.wait(timeout=timeout)
|
||||
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
elif self._computed:
|
||||
return self._exception
|
||||
else:
|
||||
raise future.TimeoutError()
|
||||
|
||||
def traceback(self, timeout=None):
|
||||
"""See future.Future.traceback for specification."""
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
if self._computed:
|
||||
return self._traceback
|
||||
|
||||
condition = threading.Condition()
|
||||
def notify_condition(unused_future):
|
||||
with condition:
|
||||
condition.notify()
|
||||
self._callbacks.append(notify_condition)
|
||||
|
||||
with condition:
|
||||
condition.wait(timeout=timeout)
|
||||
|
||||
with self._condition:
|
||||
if self._cancelled:
|
||||
raise future.CancelledError()
|
||||
elif self._computed:
|
||||
return self._traceback
|
||||
else:
|
||||
raise future.TimeoutError()
|
||||
|
||||
def add_done_callback(self, fn):
|
||||
"""See future.Future.add_done_callback for specification."""
|
||||
with self._condition:
|
||||
if self._callbacks is not None:
|
||||
self._callbacks.append(fn)
|
||||
return
|
||||
|
||||
callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self)
|
||||
|
||||
def on_operation_termination(self, operation_outcome):
|
||||
"""Indicates to this object that the operation has terminated.
|
||||
|
||||
Args:
|
||||
operation_outcome: A base_interfaces.Outcome value indicating the
|
||||
outcome of the operation.
|
||||
"""
|
||||
with self._condition:
|
||||
cancelled = self._cancelled
|
||||
if cancelled:
|
||||
callbacks = list(self._callbacks)
|
||||
self._callbacks = None
|
||||
else:
|
||||
rendezvous = self._rendezvous
|
||||
|
||||
if not cancelled:
|
||||
payload = None
|
||||
exception = None
|
||||
traceback = None
|
||||
if operation_outcome == base_interfaces.Outcome.COMPLETED:
|
||||
try:
|
||||
payload = next(rendezvous)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
exception = e
|
||||
traceback = sys.exc_info()[2]
|
||||
else:
|
||||
try:
|
||||
# We raise and then immediately catch in order to create a traceback.
|
||||
raise _control.abortion_outcome_to_exception(operation_outcome)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
exception = e
|
||||
traceback = sys.exc_info()[2]
|
||||
with self._condition:
|
||||
if not self._cancelled:
|
||||
self._computed = True
|
||||
self._payload = payload
|
||||
self._exception = exception
|
||||
self._traceback = traceback
|
||||
callbacks = list(self._callbacks)
|
||||
self._callbacks = None
|
||||
|
||||
for callback in callbacks:
|
||||
callable_util.call_logging_exceptions(
|
||||
callback, _DONE_CALLBACK_LOG_MESSAGE, self)
|
||||
|
||||
|
||||
class _Call(interfaces.Call):
|
||||
|
||||
def __init__(self, operation):
|
||||
self._operation = operation
|
||||
self.context = _control.RpcContext(operation.context)
|
||||
|
||||
def cancel(self):
|
||||
self._operation.cancel()
|
||||
|
||||
|
||||
def blocking_value_in_value_out(front, name, payload, timeout, trace_id):
|
||||
"""Services in a blocking fashion a value-in value-out servicer method."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(
|
||||
name, payload, True, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
return next(rendezvous)
|
||||
|
||||
|
||||
def future_value_in_value_out(front, name, payload, timeout, trace_id):
|
||||
"""Services a value-in value-out servicer method by returning a Future."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(
|
||||
name, payload, True, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
operation_future = _OperationFuture(rendezvous, operation)
|
||||
operation.context.add_termination_callback(
|
||||
operation_future.on_operation_termination)
|
||||
return operation_future
|
||||
|
||||
|
||||
def inline_value_in_stream_out(front, name, payload, timeout, trace_id):
|
||||
"""Services a value-in stream-out servicer method."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(
|
||||
name, payload, True, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
return _OperationCancellableIterator(rendezvous, operation)
|
||||
|
||||
|
||||
def blocking_stream_in_value_out(
|
||||
front, name, payload_iterator, timeout, trace_id):
|
||||
"""Services in a blocking fashion a stream-in value-out servicer method."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
for payload in payload_iterator:
|
||||
operation.consumer.consume(payload)
|
||||
operation.consumer.terminate()
|
||||
return next(rendezvous)
|
||||
|
||||
|
||||
def future_stream_in_value_out(
|
||||
front, name, payload_iterator, timeout, trace_id, pool):
|
||||
"""Services a stream-in value-out servicer method by returning a Future."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
_control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE),
|
||||
payload_iterator, operation.consumer, lambda: True, True)
|
||||
operation_future = _OperationFuture(rendezvous, operation)
|
||||
operation.context.add_termination_callback(
|
||||
operation_future.on_operation_termination)
|
||||
return operation_future
|
||||
|
||||
|
||||
def inline_stream_in_stream_out(
|
||||
front, name, payload_iterator, timeout, trace_id, pool):
|
||||
"""Services a stream-in stream-out servicer method."""
|
||||
rendezvous = _control.Rendezvous()
|
||||
subscription = _rendezvous_subscription(rendezvous)
|
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id)
|
||||
operation.context.add_termination_callback(rendezvous.set_outcome)
|
||||
pool.submit(
|
||||
callable_util.with_exceptions_logged(
|
||||
_control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE),
|
||||
payload_iterator, operation.consumer, lambda: True, True)
|
||||
return _OperationCancellableIterator(rendezvous, operation)
|
||||
|
||||
|
||||
def event_value_in_value_out(
|
||||
front, name, payload, completion_callback, abortion_callback, timeout,
|
||||
trace_id):
|
||||
subscription = _unary_event_subscription(
|
||||
completion_callback, abortion_callback)
|
||||
operation = front.operate(
|
||||
name, payload, True, timeout, subscription, trace_id)
|
||||
return _Call(operation)
|
||||
|
||||
|
||||
def event_value_in_stream_out(
|
||||
front, name, payload, result_payload_consumer, abortion_callback, timeout,
|
||||
trace_id):
|
||||
subscription = _stream_event_subscription(
|
||||
result_payload_consumer, abortion_callback)
|
||||
operation = front.operate(
|
||||
name, payload, True, timeout, subscription, trace_id)
|
||||
return _Call(operation)
|
||||
|
||||
|
||||
def event_stream_in_value_out(
|
||||
front, name, completion_callback, abortion_callback, timeout, trace_id):
|
||||
subscription = _unary_event_subscription(
|
||||
completion_callback, abortion_callback)
|
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id)
|
||||
return _Call(operation), operation.consumer
|
||||
|
||||
|
||||
def event_stream_in_stream_out(
|
||||
front, name, result_payload_consumer, abortion_callback, timeout, trace_id):
|
||||
subscription = _stream_event_subscription(
|
||||
result_payload_consumer, abortion_callback)
|
||||
operation = front.operate(name, None, False, timeout, subscription, trace_id)
|
||||
return _Call(operation), operation.consumer
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""State and behavior for translating between sync and async control flow."""
|
||||
|
||||
import threading
|
||||
|
||||
from grpc.framework.base import interfaces as base_interfaces
|
||||
from grpc.framework.face import exceptions
|
||||
from grpc.framework.face import interfaces
|
||||
from grpc.framework.foundation import abandonment
|
||||
from grpc.framework.foundation import stream
|
||||
|
||||
INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-('
|
||||
|
||||
_OPERATION_OUTCOME_TO_RPC_ABORTION = {
|
||||
base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED,
|
||||
base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED,
|
||||
base_interfaces.Outcome.RECEPTION_FAILURE:
|
||||
interfaces.Abortion.NETWORK_FAILURE,
|
||||
base_interfaces.Outcome.TRANSMISSION_FAILURE:
|
||||
interfaces.Abortion.NETWORK_FAILURE,
|
||||
base_interfaces.Outcome.SERVICED_FAILURE:
|
||||
interfaces.Abortion.SERVICED_FAILURE,
|
||||
base_interfaces.Outcome.SERVICER_FAILURE:
|
||||
interfaces.Abortion.SERVICER_FAILURE,
|
||||
}
|
||||
|
||||
|
||||
def _as_operation_termination_callback(rpc_abortion_callback):
|
||||
def operation_termination_callback(operation_outcome):
|
||||
rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get(
|
||||
operation_outcome, None)
|
||||
if rpc_abortion is not None:
|
||||
rpc_abortion_callback(rpc_abortion)
|
||||
return operation_termination_callback
|
||||
|
||||
|
||||
def _abortion_outcome_to_exception(abortion_outcome):
|
||||
if abortion_outcome == base_interfaces.Outcome.CANCELLED:
|
||||
return exceptions.CancellationError()
|
||||
elif abortion_outcome == base_interfaces.Outcome.EXPIRED:
|
||||
return exceptions.ExpirationError()
|
||||
elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE:
|
||||
return exceptions.ServicerError()
|
||||
elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE:
|
||||
return exceptions.ServicedError()
|
||||
else:
|
||||
return exceptions.NetworkError()
|
||||
|
||||
|
||||
class UnaryConsumer(stream.Consumer):
|
||||
"""A stream.Consumer that should only ever be passed one value."""
|
||||
|
||||
def __init__(self, on_termination):
|
||||
self._on_termination = on_termination
|
||||
self._value = None
|
||||
|
||||
def consume(self, value):
|
||||
self._value = value
|
||||
|
||||
def terminate(self):
|
||||
self._on_termination(self._value)
|
||||
|
||||
def consume_and_terminate(self, value):
|
||||
self._on_termination(value)
|
||||
|
||||
|
||||
class Rendezvous(stream.Consumer):
|
||||
"""A rendez-vous with stream.Consumer and iterator interfaces."""
|
||||
|
||||
def __init__(self):
|
||||
self._condition = threading.Condition()
|
||||
self._values = []
|
||||
self._values_completed = False
|
||||
self._abortion = None
|
||||
|
||||
def consume(self, value):
|
||||
with self._condition:
|
||||
self._values.append(value)
|
||||
self._condition.notify()
|
||||
|
||||
def terminate(self):
|
||||
with self._condition:
|
||||
self._values_completed = True
|
||||
self._condition.notify()
|
||||
|
||||
def consume_and_terminate(self, value):
|
||||
with self._condition:
|
||||
self._values.append(value)
|
||||
self._values_completed = True
|
||||
self._condition.notify()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
|
||||
def next(self):
|
||||
with self._condition:
|
||||
while ((self._abortion is None) and
|
||||
(not self._values) and
|
||||
(not self._values_completed)):
|
||||
self._condition.wait()
|
||||
if self._abortion is not None:
|
||||
raise _abortion_outcome_to_exception(self._abortion)
|
||||
elif self._values:
|
||||
return self._values.pop(0)
|
||||
elif self._values_completed:
|
||||
raise StopIteration()
|
||||
else:
|
||||
raise AssertionError('Unreachable code reached!')
|
||||
|
||||
def set_outcome(self, outcome):
|
||||
with self._condition:
|
||||
if outcome is not base_interfaces.Outcome.COMPLETED:
|
||||
self._abortion = outcome
|
||||
self._condition.notify()
|
||||
|
||||
|
||||
class RpcContext(interfaces.RpcContext):
|
||||
"""A wrapped base_interfaces.OperationContext."""
|
||||
|
||||
def __init__(self, operation_context):
|
||||
self._operation_context = operation_context
|
||||
|
||||
def is_active(self):
|
||||
return self._operation_context.is_active()
|
||||
|
||||
def time_remaining(self):
|
||||
return self._operation_context.time_remaining()
|
||||
|
||||
def add_abortion_callback(self, abortion_callback):
|
||||
self._operation_context.add_termination_callback(
|
||||
_as_operation_termination_callback(abortion_callback))
|
||||
|
||||
|
||||
def pipe_iterator_to_consumer(iterator, consumer, active, terminate):
|
||||
"""Pipes values emitted from an iterator to a stream.Consumer.
|
||||
|
||||
Args:
|
||||
iterator: An iterator from which values will be emitted.
|
||||
consumer: A stream.Consumer to which values will be passed.
|
||||
active: A no-argument callable that returns True if the work being done by
|
||||
this function is still valid and should not be abandoned and False if the
|
||||
work being done by this function should be abandoned.
|
||||
terminate: A boolean indicating whether or not this function should
|
||||
terminate the given consumer after passing to it all values emitted by the
|
||||
given iterator.
|
||||
|
||||
Raises:
|
||||
abandonment.Abandoned: If this function quits early after seeing False
|
||||
returned by the active function passed to it.
|
||||
Exception: This function raises whatever exceptions are raised by iterating
|
||||
over the given iterator.
|
||||
"""
|
||||
for element in iterator:
|
||||
if not active():
|
||||
raise abandonment.Abandoned()
|
||||
|
||||
consumer.consume(element)
|
||||
|
||||
if not active():
|
||||
raise abandonment.Abandoned()
|
||||
if terminate:
|
||||
consumer.terminate()
|
||||
|
||||
|
||||
def abortion_outcome_to_exception(abortion_outcome):
|
||||
return _abortion_outcome_to_exception(abortion_outcome)
|
||||
|
||||
|
||||
def as_operation_termination_callback(rpc_abortion_callback):
|
||||
return _as_operation_termination_callback(rpc_abortion_callback)
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Behaviors for servicing RPCs."""
|
||||
|
||||
# base_interfaces and interfaces are referenced from specification in this
|
||||
# module.
|
||||
from grpc.framework.base import interfaces as base_interfaces # pylint: disable=unused-import
|
||||
from grpc.framework.face import _control
|
||||
from grpc.framework.face import exceptions
|
||||
from grpc.framework.face import interfaces # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import abandonment
|
||||
from grpc.framework.foundation import callable_util
|
||||
from grpc.framework.foundation import stream
|
||||
from grpc.framework.foundation import stream_util
|
||||
|
||||
|
||||
class _ValueInStreamOutConsumer(stream.Consumer):
|
||||
"""A stream.Consumer that maps inputs one-to-many onto outputs."""
|
||||
|
||||
def __init__(self, behavior, context, downstream):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
behavior: A callable that takes a single value and an
|
||||
interfaces.RpcContext and returns a generator of arbitrarily many
|
||||
values.
|
||||
context: An interfaces.RpcContext.
|
||||
downstream: A stream.Consumer to which to pass the values generated by the
|
||||
given behavior.
|
||||
"""
|
||||
self._behavior = behavior
|
||||
self._context = context
|
||||
self._downstream = downstream
|
||||
|
||||
def consume(self, value):
|
||||
_control.pipe_iterator_to_consumer(
|
||||
self._behavior(value, self._context), self._downstream,
|
||||
self._context.is_active, False)
|
||||
|
||||
def terminate(self):
|
||||
self._downstream.terminate()
|
||||
|
||||
def consume_and_terminate(self, value):
|
||||
_control.pipe_iterator_to_consumer(
|
||||
self._behavior(value, self._context), self._downstream,
|
||||
self._context.is_active, True)
|
||||
|
||||
|
||||
def _pool_wrap(behavior, operation_context):
|
||||
"""Wraps an operation-related behavior so that it may be called in a pool.
|
||||
|
||||
Args:
|
||||
behavior: A callable related to carrying out an operation.
|
||||
operation_context: A base_interfaces.OperationContext for the operation.
|
||||
|
||||
Returns:
|
||||
A callable that when called carries out the behavior of the given callable
|
||||
and handles whatever exceptions it raises appropriately.
|
||||
"""
|
||||
def translation(*args):
|
||||
try:
|
||||
behavior(*args)
|
||||
except (
|
||||
abandonment.Abandoned,
|
||||
exceptions.ExpirationError,
|
||||
exceptions.CancellationError,
|
||||
exceptions.ServicedError,
|
||||
exceptions.NetworkError) as e:
|
||||
if operation_context.is_active():
|
||||
operation_context.fail(e)
|
||||
except Exception as e:
|
||||
operation_context.fail(e)
|
||||
return callable_util.with_exceptions_logged(
|
||||
translation, _control.INTERNAL_ERROR_LOG_MESSAGE)
|
||||
|
||||
|
||||
def adapt_inline_value_in_value_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
rpc_context = _control.RpcContext(operation_context)
|
||||
return stream_util.TransformingConsumer(
|
||||
lambda request: method(request, rpc_context), response_consumer)
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_inline_value_in_stream_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
rpc_context = _control.RpcContext(operation_context)
|
||||
return _ValueInStreamOutConsumer(method, rpc_context, response_consumer)
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_inline_stream_in_value_out(method, pool):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
rendezvous = _control.Rendezvous()
|
||||
operation_context.add_termination_callback(rendezvous.set_outcome)
|
||||
def in_pool_thread():
|
||||
response_consumer.consume_and_terminate(
|
||||
method(rendezvous, _control.RpcContext(operation_context)))
|
||||
pool.submit(_pool_wrap(in_pool_thread, operation_context))
|
||||
return rendezvous
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_inline_stream_in_stream_out(method, pool):
|
||||
"""Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers.
|
||||
|
||||
RPCs may be serviced by calling the return value of this function, passing
|
||||
request values to the stream.Consumer returned from that call, and receiving
|
||||
response values from the stream.Consumer passed to that call.
|
||||
|
||||
Args:
|
||||
method: An interfaces.InlineStreamInStreamOutMethod.
|
||||
pool: A thread pool.
|
||||
|
||||
Returns:
|
||||
A callable that takes a stream.Consumer and a
|
||||
base_interfaces.OperationContext and returns a stream.Consumer.
|
||||
"""
|
||||
def adaptation(response_consumer, operation_context):
|
||||
rendezvous = _control.Rendezvous()
|
||||
operation_context.add_termination_callback(rendezvous.set_outcome)
|
||||
def in_pool_thread():
|
||||
_control.pipe_iterator_to_consumer(
|
||||
method(rendezvous, _control.RpcContext(operation_context)),
|
||||
response_consumer, operation_context.is_active, True)
|
||||
pool.submit(_pool_wrap(in_pool_thread, operation_context))
|
||||
return rendezvous
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_event_value_in_value_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
def on_payload(payload):
|
||||
method(
|
||||
payload, response_consumer.consume_and_terminate,
|
||||
_control.RpcContext(operation_context))
|
||||
return _control.UnaryConsumer(on_payload)
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_event_value_in_stream_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
def on_payload(payload):
|
||||
method(
|
||||
payload, response_consumer, _control.RpcContext(operation_context))
|
||||
return _control.UnaryConsumer(on_payload)
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_event_stream_in_value_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
rpc_context = _control.RpcContext(operation_context)
|
||||
return method(response_consumer.consume_and_terminate, rpc_context)
|
||||
return adaptation
|
||||
|
||||
|
||||
def adapt_event_stream_in_stream_out(method):
|
||||
def adaptation(response_consumer, operation_context):
|
||||
return method(response_consumer, _control.RpcContext(operation_context))
|
||||
return adaptation
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Demonstration-suitable implementation of the face layer of RPC Framework."""
|
||||
|
||||
from grpc.framework.base import util as _base_util
|
||||
from grpc.framework.base import implementations as _base_implementations
|
||||
from grpc.framework.face import implementations
|
||||
from grpc.framework.foundation import logging_pool
|
||||
|
||||
_POOL_SIZE_LIMIT = 5
|
||||
|
||||
_MAXIMUM_TIMEOUT = 90
|
||||
|
||||
|
||||
class LinkedPair(object):
|
||||
"""A Server and Stub that are linked to one another.
|
||||
|
||||
Attributes:
|
||||
server: A Server.
|
||||
stub: A Stub.
|
||||
"""
|
||||
|
||||
def shut_down(self):
|
||||
"""Shuts down this object and releases its resources."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class _LinkedPair(LinkedPair):
|
||||
|
||||
def __init__(self, server, stub, front, back, pools):
|
||||
self.server = server
|
||||
self.stub = stub
|
||||
self._front = front
|
||||
self._back = back
|
||||
self._pools = pools
|
||||
|
||||
def shut_down(self):
|
||||
_base_util.wait_for_idle(self._front)
|
||||
_base_util.wait_for_idle(self._back)
|
||||
|
||||
for pool in self._pools:
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
|
||||
def server_and_stub(
|
||||
default_timeout,
|
||||
inline_value_in_value_out_methods=None,
|
||||
inline_value_in_stream_out_methods=None,
|
||||
inline_stream_in_value_out_methods=None,
|
||||
inline_stream_in_stream_out_methods=None,
|
||||
event_value_in_value_out_methods=None,
|
||||
event_value_in_stream_out_methods=None,
|
||||
event_stream_in_value_out_methods=None,
|
||||
event_stream_in_stream_out_methods=None,
|
||||
multi_method=None):
|
||||
"""Creates a Server and Stub linked together for use."""
|
||||
front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT)
|
||||
pools = (
|
||||
front_work_pool, front_transmission_pool, front_utility_pool,
|
||||
back_work_pool, back_transmission_pool, back_utility_pool,
|
||||
stub_pool)
|
||||
|
||||
servicer = implementations.servicer(
|
||||
back_work_pool,
|
||||
inline_value_in_value_out_methods=inline_value_in_value_out_methods,
|
||||
inline_value_in_stream_out_methods=inline_value_in_stream_out_methods,
|
||||
inline_stream_in_value_out_methods=inline_stream_in_value_out_methods,
|
||||
inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods,
|
||||
event_value_in_value_out_methods=event_value_in_value_out_methods,
|
||||
event_value_in_stream_out_methods=event_value_in_stream_out_methods,
|
||||
event_stream_in_value_out_methods=event_stream_in_value_out_methods,
|
||||
event_stream_in_stream_out_methods=event_stream_in_stream_out_methods,
|
||||
multi_method=multi_method)
|
||||
|
||||
front = _base_implementations.front_link(
|
||||
front_work_pool, front_transmission_pool, front_utility_pool)
|
||||
back = _base_implementations.back_link(
|
||||
servicer, back_work_pool, back_transmission_pool, back_utility_pool,
|
||||
default_timeout, _MAXIMUM_TIMEOUT)
|
||||
front.join_rear_link(back)
|
||||
back.join_fore_link(front)
|
||||
|
||||
stub = implementations.stub(front, stub_pool)
|
||||
|
||||
return _LinkedPair(implementations.server(), stub, front, back, pools)
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Exceptions used in the Face layer of RPC Framework."""
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class NoSuchMethodError(Exception):
|
||||
"""Raised by customer code to indicate an unrecognized RPC method name.
|
||||
|
||||
Attributes:
|
||||
name: The unrecognized name.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
name: The unrecognized RPC method name.
|
||||
"""
|
||||
super(NoSuchMethodError, self).__init__()
|
||||
self.name = name
|
||||
|
||||
|
||||
class RpcError(six.with_metaclass(abc.ABCMeta, Exception)):
|
||||
"""Common super type for all exceptions raised by the Face layer.
|
||||
|
||||
Only RPC Framework should instantiate and raise these exceptions.
|
||||
"""
|
||||
|
||||
|
||||
class CancellationError(RpcError):
|
||||
"""Indicates that an RPC has been cancelled."""
|
||||
|
||||
|
||||
class ExpirationError(RpcError):
|
||||
"""Indicates that an RPC has expired ("timed out")."""
|
||||
|
||||
|
||||
class NetworkError(RpcError):
|
||||
"""Indicates that some error occurred on the network."""
|
||||
|
||||
|
||||
class ServicedError(RpcError):
|
||||
"""Indicates that the Serviced failed in the course of an RPC."""
|
||||
|
||||
|
||||
class ServicerError(RpcError):
|
||||
"""Indicates that the Servicer failed in the course of servicing an RPC."""
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Entry points into the Face layer of RPC Framework."""
|
||||
|
||||
import six
|
||||
|
||||
from grpc.framework.common import cardinality
|
||||
from grpc.framework.common import style
|
||||
from grpc.framework.base import exceptions as _base_exceptions
|
||||
from grpc.framework.base import interfaces as base_interfaces
|
||||
from grpc.framework.face import _calls
|
||||
from grpc.framework.face import _service
|
||||
from grpc.framework.face import exceptions
|
||||
from grpc.framework.face import interfaces
|
||||
|
||||
|
||||
class _BaseServicer(base_interfaces.Servicer):
|
||||
|
||||
def __init__(self, methods, multi_method):
|
||||
self._methods = methods
|
||||
self._multi_method = multi_method
|
||||
|
||||
def service(self, name, context, output_consumer):
|
||||
method = self._methods.get(name, None)
|
||||
if method is not None:
|
||||
return method(output_consumer, context)
|
||||
elif self._multi_method is not None:
|
||||
try:
|
||||
return self._multi_method.service(name, output_consumer, context)
|
||||
except exceptions.NoSuchMethodError:
|
||||
raise _base_exceptions.NoSuchMethodError()
|
||||
else:
|
||||
raise _base_exceptions.NoSuchMethodError()
|
||||
|
||||
|
||||
class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable):
|
||||
|
||||
def __init__(self, front, name):
|
||||
self._front = front
|
||||
self._name = name
|
||||
|
||||
def __call__(self, request, timeout):
|
||||
return _calls.blocking_value_in_value_out(
|
||||
self._front, self._name, request, timeout, 'unused trace ID')
|
||||
|
||||
def future(self, request, timeout):
|
||||
return _calls.future_value_in_value_out(
|
||||
self._front, self._name, request, timeout, 'unused trace ID')
|
||||
|
||||
def event(self, request, response_callback, abortion_callback, timeout):
|
||||
return _calls.event_value_in_value_out(
|
||||
self._front, self._name, request, response_callback, abortion_callback,
|
||||
timeout, 'unused trace ID')
|
||||
|
||||
|
||||
class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable):
|
||||
|
||||
def __init__(self, front, name):
|
||||
self._front = front
|
||||
self._name = name
|
||||
|
||||
def __call__(self, request, timeout):
|
||||
return _calls.inline_value_in_stream_out(
|
||||
self._front, self._name, request, timeout, 'unused trace ID')
|
||||
|
||||
def event(self, request, response_consumer, abortion_callback, timeout):
|
||||
return _calls.event_value_in_stream_out(
|
||||
self._front, self._name, request, response_consumer, abortion_callback,
|
||||
timeout, 'unused trace ID')
|
||||
|
||||
|
||||
class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable):
|
||||
|
||||
def __init__(self, front, name, pool):
|
||||
self._front = front
|
||||
self._name = name
|
||||
self._pool = pool
|
||||
|
||||
def __call__(self, request_iterator, timeout):
|
||||
return _calls.blocking_stream_in_value_out(
|
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID')
|
||||
|
||||
def future(self, request_iterator, timeout):
|
||||
return _calls.future_stream_in_value_out(
|
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID',
|
||||
self._pool)
|
||||
|
||||
def event(self, response_callback, abortion_callback, timeout):
|
||||
return _calls.event_stream_in_value_out(
|
||||
self._front, self._name, response_callback, abortion_callback, timeout,
|
||||
'unused trace ID')
|
||||
|
||||
|
||||
class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable):
|
||||
|
||||
def __init__(self, front, name, pool):
|
||||
self._front = front
|
||||
self._name = name
|
||||
self._pool = pool
|
||||
|
||||
def __call__(self, request_iterator, timeout):
|
||||
return _calls.inline_stream_in_stream_out(
|
||||
self._front, self._name, request_iterator, timeout, 'unused trace ID',
|
||||
self._pool)
|
||||
|
||||
def event(self, response_consumer, abortion_callback, timeout):
|
||||
return _calls.event_stream_in_stream_out(
|
||||
self._front, self._name, response_consumer, abortion_callback, timeout,
|
||||
'unused trace ID')
|
||||
|
||||
|
||||
class _GenericStub(interfaces.GenericStub):
|
||||
"""An interfaces.GenericStub implementation."""
|
||||
|
||||
def __init__(self, front, pool):
|
||||
self._front = front
|
||||
self._pool = pool
|
||||
|
||||
def blocking_value_in_value_out(self, name, request, timeout):
|
||||
return _calls.blocking_value_in_value_out(
|
||||
self._front, name, request, timeout, 'unused trace ID')
|
||||
|
||||
def future_value_in_value_out(self, name, request, timeout):
|
||||
return _calls.future_value_in_value_out(
|
||||
self._front, name, request, timeout, 'unused trace ID')
|
||||
|
||||
def inline_value_in_stream_out(self, name, request, timeout):
|
||||
return _calls.inline_value_in_stream_out(
|
||||
self._front, name, request, timeout, 'unused trace ID')
|
||||
|
||||
def blocking_stream_in_value_out(self, name, request_iterator, timeout):
|
||||
return _calls.blocking_stream_in_value_out(
|
||||
self._front, name, request_iterator, timeout, 'unused trace ID')
|
||||
|
||||
def future_stream_in_value_out(self, name, request_iterator, timeout):
|
||||
return _calls.future_stream_in_value_out(
|
||||
self._front, name, request_iterator, timeout, 'unused trace ID',
|
||||
self._pool)
|
||||
|
||||
def inline_stream_in_stream_out(self, name, request_iterator, timeout):
|
||||
return _calls.inline_stream_in_stream_out(
|
||||
self._front, name, request_iterator, timeout, 'unused trace ID',
|
||||
self._pool)
|
||||
|
||||
def event_value_in_value_out(
|
||||
self, name, request, response_callback, abortion_callback, timeout):
|
||||
return _calls.event_value_in_value_out(
|
||||
self._front, name, request, response_callback, abortion_callback,
|
||||
timeout, 'unused trace ID')
|
||||
|
||||
def event_value_in_stream_out(
|
||||
self, name, request, response_consumer, abortion_callback, timeout):
|
||||
return _calls.event_value_in_stream_out(
|
||||
self._front, name, request, response_consumer, abortion_callback,
|
||||
timeout, 'unused trace ID')
|
||||
|
||||
def event_stream_in_value_out(
|
||||
self, name, response_callback, abortion_callback, timeout):
|
||||
return _calls.event_stream_in_value_out(
|
||||
self._front, name, response_callback, abortion_callback, timeout,
|
||||
'unused trace ID')
|
||||
|
||||
def event_stream_in_stream_out(
|
||||
self, name, response_consumer, abortion_callback, timeout):
|
||||
return _calls.event_stream_in_stream_out(
|
||||
self._front, name, response_consumer, abortion_callback, timeout,
|
||||
'unused trace ID')
|
||||
|
||||
def unary_unary_multi_callable(self, name):
|
||||
return _UnaryUnaryMultiCallable(self._front, name)
|
||||
|
||||
def unary_stream_multi_callable(self, name):
|
||||
return _UnaryStreamMultiCallable(self._front, name)
|
||||
|
||||
def stream_unary_multi_callable(self, name):
|
||||
return _StreamUnaryMultiCallable(self._front, name, self._pool)
|
||||
|
||||
def stream_stream_multi_callable(self, name):
|
||||
return _StreamStreamMultiCallable(self._front, name, self._pool)
|
||||
|
||||
|
||||
class _DynamicStub(interfaces.DynamicStub):
|
||||
"""An interfaces.DynamicStub implementation."""
|
||||
|
||||
def __init__(self, cardinalities, front, pool):
|
||||
self._cardinalities = cardinalities
|
||||
self._front = front
|
||||
self._pool = pool
|
||||
|
||||
def __getattr__(self, attr):
|
||||
method_cardinality = self._cardinalities.get(attr)
|
||||
if method_cardinality is cardinality.Cardinality.UNARY_UNARY:
|
||||
return _UnaryUnaryMultiCallable(self._front, attr)
|
||||
elif method_cardinality is cardinality.Cardinality.UNARY_STREAM:
|
||||
return _UnaryStreamMultiCallable(self._front, attr)
|
||||
elif method_cardinality is cardinality.Cardinality.STREAM_UNARY:
|
||||
return _StreamUnaryMultiCallable(self._front, attr, self._pool)
|
||||
elif method_cardinality is cardinality.Cardinality.STREAM_STREAM:
|
||||
return _StreamStreamMultiCallable(self._front, attr, self._pool)
|
||||
else:
|
||||
raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr)
|
||||
|
||||
|
||||
def _adapt_method_implementations(method_implementations, pool):
|
||||
adapted_implementations = {}
|
||||
for name, method_implementation in six.iteritems(method_implementations):
|
||||
if method_implementation.style is style.Service.INLINE:
|
||||
if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
|
||||
adapted_implementations[name] = _service.adapt_inline_value_in_value_out(
|
||||
method_implementation.unary_unary_inline)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
|
||||
adapted_implementations[name] = _service.adapt_inline_value_in_stream_out(
|
||||
method_implementation.unary_stream_inline)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
|
||||
adapted_implementations[name] = _service.adapt_inline_stream_in_value_out(
|
||||
method_implementation.stream_unary_inline, pool)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
|
||||
adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out(
|
||||
method_implementation.stream_stream_inline, pool)
|
||||
elif method_implementation.style is style.Service.EVENT:
|
||||
if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY:
|
||||
adapted_implementations[name] = _service.adapt_event_value_in_value_out(
|
||||
method_implementation.unary_unary_event)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM:
|
||||
adapted_implementations[name] = _service.adapt_event_value_in_stream_out(
|
||||
method_implementation.unary_stream_event)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY:
|
||||
adapted_implementations[name] = _service.adapt_event_stream_in_value_out(
|
||||
method_implementation.stream_unary_event)
|
||||
elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM:
|
||||
adapted_implementations[name] = _service.adapt_event_stream_in_stream_out(
|
||||
method_implementation.stream_stream_event)
|
||||
return adapted_implementations
|
||||
|
||||
|
||||
def servicer(pool, method_implementations, multi_method_implementation):
|
||||
"""Creates a base_interfaces.Servicer.
|
||||
|
||||
It is guaranteed that any passed interfaces.MultiMethodImplementation will
|
||||
only be called to service an RPC if there is no
|
||||
interfaces.MethodImplementation for the RPC method in the passed
|
||||
method_implementations dictionary.
|
||||
|
||||
Args:
|
||||
pool: A thread pool.
|
||||
method_implementations: A dictionary from RPC method name to
|
||||
interfaces.MethodImplementation object to be used to service the named
|
||||
RPC method.
|
||||
multi_method_implementation: An interfaces.MultiMethodImplementation to be
|
||||
used to service any RPCs not serviced by the
|
||||
interfaces.MethodImplementations given in the method_implementations
|
||||
dictionary, or None.
|
||||
|
||||
Returns:
|
||||
A base_interfaces.Servicer that services RPCs via the given implementations.
|
||||
"""
|
||||
adapted_implementations = _adapt_method_implementations(
|
||||
method_implementations, pool)
|
||||
return _BaseServicer(adapted_implementations, multi_method_implementation)
|
||||
|
||||
|
||||
def generic_stub(front, pool):
|
||||
"""Creates an interfaces.GenericStub.
|
||||
|
||||
Args:
|
||||
front: A base_interfaces.Front.
|
||||
pool: A futures.ThreadPoolExecutor.
|
||||
|
||||
Returns:
|
||||
An interfaces.GenericStub that performs RPCs via the given
|
||||
base_interfaces.Front.
|
||||
"""
|
||||
return _GenericStub(front, pool)
|
||||
|
||||
|
||||
def dynamic_stub(cardinalities, front, pool, prefix):
|
||||
"""Creates an interfaces.DynamicStub.
|
||||
|
||||
Args:
|
||||
cardinalities: A dict from RPC method name to cardinality.Cardinality
|
||||
value identifying the cardinality of every RPC method to be supported by
|
||||
the created interfaces.DynamicStub.
|
||||
front: A base_interfaces.Front.
|
||||
pool: A futures.ThreadPoolExecutor.
|
||||
prefix: A string to prepend when mapping requested attribute name to RPC
|
||||
method name during attribute access on the created
|
||||
interfaces.DynamicStub.
|
||||
|
||||
Returns:
|
||||
An interfaces.DynamicStub that performs RPCs via the given
|
||||
base_interfaces.Front.
|
||||
"""
|
||||
return _DynamicStub(cardinalities, front, pool)
|
||||
|
|
@ -1,634 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Interfaces for the face layer of RPC Framework."""
|
||||
|
||||
import abc
|
||||
import enum
|
||||
|
||||
import six
|
||||
|
||||
# cardinality, style, exceptions, abandonment, future, and stream are
|
||||
# referenced from specification in this module.
|
||||
from grpc.framework.common import cardinality # pylint: disable=unused-import
|
||||
from grpc.framework.common import style # pylint: disable=unused-import
|
||||
from grpc.framework.face import exceptions # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import abandonment # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import future # pylint: disable=unused-import
|
||||
from grpc.framework.foundation import stream # pylint: disable=unused-import
|
||||
|
||||
|
||||
@enum.unique
|
||||
class Abortion(enum.Enum):
|
||||
"""Categories of RPC abortion."""
|
||||
CANCELLED = 'cancelled'
|
||||
EXPIRED = 'expired'
|
||||
NETWORK_FAILURE = 'network failure'
|
||||
SERVICED_FAILURE = 'serviced failure'
|
||||
SERVICER_FAILURE = 'servicer failure'
|
||||
|
||||
|
||||
class CancellableIterator(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Implements the Iterator protocol and affords a cancel method."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __iter__(self):
|
||||
"""Returns the self object in accordance with the Iterator protocol."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def __next__(self):
|
||||
return self.next()
|
||||
|
||||
@abc.abstractmethod
|
||||
def next(self):
|
||||
"""Returns a value or raises StopIteration per the Iterator protocol."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self):
|
||||
"""Requests cancellation of whatever computation underlies this iterator."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class RpcContext(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Provides RPC-related information and action."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_active(self):
|
||||
"""Describes whether the RPC is active or has terminated."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def time_remaining(self):
|
||||
"""Describes the length of allowed time remaining for the RPC.
|
||||
|
||||
Returns:
|
||||
A nonnegative float indicating the length of allowed time in seconds
|
||||
remaining for the RPC to complete before it is considered to have timed
|
||||
out.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_abortion_callback(self, abortion_callback):
|
||||
"""Registers a callback to be called if the RPC is aborted.
|
||||
|
||||
Args:
|
||||
abortion_callback: A callable to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Call(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Invocation-side representation of an RPC.
|
||||
|
||||
Attributes:
|
||||
context: An RpcContext affording information about the RPC.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def cancel(self):
|
||||
"""Requests cancellation of the RPC."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class UnaryUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a unary-unary RPC in any call style."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def future(self, request, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future's result value will be the response value of the RPC.
|
||||
In the event of RPC abortion, the returned Future's exception value
|
||||
will be an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event(self, request, response_callback, abortion_callback, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
response_callback: A callback to be called to accept the restponse value
|
||||
of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A Call object for the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class UnaryStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a unary-stream RPC in any call style."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A CancellableIterator that yields the response values of the RPC and
|
||||
affords RPC cancellation. Drawing response values from the returned
|
||||
CancellableIterator may raise exceptions.RpcError indicating abortion
|
||||
of the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event(self, request, response_consumer, abortion_callback, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
response_consumer: A stream.Consumer to be called to accept the restponse
|
||||
values of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A Call object for the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class StreamUnaryMultiCallable(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a stream-unary RPC in any call style."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request_iterator, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator that yields request values for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def future(self, request_iterator, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator that yields request values for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future's result value will be the response value of the RPC.
|
||||
In the event of RPC abortion, the returned Future's exception value
|
||||
will be an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event(self, response_callback, abortion_callback, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request: The request value for the RPC.
|
||||
response_callback: A callback to be called to accept the restponse value
|
||||
of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A pair of a Call object for the RPC and a stream.Consumer to which the
|
||||
request values of the RPC should be passed.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class StreamStreamMultiCallable(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords invoking a stream-stream RPC in any call style."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self, request_iterator, timeout):
|
||||
"""Synchronously invokes the underlying RPC.
|
||||
|
||||
Args:
|
||||
request_iterator: An iterator that yields request values for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A CancellableIterator that yields the response values of the RPC and
|
||||
affords RPC cancellation. Drawing response values from the returned
|
||||
CancellableIterator may raise exceptions.RpcError indicating abortion
|
||||
of the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event(self, response_consumer, abortion_callback, timeout):
|
||||
"""Asynchronously invokes the underlying RPC.
|
||||
|
||||
l Args:
|
||||
response_consumer: A stream.Consumer to be called to accept the restponse
|
||||
values of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A pair of a Call object for the RPC and a stream.Consumer to which the
|
||||
request values of the RPC should be passed.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class MethodImplementation(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A sum type that describes an RPC method implementation.
|
||||
|
||||
Attributes:
|
||||
cardinality: A cardinality.Cardinality value.
|
||||
style: A style.Service value.
|
||||
unary_unary_inline: The implementation of the RPC method as a callable
|
||||
value that takes a request value and an RpcContext object and returns a
|
||||
response value. Only non-None if cardinality is
|
||||
cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE.
|
||||
unary_stream_inline: The implementation of the RPC method as a callable
|
||||
value that takes a request value and an RpcContext object and returns an
|
||||
iterator of response values. Only non-None if cardinality is
|
||||
cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE.
|
||||
stream_unary_inline: The implementation of the RPC method as a callable
|
||||
value that takes an iterator of request values and an RpcContext object
|
||||
and returns a response value. Only non-None if cardinality is
|
||||
cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE.
|
||||
stream_stream_inline: The implementation of the RPC method as a callable
|
||||
value that takes an iterator of request values and an RpcContext object
|
||||
and returns an iterator of response values. Only non-None if cardinality
|
||||
is cardinality.Cardinality.STREAM_STREAM and style is
|
||||
style.Service.INLINE.
|
||||
unary_unary_event: The implementation of the RPC method as a callable value
|
||||
that takes a request value, a response callback to which to pass the
|
||||
response value of the RPC, and an RpcContext. Only non-None if
|
||||
cardinality is cardinality.Cardinality.UNARY_UNARY and style is
|
||||
style.Service.EVENT.
|
||||
unary_stream_event: The implementation of the RPC method as a callable
|
||||
value that takes a request value, a stream.Consumer to which to pass the
|
||||
the response values of the RPC, and an RpcContext. Only non-None if
|
||||
cardinality is cardinality.Cardinality.UNARY_STREAM and style is
|
||||
style.Service.EVENT.
|
||||
stream_unary_event: The implementation of the RPC method as a callable
|
||||
value that takes a response callback to which to pass the response value
|
||||
of the RPC and an RpcContext and returns a stream.Consumer to which the
|
||||
request values of the RPC should be passed. Only non-None if cardinality
|
||||
is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT.
|
||||
stream_stream_event: The implementation of the RPC method as a callable
|
||||
value that takes a stream.Consumer to which to pass the response values
|
||||
of the RPC and an RpcContext and returns a stream.Consumer to which the
|
||||
request values of the RPC should be passed. Only non-None if cardinality
|
||||
is cardinality.Cardinality.STREAM_STREAM and style is
|
||||
style.Service.EVENT.
|
||||
"""
|
||||
|
||||
|
||||
class MultiMethodImplementation(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A general type able to service many RPC methods."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def service(self, name, response_consumer, context):
|
||||
"""Services an RPC.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
response_consumer: A stream.Consumer to be called to accept the response
|
||||
values of the RPC.
|
||||
context: An RpcContext object.
|
||||
|
||||
Returns:
|
||||
A stream.Consumer with which to accept the request values of the RPC. The
|
||||
consumer returned from this method may or may not be invoked to
|
||||
completion: in the case of RPC abortion, RPC Framework will simply stop
|
||||
passing values to this object. Implementations must not assume that this
|
||||
object will be called to completion of the request stream or even called
|
||||
at all.
|
||||
|
||||
Raises:
|
||||
abandonment.Abandoned: May or may not be raised when the RPC has been
|
||||
aborted.
|
||||
exceptions.NoSuchMethodError: If this MultiMethod does not recognize the
|
||||
given RPC method name and is not able to service the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class GenericStub(six.with_metaclass(abc.ABCMeta)):
|
||||
"""Affords RPC methods to callers."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def blocking_value_in_value_out(self, name, request, timeout):
|
||||
"""Invokes a unary-request-unary-response RPC method.
|
||||
|
||||
This method blocks until either returning the response value of the RPC
|
||||
(in the event of RPC completion) or raising an exception (in the event of
|
||||
RPC abortion).
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def future_value_in_value_out(self, name, request, timeout):
|
||||
"""Invokes a unary-request-unary-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future will return an outcome indicating that the RPC returned
|
||||
the response value of the RPC. In the event of RPC abortion, the
|
||||
returned Future will return an outcome indicating that the RPC raised
|
||||
an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def inline_value_in_stream_out(self, name, request, timeout):
|
||||
"""Invokes a unary-request-stream-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request: The request value for the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A CancellableIterator that yields the response values of the RPC and
|
||||
affords RPC cancellation. Drawing response values from the returned
|
||||
CancellableIterator may raise exceptions.RpcError indicating abortion of
|
||||
the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def blocking_stream_in_value_out(self, name, request_iterator, timeout):
|
||||
"""Invokes a stream-request-unary-response RPC method.
|
||||
|
||||
This method blocks until either returning the response value of the RPC
|
||||
(in the event of RPC completion) or raising an exception (in the event of
|
||||
RPC abortion).
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request_iterator: An iterator that yields the request values of the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
The response value for the RPC.
|
||||
|
||||
Raises:
|
||||
exceptions.RpcError: Indicating that the RPC was aborted.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def future_stream_in_value_out(self, name, request_iterator, timeout):
|
||||
"""Invokes a stream-request-unary-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request_iterator: An iterator that yields the request values of the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A future.Future representing the RPC. In the event of RPC completion, the
|
||||
returned Future will return an outcome indicating that the RPC returned
|
||||
the response value of the RPC. In the event of RPC abortion, the
|
||||
returned Future will return an outcome indicating that the RPC raised
|
||||
an exceptions.RpcError.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def inline_stream_in_stream_out(self, name, request_iterator, timeout):
|
||||
"""Invokes a stream-request-stream-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request_iterator: An iterator that yields the request values of the RPC.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A CancellableIterator that yields the response values of the RPC and
|
||||
affords RPC cancellation. Drawing response values from the returned
|
||||
CancellableIterator may raise exceptions.RpcError indicating abortion of
|
||||
the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event_value_in_value_out(
|
||||
self, name, request, response_callback, abortion_callback, timeout):
|
||||
"""Event-driven invocation of a unary-request-unary-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request: The request value for the RPC.
|
||||
response_callback: A callback to be called to accept the response value
|
||||
of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A Call object for the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event_value_in_stream_out(
|
||||
self, name, request, response_consumer, abortion_callback, timeout):
|
||||
"""Event-driven invocation of a unary-request-stream-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
request: The request value for the RPC.
|
||||
response_consumer: A stream.Consumer to be called to accept the response
|
||||
values of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A Call object for the RPC.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event_stream_in_value_out(
|
||||
self, name, response_callback, abortion_callback, timeout):
|
||||
"""Event-driven invocation of a unary-request-unary-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
response_callback: A callback to be called to accept the response value
|
||||
of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A pair of a Call object for the RPC and a stream.Consumer to which the
|
||||
request values of the RPC should be passed.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def event_stream_in_stream_out(
|
||||
self, name, response_consumer, abortion_callback, timeout):
|
||||
"""Event-driven invocation of a unary-request-stream-response RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
response_consumer: A stream.Consumer to be called to accept the response
|
||||
values of the RPC.
|
||||
abortion_callback: A callback to be called and passed an Abortion value
|
||||
in the event of RPC abortion.
|
||||
timeout: A duration of time in seconds to allow for the RPC.
|
||||
|
||||
Returns:
|
||||
A pair of a Call object for the RPC and a stream.Consumer to which the
|
||||
request values of the RPC should be passed.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def unary_unary_multi_callable(self, name):
|
||||
"""Creates a UnaryUnaryMultiCallable for a unary-unary RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
|
||||
Returns:
|
||||
A UnaryUnaryMultiCallable value for the named unary-unary RPC method.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def unary_stream_multi_callable(self, name):
|
||||
"""Creates a UnaryStreamMultiCallable for a unary-stream RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
|
||||
Returns:
|
||||
A UnaryStreamMultiCallable value for the name unary-stream RPC method.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def stream_unary_multi_callable(self, name):
|
||||
"""Creates a StreamUnaryMultiCallable for a stream-unary RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
|
||||
Returns:
|
||||
A StreamUnaryMultiCallable value for the named stream-unary RPC method.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def stream_stream_multi_callable(self, name):
|
||||
"""Creates a StreamStreamMultiCallable for a stream-stream RPC method.
|
||||
|
||||
Args:
|
||||
name: The RPC method name.
|
||||
|
||||
Returns:
|
||||
A StreamStreamMultiCallable value for the named stream-stream RPC method.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class DynamicStub(six.with_metaclass(abc.ABCMeta)):
|
||||
"""A stub with RPC-method-bound multi-callable attributes.
|
||||
|
||||
Instances of this type responsd to attribute access as follows: if the
|
||||
requested attribute is the name of a unary-unary RPC method, the value of the
|
||||
attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC
|
||||
method; if the requested attribute is the name of a unary-stream RPC method,
|
||||
the value of the attribute will be a UnaryStreamMultiCallable with which to
|
||||
invoke the RPC method; if the requested attribute is the name of a
|
||||
stream-unary RPC method, the value of the attribute will be a
|
||||
StreamUnaryMultiCallable with which to invoke the RPC method; and if the
|
||||
requested attribute is the name of a stream-stream RPC method, the value of
|
||||
the attribute will be a StreamStreamMultiCallable with which to invoke the
|
||||
RPC method.
|
||||
"""
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Utilities for RPC framework's face layer."""
|
||||
|
||||
import collections
|
||||
|
||||
from grpc.framework.common import cardinality
|
||||
from grpc.framework.common import style
|
||||
from grpc.framework.face import interfaces
|
||||
from grpc.framework.foundation import stream
|
||||
|
||||
|
||||
class _MethodImplementation(
|
||||
interfaces.MethodImplementation,
|
||||
collections.namedtuple(
|
||||
'_MethodImplementation',
|
||||
['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline',
|
||||
'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event',
|
||||
'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])):
|
||||
pass
|
||||
|
||||
|
||||
def unary_unary_inline(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a unary-unary RPC method as a callable value
|
||||
that takes a request value and an interfaces.RpcContext object and
|
||||
returns a response value.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior,
|
||||
None, None, None, None, None, None, None)
|
||||
|
||||
|
||||
def unary_stream_inline(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a unary-stream RPC method as a callable
|
||||
value that takes a request value and an interfaces.RpcContext object and
|
||||
returns an iterator of response values.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None,
|
||||
behavior, None, None, None, None, None, None)
|
||||
|
||||
|
||||
def stream_unary_inline(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a stream-unary RPC method as a callable
|
||||
value that takes an iterator of request values and an
|
||||
interfaces.RpcContext object and returns a response value.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None,
|
||||
behavior, None, None, None, None, None)
|
||||
|
||||
|
||||
def stream_stream_inline(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a stream-stream RPC method as a callable
|
||||
value that takes an iterator of request values and an
|
||||
interfaces.RpcContext object and returns an iterator of response values.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None,
|
||||
None, behavior, None, None, None, None)
|
||||
|
||||
|
||||
def unary_unary_event(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a unary-unary RPC method as a callable
|
||||
value that takes a request value, a response callback to which to pass
|
||||
the response value of the RPC, and an interfaces.RpcContext.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None,
|
||||
None, None, behavior, None, None, None)
|
||||
|
||||
|
||||
def unary_stream_event(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a unary-stream RPC method as a callable
|
||||
value that takes a request value, a stream.Consumer to which to pass the
|
||||
the response values of the RPC, and an interfaces.RpcContext.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None,
|
||||
None, None, None, behavior, None, None)
|
||||
|
||||
|
||||
def stream_unary_event(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a stream-unary RPC method as a callable
|
||||
value that takes a response callback to which to pass the response value
|
||||
of the RPC and an interfaces.RpcContext and returns a stream.Consumer to
|
||||
which the request values of the RPC should be passed.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None,
|
||||
None, None, None, None, behavior, None)
|
||||
|
||||
|
||||
def stream_stream_event(behavior):
|
||||
"""Creates an interfaces.MethodImplementation for the given behavior.
|
||||
|
||||
Args:
|
||||
behavior: The implementation of a stream-stream RPC method as a callable
|
||||
value that takes a stream.Consumer to which to pass the response values
|
||||
of the RPC and an interfaces.RpcContext and returns a stream.Consumer to
|
||||
which the request values of the RPC should be passed.
|
||||
|
||||
Returns:
|
||||
An interfaces.MethodImplementation derived from the given behavior.
|
||||
"""
|
||||
return _MethodImplementation(
|
||||
cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None,
|
||||
None, None, None, None, None, behavior)
|
||||
|
|
@ -45,6 +45,7 @@ import unittest
|
|||
from six import moves
|
||||
|
||||
from grpc.beta import implementations
|
||||
from grpc.beta import interfaces
|
||||
from grpc.framework.foundation import future
|
||||
from grpc.framework.interfaces.face import face
|
||||
from tests.unit.framework.common import test_constants
|
||||
|
|
@ -178,6 +179,36 @@ def _CreateService(test_pb2):
|
|||
server.stop(0)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _CreateIncompleteService(test_pb2):
|
||||
"""Provides a servicer backend that fails to implement methods and its stub.
|
||||
|
||||
The servicer is just the implementation of the actual servicer passed to the
|
||||
face player of the python RPC implementation; the two are detached.
|
||||
|
||||
Args:
|
||||
test_pb2: The test_pb2 module generated by this test.
|
||||
|
||||
Yields:
|
||||
A (servicer_methods, stub) pair where servicer_methods is the back-end of
|
||||
the service bound to the stub and and stub is the stub on which to invoke
|
||||
RPCs.
|
||||
"""
|
||||
servicer_methods = _ServicerMethods(test_pb2)
|
||||
|
||||
class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)):
|
||||
pass
|
||||
|
||||
servicer = Servicer()
|
||||
server = getattr(test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer)
|
||||
port = server.add_insecure_port('[::]:0')
|
||||
server.start()
|
||||
channel = implementations.insecure_channel('localhost', port)
|
||||
stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)(channel)
|
||||
yield servicer_methods, stub
|
||||
server.stop(0)
|
||||
|
||||
|
||||
def _streaming_input_request_iterator(test_pb2):
|
||||
for _ in range(3):
|
||||
request = test_pb2.StreamingInputCallRequest()
|
||||
|
|
@ -264,6 +295,16 @@ class PythonPluginTest(unittest.TestCase):
|
|||
with _CreateService(test_pb2) as (servicer, stub):
|
||||
request = test_pb2.SimpleRequest(response_size=13)
|
||||
|
||||
def testIncompleteServicer(self):
|
||||
import protoc_plugin_test_pb2 as test_pb2
|
||||
moves.reload_module(test_pb2)
|
||||
with _CreateIncompleteService(test_pb2) as (servicer, stub):
|
||||
request = test_pb2.SimpleRequest(response_size=13)
|
||||
try:
|
||||
response = stub.UnaryCall(request, test_constants.LONG_TIMEOUT)
|
||||
except face.AbortionError as error:
|
||||
self.assertEqual(interfaces.StatusCode.UNIMPLEMENTED, error.code)
|
||||
|
||||
def testUnaryCall(self):
|
||||
import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top
|
||||
moves.reload_module(test_pb2)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2015, Google Inc.
|
||||
# Copyright 2016, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -26,5 +26,3 @@
|
|||
# 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.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
# Copyright 2016, 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.
|
||||
|
||||
"""Defines test client behaviors (UNARY/STREAMING) (SYNC/ASYNC)."""
|
||||
|
||||
import abc
|
||||
import time
|
||||
try:
|
||||
import Queue as queue # Python 2.x
|
||||
except ImportError:
|
||||
import queue # Python 3
|
||||
|
||||
from concurrent import futures
|
||||
|
||||
from grpc.beta import implementations
|
||||
from src.proto.grpc.testing import messages_pb2
|
||||
from src.proto.grpc.testing import services_pb2
|
||||
from tests.unit import resources
|
||||
from tests.unit.beta import test_utilities
|
||||
|
||||
_TIMEOUT = 60 * 60 * 24
|
||||
|
||||
|
||||
class BenchmarkClient:
|
||||
"""Benchmark client interface that exposes a non-blocking send_request()."""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, server, config, hist):
|
||||
# Create the stub
|
||||
host, port = server.split(':')
|
||||
port = int(port)
|
||||
if config.HasField('security_params'):
|
||||
creds = implementations.ssl_channel_credentials(
|
||||
resources.test_root_certificates())
|
||||
channel = test_utilities.not_really_secure_channel(
|
||||
host, port, creds, config.security_params.server_host_override)
|
||||
else:
|
||||
channel = implementations.insecure_channel(host, port)
|
||||
|
||||
if config.payload_config.WhichOneof('payload') == 'simple_params':
|
||||
self._generic = False
|
||||
self._stub = services_pb2.beta_create_BenchmarkService_stub(channel)
|
||||
payload = messages_pb2.Payload(
|
||||
body='\0' * config.payload_config.simple_params.req_size)
|
||||
self._request = messages_pb2.SimpleRequest(
|
||||
payload=payload,
|
||||
response_size=config.payload_config.simple_params.resp_size)
|
||||
else:
|
||||
self._generic = True
|
||||
self._stub = implementations.generic_stub(channel)
|
||||
self._request = '\0' * config.payload_config.bytebuf_params.req_size
|
||||
|
||||
self._hist = hist
|
||||
self._response_callbacks = []
|
||||
|
||||
def add_response_callback(self, callback):
|
||||
self._response_callbacks.append(callback)
|
||||
|
||||
@abc.abstractmethod
|
||||
def send_request(self):
|
||||
"""Non-blocking wrapper for a client's request operation."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def _handle_response(self, query_time):
|
||||
self._hist.add(query_time * 1e9) # Report times in nanoseconds
|
||||
for callback in self._response_callbacks:
|
||||
callback(query_time)
|
||||
|
||||
|
||||
class UnarySyncBenchmarkClient(BenchmarkClient):
|
||||
|
||||
def __init__(self, server, config, hist):
|
||||
super(UnarySyncBenchmarkClient, self).__init__(server, config, hist)
|
||||
self._pool = futures.ThreadPoolExecutor(
|
||||
max_workers=config.outstanding_rpcs_per_channel)
|
||||
|
||||
def send_request(self):
|
||||
# Send requests in seperate threads to support multiple outstanding rpcs
|
||||
# (See src/proto/grpc/testing/control.proto)
|
||||
self._pool.submit(self._dispatch_request)
|
||||
|
||||
def stop(self):
|
||||
self._pool.shutdown(wait=True)
|
||||
self._stub = None
|
||||
|
||||
def _dispatch_request(self):
|
||||
start_time = time.time()
|
||||
self._stub.UnaryCall(self._request, _TIMEOUT)
|
||||
end_time = time.time()
|
||||
self._handle_response(end_time - start_time)
|
||||
|
||||
|
||||
class UnaryAsyncBenchmarkClient(BenchmarkClient):
|
||||
|
||||
def send_request(self):
|
||||
# Use the Future callback api to support multiple outstanding rpcs
|
||||
start_time = time.time()
|
||||
response_future = self._stub.UnaryCall.future(self._request, _TIMEOUT)
|
||||
response_future.add_done_callback(
|
||||
lambda resp: self._response_received(start_time, resp))
|
||||
|
||||
def _response_received(self, start_time, resp):
|
||||
resp.result()
|
||||
end_time = time.time()
|
||||
self._handle_response(end_time - start_time)
|
||||
|
||||
def stop(self):
|
||||
self._stub = None
|
||||
|
||||
|
||||
class StreamingAsyncBenchmarkClient(BenchmarkClient):
|
||||
|
||||
def __init__(self, server, config, hist):
|
||||
super(StreamingAsyncBenchmarkClient, self).__init__(server, config, hist)
|
||||
self._is_streaming = False
|
||||
self._pool = futures.ThreadPoolExecutor(max_workers=1)
|
||||
# Use a thread-safe queue to put requests on the stream
|
||||
self._request_queue = queue.Queue()
|
||||
self._send_time_queue = queue.Queue()
|
||||
|
||||
def send_request(self):
|
||||
self._send_time_queue.put(time.time())
|
||||
self._request_queue.put(self._request)
|
||||
|
||||
def start(self):
|
||||
self._is_streaming = True
|
||||
self._pool.submit(self._request_stream)
|
||||
|
||||
def stop(self):
|
||||
self._is_streaming = False
|
||||
self._pool.shutdown(wait=True)
|
||||
self._stub = None
|
||||
|
||||
def _request_stream(self):
|
||||
self._is_streaming = True
|
||||
if self._generic:
|
||||
response_stream = self._stub.inline_stream_stream(
|
||||
'grpc.testing.BenchmarkService', 'StreamingCall',
|
||||
self._request_generator(), _TIMEOUT)
|
||||
else:
|
||||
response_stream = self._stub.StreamingCall(self._request_generator(),
|
||||
_TIMEOUT)
|
||||
for _ in response_stream:
|
||||
end_time = time.time()
|
||||
self._handle_response(end_time - self._send_time_queue.get_nowait())
|
||||
|
||||
def _request_generator(self):
|
||||
while self._is_streaming:
|
||||
try:
|
||||
request = self._request_queue.get(block=True, timeout=1.0)
|
||||
yield request
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2015, Google Inc.
|
||||
# Copyright 2016, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -27,42 +27,32 @@
|
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Base Dockerfile for gRPC dev images
|
||||
FROM debian:latest
|
||||
from src.proto.grpc.testing import messages_pb2
|
||||
from src.proto.grpc.testing import services_pb2
|
||||
|
||||
# Install Git.
|
||||
RUN apt-get update && apt-get install -y \
|
||||
autoconf \
|
||||
autotools-dev \
|
||||
build-essential \
|
||||
bzip2 \
|
||||
curl \
|
||||
gcc \
|
||||
git \
|
||||
libc6 \
|
||||
libc6-dbg \
|
||||
libc6-dev \
|
||||
libgtest-dev \
|
||||
libtool \
|
||||
make \
|
||||
strace \
|
||||
python-dev \
|
||||
python-setuptools \
|
||||
telnet \
|
||||
unzip \
|
||||
wget \
|
||||
zip && apt-get clean
|
||||
|
||||
# Install useful useful python modules
|
||||
RUN easy_install -U pip
|
||||
RUN pip install -U crcmod # makes downloads from cloud storage faster
|
||||
class BenchmarkServer(services_pb2.BetaBenchmarkServiceServicer):
|
||||
"""Synchronous Server implementation for the Benchmark service."""
|
||||
|
||||
# Install GCloud
|
||||
RUN wget https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.zip \
|
||||
&& unzip google-cloud-sdk.zip && rm google-cloud-sdk.zip
|
||||
ENV CLOUD_SDK /google-cloud-sdk
|
||||
RUN $CLOUD_SDK/install.sh --usage-reporting=true --path-update=true --bash-completion=true --rc-path=/.bashrc --disable-installation-options
|
||||
ENV PATH $CLOUD_SDK/bin:$PATH
|
||||
def UnaryCall(self, request, context):
|
||||
payload = messages_pb2.Payload(body='\0' * request.response_size)
|
||||
return messages_pb2.SimpleResponse(payload=payload)
|
||||
|
||||
# Define the default command.
|
||||
CMD ["bash"]
|
||||
def StreamingCall(self, request_iterator, context):
|
||||
for request in request_iterator:
|
||||
payload = messages_pb2.Payload(body='\0' * request.response_size)
|
||||
yield messages_pb2.SimpleResponse(payload=payload)
|
||||
|
||||
|
||||
class GenericBenchmarkServer(services_pb2.BetaBenchmarkServiceServicer):
|
||||
"""Generic Server implementation for the Benchmark service."""
|
||||
|
||||
def __init__(self, resp_size):
|
||||
self._response = '\0' * resp_size
|
||||
|
||||
def UnaryCall(self, request, context):
|
||||
return self._response
|
||||
|
||||
def StreamingCall(self, request_iterator, context):
|
||||
for request in request_iterator:
|
||||
yield self._response
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# Copyright 2016, 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.
|
||||
|
||||
"""Defines behavior for WHEN clients send requests.
|
||||
|
||||
Each client exposes a non-blocking send_request() method that the
|
||||
ClientRunner invokes either periodically or in response to some event.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import thread
|
||||
import time
|
||||
|
||||
|
||||
class ClientRunner:
|
||||
"""Abstract interface for sending requests from clients."""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, client):
|
||||
self._client = client
|
||||
|
||||
@abc.abstractmethod
|
||||
def start(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def stop(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class OpenLoopClientRunner(ClientRunner):
|
||||
|
||||
def __init__(self, client, interval_generator):
|
||||
super(OpenLoopClientRunner, self).__init__(client)
|
||||
self._is_running = False
|
||||
self._interval_generator = interval_generator
|
||||
|
||||
def start(self):
|
||||
self._is_running = True
|
||||
self._client.start()
|
||||
thread.start_new_thread(self._dispatch_requests, ())
|
||||
|
||||
def stop(self):
|
||||
self._is_running = False
|
||||
self._client.stop()
|
||||
self._client = None
|
||||
|
||||
def _dispatch_requests(self):
|
||||
while self._is_running:
|
||||
self._client.send_request()
|
||||
time.sleep(next(self._interval_generator))
|
||||
|
||||
|
||||
class ClosedLoopClientRunner(ClientRunner):
|
||||
|
||||
def __init__(self, client, request_count):
|
||||
super(ClosedLoopClientRunner, self).__init__(client)
|
||||
self._is_running = False
|
||||
self._request_count = request_count
|
||||
# Send a new request on each response for closed loop
|
||||
self._client.add_response_callback(self._send_request)
|
||||
|
||||
def start(self):
|
||||
self._is_running = True
|
||||
for _ in xrange(self._request_count):
|
||||
self._client.send_request()
|
||||
self._client.start()
|
||||
|
||||
def stop(self):
|
||||
self._is_running = False
|
||||
self._client.stop()
|
||||
self._client = None
|
||||
|
||||
def _send_request(self, response_time):
|
||||
if self._is_running:
|
||||
self._client.send_request()
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2015, Google Inc.
|
||||
# Copyright 2016, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -27,38 +27,59 @@
|
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""State and behavior for operation cancellation."""
|
||||
import math
|
||||
import threading
|
||||
|
||||
from grpc.framework.base import _interfaces
|
||||
from grpc.framework.base import interfaces
|
||||
from src.proto.grpc.testing import stats_pb2
|
||||
|
||||
|
||||
class CancellationManager(_interfaces.CancellationManager):
|
||||
"""An implementation of _interfaces.CancellationManager."""
|
||||
class Histogram(object):
|
||||
"""Histogram class used for recording performance testing data.
|
||||
|
||||
def __init__(
|
||||
self, lock, termination_manager, transmission_manager, ingestion_manager,
|
||||
expiration_manager):
|
||||
"""Constructor.
|
||||
This class is thread safe.
|
||||
"""
|
||||
|
||||
Args:
|
||||
lock: The operation-wide lock.
|
||||
termination_manager: The _interfaces.TerminationManager for the operation.
|
||||
transmission_manager: The _interfaces.TransmissionManager for the
|
||||
operation.
|
||||
ingestion_manager: The _interfaces.IngestionManager for the operation.
|
||||
expiration_manager: The _interfaces.ExpirationManager for the operation.
|
||||
"""
|
||||
self._lock = lock
|
||||
self._termination_manager = termination_manager
|
||||
self._transmission_manager = transmission_manager
|
||||
self._ingestion_manager = ingestion_manager
|
||||
self._expiration_manager = expiration_manager
|
||||
def __init__(self, resolution, max_possible):
|
||||
self._lock = threading.Lock()
|
||||
self._resolution = resolution
|
||||
self._max_possible = max_possible
|
||||
self._sum = 0
|
||||
self._sum_of_squares = 0
|
||||
self.multiplier = 1.0 + self._resolution
|
||||
self._count = 0
|
||||
self._min = self._max_possible
|
||||
self._max = 0
|
||||
self._buckets = [0] * (self._bucket_for(self._max_possible) + 1)
|
||||
|
||||
def cancel(self):
|
||||
"""See _interfaces.CancellationManager.cancel for specification."""
|
||||
def reset(self):
|
||||
with self._lock:
|
||||
self._termination_manager.abort(interfaces.Outcome.CANCELLED)
|
||||
self._transmission_manager.abort(interfaces.Outcome.CANCELLED)
|
||||
self._ingestion_manager.abort()
|
||||
self._expiration_manager.abort()
|
||||
self._sum = 0
|
||||
self._sum_of_squares = 0
|
||||
self._count = 0
|
||||
self._min = self._max_possible
|
||||
self._max = 0
|
||||
self._buckets = [0] * (self._bucket_for(self._max_possible) + 1)
|
||||
|
||||
def add(self, val):
|
||||
with self._lock:
|
||||
self._sum += val
|
||||
self._sum_of_squares += val * val
|
||||
self._count += 1
|
||||
self._min = min(self._min, val)
|
||||
self._max = max(self._max, val)
|
||||
self._buckets[self._bucket_for(val)] += 1
|
||||
|
||||
def get_data(self):
|
||||
with self._lock:
|
||||
data = stats_pb2.HistogramData()
|
||||
data.bucket.extend(self._buckets)
|
||||
data.min_seen = self._min
|
||||
data.max_seen = self._max
|
||||
data.sum = self._sum
|
||||
data.sum_of_squares = self._sum_of_squares
|
||||
data.count = self._count
|
||||
return data
|
||||
|
||||
def _bucket_for(self, val):
|
||||
val = min(val, self._max_possible)
|
||||
return int(math.log(val, self.multiplier))
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2015, Google Inc.
|
||||
# Copyright 2016, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -27,30 +27,34 @@
|
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""Null links that ignore tickets passed to them."""
|
||||
"""The entry point for the qps worker."""
|
||||
|
||||
from grpc.framework.base import interfaces
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from src.proto.grpc.testing import services_pb2
|
||||
|
||||
from tests.qps import worker_server
|
||||
|
||||
|
||||
class _NullForeLink(interfaces.ForeLink):
|
||||
"""A do-nothing ForeLink."""
|
||||
|
||||
def accept_back_to_front_ticket(self, ticket):
|
||||
pass
|
||||
|
||||
def join_rear_link(self, rear_link):
|
||||
raise NotImplementedError()
|
||||
def run_worker_server(port):
|
||||
servicer = worker_server.WorkerServer()
|
||||
server = services_pb2.beta_create_WorkerService_server(servicer)
|
||||
server.add_insecure_port('[::]:{}'.format(port))
|
||||
server.start()
|
||||
servicer.wait_for_quit()
|
||||
# Drain outstanding requests for clean exit
|
||||
time.sleep(2)
|
||||
server.stop(0)
|
||||
|
||||
|
||||
class _NullRearLink(interfaces.RearLink):
|
||||
"""A do-nothing RearLink."""
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='gRPC Python performance testing worker')
|
||||
parser.add_argument('--driver_port',
|
||||
type=int,
|
||||
dest='port',
|
||||
help='The port the worker should listen on')
|
||||
args = parser.parse_args()
|
||||
|
||||
def accept_front_to_back_ticket(self, ticket):
|
||||
pass
|
||||
|
||||
def join_fore_link(self, fore_link):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
NULL_FORE_LINK = _NullForeLink()
|
||||
NULL_REAR_LINK = _NullRearLink()
|
||||
run_worker_server(args.port)
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
# Copyright 2016, 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.
|
||||
|
||||
import multiprocessing
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
|
||||
from grpc.beta import implementations
|
||||
from grpc.framework.interfaces.face import utilities
|
||||
from src.proto.grpc.testing import control_pb2
|
||||
from src.proto.grpc.testing import services_pb2
|
||||
from src.proto.grpc.testing import stats_pb2
|
||||
|
||||
from tests.qps import benchmark_client
|
||||
from tests.qps import benchmark_server
|
||||
from tests.qps import client_runner
|
||||
from tests.qps import histogram
|
||||
from tests.unit import resources
|
||||
|
||||
|
||||
class WorkerServer(services_pb2.BetaWorkerServiceServicer):
|
||||
"""Python Worker Server implementation."""
|
||||
|
||||
def __init__(self):
|
||||
self._quit_event = threading.Event()
|
||||
|
||||
def RunServer(self, request_iterator, context):
|
||||
config = next(request_iterator).setup
|
||||
server, port = self._create_server(config)
|
||||
cores = multiprocessing.cpu_count()
|
||||
server.start()
|
||||
start_time = time.time()
|
||||
yield self._get_server_status(start_time, start_time, port, cores)
|
||||
|
||||
for request in request_iterator:
|
||||
end_time = time.time()
|
||||
status = self._get_server_status(start_time, end_time, port, cores)
|
||||
if request.mark.reset:
|
||||
start_time = end_time
|
||||
yield status
|
||||
server.stop(0)
|
||||
|
||||
def _get_server_status(self, start_time, end_time, port, cores):
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
stats = stats_pb2.ServerStats(time_elapsed=elapsed_time,
|
||||
time_user=elapsed_time,
|
||||
time_system=elapsed_time)
|
||||
return control_pb2.ServerStatus(stats=stats, port=port, cores=cores)
|
||||
|
||||
def _create_server(self, config):
|
||||
if config.server_type == control_pb2.SYNC_SERVER:
|
||||
servicer = benchmark_server.BenchmarkServer()
|
||||
server = services_pb2.beta_create_BenchmarkService_server(servicer)
|
||||
elif config.server_type == control_pb2.ASYNC_GENERIC_SERVER:
|
||||
resp_size = config.payload_config.bytebuf_params.resp_size
|
||||
servicer = benchmark_server.GenericBenchmarkServer(resp_size)
|
||||
method_implementations = {
|
||||
('grpc.testing.BenchmarkService', 'StreamingCall'):
|
||||
utilities.stream_stream_inline(servicer.StreamingCall),
|
||||
('grpc.testing.BenchmarkService', 'UnaryCall'):
|
||||
utilities.unary_unary_inline(servicer.UnaryCall),
|
||||
}
|
||||
server = implementations.server(method_implementations)
|
||||
else:
|
||||
raise Exception('Unsupported server type {}'.format(config.server_type))
|
||||
|
||||
if config.HasField('security_params'): # Use SSL
|
||||
server_creds = implementations.ssl_server_credentials([(
|
||||
resources.private_key(), resources.certificate_chain())])
|
||||
port = server.add_secure_port('[::]:{}'.format(config.port), server_creds)
|
||||
else:
|
||||
port = server.add_insecure_port('[::]:{}'.format(config.port))
|
||||
|
||||
return (server, port)
|
||||
|
||||
def RunClient(self, request_iterator, context):
|
||||
config = next(request_iterator).setup
|
||||
client_runners = []
|
||||
qps_data = histogram.Histogram(config.histogram_params.resolution,
|
||||
config.histogram_params.max_possible)
|
||||
start_time = time.time()
|
||||
|
||||
# Create a client for each channel
|
||||
for i in xrange(config.client_channels):
|
||||
server = config.server_targets[i % len(config.server_targets)]
|
||||
runner = self._create_client_runner(server, config, qps_data)
|
||||
client_runners.append(runner)
|
||||
runner.start()
|
||||
|
||||
end_time = time.time()
|
||||
yield self._get_client_status(start_time, end_time, qps_data)
|
||||
|
||||
# Respond to stat requests
|
||||
for request in request_iterator:
|
||||
end_time = time.time()
|
||||
status = self._get_client_status(start_time, end_time, qps_data)
|
||||
if request.mark.reset:
|
||||
qps_data.reset()
|
||||
start_time = time.time()
|
||||
yield status
|
||||
|
||||
# Cleanup the clients
|
||||
for runner in client_runners:
|
||||
runner.stop()
|
||||
|
||||
def _get_client_status(self, start_time, end_time, qps_data):
|
||||
latencies = qps_data.get_data()
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
stats = stats_pb2.ClientStats(latencies=latencies,
|
||||
time_elapsed=elapsed_time,
|
||||
time_user=elapsed_time,
|
||||
time_system=elapsed_time)
|
||||
return control_pb2.ClientStatus(stats=stats)
|
||||
|
||||
def _create_client_runner(self, server, config, qps_data):
|
||||
if config.client_type == control_pb2.SYNC_CLIENT:
|
||||
if config.rpc_type == control_pb2.UNARY:
|
||||
client = benchmark_client.UnarySyncBenchmarkClient(
|
||||
server, config, qps_data)
|
||||
else:
|
||||
raise Exception('STREAMING SYNC client not supported')
|
||||
elif config.client_type == control_pb2.ASYNC_CLIENT:
|
||||
if config.rpc_type == control_pb2.UNARY:
|
||||
client = benchmark_client.UnaryAsyncBenchmarkClient(
|
||||
server, config, qps_data)
|
||||
elif config.rpc_type == control_pb2.STREAMING:
|
||||
client = benchmark_client.StreamingAsyncBenchmarkClient(
|
||||
server, config, qps_data)
|
||||
else:
|
||||
raise Exception('Unsupported client type {}'.format(config.client_type))
|
||||
|
||||
# In multi-channel tests, we split the load across all channels
|
||||
load_factor = float(config.client_channels)
|
||||
if config.load_params.WhichOneof('load') == 'closed_loop':
|
||||
runner = client_runner.ClosedLoopClientRunner(
|
||||
client, config.outstanding_rpcs_per_channel)
|
||||
else: # Open loop Poisson
|
||||
alpha = config.load_params.poisson.offered_load / load_factor
|
||||
def poisson():
|
||||
while True:
|
||||
yield random.expovariate(alpha)
|
||||
|
||||
runner = client_runner.OpenLoopClientRunner(client, poisson())
|
||||
|
||||
return runner
|
||||
|
||||
def CoreCount(self, request, context):
|
||||
return control_pb2.CoreResponse(cores=multiprocessing.cpu_count())
|
||||
|
||||
def QuitWorker(self, request, context):
|
||||
self._quit_event.set()
|
||||
return control_pb2.Void()
|
||||
|
||||
def wait_for_quit(self):
|
||||
self._quit_event.wait()
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# 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.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue