Internal code sync (#1140)

This commit is contained in:
Eryu Xia 2021-09-28 10:48:20 -07:00 committed by GitHub
parent 627e33718d
commit f1fe57473f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 281 additions and 282 deletions

View File

@ -3,11 +3,11 @@ ROOT_DIR := $(shell pwd)
all: clean
plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make
install-plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make install
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make install
clean:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make clean
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make clean
cd "$(ROOT_DIR)"

View File

@ -63,64 +63,18 @@ enum ImportStyle {
const char GRPC_PROMISE[] = "grpc.web.promise.GrpcWebPromise";
const char* kKeyword[] = {
"abstract",
"boolean",
"break",
"byte",
"case",
"catch",
"char",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"double",
"else",
"enum",
"export",
"extends",
"false",
"final",
"finally",
"float",
"for",
"function",
"goto",
"if",
"implements",
"import",
"in",
"instanceof",
"int",
"interface",
"long",
"native",
"new",
"null",
"package",
"private",
"protected",
"public",
"return",
"short",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"transient",
"try",
"typeof",
"var",
"void",
"volatile",
"while",
"with",
"abstract", "boolean", "break", "byte", "case",
"catch", "char", "class", "const", "continue",
"debugger", "default", "delete", "do", "double",
"else", "enum", "export", "extends", "false",
"final", "finally", "float", "for", "function",
"goto", "if", "implements", "import", "in",
"instanceof", "int", "interface", "long", "native",
"new", "null", "package", "private", "protected",
"public", "return", "short", "static", "super",
"switch", "synchronized", "this", "throw", "throws",
"transient", "try", "typeof", "var", "void",
"volatile", "while", "with",
};
bool IsReserved(const string& ident) {
@ -204,10 +158,9 @@ string Uppercase(string s) {
// The following 5 functions were copied from
// google/protobuf/src/google/protobuf/stubs/strutil.h
inline bool HasPrefixString(const string& str,
const string& prefix) {
inline bool HasPrefixString(const string& str, const string& prefix) {
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
str.compare(0, prefix.size(), prefix) == 0;
}
inline string StripPrefixString(const string& str, const string& prefix) {
@ -218,10 +171,9 @@ inline string StripPrefixString(const string& str, const string& prefix) {
}
}
inline bool HasSuffixString(const string& str,
const string& suffix) {
inline bool HasSuffixString(const string& str, const string& suffix) {
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
inline string StripSuffixString(const string& str, const string& suffix) {
@ -232,17 +184,15 @@ inline string StripSuffixString(const string& str, const string& suffix) {
}
}
void ReplaceCharacters(string *s, const char *remove, char replacewith) {
const char *str_start = s->c_str();
const char *str = str_start;
for (str = strpbrk(str, remove);
str != nullptr;
void ReplaceCharacters(string* s, const char* remove, char replacewith) {
const char* str_start = s->c_str();
const char* str = str_start;
for (str = strpbrk(str, remove); str != nullptr;
str = strpbrk(str + 1, remove)) {
(*s)[str - str_start] = replacewith;
}
}
// The following function was copied from
// google/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc
@ -313,7 +263,7 @@ string ModuleAlias(const string& filename) {
return basename + "_pb";
}
string JSMessageType(const Descriptor *desc, const FileDescriptor *file) {
string JSMessageType(const Descriptor* desc, const FileDescriptor* file) {
string class_name;
class_name = StripPrefixString(desc->full_name(), desc->file()->package());
if (!class_name.empty() && class_name[0] == '.') {
@ -327,11 +277,11 @@ string JSMessageType(const Descriptor *desc, const FileDescriptor *file) {
return ModuleAlias(desc->file()->name()) + "." + class_name;
}
string JSMessageType(const Descriptor *desc) {
string JSMessageType(const Descriptor* desc) {
return JSMessageType(desc, nullptr);
}
string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) {
string JSElementType(const FieldDescriptor* desc, const FileDescriptor* file) {
switch (desc->type()) {
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT:
@ -367,15 +317,13 @@ string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) {
// [for protobuf .d.ts files only] Do not add the module prefix for
// local messages.
string enum_name =
StripPrefixString(
desc->enum_type()->full_name(),
desc->enum_type()->file()->package());
StripPrefixString(desc->enum_type()->full_name(),
desc->enum_type()->file()->package());
return StripPrefixString(enum_name, ".");
}
return ModuleAlias(desc->enum_type()->file()->name())
+ StripPrefixString(
desc->enum_type()->full_name(),
desc->enum_type()->file()->package());
return ModuleAlias(desc->enum_type()->file()->name()) +
StripPrefixString(desc->enum_type()->full_name(),
desc->enum_type()->file()->package());
case FieldDescriptor::TYPE_MESSAGE:
return JSMessageType(desc->message_type(), file);
@ -385,22 +333,21 @@ string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) {
}
}
string JSFieldType(const FieldDescriptor *desc, const FileDescriptor *file) {
string JSFieldType(const FieldDescriptor* desc, const FileDescriptor* file) {
string js_field_type = JSElementType(desc, file);
if (desc->is_map()) {
string key_type = JSFieldType(desc->message_type()->field(0), file);
string value_type = JSFieldType(desc->message_type()->field(1), file);
return "jspb.Map<" + key_type + ", " + value_type + ">";
}
if (desc->is_repeated())
{
if (desc->is_repeated()) {
return "Array<" + js_field_type + ">";
}
return js_field_type;
}
string AsObjectFieldType(
const FieldDescriptor *desc, const FileDescriptor *file) {
string AsObjectFieldType(const FieldDescriptor* desc,
const FileDescriptor* file) {
if (desc->type() != FieldDescriptor::TYPE_MESSAGE) {
return JSFieldType(desc, file);
}
@ -417,11 +364,11 @@ string AsObjectFieldType(
return field_type;
}
string JSElementName(const FieldDescriptor *desc) {
string JSElementName(const FieldDescriptor* desc) {
return ToUpperCamel(ParseLowerUnderscore(desc->name()));
}
string JSFieldName(const FieldDescriptor *desc) {
string JSFieldName(const FieldDescriptor* desc) {
string js_field_name = JSElementName(desc);
if (desc->is_map()) {
js_field_name += "Map";
@ -432,20 +379,17 @@ string JSFieldName(const FieldDescriptor *desc) {
}
// Like ToUpperCamel except the first letter is not converted.
string ToCamelCase(const std::vector<string>& words)
{
string ToCamelCase(const std::vector<string>& words) {
if (words.empty()) {
return "";
return "";
}
string result = words[0];
return result + ToUpperCamel(std::vector<string>(
words.begin()+1,
words.begin()+words.size()));
words.begin() + 1, words.begin() + words.size()));
}
// Like JSFieldName, but with first letter not uppercased
string CamelCaseJSFieldName(const FieldDescriptor *desc)
{
string CamelCaseJSFieldName(const FieldDescriptor* desc) {
string js_field_name = ToCamelCase(ParseLowerUnderscore(desc->name()));
if (desc->is_map()) {
js_field_name += "Map";
@ -463,8 +407,8 @@ string GetNestedMessageName(const Descriptor* descriptor) {
if (descriptor == nullptr) {
return "";
}
string result = StripPrefixString(descriptor->full_name(),
descriptor->file()->package());
string result =
StripPrefixString(descriptor->full_name(), descriptor->file()->package());
// Add a leading dot if one is not already present.
if (!result.empty() && result[0] != '.') {
result = "." + result;
@ -534,7 +478,7 @@ std::map<string, const Descriptor*> GetAllMessages(const FileDescriptor* file) {
for (int s = 0; s < file->service_count(); ++s) {
const ServiceDescriptor* service = file->service(s);
for (int m = 0; m < service->method_count(); ++m) {
const MethodDescriptor *method = service->method(m);
const MethodDescriptor* method = service->method(m);
messages[method->input_type()->full_name()] = method->input_type();
messages[method->output_type()->full_name()] = method->output_type();
}
@ -544,12 +488,10 @@ std::map<string, const Descriptor*> GetAllMessages(const FileDescriptor* file) {
}
void PrintClosureDependencies(Printer* printer, const FileDescriptor* file) {
for (const auto &entry : GetAllMessages(file)) {
printer->Print(
"goog.require('proto.$full_name$');\n",
"full_name", entry.second->full_name());
for (const auto& entry : GetAllMessages(file)) {
printer->Print("goog.require('proto.$full_name$');\n", "full_name",
entry.second->full_name());
}
printer->Print("\n\n\n");
}
void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
@ -560,9 +502,7 @@ void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
vars["alias"] = ModuleAlias(name);
vars["dep_filename"] = GetRootPath(file->name(), name) + StripProto(name);
// we need to give each cross-file import an alias
printer->Print(
vars,
"\nvar $alias$ = require('$dep_filename$_pb.js')\n");
printer->Print(vars, "\nvar $alias$ = require('$dep_filename$_pb.js')\n");
}
const string& package = file->package();
@ -587,13 +527,10 @@ void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
vars["filename"] = GetBasename(StripProto(file->name()));
if (!package.empty()) {
printer->Print(
vars,
"proto.$package_name$ = require('./$filename$_pb.js');\n\n");
printer->Print(vars,
"proto.$package_name$ = require('./$filename$_pb.js');\n\n");
} else {
printer->Print(
vars,
"const proto = require('./$filename$_pb.js');\n\n");
printer->Print(vars, "const proto = require('./$filename$_pb.js');\n\n");
}
}
@ -603,7 +540,7 @@ void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
printer->Print("import * as grpcWeb from 'grpc-web';\n\n");
std::set<string> imports;
for (const auto &entry : GetAllMessages(file)) {
for (const auto& entry : GetAllMessages(file)) {
const string& name = entry.second->file()->name();
string dep_filename = GetRootPath(file->name(), name) + StripProto(name);
if (imports.find(dep_filename) != imports.end()) {
@ -611,10 +548,8 @@ void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
}
imports.insert(dep_filename);
// We need to give each cross-file import an alias.
printer->Print(
"import * as $alias$ from '$dep_filename$_pb';\n",
"alias", ModuleAlias(name),
"dep_filename", dep_filename);
printer->Print("import * as $alias$ from '$dep_filename$_pb';\n", "alias",
ModuleAlias(name), "dep_filename", dep_filename);
}
printer->Print("\n\n");
}
@ -687,13 +622,12 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
"metadata?: grpcWeb.Metadata) {\n");
printer->Print(vars, "return this.client_.serverStreaming(\n");
printer->Indent();
printer->Print(
vars,
"this.hostname_ +\n"
" '/$package_dot$$service_name$/$method_name$',\n"
"request,\n"
"metadata || {},\n"
"this.methodInfo$method_name$);\n");
printer->Print(vars,
"this.hostname_ +\n"
" '/$package_dot$$service_name$/$method_name$',\n"
"request,\n"
"metadata || {},\n"
"this.methodInfo$method_name$);\n");
printer->Outdent();
printer->Outdent();
printer->Print("}\n\n");
@ -727,14 +661,13 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
printer->Indent();
printer->Print(vars, "return this.client_.rpcCall(\n");
printer->Indent();
printer->Print(
vars,
"this.hostname_ +\n"
" '/$package_dot$$service_name$/$method_name$',\n"
"request,\n"
"metadata || {},\n"
"this.methodInfo$method_name$,\n"
"callback);\n");
printer->Print(vars,
"this.hostname_ +\n"
" '/$package_dot$$service_name$/$method_name$',\n"
"request,\n"
"metadata || {},\n"
"this.methodInfo$method_name$,\n"
"callback);\n");
printer->Outdent();
printer->Outdent();
printer->Print(vars,
@ -757,7 +690,7 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
}
void PrintGrpcWebDtsClientClass(Printer* printer, const FileDescriptor* file,
const string &client_type) {
const string& client_type) {
std::map<string, string> vars;
vars["client_type"] = client_type;
vars["promise"] = "Promise";
@ -824,15 +757,13 @@ void PrintGrpcWebDtsFile(Printer* printer, const FileDescriptor* file) {
PrintGrpcWebDtsClientClass(printer, file, "PromiseClient");
}
void PrintProtoDtsEnum(Printer *printer, const EnumDescriptor *desc)
{
void PrintProtoDtsEnum(Printer* printer, const EnumDescriptor* desc) {
std::map<string, string> vars;
vars["enum_name"] = desc->name();
printer->Print(vars, "export enum $enum_name$ { \n");
printer->Indent();
for (int i = 0; i < desc->value_count(); i++)
{
for (int i = 0; i < desc->value_count(); i++) {
vars["value_name"] = Uppercase(desc->value(i)->name());
vars["value_number"] = std::to_string(desc->value(i)->number());
printer->Print(vars, "$value_name$ = $value_number$,\n");
@ -841,8 +772,7 @@ void PrintProtoDtsEnum(Printer *printer, const EnumDescriptor *desc)
printer->Print("}\n");
}
void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc)
{
void PrintProtoDtsOneofCase(Printer* printer, const OneofDescriptor* desc) {
std::map<string, string> vars;
vars["oneof_name"] = ToUpperCamel(ParseLowerUnderscore(desc->name()));
vars["oneof_name_upper"] = Uppercase(desc->name());
@ -851,7 +781,7 @@ void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc)
printer->Indent();
printer->Print(vars, "$oneof_name_upper$_NOT_SET = 0,\n");
for (int i = 0; i < desc->field_count(); i++) {
const FieldDescriptor *field = desc->field(i);
const FieldDescriptor* field = desc->field(i);
vars["field_name"] = Uppercase(field->name());
vars["field_number"] = std::to_string(field->number());
printer->Print(vars, "$field_name$ = $field_number$,\n");
@ -860,8 +790,8 @@ void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc)
printer->Print("}\n");
}
void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
const FileDescriptor *file) {
void PrintProtoDtsMessage(Printer* printer, const Descriptor* desc,
const FileDescriptor* file) {
const string& class_name = desc->name();
std::map<string, string> vars;
vars["class_name"] = class_name;
@ -874,8 +804,7 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
vars["js_field_type"] = JSFieldType(field, file);
if (field->type() != FieldDescriptor::TYPE_MESSAGE ||
field->is_repeated()) {
printer->Print(vars,
"get$js_field_name$(): $js_field_type$;\n");
printer->Print(vars, "get$js_field_name$(): $js_field_type$;\n");
} else {
printer->Print(vars,
"get$js_field_name$(): $js_field_type$ | undefined;\n");
@ -895,8 +824,8 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
"set$js_field_name$(value?: $js_field_type$): "
"$class_name$;\n");
}
if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_repeated()
&& !field->is_map()) {
if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
!field->is_repeated() && !field->is_map()) {
printer->Print(vars, "has$js_field_name$(): boolean;\n");
}
if (field->type() == FieldDescriptor::TYPE_MESSAGE ||
@ -924,8 +853,7 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
const OneofDescriptor* oneof = desc->oneof_decl(i);
vars["js_oneof_name"] = ToUpperCamel(ParseLowerUnderscore(oneof->name()));
printer->Print(
vars,
"get$js_oneof_name$Case(): $class_name$.$js_oneof_name$Case;\n");
vars, "get$js_oneof_name$Case(): $class_name$.$js_oneof_name$Case;\n");
printer->Print("\n");
}
@ -988,17 +916,15 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
printer->Print("}\n\n");
}
void PrintProtoDtsFile(Printer *printer, const FileDescriptor *file)
{
void PrintProtoDtsFile(Printer* printer, const FileDescriptor* file) {
printer->Print("import * as jspb from 'google-protobuf'\n\n");
for (int i = 0; i < file->dependency_count(); i++) {
const string& name = file->dependency(i)->name();
// We need to give each cross-file import an alias.
printer->Print(
"import * as $alias$ from '$dep_filename$_pb';\n",
"alias", ModuleAlias(name),
"dep_filename", GetRootPath(file->name(), name) + StripProto(name));
printer->Print("import * as $alias$ from '$dep_filename$_pb';\n", "alias",
ModuleAlias(name), "dep_filename",
GetRootPath(file->name(), name) + StripProto(name));
}
printer->Print("\n\n");
@ -1080,8 +1006,9 @@ void PrintMethodDescriptorFile(Printer* printer,
" * @param {!proto.$in$} request\n");
printer->Print(
(" * @return {" + GetSerializeMethodReturnType(vars) + "}\n").c_str());
printer->Print(" */\n"
"function(request) {\n");
printer->Print(
" */\n"
"function(request) {\n");
printer->Print(
(" return request." + GetSerializeMethodName(vars) + "();\n").c_str());
printer->Print("},\n");
@ -1102,7 +1029,7 @@ void PrintServiceConstructor(Printer* printer, std::map<string, string> vars,
"/**\n"
" * @param {string} hostname\n"
" * @param {?Object} credentials\n"
" * @param {?Object} options\n"
" * @param {?grpc.web.ClientOptions} options\n"
" * @constructor\n"
" * @struct\n"
" * @final\n"
@ -1111,7 +1038,7 @@ void PrintServiceConstructor(Printer* printer, std::map<string, string> vars,
" function(hostname, credentials, options) {\n"
" if (!options) options = {};\n");
if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) {
printer->Print(vars, " options['format'] = '$format$';\n\n");
printer->Print(vars, " options.format = '$format$';\n\n");
}
if (vars["mode"] == GetModeVar(Mode::OP)) {
printer->Print(
@ -1213,7 +1140,7 @@ void PrintPromiseUnaryCall(Printer* printer, std::map<string, string> vars) {
"/**\n"
" * @param {!proto.$in$} request The\n"
" * request proto\n"
" * @param {?Object<string, string>} metadata User defined\n"
" * @param {?Object<string, string>=} metadata User defined\n"
" * call metadata\n"
" * @return {!$promise$<!proto.$out$>}\n"
" * Promise that resolves to the response\n"
@ -1243,17 +1170,16 @@ void PrintPromiseUnaryCall(Printer* printer, std::map<string, string> vars) {
}
void PrintServerStreamingCall(Printer* printer, std::map<string, string> vars) {
printer->Print(
vars,
"/**\n"
" * @param {!proto.$in$} request The request proto\n"
" * @param {?Object<string, string>} metadata User defined\n"
" * call metadata\n"
" * @return {!grpc.web.ClientReadableStream<!proto.$out$>}\n"
" * The XHR Node Readable Stream\n"
" */\n"
"proto.$package_dot$$service_name$$client_type$.prototype."
"$js_method_name$ =\n");
printer->Print(vars,
"/**\n"
" * @param {!proto.$in$} request The request proto\n"
" * @param {?Object<string, string>=} metadata User defined\n"
" * call metadata\n"
" * @return {!grpc.web.ClientReadableStream<!proto.$out$>}\n"
" * The XHR Node Readable Stream\n"
" */\n"
"proto.$package_dot$$service_name$$client_type$.prototype."
"$js_method_name$ =\n");
printer->Indent();
printer->Print(
" function(request, metadata) {\n"
@ -1359,6 +1285,11 @@ void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
PrintClosureDependencies(&printer1, file);
PrintClosureDependencies(&printer2, file);
printer1.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer2.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer1.Print("\n\n\n");
printer2.Print("\n\n\n");
printer1.Print("goog.scope(function() {\n\n");
printer2.Print("goog.scope(function() {\n\n");
@ -1403,20 +1334,20 @@ void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
printer2.Print("}); // goog.scope\n\n");
}
void PrintClosureES6Imports(
Printer* printer, const FileDescriptor* file, string package_dot) {
void PrintClosureES6Imports(Printer* printer, const FileDescriptor* file,
string package_dot) {
for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);
string service_namespace = "proto." + package_dot + service->name();
printer->Print(
"import $service_name$Client_import from 'goog:$namespace$';\n",
"service_name", service->name(),
"namespace", service_namespace + "Client");
"service_name", service->name(), "namespace",
service_namespace + "Client");
printer->Print(
"import $service_name$PromiseClient_import from 'goog:$namespace$';\n",
"service_name", service->name(),
"namespace", service_namespace + "PromiseClient");
"service_name", service->name(), "namespace",
service_namespace + "PromiseClient");
}
printer->Print("\n\n\n");
@ -1441,9 +1372,8 @@ void PrintGrpcWebClosureES6File(Printer* printer, const FileDescriptor* file) {
const ServiceDescriptor* service = file->service(i);
string service_namespace = "proto." + package_dot + service->name();
printer->Print(
"export const $name$Client = $name$Client_import;\n",
"name", service->name());
printer->Print("export const $name$Client = $name$Client_import;\n", "name",
service->name());
printer->Print(
"export const $name$PromiseClient = $name$PromiseClient_import;\n",
"name", service->name());
@ -1664,7 +1594,8 @@ class GrpcCodeGenerator : public CodeGenerator {
printer.Print(vars, "goog.require('grpc.web.RpcError');\n");
PrintClosureDependencies(&printer, file);
printer.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer.Print("\n\n\n");
printer.Print("goog.scope(function() {\n\n");
break;
case ImportStyle::COMMONJS:

View File

@ -1,41 +0,0 @@
/**
* @fileoverview base interface for grpc web GenericClient.
*/
goog.module('grpc.web.GenericClient');
goog.module.declareLegacyNamespace();
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const Request = goog.require('grpc.web.Request');
const UnaryResponse = goog.require('grpc.web.UnaryResponse');
/**
* @interface
*/
const GenericClient = function() {};
/**
* @param {!Request<REQUEST, RESPONSE>} request The wrapped gRPC-Web request
* @return {!Promise<!UnaryResponse<RESPONSE>>} A promise that resolves to the
* response message and metadata
* @template REQUEST, RESPONSE
* @abstract
*/
GenericClient.prototype.unaryCall = function(request) {};
/**
* Simplified version of GenericClient.prototype.unaryCall. Users are expected
* to use this method if they don't have the need to customize metadata and
* callOptions .
* @param {!REQUEST} requestMessage The request message
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor Information of
* this RPC method
* @return {!Promise<RESPONSE>} A promise that resolves to the
* response message
* @template REQUEST, RESPONSE
* @abstract
*/
GenericClient.prototype.call = function(requestMessage, methodDescriptor) {};
exports = GenericClient;

View File

@ -42,6 +42,7 @@ const XhrIo = goog.require('goog.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const {Status} = goog.require('grpc.web.Status');
const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');
const {toObject} = goog.require('goog.collections.maps');
@ -193,10 +194,13 @@ class GrpcWebClientBase {
stream.setResponseDeserializeFn(
methodDescriptor.getResponseDeserializeFn());
xhr.headers.addAll(request.getMetadata());
const metadata = request.getMetadata();
for(const key in metadata) {
xhr.headers.set(key, metadata[key]);
}
this.processHeaders_(xhr);
if (this.suppressCorsPreflight_) {
const headerObject = xhr.headers.toObject();
const headerObject = toObject(xhr.headers);
xhr.headers.clear();
path = GrpcWebClientBase.setCorsOverride_(path, headerObject);
}
@ -310,11 +314,11 @@ class GrpcWebClientBase {
}
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Grpc-Web', '1');
if (xhr.headers.containsKey('deadline')) {
const deadline = xhr.headers.get('deadline'); // in ms
if (xhr.headers.has('deadline')) {
const deadline = Number(xhr.headers.get('deadline')); // in ms
const currentTime = (new Date()).getTime();
let timeout = Math.round(deadline - currentTime);
xhr.headers.remove('deadline');
xhr.headers.delete('deadline');
if (timeout === Infinity) {
// grpc-timeout header defaults to infinity if not set.
timeout = 0;

View File

@ -24,12 +24,17 @@ const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const ReadyState = goog.require('goog.net.XmlHttp.ReadyState');
const Request = goog.requireType('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.testing.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const testSuite = goog.require('goog.testing.testSuite');
const {StreamInterceptor} = goog.require('grpc.web.Interceptor');
goog.require('goog.testing.jsunit');
// This parses to [ { DATA: [4, 5, 6] }, { TRAILER: "a: b" } ]
const DEFAULT_RPC_RESPONSE =
new Uint8Array([0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98]);
const DEFAULT_RPC_RESPONSE_DATA = [4, 5, 6];
const DEFAULT_UNARY_HEADERS =
['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web'];
const DEFAULT_UNARY_HEADER_VALUES = [
@ -47,7 +52,7 @@ testSuite({
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes));
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
@ -58,10 +63,8 @@ testSuite({
assertNull(error);
resolve(response);
});
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
@ -86,10 +89,8 @@ testSuite({
assertNull(error);
resolve();
});
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
@ -122,11 +123,34 @@ testSuite({
assertEquals(3, error.code);
},
async testRpcDeserializationError() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const responseDeserializeFn = () => {
throw new Error('Decoding error :)');
};
const methodDescriptor = createMethodDescriptor(responseDeserializeFn);
const error = await new Promise((resolve, reject) => {
client.rpcCall(
'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(response);
resolve(error);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
});
assertTrue(error instanceof RpcError);
assertEquals(StatusCode.INTERNAL, error.code);
},
async testRpcResponseHeader() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes));
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
@ -139,11 +163,8 @@ testSuite({
call.on('metadata', (metadata) => {
resolve(metadata);
});
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
{
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)), {
'Content-Type': 'application/grpc-web-text',
'initial-metadata-key': 'initial-metadata-value',
});
@ -156,7 +177,7 @@ testSuite({
const xhr = new XhrIo();
const interceptor = new StreamResponseInterceptor();
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes));
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
const client =
@ -169,10 +190,8 @@ testSuite({
assertNull(error);
resolve(response);
});
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});

View File

@ -152,16 +152,16 @@ class GrpcWebClientReadableStream {
var byteSource = new Uint8Array(
/** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
} else {
self.handleError_(new RpcError(
StatusCode.UNKNOWN, 'Unknown Content-type received.'));
self.handleError_(
new RpcError(StatusCode.UNKNOWN, 'Unknown Content-type received.'));
return;
}
var messages = null;
try {
messages = self.parser_.parse(byteSource);
} catch (err) {
self.handleError_(new RpcError(
StatusCode.UNKNOWN, 'Error in parsing response body'));
self.handleError_(
new RpcError(StatusCode.UNKNOWN, 'Error in parsing response body'));
}
if (messages) {
var FrameType = GrpcWebStreamParser.FrameType;
@ -169,15 +169,16 @@ class GrpcWebClientReadableStream {
if (FrameType.DATA in messages[i]) {
var data = messages[i][FrameType.DATA];
if (data) {
let response;
try {
var response = self.responseDeserializeFn_(data);
if (response) {
self.sendDataCallbacks_(response);
}
response = self.responseDeserializeFn_(data);
} catch (err) {
self.handleError_(new RpcError(
StatusCode.UNKNOWN,
'Error in response deserializer function.'));
StatusCode.INTERNAL,
`Error when deserializing response data: ${response}`));
}
if (response) {
self.sendDataCallbacks_(response);
}
}
}
@ -193,15 +194,16 @@ class GrpcWebClientReadableStream {
var grpcStatusCode = StatusCode.OK;
var grpcStatusMessage = '';
if (GRPC_STATUS in trailers) {
grpcStatusCode = trailers[GRPC_STATUS];
grpcStatusCode =
/** @type {!StatusCode} */ (Number(trailers[GRPC_STATUS]));
delete trailers[GRPC_STATUS];
}
if (GRPC_STATUS_MESSAGE in trailers) {
grpcStatusMessage = trailers[GRPC_STATUS_MESSAGE];
delete trailers[GRPC_STATUS_MESSAGE];
}
self.handleError_(new RpcError(
Number(grpcStatusCode), grpcStatusMessage, trailers));
self.handleError_(
new RpcError(grpcStatusCode, grpcStatusMessage, trailers));
}
}
}
@ -210,7 +212,7 @@ class GrpcWebClientReadableStream {
events.listen(this.xhr_, EventType.COMPLETE, function(e) {
var lastErrorCode = self.xhr_.getLastErrorCode();
var grpcStatusCode;
var grpcStatusCode = StatusCode.UNKNOWN;
var grpcStatusMessage = '';
var initialMetadata = /** @type {!Metadata} */ ({});
@ -249,14 +251,14 @@ class GrpcWebClientReadableStream {
// Check whethere there are grpc specific response headers
if (GRPC_STATUS in responseHeaders) {
grpcStatusCode = self.xhr_.getResponseHeader(GRPC_STATUS);
grpcStatusCode = /** @type {!StatusCode} */ (
Number(self.xhr_.getResponseHeader(GRPC_STATUS)));
if (GRPC_STATUS_MESSAGE in responseHeaders) {
grpcStatusMessage = self.xhr_.getResponseHeader(GRPC_STATUS_MESSAGE);
}
if (Number(grpcStatusCode) != StatusCode.OK) {
if (grpcStatusCode != StatusCode.OK) {
self.handleError_(new RpcError(
Number(grpcStatusCode), grpcStatusMessage || '',
responseHeaders));
grpcStatusCode, grpcStatusMessage || '', responseHeaders));
errorEmitted = true;
}
}

View File

@ -26,16 +26,21 @@
goog.module('grpc.web.RpcError');
const Metadata = goog.require('grpc.web.Metadata');
const StatusCode = goog.require('grpc.web.StatusCode');
/**
* gRPC-Web Error object, contains the {@link StatusCode}, a string message
* and {@link Metadata} contained in the error response.
*/
class RpcError extends Error {
/**
* @param {number} code
* @param {!StatusCode} code
* @param {string} message
* @param {!Metadata=} metadata
*/
constructor(code, message, metadata = {}) {
super(message);
/** @type {number} */
/** @type {!StatusCode} */
this.code = code;
/** @type {!Metadata} */
this.metadata = metadata;

View File

@ -23,11 +23,9 @@
* @author stanleycheung@google.com (Stanley Cheung)
*/
goog.module('grpc.web.Status');
goog.module.declareLegacyNamespace();
/** @record */
function Status() {}

View File

@ -26,7 +26,8 @@ goog.module('grpc.web.StatusCode');
/**
* gRPC Status Codes
* See: https://github.com/grpc/grpc/blob/master/include/grpc%2B%2B/impl/codegen/status_code_enum.h
* See:
* https://github.com/grpc/grpc/blob/master/include/grpcpp/impl/codegen/status_code_enum.h
* @enum {number}
*/
const StatusCode = {
@ -140,11 +141,11 @@ const StatusCode = {
/**
* Convert HTTP Status code to gRPC Status code
* @param {number} http_status HTTP Status Code
* @return {number} gRPC Status Code
* @param {number} httpStatus HTTP Status Code
* @return {!StatusCode} gRPC Status Code
*/
StatusCode.fromHttpStatus = function(http_status) {
switch (http_status) {
StatusCode.fromHttpStatus = function(httpStatus) {
switch (httpStatus) {
case 200:
return StatusCode.OK;
case 400:
@ -178,4 +179,44 @@ StatusCode.fromHttpStatus = function(http_status) {
};
/**
* Convert a {@link StatusCode} to an HTTP Status code
* @param {!StatusCode} statusCode GRPC Status Code
* @return {number} HTTP Status code
*/
StatusCode.getHttpStatus = function(statusCode) {
switch (statusCode) {
case StatusCode.OK:
return 200;
case StatusCode.INVALID_ARGUMENT:
return 400;
case StatusCode.UNAUTHENTICATED:
return 401;
case StatusCode.PERMISSION_DENIED:
return 403;
case StatusCode.NOT_FOUND:
return 404;
case StatusCode.ABORTED:
return 409;
case StatusCode.FAILED_PRECONDITION:
return 412;
case StatusCode.RESOURCE_EXHAUSTED:
return 429;
case StatusCode.CANCELLED:
return 499;
case StatusCode.UNKNOWN:
return 500;
case StatusCode.UNIMPLEMENTED:
return 501;
case StatusCode.UNAVAILABLE:
return 503;
case StatusCode.DEADLINE_EXCEEDED:
return 504;
/* everything else is unknown */
default:
return 0;
}
};
exports = StatusCode;

View File

@ -0,0 +1,41 @@
goog.module('grpc.web.StatusCodeTest');
goog.setTestOnly('grpc.web.StatusCodeTest');
const StatusCode = goog.require('grpc.web.StatusCode');
const testSuite = goog.require('goog.testing.testSuite');
/** @type {!Map<number, !StatusCode>} */
const statusMap = new Map([
[200, StatusCode.OK],
[400, StatusCode.INVALID_ARGUMENT],
[401, StatusCode.UNAUTHENTICATED],
[403, StatusCode.PERMISSION_DENIED],
[404, StatusCode.NOT_FOUND],
[409, StatusCode.ABORTED],
[412, StatusCode.FAILED_PRECONDITION],
[429, StatusCode.RESOURCE_EXHAUSTED],
[500, StatusCode.UNKNOWN],
[501, StatusCode.UNIMPLEMENTED],
[503, StatusCode.UNAVAILABLE],
[504, StatusCode.DEADLINE_EXCEEDED],
]);
testSuite({
testFromHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.fromHttpStatus(httpStatus), statusCode);
});
},
testGetHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.getHttpStatus(statusCode), httpStatus);
});
},
testUnknown() {
assertEquals(StatusCode.getHttpStatus(StatusCode.UNKNOWN), 500);
assertEquals(StatusCode.fromHttpStatus(511), StatusCode.UNKNOWN);
}
});

View File

@ -73,11 +73,10 @@ WORKDIR /github/grpc-web
# Copy only files necessary to build the protoc-gen-grpc-web first as an optimization because they
# are rarely updated compared with the javascript files.
COPY ./WORKSPACE ./WORKSPACE
COPY ./javascript/net/grpc/web/grpc_generator.cc javascript/net/grpc/web/grpc_generator.cc
COPY ./javascript/net/grpc/web/BUILD.bazel javascript/net/grpc/web/BUILD.bazel
COPY ./javascript/net/grpc/web/generator javascript/net/grpc/web/generator
RUN bazel build javascript/net/grpc/web:protoc-gen-grpc-web && \
cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/protoc-gen-grpc-web \
RUN bazel build javascript/net/grpc/web/generator:protoc-gen-grpc-web && \
cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/generator/protoc-gen-grpc-web \
/usr/local/bin/protoc-gen-grpc-web
COPY ./javascript ./javascript

View File

@ -32,5 +32,5 @@ WORKDIR /github/grpc-web
RUN cd ./third_party/protobuf && \
./autogen.sh && ./configure && make && make install && ldconfig
RUN cd ./javascript/net/grpc/web && \
RUN cd ./javascript/net/grpc/web/generator && \
make protoc-gen-grpc-web

View File

@ -6,6 +6,6 @@
"dependencies": {},
"devDependencies": {
"google-closure-compiler": "~20200224.0.0",
"google-closure-library": "~20201102.0.1"
"google-closure-library": "~20210808.0.0"
}
}

View File

@ -376,8 +376,8 @@ describe('grpc-web generated code (commonjs+grpcwebtext)', function() {
if (response) {
assert.fail('should not receive response');
}
assert.equal(2, err.code);
assert.equal(true, err.message.toLowerCase().includes('deserialize'));
assert.equal(13 /* StatusCode.INTERNAL */, err.code);
assert.equal(true, err.message.toLowerCase().includes('deserializing'));
assert.equal(true, err.message.toLowerCase().includes('error'));
done();
});

View File

@ -16,7 +16,7 @@ in Docker Desktop for Mac (e.g. to 4 - 6GB) if you see the following errors:
```
$ bazel test --cache_test_results=no //javascript/net/grpc/web/... //net/grpc/gateway/examples/...
$ bazel build //javascript/net/grpc/web/generator/... //net/grpc/gateway/examples/echo/...
...

View File

@ -21,5 +21,5 @@ set -ex
cd /github/grpc-web && \
bazel clean && \
bazel build \
//javascript/net/grpc/web/... \
//net/grpc/gateway/examples/...
//javascript/net/grpc/web/generator/... \
//net/grpc/gateway/examples/echo/...