diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index f386b87b583c13879b84c167feff947efbcaa755..23f90d0dc806a05ea11005fae9619ad7237232b9 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -24,7 +24,6 @@ # load("//bazel:cc_grpc_library.bzl", "cc_grpc_library") -load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") load("@upb//bazel:upb_proto_library.bzl", "upb_proto_library") load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") @@ -239,19 +238,13 @@ def grpc_cc_binary(name, srcs = [], deps = [], external_deps = [], args = [], da ) def grpc_generate_one_off_targets(): - apple_resource_bundle( - # The choice of name is signicant here, since it determines the bundle name. - name = "gRPCCertificates", - resources = ["etc/roots.pem"], - ) - # In open-source, grpc_objc* libraries depend directly on //:grpc native.alias( name = "grpc_objc", actual = "//:grpc", ) -def grpc_objc_use_cronet_config(): +def grpc_generate_objc_one_off_targets(): pass def grpc_sh_test(name, srcs, args = [], data = []): @@ -296,7 +289,7 @@ def grpc_package(name, visibility = "private", features = []): def grpc_objc_library( name, - srcs, + srcs = [], hdrs = [], textual_hdrs = [], data = [], diff --git a/gRPC-ProtoRPC.podspec b/gRPC-ProtoRPC.podspec index 57dac5a10e52d240a81563f87bdff2dd6818a5e1..4c5ebb36562c64cdc04f99192fa5d965b098078d 100644 --- a/gRPC-ProtoRPC.podspec +++ b/gRPC-ProtoRPC.podspec @@ -42,22 +42,40 @@ Pod::Spec.new do |s| s.module_name = name s.header_dir = name - src_dir = 'src/objective-c/ProtoRPC' + s.default_subspec = 'Main', 'Legacy', 'Legacy-Header' - s.default_subspec = 'Main' + s.subspec 'Legacy-Header' do |ss| + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.public_header_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.h" + ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.h" + end s.subspec 'Main' do |ss| - ss.header_mappings_dir = "#{src_dir}" - ss.dependency 'gRPC', version + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.dependency "#{s.name}/Legacy-Header", version + ss.dependency 'gRPC/Interface', version + ss.dependency 'Protobuf', '~> 3.0' + + ss.source_files = "src/objective-c/ProtoRPC/ProtoMethod.{h,m}", + "src/objective-c/ProtoRPC/ProtoRPC.{h,m}", + "src/objective-c/ProtoRPC/ProtoService.{h,m}" + end + + s.subspec 'Legacy' do |ss| + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.dependency "#{s.name}/Main", version + ss.dependency "#{s.name}/Legacy-Header", version + ss.dependency 'gRPC/GRPCCore', version ss.dependency 'gRPC-RxLibrary', version ss.dependency 'Protobuf', '~> 3.0' - ss.source_files = "#{src_dir}/*.{h,m}" + ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.m", + "src/objective-c/ProtoRPC/ProtoServiceLegacy.m" end # CFStream is now default. Leaving this subspec only for compatibility purpose. s.subspec 'CFStream' do |ss| - ss.dependency "#{s.name}/Main", version + ss.dependency "#{s.name}/Legacy", version end s.pod_target_xcconfig = { diff --git a/gRPC-RxLibrary.podspec b/gRPC-RxLibrary.podspec index 9666c3c1b98a139263d9a8e2795c2a9caaf1f99d..2e412cf67d69112f12f9a945f71facbda4381b11 100644 --- a/gRPC-RxLibrary.podspec +++ b/gRPC-RxLibrary.podspec @@ -42,6 +42,23 @@ Pod::Spec.new do |s| s.module_name = name s.header_dir = name + s.default_subspec = 'Interface', 'Implementation' + + src_dir = 'src/objective-c/RxLibrary' + s.subspec 'Interface' do |ss| + ss.header_mappings_dir = "#{src_dir}" + ss.source_files = "#{src_dir}/*.h" + ss.public_header_files = "#{src_dir}/*.h" + end + + s.subspec 'Implementation' do |ss| + ss.header_mappings_dir = "#{src_dir}" + ss.source_files = "#{src_dir}/*.m", "#{src_dir}/**/*.{h,m}" + ss.private_header_files = "#{src_dir}/**/*.h" + + ss.dependency "#{s.name}/Interface" + end + src_dir = 'src/objective-c/RxLibrary' s.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" s.private_header_files = "#{src_dir}/private/*.h" diff --git a/gRPC.podspec b/gRPC.podspec index 09baf1e3b4b21b5f3428afca4b4f35f96446112a..e18adcd276e8d3fca1c9b8a4a9ddeff045a1673e 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -41,13 +41,7 @@ Pod::Spec.new do |s| s.module_name = name s.header_dir = name - src_dir = 'src/objective-c/GRPCClient' - - s.dependency 'gRPC-RxLibrary', version - s.default_subspec = 'Main' - - # Certificates, to be able to establish TLS connections: - s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + s.default_subspec = 'Interface', 'GRPCCore', 'Interface-Legacy' s.pod_target_xcconfig = { # This is needed by all pods that depend on gRPC-RxLibrary: @@ -55,29 +49,103 @@ Pod::Spec.new do |s| 'CLANG_WARN_STRICT_PROTOTYPES' => 'NO', } - s.subspec 'Main' do |ss| - ss.header_mappings_dir = "#{src_dir}" - - ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" - ss.exclude_files = "#{src_dir}/GRPCCall+GID.{h,m}" - ss.private_header_files = "#{src_dir}/private/*.h", "#{src_dir}/internal/*.h" - - ss.dependency 'gRPC-Core', version + s.subspec 'Interface-Legacy' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "src/objective-c/GRPCClient/GRPCCallLegacy.h", + "src/objective-c/GRPCClient/GRPCTypes.h" + + ss.source_files = "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "src/objective-c/GRPCClient/GRPCCallLegacy.h", + "src/objective-c/GRPCClient/GRPCTypes.h" + ss.dependency "gRPC-RxLibrary/Interface", version end - # CFStream is now default. Leaving this subspec only for compatibility purpose. - s.subspec 'CFStream' do |ss| - ss.dependency "#{s.name}/Main", version + s.subspec 'Interface' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = 'src/objective-c/GRPCClient/GRPCCall.h', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.h', + 'src/objective-c/GRPCClient/GRPCCallOptions.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.h', + 'src/objective-c/GRPCClient/GRPCTransport.h', + 'src/objective-c/GRPCClient/GRPCDispatchable.h', + 'src/objective-c/GRPCClient/version.h' + + ss.source_files = 'src/objective-c/GRPCClient/GRPCCall.h', + 'src/objective-c/GRPCClient/GRPCCall.m', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.h', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.m', + 'src/objective-c/GRPCClient/GRPCCallOptions.h', + 'src/objective-c/GRPCClient/GRPCCallOptions.m', + 'src/objective-c/GRPCClient/GRPCDispatchable.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.m', + 'src/objective-c/GRPCClient/GRPCTransport.h', + 'src/objective-c/GRPCClient/GRPCTransport.m', + 'src/objective-c/GRPCClient/internal/*.h', + 'src/objective-c/GRPCClient/private/GRPCTransport+Private.h', + 'src/objective-c/GRPCClient/private/GRPCTransport+Private.m', + 'src/objective-c/GRPCClient/version.h' + + ss.dependency "#{s.name}/Interface-Legacy", version end - s.subspec 'GID' do |ss| - ss.ios.deployment_target = '7.0' + s.subspec 'GRPCCore' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.h', + 'src/objective-c/GRPCClient/GRPCCall+Tests.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.h', + 'src/objective-c/GRPCClient/internal_testing/*.h' + ss.private_header_files = 'src/objective-c/GRPCClient/private/GRPCCore/*.h' + ss.source_files = 'src/objective-c/GRPCClient/internal_testing/*.{h,m}', + 'src/objective-c/GRPCClient/private/GRPCCore/*.{h,m}', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.m', + 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.m', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.h', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.m', + 'src/objective-c/GRPCClient/GRPCCall+Tests.h', + 'src/objective-c/GRPCClient/GRPCCall+Tests.m', + 'src/objective-c/GRPCClient/GRPCCallLegacy.m' + + # Certificates, to be able to establish TLS connections: + ss.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + + ss.dependency "#{s.name}/Interface-Legacy", version + ss.dependency "#{s.name}/Interface", version + ss.dependency 'gRPC-Core', version + ss.dependency 'gRPC-RxLibrary', version + end - ss.header_mappings_dir = "#{src_dir}" + s.subspec 'GRPCCoreCronet' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' - ss.source_files = "#{src_dir}/GRPCCall+GID.{h,m}" + ss.source_files = 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.m', + 'src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/*.{h,m}' + ss.dependency "#{s.name}/GRPCCore", version + ss.dependency 'gRPC-Core/Cronet-Implementation', version + ss.dependency 'CronetFramework' + end - ss.dependency "#{s.name}/Main", version - ss.dependency 'Google/SignIn' + # CFStream is now default. Leaving this subspec only for compatibility purpose. + s.subspec 'CFStream' do |ss| + ss.dependency "#{s.name}/GRPCCore", version end end diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc index 24845ecdb06622773a41cc92be2d9b60cd250f9b..ed262308aba9123974c1b20df0408136ecb65e3a 100644 --- a/src/compiler/objective_c_generator.cc +++ b/src/compiler/objective_c_generator.cc @@ -238,19 +238,21 @@ void PrintV2Implementation(Printer* printer, const MethodDescriptor* method, } void PrintMethodImplementations(Printer* printer, - const MethodDescriptor* method) { + const MethodDescriptor* method, + const Parameters& generator_params) { map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method); PrintProtoRpcDeclarationAsPragma(printer, method, vars); - // TODO(jcanizales): Print documentation from the method. - printer->Print("// Deprecated methods.\n"); - PrintSimpleSignature(printer, method, vars); - PrintSimpleImplementation(printer, method, vars); + if (!generator_params.no_v1_compatibility) { + // TODO(jcanizales): Print documentation from the method. + PrintSimpleSignature(printer, method, vars); + PrintSimpleImplementation(printer, method, vars); - printer->Print("// Returns a not-yet-started RPC object.\n"); - PrintAdvancedSignature(printer, method, vars); - PrintAdvancedImplementation(printer, method, vars); + printer->Print("// Returns a not-yet-started RPC object.\n"); + PrintAdvancedSignature(printer, method, vars); + PrintAdvancedImplementation(printer, method, vars); + } PrintV2Signature(printer, method, vars); PrintV2Implementation(printer, method, vars); @@ -276,9 +278,12 @@ void PrintMethodImplementations(Printer* printer, return output; } -::grpc::string GetProtocol(const ServiceDescriptor* service) { +::grpc::string GetProtocol(const ServiceDescriptor* service, + const Parameters& generator_params) { ::grpc::string output; + if (generator_params.no_v1_compatibility) return output; + // Scope the output stream so it closes and finalizes output to the string. grpc::protobuf::io::StringOutputStream output_stream(&output); Printer printer(&output_stream, '$'); @@ -321,7 +326,8 @@ void PrintMethodImplementations(Printer* printer, return output; } -::grpc::string GetInterface(const ServiceDescriptor* service) { +::grpc::string GetInterface(const ServiceDescriptor* service, + const Parameters& generator_params) { ::grpc::string output; // Scope the output stream so it closes and finalizes output to the string. @@ -338,7 +344,11 @@ void PrintMethodImplementations(Printer* printer, " */\n"); printer.Print(vars, "@interface $service_class$ :" - " GRPCProtoService<$service_class$, $service_class$2>\n"); + " GRPCProtoService<$service_class$2"); + if (!generator_params.no_v1_compatibility) { + printer.Print(vars, ", $service_class$"); + } + printer.Print(">\n"); printer.Print( "- (instancetype)initWithHost:(NSString *)host " "callOptions:(GRPCCallOptions " @@ -347,17 +357,20 @@ void PrintMethodImplementations(Printer* printer, printer.Print( "+ (instancetype)serviceWithHost:(NSString *)host " "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n"); - printer.Print( - "// The following methods belong to a set of old APIs that have been " - "deprecated.\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host;\n"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); + if (!generator_params.no_v1_compatibility) { + printer.Print( + "// The following methods belong to a set of old APIs that have been " + "deprecated.\n"); + printer.Print("- (instancetype)initWithHost:(NSString *)host;\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); + } printer.Print("@end\n"); return output; } -::grpc::string GetSource(const ServiceDescriptor* service) { +::grpc::string GetSource(const ServiceDescriptor* service, + const Parameters& generator_params) { ::grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -381,22 +394,28 @@ void PrintMethodImplementations(Printer* printer, " packageName:@\"$package$\"\n" " serviceName:@\"$service_name$\"\n" " callOptions:callOptions];\n" - "}\n\n" - "- (instancetype)initWithHost:(NSString *)host {\n" - " return [super initWithHost:host\n" - " packageName:@\"$package$\"\n" - " serviceName:@\"$service_name$\"];\n" - "}\n\n" - "#pragma clang diagnostic pop\n\n"); - + "}\n\n"); + if (!generator_params.no_v1_compatibility) { + printer.Print(vars, + "- (instancetype)initWithHost:(NSString *)host {\n" + " return [super initWithHost:host\n" + " packageName:@\"$package$\"\n" + " serviceName:@\"$service_name$\"];\n" + "}\n\n"); + } + printer.Print("#pragma clang diagnostic pop\n\n"); + + if (!generator_params.no_v1_compatibility) { + printer.Print( + "// Override superclass initializer to disallow different" + " package and service names.\n" + "- (instancetype)initWithHost:(NSString *)host\n" + " packageName:(NSString *)packageName\n" + " serviceName:(NSString *)serviceName {\n" + " return [self initWithHost:host];\n" + "}\n\n"); + } printer.Print( - "// Override superclass initializer to disallow different" - " package and service names.\n" - "- (instancetype)initWithHost:(NSString *)host\n" - " packageName:(NSString *)packageName\n" - " serviceName:(NSString *)serviceName {\n" - " return [self initWithHost:host];\n" - "}\n\n" "- (instancetype)initWithHost:(NSString *)host\n" " packageName:(NSString *)packageName\n" " serviceName:(NSString *)serviceName\n" @@ -404,11 +423,14 @@ void PrintMethodImplementations(Printer* printer, " return [self initWithHost:host callOptions:callOptions];\n" "}\n\n"); + printer.Print("#pragma mark - Class Methods\n\n"); + if (!generator_params.no_v1_compatibility) { + printer.Print( + "+ (instancetype)serviceWithHost:(NSString *)host {\n" + " return [[self alloc] initWithHost:host];\n" + "}\n\n"); + } printer.Print( - "#pragma mark - Class Methods\n\n" - "+ (instancetype)serviceWithHost:(NSString *)host {\n" - " return [[self alloc] initWithHost:host];\n" - "}\n\n" "+ (instancetype)serviceWithHost:(NSString *)host " "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n" " return [[self alloc] initWithHost:host callOptions:callOptions];\n" @@ -417,7 +439,8 @@ void PrintMethodImplementations(Printer* printer, printer.Print("#pragma mark - Method Implementations\n\n"); for (int i = 0; i < service->method_count(); i++) { - PrintMethodImplementations(&printer, service->method(i)); + PrintMethodImplementations(&printer, service->method(i), + generator_params); } printer.Print("@end\n"); diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h index c171e5bf772daa67752f093e2b890adc1b2b0952..518962fceee26ab928ed1dc6385ab8b2435662af 100644 --- a/src/compiler/objective_c_generator.h +++ b/src/compiler/objective_c_generator.h @@ -23,6 +23,11 @@ namespace grpc_objective_c_generator { +struct Parameters { + // Do not generate V1 interface and implementation + bool no_v1_compatibility; +}; + using ::grpc::protobuf::FileDescriptor; using ::grpc::protobuf::FileDescriptor; using ::grpc::protobuf::ServiceDescriptor; @@ -34,7 +39,8 @@ string GetAllMessageClasses(const FileDescriptor* file); // Returns the content to be included defining the @protocol segment at the // insertion point of the generated implementation file. This interface is // legacy and for backwards compatibility. -string GetProtocol(const ServiceDescriptor* service); +string GetProtocol(const ServiceDescriptor* service, + const Parameters& generator_params); // Returns the content to be included defining the @protocol segment at the // insertion point of the generated implementation file. @@ -42,11 +48,13 @@ string GetV2Protocol(const ServiceDescriptor* service); // Returns the content to be included defining the @interface segment at the // insertion point of the generated implementation file. -string GetInterface(const ServiceDescriptor* service); +string GetInterface(const ServiceDescriptor* service, + const Parameters& generator_params); // Returns the content to be included in the "global_scope" insertion point of // the generated implementation file. -string GetSource(const ServiceDescriptor* service); +string GetSource(const ServiceDescriptor* service, + const Parameters& generator_params); } // namespace grpc_objective_c_generator diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc index f398033f6db4c8ea44d1d04214f84f91980faecc..a08064a08bd60b44be64978e23434b0a965531b9 100644 --- a/src/compiler/objective_c_plugin.cc +++ b/src/compiler/objective_c_plugin.cc @@ -111,6 +111,22 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { ::grpc::string file_name = google::protobuf::compiler::objectivec::FilePath(file); + grpc_objective_c_generator::Parameters generator_params; + generator_params.no_v1_compatibility = false; + + if (!parameter.empty()) { + std::vector<grpc::string> parameters_list = + grpc_generator::tokenize(parameter, ","); + for (auto parameter_string = parameters_list.begin(); + parameter_string != parameters_list.end(); parameter_string++) { + std::vector<grpc::string> param = + grpc_generator::tokenize(*parameter_string, "="); + if (param[0] == "no_v1_compatibility") { + generator_params.no_v1_compatibility = true; + } + } + } + { // Generate .pbrpc.h @@ -121,18 +137,25 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { imports = FrameworkImport(file_name + ".pbobjc.h", framework); } - ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") + - SystemImport("ProtoRPC/ProtoRPC.h") + - SystemImport("RxLibrary/GRXWriteable.h") + - SystemImport("RxLibrary/GRXWriter.h"); + ::grpc::string system_imports = + SystemImport("ProtoRPC/ProtoService.h") + + (generator_params.no_v1_compatibility + ? SystemImport("ProtoRPC/ProtoRPC.h") + : SystemImport("ProtoRPC/ProtoRPCLegacy.h")); + if (!generator_params.no_v1_compatibility) { + system_imports += SystemImport("RxLibrary/GRXWriteable.h") + + SystemImport("RxLibrary/GRXWriter.h"); + } ::grpc::string forward_declarations = - "@class GRPCProtoCall;\n" "@class GRPCUnaryProtoCall;\n" "@class GRPCStreamingProtoCall;\n" "@class GRPCCallOptions;\n" - "@protocol GRPCProtoResponseHandler;\n" - "\n"; + "@protocol GRPCProtoResponseHandler;\n"; + if (!generator_params.no_v1_compatibility) { + forward_declarations += "@class GRPCProtoCall;\n"; + } + forward_declarations += "\n"; ::grpc::string class_declarations = grpc_objective_c_generator::GetAllMessageClasses(file); @@ -152,13 +175,15 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { ::grpc::string protocols; for (int i = 0; i < file->service_count(); i++) { const grpc::protobuf::ServiceDescriptor* service = file->service(i); - protocols += grpc_objective_c_generator::GetProtocol(service); + protocols += + grpc_objective_c_generator::GetProtocol(service, generator_params); } ::grpc::string interfaces; for (int i = 0; i < file->service_count(); i++) { const grpc::protobuf::ServiceDescriptor* service = file->service(i); - interfaces += grpc_objective_c_generator::GetInterface(service); + interfaces += + grpc_objective_c_generator::GetInterface(service, generator_params); } Write(context, file_name + ".pbrpc.h", @@ -178,14 +203,16 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { ::grpc::string imports; if (framework.empty()) { imports = LocalImport(file_name + ".pbrpc.h") + - LocalImport(file_name + ".pbobjc.h") + - SystemImport("ProtoRPC/ProtoRPC.h") + - SystemImport("RxLibrary/GRXWriter+Immediate.h"); + LocalImport(file_name + ".pbobjc.h"); } else { imports = FrameworkImport(file_name + ".pbrpc.h", framework) + - FrameworkImport(file_name + ".pbobjc.h", framework) + - SystemImport("ProtoRPC/ProtoRPC.h") + - SystemImport("RxLibrary/GRXWriter+Immediate.h"); + FrameworkImport(file_name + ".pbobjc.h", framework); + } + imports += (generator_params.no_v1_compatibility + ? SystemImport("ProtoRPC/ProtoRPC.h") + : SystemImport("ProtoRPC/ProtoRPCLegacy.h")); + if (!generator_params.no_v1_compatibility) { + imports += SystemImport("RxLibrary/GRXWriter+Immediate.h"); } ::grpc::string class_imports; @@ -196,7 +223,8 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { ::grpc::string definitions; for (int i = 0; i < file->service_count(); i++) { const grpc::protobuf::ServiceDescriptor* service = file->service(i); - definitions += grpc_objective_c_generator::GetSource(service); + definitions += + grpc_objective_c_generator::GetSource(service, generator_params); } Write(context, file_name + ".pbrpc.m", diff --git a/src/objective-c/BUILD b/src/objective-c/BUILD index 3a71086f0f60f9efa2189733f77aeb7d386a1fe6..5f53486d17eeb1040ceaec6a0844ed7975d6ebb8 100644 --- a/src/objective-c/BUILD +++ b/src/objective-c/BUILD @@ -18,24 +18,30 @@ licenses(["notice"]) # Apache v2 package(default_visibility = ["//visibility:public"]) -load("//bazel:grpc_build_system.bzl", "grpc_objc_library", "grpc_objc_use_cronet_config") +load("//bazel:grpc_build_system.bzl", "grpc_objc_library", "grpc_generate_objc_one_off_targets") exports_files(["LICENSE"]) -grpc_objc_use_cronet_config() +grpc_generate_objc_one_off_targets() + +grpc_objc_library( + name = "rx_library_headers", + hdrs = glob([ + "RxLibrary/*.h", + ]), + includes = ["."], +) grpc_objc_library( name = "rx_library", srcs = glob([ "RxLibrary/*.m", - "RxLibrary/transformations/*.m", - ]), - hdrs = glob([ - "RxLibrary/*.h", - "RxLibrary/transformations/*.h", ]), includes = ["."], - deps = [":rx_library_private"], + deps = [ + ":rx_library_headers", + ":rx_library_private", + ], ) grpc_objc_library( @@ -50,84 +56,196 @@ grpc_objc_library( ) grpc_objc_library( - name = "grpc_objc_client", - srcs = glob( - [ - "GRPCClient/*.m", - "GRPCClient/private/*.m", - ], - exclude = ["GRPCClient/GRPCCall+GID.m"], - ), - hdrs = glob( - [ - "GRPCClient/*.h", - "GRPCClient/internal/*.h", - ], - exclude = ["GRPCClient/GRPCCall+GID.h"], - ), - data = ["//:gRPCCertificates"], + name = "grpc_objc_interface_legacy", + hdrs = [ + "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "GRPCClient/GRPCCallLegacy.h", + "GRPCClient/GRPCTypes.h", + ], + deps = [ + "rx_library_headers", + ], +) + +grpc_objc_library( + name = "grpc_objc_interface", + hdrs = [ + "GRPCClient/GRPCCall.h", + "GRPCClient/GRPCCall+Interceptor.h", + "GRPCClient/GRPCCallOptions.h", + "GRPCClient/GRPCInterceptor.h", + "GRPCClient/GRPCTransport.h", + "GRPCClient/GRPCDispatchable.h", + "GRPCClient/internal/GRPCCallOptions+Internal.h", + "GRPCClient/version.h", + ], + srcs = [ + "GRPCClient/GRPCCall.m", + "GRPCClient/GRPCCall+Interceptor.m", + "GRPCClient/GRPCCallOptions.m", + "GRPCClient/GRPCInterceptor.m", + "GRPCClient/GRPCTransport.m", + "GRPCClient/private/GRPCTransport+Private.m", + ], includes = ["."], - textual_hdrs = glob([ - "GRPCClient/private/*.h", - ]), + textual_hdrs = [ + "GRPCClient/private/GRPCTransport+Private.h", + ], deps = [ + ":grpc_objc_interface_legacy", + ], +) + +grpc_objc_library( + name = "grpc_objc_client_core", + hdrs = [ + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "GRPCClient/GRPCCall+ChannelArg.h", + ], + textual_hdrs = glob(["GRPCClient/private/GRPCCore/*.h"]), + srcs = [ + "GRPCClient/GRPCCall+ChannelArg.m", + "GRPCClient/GRPCCall+ChannelCredentials.m", + "GRPCClient/GRPCCall+Cronet.m", + "GRPCClient/GRPCCall+OAuth2.m", + "GRPCClient/GRPCCall+Tests.m", + "GRPCClient/GRPCCallLegacy.m", + ] + glob(["GRPCClient/private/GRPCCore/*.m"]), + data = [":gRPCCertificates"], + includes = ["."], + deps = [ + ":grpc_objc_interface", + ":grpc_objc_interface_legacy", ":rx_library", "//:grpc_objc", ], ) +alias( + name = "grpc_objc_client", + actual = "grpc_objc_client_core", +) + +grpc_objc_library( + name = "proto_objc_rpc_legacy_header", + hdrs = [ + "ProtoRPC/ProtoRPCLegacy.h", + ], + includes = ["."], +) + grpc_objc_library( - name = "proto_objc_rpc", - srcs = glob( - ["ProtoRPC/*.m"], - ), - hdrs = glob( - ["ProtoRPC/*.h"], - ), - # Different from Cocoapods, do not import as if @com_google_protobuf//:protobuf_objc is a framework, - # use the real paths of @com_google_protobuf//:protobuf_objc instead + name = "proto_objc_rpc_v2", + srcs = [ + "ProtoRPC/ProtoMethod.m", + "ProtoRPC/ProtoRPC.m", + "ProtoRPC/ProtoService.m", + ], + hdrs = [ + "ProtoRPC/ProtoMethod.h", + "ProtoRPC/ProtoRPC.h", + "ProtoRPC/ProtoService.h", + ], + includes = ["."], defines = ["GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=0"], deps = [ - ":grpc_objc_client", + ":grpc_objc_interface", + ":proto_objc_rpc_legacy_header", + "@com_google_protobuf//:protobuf_objc", + ], +) + +grpc_objc_library( + name = "proto_objc_rpc", + srcs = [ + "ProtoRPC/ProtoRPCLegacy.m", + "ProtoRPC/ProtoServiceLegacy.m", + ], + hdrs = [ + "ProtoRPC/ProtoMethod.h", + "ProtoRPC/ProtoRPCLegacy.h", + "ProtoRPC/ProtoService.h", + ], + deps = [ ":rx_library", + ":proto_objc_rpc_v2", + ":proto_objc_rpc_legacy_header", + ":grpc_objc_client_core", "@com_google_protobuf//:protobuf_objc", ], ) +load("@build_bazel_rules_apple//apple:resources.bzl", "apple_resource_bundle") + +apple_resource_bundle( + # The choice of name is signicant here, since it determines the bundle name. + name = "gRPCCertificates", + resources = ["//:etc/roots.pem"], +) + +# Internal target combining grpc_objc_client_core and proto_objc_rpc for testing grpc_objc_library( - name = "grpc_objc_client_internal_testing", - srcs = glob( - [ - "GRPCClient/*.m", - "GRPCClient/private/*.m", - "GRPCClient/internal_testing/*.m", - "ProtoRPC/*.m", - ], - exclude = ["GRPCClient/GRPCCall+GID.m"], - ), - hdrs = glob( - [ - "GRPCClient/*.h", - "GRPCClient/internal/*.h", - "GRPCClient/internal_testing/*.h", - "ProtoRPC/*.h", - ], - exclude = ["GRPCClient/GRPCCall+GID.h"], - ), + name = "grpc_objc_client_core_internal_testing", + hdrs = [ + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/internal_testing/GRPCCall+InternalTests.h", + ], + textual_hdrs = glob(["GRPCClient/private/GRPCCore/*.h"]), + srcs = [ + "GRPCClient/GRPCCall+ChannelArg.m", + "GRPCClient/GRPCCall+ChannelCredentials.m", + "GRPCClient/GRPCCall+Cronet.m", + "GRPCClient/GRPCCall+OAuth2.m", + "GRPCClient/GRPCCall+Tests.m", + "GRPCClient/GRPCCallLegacy.m", + "GRPCClient/internal_testing/GRPCCall+InternalTests.m", + ] + glob(["GRPCClient/private/GRPCCore/*.m"]), + data = [":gRPCCertificates"], includes = ["."], - data = ["//:gRPCCertificates"], defines = [ "GRPC_TEST_OBJC=1", - "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=0", ], - textual_hdrs = glob( - [ - "GRPCClient/private/*.h", - ], - ), deps = [ + ":grpc_objc_interface", + ":grpc_objc_interface_legacy", ":rx_library", "//:grpc_objc", + ], +) + +grpc_objc_library( + name = "proto_objc_rpc_internal_testing", + srcs = [ + "ProtoRPC/ProtoRPCLegacy.m", + "ProtoRPC/ProtoServiceLegacy.m", + ], + hdrs = [ + "ProtoRPC/ProtoMethod.h", + "ProtoRPC/ProtoRPC.h", + "ProtoRPC/ProtoRPCLegacy.h", + "ProtoRPC/ProtoService.h", + ], + deps = [ + ":rx_library", + ":proto_objc_rpc_v2", + ":proto_objc_rpc_legacy_header", + ":grpc_objc_client_core_internal_testing", "@com_google_protobuf//:protobuf_objc", ], ) + +alias( + name = "grpc_objc_client_internal_testing", + actual = "proto_objc_rpc_internal_testing", +) diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h index 2ddd53a5c6695257d9504be01fd880ebedf51482..ff5a153a0f65ecd3dd4eb20f4a0be2bfce620da8 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h @@ -15,7 +15,7 @@ * limitations under the License. * */ -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" #include <AvailabilityMacros.h> diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m index ae60d6208e11c42492b00c599c6c6b323fa7059a..aae1b740c71b6c3d6fcb510e36f587e88b9a3863 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m @@ -18,8 +18,8 @@ #import "GRPCCall+ChannelArg.h" -#import "private/GRPCChannelPool.h" -#import "private/GRPCHost.h" +#import "private/GRPCCore/GRPCChannelPool.h" +#import "private/GRPCCore/GRPCHost.h" #import <grpc/impl/codegen/compression_types.h> diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h index 7d6f79b7655ed4bf1296fd6cf37cc883eacf7254..c3d194bff9451f870e2f1ee94e353c9a88ccdc00 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h @@ -16,7 +16,7 @@ * */ -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" // Deprecated interface. Please use GRPCCallOptions instead. @interface GRPCCall (ChannelCredentials) diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m index 2689ec2effb22e5184a2bb221dcc2034ba0ca7d1..aa97ca82bf8de2e1cc497084071cb0c26dc49746 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m @@ -18,7 +18,7 @@ #import "GRPCCall+ChannelCredentials.h" -#import "private/GRPCHost.h" +#import "private/GRPCCore/GRPCHost.h" @implementation GRPCCall (ChannelCredentials) diff --git a/src/objective-c/GRPCClient/GRPCCall+Cronet.h b/src/objective-c/GRPCClient/GRPCCall+Cronet.h index 3059c6f18600e5bae74752283de1008e4d01d921..d107ada367258759c0c5f05823dd667aca75a3a4 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Cronet.h +++ b/src/objective-c/GRPCClient/GRPCCall+Cronet.h @@ -15,12 +15,16 @@ * limitations under the License. * */ -#ifdef GRPC_COMPILE_WITH_CRONET -#import <Cronet/Cronet.h> -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" +#import "GRPCTypes.h" -// Deprecated interface. Please use GRPCCallOptions instead. +typedef struct stream_engine stream_engine; + +// Transport id for Cronet transport +extern const GRPCTransportId gGRPCCoreCronetId; + +// Deprecated class. Please use the gGRPCCoreCronetId with GRPCCallOptions.transport instead. @interface GRPCCall (Cronet) + (void)useCronetWithEngine:(stream_engine*)engine; @@ -28,4 +32,3 @@ + (BOOL)isUsingCronet; @end -#endif diff --git a/src/objective-c/GRPCClient/GRPCCall+Cronet.m b/src/objective-c/GRPCClient/GRPCCall+Cronet.m index ba8d2c23db8d86a9c3700996a6d00d97603edcd0..a732208e1f645f1738e4576030345374f00511cd 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Cronet.m +++ b/src/objective-c/GRPCClient/GRPCCall+Cronet.m @@ -18,7 +18,8 @@ #import "GRPCCall+Cronet.h" -#ifdef GRPC_COMPILE_WITH_CRONET +const GRPCTransportId gGRPCCoreCronetId = "io.grpc.transport.core.cronet"; + static BOOL useCronet = NO; static stream_engine *globalCronetEngine; @@ -38,4 +39,3 @@ static stream_engine *globalCronetEngine; } @end -#endif diff --git a/src/objective-c/GRPCClient/GRPCCall+GID.h b/src/objective-c/GRPCClient/GRPCCall+GID.h index 8293e92ebe9ec848a69c4b1b4f2e5af24f10e20f..80e34ea98da86cf7df0853a4b31844a509f3ceb7 100644 --- a/src/objective-c/GRPCClient/GRPCCall+GID.h +++ b/src/objective-c/GRPCClient/GRPCCall+GID.h @@ -17,7 +17,7 @@ */ #import "GRPCCall+OAuth2.h" -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" #import <Google/SignIn.h> diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h index 60cdc50bfda10ba28ed1b18066e83db41b729991..cf60c794f270fe8e1fae0cbcd27853bcd72c6126 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -16,9 +16,9 @@ * */ -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" -#import "GRPCCallOptions.h" +@protocol GRPCAuthorizationProtocol; // Deprecated interface. Please use GRPCCallOptions instead. @interface GRPCCall (OAuth2) diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h index edaa5ed582c6a897fbcfe20a91c0ab847ef95fc0..af81eec6b8279e7612d392bd691d4f5f0423e48c 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Tests.h +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h @@ -16,7 +16,7 @@ * */ -#import "GRPCCall.h" +#import "GRPCCallLegacy.h" // Deprecated interface. Please use GRPCCallOptions instead. @interface GRPCCall (Tests) diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m index 20f5cba4178073e770397653748dd5bcc2151a36..3a1dff388681f6ff6cae4820694f26a49d11d24d 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Tests.m +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m @@ -18,7 +18,7 @@ #import "GRPCCall+Tests.h" -#import "private/GRPCHost.h" +#import "private/GRPCCore/GRPCHost.h" #import "GRPCCallOptions.h" diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index d02ec60172768f4e5cea7a8ce17c7df3aac47289..1c7a10048cf0d863d8629f5083e8354a701cb281 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -33,134 +33,19 @@ */ #import <Foundation/Foundation.h> -#import <RxLibrary/GRXWriter.h> -#include <AvailabilityMacros.h> +#import "GRPCCallOptions.h" +#import "GRPCDispatchable.h" +#import "GRPCTypes.h" -#include "GRPCCallOptions.h" +// The legacy header is included for backwards compatibility. Some V1 API users are still using +// GRPCCall by importing GRPCCall.h header so we need this import. +#import "GRPCCallLegacy.h" NS_ASSUME_NONNULL_BEGIN -#pragma mark gRPC errors - -/** Domain of NSError objects produced by gRPC. */ -extern NSString *const kGRPCErrorDomain; - -/** - * gRPC error codes. - * Note that a few of these are never produced by the gRPC libraries, but are of general utility for - * server applications to produce. - */ -typedef NS_ENUM(NSUInteger, GRPCErrorCode) { - /** The operation was cancelled (typically by the caller). */ - GRPCErrorCodeCancelled = 1, - - /** - * Unknown error. Errors raised by APIs that do not return enough error information may be - * converted to this error. - */ - GRPCErrorCodeUnknown = 2, - - /** - * The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. - * INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the - * server (e.g., a malformed file name). - */ - GRPCErrorCodeInvalidArgument = 3, - - /** - * Deadline expired before operation could complete. For operations that change the state of the - * server, this error may be returned even if the operation has completed successfully. For - * example, a successful response from the server could have been delayed long enough for the - * deadline to expire. - */ - GRPCErrorCodeDeadlineExceeded = 4, - - /** Some requested entity (e.g., file or directory) was not found. */ - GRPCErrorCodeNotFound = 5, - - /** Some entity that we attempted to create (e.g., file or directory) already exists. */ - GRPCErrorCodeAlreadyExists = 6, - - /** - * The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't - * used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for - * those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller - * (UNAUTHENTICATED is used instead for those errors). - */ - GRPCErrorCodePermissionDenied = 7, - - /** - * The request does not have valid authentication credentials for the operation (e.g. the caller's - * identity can't be verified). - */ - GRPCErrorCodeUnauthenticated = 16, - - /** Some resource has been exhausted, perhaps a per-user quota. */ - GRPCErrorCodeResourceExhausted = 8, - - /** - * The RPC was rejected because the server is not in a state required for the procedure's - * execution. For example, a directory to be deleted may be non-empty, etc. - * The client should not retry until the server state has been explicitly fixed (e.g. by - * performing another RPC). The details depend on the service being called, and should be found in - * the NSError's userInfo. - */ - GRPCErrorCodeFailedPrecondition = 9, - - /** - * The RPC was aborted, typically due to a concurrency issue like sequencer check failures, - * transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read- - * modify-write sequence). - */ - GRPCErrorCodeAborted = 10, - - /** - * The RPC was attempted past the valid range. E.g., enumerating past the end of a list. - * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state - * changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked - * to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return - * the element at an index past the current size of the list. - */ - GRPCErrorCodeOutOfRange = 11, - - /** The procedure is not implemented or not supported/enabled in this server. */ - GRPCErrorCodeUnimplemented = 12, - - /** - * Internal error. Means some invariant expected by the server application or the gRPC library has - * been broken. - */ - GRPCErrorCodeInternal = 13, - - /** - * The server is currently unavailable. This is most likely a transient condition and may be - * corrected by retrying with a backoff. Note that it is not always safe to retry - * non-idempotent operations. - */ - GRPCErrorCodeUnavailable = 14, - - /** Unrecoverable data loss or corruption. */ - GRPCErrorCodeDataLoss = 15, -}; - -/** - * Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by - * the server. - */ -extern NSString *const kGRPCHeadersKey; -extern NSString *const kGRPCTrailersKey; - /** An object can implement this protocol to receive responses from server from a call. */ -@protocol GRPCResponseHandler<NSObject> - -@required - -/** - * All the responses must be issued to a user-provided dispatch queue. This property specifies the - * dispatch queue to be used for issuing the notifications. - */ -@property(atomic, readonly) dispatch_queue_t dispatchQueue; +@protocol GRPCResponseHandler<NSObject, GRPCDispatchable> @optional @@ -302,114 +187,3 @@ extern NSString *const kGRPCTrailersKey; @end NS_ASSUME_NONNULL_END - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnullability-completeness" - -/** - * This interface is deprecated. Please use \a GRPCcall2. - * - * Represents a single gRPC remote call. - */ -@interface GRPCCall : GRXWriter - -- (instancetype)init NS_UNAVAILABLE; - -/** - * The container of the request headers of an RPC conforms to this protocol, which is a subset of - * NSMutableDictionary's interface. It will become a NSMutableDictionary later on. - * The keys of this container are the header names, which per the HTTP standard are case- - * insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and - * can only consist of ASCII characters. - * A header value is a NSString object (with only ASCII characters), unless the header name has the - * suffix "-bin", in which case the value has to be a NSData object. - */ -/** - * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a - * name-value pair with string names and either string or binary values. - * - * The passed dictionary has to use NSString keys, corresponding to the header names. The value - * associated to each can be a NSString object or a NSData object. E.g.: - * - * call.requestHeaders = @{@"authorization": @"Bearer ..."}; - * - * call.requestHeaders[@"my-header-bin"] = someData; - * - * After the call is started, trying to modify this property is an error. - * - * The property is initialized to an empty NSMutableDictionary. - */ -@property(atomic, readonly) NSMutableDictionary *requestHeaders; - -/** - * This dictionary is populated with the HTTP headers received from the server. This happens before - * any response message is received from the server. It has the same structure as the request - * headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a - * NSData value; the others have a NSString value. - * - * The value of this property is nil until all response headers are received, and will change before - * any of -writeValue: or -writesFinishedWithError: are sent to the writeable. - */ -@property(atomic, readonly) NSDictionary *responseHeaders; - -/** - * Same as responseHeaders, but populated with the HTTP trailers received from the server before the - * call finishes. - * - * The value of this property is nil until all response trailers are received, and will change - * before -writesFinishedWithError: is sent to the writeable. - */ -@property(atomic, readonly) NSDictionary *responseTrailers; - -/** - * The request writer has to write NSData objects into the provided Writeable. The server will - * receive each of those separately and in order as distinct messages. - * A gRPC call might not complete until the request writer finishes. On the other hand, the request - * finishing doesn't necessarily make the call to finish, as the server might continue sending - * messages to the response side of the call indefinitely (depending on the semantics of the - * specific remote method called). - * To finish a call right away, invoke cancel. - * host parameter should not contain the scheme (http:// or https://), only the name or IP addr - * and the port number, for example @"localhost:5050". - */ -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - requestsWriter:(GRXWriter *)requestWriter; - -/** - * Finishes the request side of this call, notifies the server that the RPC should be cancelled, and - * finishes the response side of the call with an error of code CANCELED. - */ -- (void)cancel; - -/** - * The following methods are deprecated. - */ -+ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path; -@property(atomic, copy, readwrite) NSString *serverName; -@property NSTimeInterval timeout; -- (void)setResponseDispatchQueue:(dispatch_queue_t)queue; - -@end - -#pragma mark Backwards compatibiity - -/** This protocol is kept for backwards compatibility with existing code. */ -DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") -@protocol GRPCRequestHeaders<NSObject> -@property(nonatomic, readonly) NSUInteger count; - -- (id)objectForKeyedSubscript:(id)key; -- (void)setObject:(id)obj forKeyedSubscript:(id)key; - -- (void)removeAllObjects; -- (void)removeObjectForKey:(id)key; -@end - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" -/** This is only needed for backwards-compatibility. */ -@interface NSMutableDictionary (GRPCRequestHeaders)<GRPCRequestHeaders> -@end -#pragma clang diagnostic pop -#pragma clang diagnostic pop diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index cd293cc8e59c0bacb6b66336ed132d56a15d4f0f..73ee530ef2c4373a535c3d6adc894119424ea834 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -17,56 +17,86 @@ */ #import "GRPCCall.h" + #import "GRPCCall+Interceptor.h" -#import "GRPCCall+OAuth2.h" #import "GRPCCallOptions.h" #import "GRPCInterceptor.h" -#import <RxLibrary/GRXBufferedPipe.h> -#import <RxLibrary/GRXConcurrentWriteable.h> -#import <RxLibrary/GRXImmediateSingleWriter.h> -#import <RxLibrary/GRXWriter+Immediate.h> -#include <grpc/grpc.h> -#include <grpc/support/time.h> - -#import "private/GRPCCall+V2API.h" -#import "private/GRPCCallInternal.h" -#import "private/GRPCChannelPool.h" -#import "private/GRPCCompletionQueue.h" -#import "private/GRPCHost.h" -#import "private/GRPCRequestHeaders.h" -#import "private/GRPCWrappedCall.h" -#import "private/NSData+GRPC.h" -#import "private/NSDictionary+GRPC.h" -#import "private/NSError+GRPC.h" - -// At most 6 ops can be in an op batch for a client: SEND_INITIAL_METADATA, -// SEND_MESSAGE, SEND_CLOSE_FROM_CLIENT, RECV_INITIAL_METADATA, RECV_MESSAGE, -// and RECV_STATUS_ON_CLIENT. -NSInteger kMaxClientBatch = 6; +#import "private/GRPCTransport+Private.h" NSString *const kGRPCHeadersKey = @"io.grpc.HeadersKey"; NSString *const kGRPCTrailersKey = @"io.grpc.TrailersKey"; -static NSMutableDictionary *callFlags; -static NSString *const kAuthorizationHeader = @"authorization"; -static NSString *const kBearerPrefix = @"Bearer "; +NSString *const kGRPCErrorDomain = @"io.grpc"; + +/** + * The response dispatcher creates its own serial dispatch queue and target the queue to the + * dispatch queue of a user provided response handler. It removes the requirement of having to use + * serial dispatch queue in the user provided response handler. + */ +@interface GRPCResponseDispatcher : NSObject<GRPCResponseHandler> + +- (nullable instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler; + +@end -const char *kCFStreamVarName = "grpc_cfstream"; +@implementation GRPCResponseDispatcher { + id<GRPCResponseHandler> _responseHandler; + dispatch_queue_t _dispatchQueue; +} -@interface GRPCCall ()<GRXWriteable> -// Make them read-write. -@property(atomic, strong) NSDictionary *responseHeaders; -@property(atomic, strong) NSDictionary *responseTrailers; +- (instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler { + if ((self = [super init])) { + _responseHandler = responseHandler; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + if (@available(iOS 8.0, macOS 10.10, *)) { + _dispatchQueue = dispatch_queue_create( + NULL, + dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); + } else { +#else + { +#endif + _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + } + dispatch_set_target_queue(_dispatchQueue, _responseHandler.dispatchQueue); + } -- (void)receiveNextMessages:(NSUInteger)numberOfMessages; + return self; +} + +- (dispatch_queue_t)dispatchQueue { + return _dispatchQueue; +} + +- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata { + if ([_responseHandler respondsToSelector:@selector(didReceiveInitialMetadata:)]) { + [_responseHandler didReceiveInitialMetadata:initialMetadata]; + } +} + +- (void)didReceiveData:(id)data { + // For backwards compatibility with didReceiveRawMessage, if the user provided a response handler + // that handles didReceiveRawMesssage, we issue to that method instead + if ([_responseHandler respondsToSelector:@selector(didReceiveRawMessage:)]) { + [_responseHandler didReceiveRawMessage:data]; + } else if ([_responseHandler respondsToSelector:@selector(didReceiveData:)]) { + [_responseHandler didReceiveData:data]; + } +} + +- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata + error:(nullable NSError *)error { + if ([_responseHandler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { + [_responseHandler didCloseWithTrailingMetadata:trailingMetadata error:error]; + } +} -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - callSafety:(GRPCCallSafety)safety - requestsWriter:(GRXWriter *)requestsWriter - callOptions:(GRPCCallOptions *)callOptions - writeDone:(void (^)(void))writeDone; +- (void)didWriteData { + if ([_responseHandler respondsToSelector:@selector(didWriteData)]) { + [_responseHandler didWriteData]; + } +} @end @@ -140,54 +170,39 @@ const char *kCFStreamVarName = "grpc_cfstream"; } _responseHandler = responseHandler; - // Initialize the interceptor chain - - // First initialize the internal call - GRPCCall2Internal *internalCall = [[GRPCCall2Internal alloc] init]; - id<GRPCInterceptorInterface> nextInterceptor = internalCall; - GRPCInterceptorManager *nextManager = nil; - - // Then initialize the global interceptor, if applicable + GRPCResponseDispatcher *dispatcher = + [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler]; + NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories; + if (_actualCallOptions.interceptorFactories != nil) { + interceptorFactories = + [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories]; + } else { + interceptorFactories = [NSMutableArray array]; + } id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory]; - if (globalInterceptorFactory) { - GRPCInterceptorManager *manager = - [[GRPCInterceptorManager alloc] initWithNextInterceptor:nextInterceptor]; - GRPCInterceptor *interceptor = - [globalInterceptorFactory createInterceptorWithManager:manager]; - if (interceptor != nil) { - [internalCall setResponseHandler:interceptor]; - nextInterceptor = interceptor; - nextManager = manager; - } + if (globalInterceptorFactory != nil) { + [interceptorFactories addObject:globalInterceptorFactory]; } - - // Finally initialize the interceptors in the chain - NSArray *interceptorFactories = _actualCallOptions.interceptorFactories; - for (int i = (int)interceptorFactories.count - 1; i >= 0; i--) { - GRPCInterceptorManager *manager = - [[GRPCInterceptorManager alloc] initWithNextInterceptor:nextInterceptor]; - GRPCInterceptor *interceptor = [interceptorFactories[i] createInterceptorWithManager:manager]; - NSAssert(interceptor != nil, @"Failed to create interceptor from factory: %@", - interceptorFactories[i]); - if (interceptor == nil) { - NSLog(@"Failed to create interceptor from factory: %@", interceptorFactories[i]); - continue; - } - if (nextManager == nil) { - [internalCall setResponseHandler:interceptor]; + // continuously create interceptor until one is successfully created + while (_firstInterceptor == nil) { + if (interceptorFactories.count == 0) { + _firstInterceptor = [[GRPCTransportManager alloc] initWithTransportId:_callOptions.transport + previousInterceptor:dispatcher]; + break; } else { - [nextManager setPreviousInterceptor:interceptor]; + _firstInterceptor = + [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories + previousInterceptor:dispatcher + transportId:_callOptions.transport]; + if (_firstInterceptor == nil) { + [interceptorFactories removeObjectAtIndex:0]; + } } - nextInterceptor = interceptor; - nextManager = manager; } - if (nextManager == nil) { - [internalCall setResponseHandler:_responseHandler]; - } else { - [nextManager setPreviousInterceptor:_responseHandler]; + NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport."); + if (_firstInterceptor == nil) { + NSLog(@"Failed to create interceptor or transport."); } - - _firstInterceptor = nextInterceptor; } return self; @@ -200,696 +215,42 @@ const char *kCFStreamVarName = "grpc_cfstream"; } - (void)start { - id<GRPCInterceptorInterface> copiedFirstInterceptor; - @synchronized(self) { - copiedFirstInterceptor = _firstInterceptor; - } - GRPCRequestOptions *requestOptions = [_requestOptions copy]; - GRPCCallOptions *callOptions = [_actualCallOptions copy]; - if ([copiedFirstInterceptor respondsToSelector:@selector(startWithRequestOptions:callOptions:)]) { - dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{ - [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; - }); - } + id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; + GRPCRequestOptions *requestOptions = _requestOptions; + GRPCCallOptions *callOptions = _actualCallOptions; + dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ + [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; + }); } - (void)cancel { - id<GRPCInterceptorInterface> copiedFirstInterceptor; - @synchronized(self) { - copiedFirstInterceptor = _firstInterceptor; - } - if ([copiedFirstInterceptor respondsToSelector:@selector(cancel)]) { - dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{ + id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; + if (copiedFirstInterceptor != nil) { + dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor cancel]; }); } } - (void)writeData:(id)data { - id<GRPCInterceptorInterface> copiedFirstInterceptor; - @synchronized(self) { - copiedFirstInterceptor = _firstInterceptor; - } - if ([copiedFirstInterceptor respondsToSelector:@selector(writeData:)]) { - dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{ - [copiedFirstInterceptor writeData:data]; - }); - } -} - -- (void)finish { - id<GRPCInterceptorInterface> copiedFirstInterceptor; - @synchronized(self) { - copiedFirstInterceptor = _firstInterceptor; - } - if ([copiedFirstInterceptor respondsToSelector:@selector(finish)]) { - dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{ - [copiedFirstInterceptor finish]; - }); - } -} - -- (void)receiveNextMessages:(NSUInteger)numberOfMessages { - id<GRPCInterceptorInterface> copiedFirstInterceptor; - @synchronized(self) { - copiedFirstInterceptor = _firstInterceptor; - } - if ([copiedFirstInterceptor respondsToSelector:@selector(receiveNextMessages:)]) { - dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{ - [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; - }); - } -} - -@end - -// The following methods of a C gRPC call object aren't reentrant, and thus -// calls to them must be serialized: -// - start_batch -// - destroy -// -// start_batch with a SEND_MESSAGE argument can only be called after the -// OP_COMPLETE event for any previous write is received. This is achieved by -// pausing the requests writer immediately every time it writes a value, and -// resuming it again when OP_COMPLETE is received. -// -// Similarly, start_batch with a RECV_MESSAGE argument can only be called after -// the OP_COMPLETE event for any previous read is received.This is easier to -// enforce, as we're writing the received messages into the writeable: -// start_batch is enqueued once upon receiving the OP_COMPLETE event for the -// RECV_METADATA batch, and then once after receiving each OP_COMPLETE event for -// each RECV_MESSAGE batch. -@implementation GRPCCall { - dispatch_queue_t _callQueue; - - NSString *_host; - NSString *_path; - GRPCCallSafety _callSafety; - GRPCCallOptions *_callOptions; - GRPCWrappedCall *_wrappedCall; - - // The C gRPC library has less guarantees on the ordering of events than we - // do. Particularly, in the face of errors, there's no ordering guarantee at - // all. This wrapper over our actual writeable ensures thread-safety and - // correct ordering. - GRXConcurrentWriteable *_responseWriteable; - - // The network thread wants the requestWriter to resume (when the server is ready for more input), - // or to stop (on errors), concurrently with user threads that want to start it, pause it or stop - // it. Because a writer isn't thread-safe, we'll synchronize those operations on it. - // We don't use a dispatch queue for that purpose, because the writer can call writeValue: or - // writesFinishedWithError: on this GRPCCall as part of those operations. We want to be able to - // pause the writer immediately on writeValue:, so we need our locking to be recursive. - GRXWriter *_requestWriter; - - // To create a retain cycle when a call is started, up until it finishes. See - // |startWithWriteable:| and |finishWithError:|. This saves users from having to retain a - // reference to the call object if all they're interested in is the handler being executed when - // the response arrives. - GRPCCall *_retainSelf; - - GRPCRequestHeaders *_requestHeaders; - - // In the case that the call is a unary call (i.e. the writer to GRPCCall is of type - // GRXImmediateSingleWriter), GRPCCall will delay sending ops (not send them to C core - // immediately) and buffer them into a batch _unaryOpBatch. The batch is sent to C core when - // the SendClose op is added. - BOOL _unaryCall; - NSMutableArray *_unaryOpBatch; - - // The dispatch queue to be used for enqueuing responses to user. Defaulted to the main dispatch - // queue - dispatch_queue_t _responseQueue; - - // The OAuth2 token fetched from a token provider. - NSString *_fetchedOauth2AccessToken; - - // The callback to be called when a write message op is done. - void (^_writeDone)(void); - - // Indicate a read request to core is pending. - BOOL _pendingCoreRead; - - // Indicate pending read message request from user. - NSUInteger _pendingReceiveNextMessages; -} - -@synthesize state = _state; - -+ (void)initialize { - // Guarantees the code in {} block is invoked only once. See ref at: - // https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?language=objc - if (self == [GRPCCall self]) { - grpc_init(); - callFlags = [NSMutableDictionary dictionary]; - } -} - -+ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path { - if (host.length == 0 || path.length == 0) { - return; - } - NSString *hostAndPath = [NSString stringWithFormat:@"%@/%@", host, path]; - @synchronized(callFlags) { - switch (callSafety) { - case GRPCCallSafetyDefault: - callFlags[hostAndPath] = @0; - break; - case GRPCCallSafetyIdempotentRequest: - callFlags[hostAndPath] = @GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; - break; - case GRPCCallSafetyCacheableRequest: - callFlags[hostAndPath] = @GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; - break; - default: - break; - } - } -} - -+ (uint32_t)callFlagsForHost:(NSString *)host path:(NSString *)path { - NSString *hostAndPath = [NSString stringWithFormat:@"%@/%@", host, path]; - @synchronized(callFlags) { - return [callFlags[hostAndPath] intValue]; - } -} - -// Designated initializer -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - requestsWriter:(GRXWriter *)requestWriter { - return [self initWithHost:host - path:path - callSafety:GRPCCallSafetyDefault - requestsWriter:requestWriter - callOptions:nil]; -} - -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - callSafety:(GRPCCallSafety)safety - requestsWriter:(GRXWriter *)requestsWriter - callOptions:(GRPCCallOptions *)callOptions { - return [self initWithHost:host - path:path - callSafety:safety - requestsWriter:requestsWriter - callOptions:callOptions - writeDone:nil]; -} - -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - callSafety:(GRPCCallSafety)safety - requestsWriter:(GRXWriter *)requestsWriter - callOptions:(GRPCCallOptions *)callOptions - writeDone:(void (^)(void))writeDone { - // Purposely using pointer rather than length (host.length == 0) for backwards compatibility. - NSAssert(host != nil && path != nil, @"Neither host nor path can be nil."); - NSAssert(safety <= GRPCCallSafetyCacheableRequest, @"Invalid call safety value."); - NSAssert(requestsWriter.state == GRXWriterStateNotStarted, - @"The requests writer can't be already started."); - if (!host || !path) { - return nil; - } - if (safety > GRPCCallSafetyCacheableRequest) { - return nil; - } - if (requestsWriter.state != GRXWriterStateNotStarted) { - return nil; - } - - if ((self = [super init])) { - _host = [host copy]; - _path = [path copy]; - _callSafety = safety; - _callOptions = [callOptions copy]; - - // Serial queue to invoke the non-reentrant methods of the grpc_call object. - _callQueue = dispatch_queue_create("io.grpc.call", DISPATCH_QUEUE_SERIAL); - - _requestWriter = requestsWriter; - _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; - _writeDone = writeDone; - - if ([requestsWriter isKindOfClass:[GRXImmediateSingleWriter class]]) { - _unaryCall = YES; - _unaryOpBatch = [NSMutableArray arrayWithCapacity:kMaxClientBatch]; - } - - _responseQueue = dispatch_get_main_queue(); - - // do not start a read until initial metadata is received - _pendingReceiveNextMessages = 0; - _pendingCoreRead = YES; - } - return self; -} - -- (void)setResponseDispatchQueue:(dispatch_queue_t)queue { - @synchronized(self) { - if (_state != GRXWriterStateNotStarted) { - return; - } - _responseQueue = queue; - } -} - -#pragma mark Finish - -// This function should support being called within a @synchronized(self) block in another function -// Should not manipulate _requestWriter for deadlock prevention. -- (void)finishWithError:(NSError *)errorOrNil { - @synchronized(self) { - if (_state == GRXWriterStateFinished) { - return; - } - _state = GRXWriterStateFinished; - - if (errorOrNil) { - [_responseWriteable cancelWithError:errorOrNil]; - } else { - [_responseWriteable enqueueSuccessfulCompletion]; - } - - // If the call isn't retained anywhere else, it can be deallocated now. - _retainSelf = nil; - } -} - -- (void)cancel { - @synchronized(self) { - if (_state == GRXWriterStateFinished) { - return; - } - [self finishWithError:[NSError - errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeCancelled - userInfo:@{NSLocalizedDescriptionKey : @"Canceled by app"}]]; - [_wrappedCall cancel]; - } - _requestWriter.state = GRXWriterStateFinished; -} - -- (void)dealloc { - __block GRPCWrappedCall *wrappedCall = _wrappedCall; - dispatch_async(_callQueue, ^{ - wrappedCall = nil; - }); -} - -#pragma mark Read messages - -// Only called from the call queue. -// The handler will be called from the network queue. -- (void)startReadWithHandler:(void (^)(grpc_byte_buffer *))handler { - // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[ [[GRPCOpRecvMessage alloc] initWithHandler:handler] ]]; -} - -// Called initially from the network queue once response headers are received, -// then "recursively" from the responseWriteable queue after each response from the -// server has been written. -// If the call is currently paused, this is a noop. Restarting the call will invoke this -// method. -// TODO(jcanizales): Rename to readResponseIfNotPaused. -- (void)maybeStartNextRead { - @synchronized(self) { - if (_state != GRXWriterStateStarted) { - return; - } - if (_callOptions.flowControlEnabled && (_pendingCoreRead || _pendingReceiveNextMessages == 0)) { - return; - } - _pendingCoreRead = YES; - _pendingReceiveNextMessages--; - } - - dispatch_async(_callQueue, ^{ - __weak GRPCCall *weakSelf = self; - [self startReadWithHandler:^(grpc_byte_buffer *message) { - if (message == NULL) { - // No more messages from the server - return; - } - __strong GRPCCall *strongSelf = weakSelf; - if (strongSelf == nil) { - grpc_byte_buffer_destroy(message); - return; - } - NSData *data = [NSData grpc_dataWithByteBuffer:message]; - grpc_byte_buffer_destroy(message); - if (!data) { - // The app doesn't have enough memory to hold the server response. We - // don't want to throw, because the app shouldn't crash for a behavior - // that's on the hands of any server to have. Instead we finish and ask - // the server to cancel. - @synchronized(strongSelf) { - strongSelf->_pendingCoreRead = NO; - [strongSelf - finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeResourceExhausted - userInfo:@{ - NSLocalizedDescriptionKey : - @"Client does not have enough memory to " - @"hold the server response." - }]]; - [strongSelf->_wrappedCall cancel]; - } - strongSelf->_requestWriter.state = GRXWriterStateFinished; - } else { - @synchronized(strongSelf) { - [strongSelf->_responseWriteable enqueueValue:data - completionHandler:^{ - __strong GRPCCall *strongSelf = weakSelf; - if (strongSelf) { - @synchronized(strongSelf) { - strongSelf->_pendingCoreRead = NO; - [strongSelf maybeStartNextRead]; - } - } - }]; - } - } - }]; + id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; + dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ + [copiedFirstInterceptor writeData:data]; }); } -#pragma mark Send headers - -- (void)sendHeaders { - // TODO (mxyan): Remove after deprecated methods are removed - uint32_t callSafetyFlags = 0; - switch (_callSafety) { - case GRPCCallSafetyDefault: - callSafetyFlags = 0; - break; - case GRPCCallSafetyIdempotentRequest: - callSafetyFlags = GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; - break; - case GRPCCallSafetyCacheableRequest: - callSafetyFlags = GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; - break; - } - - NSMutableDictionary *headers = [_requestHeaders mutableCopy]; - NSString *fetchedOauth2AccessToken; - @synchronized(self) { - fetchedOauth2AccessToken = _fetchedOauth2AccessToken; - } - if (fetchedOauth2AccessToken != nil) { - headers[@"authorization"] = [kBearerPrefix stringByAppendingString:fetchedOauth2AccessToken]; - } else if (_callOptions.oauth2AccessToken != nil) { - headers[@"authorization"] = - [kBearerPrefix stringByAppendingString:_callOptions.oauth2AccessToken]; - } - - // TODO(jcanizales): Add error handlers for async failures - GRPCOpSendMetadata *op = [[GRPCOpSendMetadata alloc] - initWithMetadata:headers - flags:callSafetyFlags - handler:nil]; // No clean-up needed after SEND_INITIAL_METADATA - dispatch_async(_callQueue, ^{ - if (!self->_unaryCall) { - [self->_wrappedCall startBatchWithOperations:@[ op ]]; - } else { - [self->_unaryOpBatch addObject:op]; - } +- (void)finish { + id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; + dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ + [copiedFirstInterceptor finish]; }); } - (void)receiveNextMessages:(NSUInteger)numberOfMessages { - if (numberOfMessages == 0) { - return; - } - @synchronized(self) { - _pendingReceiveNextMessages += numberOfMessages; - - if (_state != GRXWriterStateStarted || !_callOptions.flowControlEnabled) { - return; - } - [self maybeStartNextRead]; - } -} - -#pragma mark GRXWriteable implementation - -// Only called from the call queue. The error handler will be called from the -// network queue if the write didn't succeed. -// If the call is a unary call, parameter \a errorHandler will be ignored and -// the error handler of GRPCOpSendClose will be executed in case of error. -- (void)writeMessage:(NSData *)message withErrorHandler:(void (^)(void))errorHandler { - __weak GRPCCall *weakSelf = self; - void (^resumingHandler)(void) = ^{ - // Resume the request writer. - GRPCCall *strongSelf = weakSelf; - if (strongSelf) { - strongSelf->_requestWriter.state = GRXWriterStateStarted; - if (strongSelf->_writeDone) { - strongSelf->_writeDone(); - } - } - }; - GRPCOpSendMessage *op = - [[GRPCOpSendMessage alloc] initWithMessage:message handler:resumingHandler]; - if (!_unaryCall) { - [_wrappedCall startBatchWithOperations:@[ op ] errorHandler:errorHandler]; - } else { - // Ignored errorHandler since it is the same as the one for GRPCOpSendClose. - // TODO (mxyan): unify the error handlers of all Ops into a single closure. - [_unaryOpBatch addObject:op]; - } -} - -- (void)writeValue:(id)value { - NSAssert([value isKindOfClass:[NSData class]], @"value must be of type NSData"); - - @synchronized(self) { - if (_state == GRXWriterStateFinished) { - return; - } - } - - // Pause the input and only resume it when the C layer notifies us that writes - // can proceed. - _requestWriter.state = GRXWriterStatePaused; - - dispatch_async(_callQueue, ^{ - // Write error is not processed here. It is handled by op batch of GRPC_OP_RECV_STATUS_ON_CLIENT - [self writeMessage:value withErrorHandler:nil]; - }); -} - -// Only called from the call queue. The error handler will be called from the -// network queue if the requests stream couldn't be closed successfully. -- (void)finishRequestWithErrorHandler:(void (^)(void))errorHandler { - if (!_unaryCall) { - [_wrappedCall startBatchWithOperations:@[ [[GRPCOpSendClose alloc] init] ] - errorHandler:errorHandler]; - } else { - [_unaryOpBatch addObject:[[GRPCOpSendClose alloc] init]]; - [_wrappedCall startBatchWithOperations:_unaryOpBatch errorHandler:errorHandler]; - } -} - -- (void)writesFinishedWithError:(NSError *)errorOrNil { - if (errorOrNil) { - [self cancel]; - } else { - dispatch_async(_callQueue, ^{ - // EOS error is not processed here. It is handled by op batch of GRPC_OP_RECV_STATUS_ON_CLIENT - [self finishRequestWithErrorHandler:nil]; - }); - } -} - -#pragma mark Invoke - -// Both handlers will eventually be called, from the network queue. Writes can start immediately -// after this. -// The first one (headersHandler), when the response headers are received. -// The second one (completionHandler), whenever the RPC finishes for any reason. -- (void)invokeCallWithHeadersHandler:(void (^)(NSDictionary *))headersHandler - completionHandler:(void (^)(NSError *, NSDictionary *))completionHandler { - dispatch_async(_callQueue, ^{ - // TODO(jcanizales): Add error handlers for async failures - [self->_wrappedCall - startBatchWithOperations:@[ [[GRPCOpRecvMetadata alloc] initWithHandler:headersHandler] ]]; - [self->_wrappedCall - startBatchWithOperations:@[ [[GRPCOpRecvStatus alloc] initWithHandler:completionHandler] ]]; + id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; + dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ + [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; }); } -- (void)invokeCall { - __weak GRPCCall *weakSelf = self; - [self invokeCallWithHeadersHandler:^(NSDictionary *headers) { - // Response headers received. - __strong GRPCCall *strongSelf = weakSelf; - if (strongSelf) { - @synchronized(strongSelf) { - // it is ok to set nil because headers are only received once - strongSelf.responseHeaders = nil; - // copy the header so that the GRPCOpRecvMetadata object may be dealloc'ed - NSDictionary *copiedHeaders = - [[NSDictionary alloc] initWithDictionary:headers copyItems:YES]; - strongSelf.responseHeaders = copiedHeaders; - strongSelf->_pendingCoreRead = NO; - [strongSelf maybeStartNextRead]; - } - } - } - completionHandler:^(NSError *error, NSDictionary *trailers) { - __strong GRPCCall *strongSelf = weakSelf; - if (strongSelf) { - strongSelf.responseTrailers = trailers; - - if (error) { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - if (error.userInfo) { - [userInfo addEntriesFromDictionary:error.userInfo]; - } - userInfo[kGRPCTrailersKey] = strongSelf.responseTrailers; - // Since gRPC core does not guarantee the headers block being called before this block, - // responseHeaders might be nil. - userInfo[kGRPCHeadersKey] = strongSelf.responseHeaders; - error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; - } - [strongSelf finishWithError:error]; - strongSelf->_requestWriter.state = GRXWriterStateFinished; - } - }]; -} - -#pragma mark GRXWriter implementation - -// Lock acquired inside startWithWriteable: -- (void)startCallWithWriteable:(id<GRXWriteable>)writeable { - @synchronized(self) { - if (_state == GRXWriterStateFinished) { - return; - } - - _responseWriteable = - [[GRXConcurrentWriteable alloc] initWithWriteable:writeable dispatchQueue:_responseQueue]; - - GRPCPooledChannel *channel = - [[GRPCChannelPool sharedInstance] channelWithHost:_host callOptions:_callOptions]; - _wrappedCall = [channel wrappedCallWithPath:_path - completionQueue:[GRPCCompletionQueue completionQueue] - callOptions:_callOptions]; - - if (_wrappedCall == nil) { - [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to create call or channel." - }]]; - return; - } - - [self sendHeaders]; - [self invokeCall]; - } - - // Now that the RPC has been initiated, request writes can start. - [_requestWriter startWithWriteable:self]; -} - -- (void)startWithWriteable:(id<GRXWriteable>)writeable { - id<GRPCAuthorizationProtocol> tokenProvider = nil; - @synchronized(self) { - _state = GRXWriterStateStarted; - - // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). - // This makes RPCs in which the call isn't externally retained possible (as long as it is - // started before being autoreleased). Care is taken not to retain self strongly in any of the - // blocks used in this implementation, so that the life of the instance is determined by this - // retain cycle. - _retainSelf = self; - - if (_callOptions == nil) { - GRPCMutableCallOptions *callOptions = [[GRPCHost callOptionsForHost:_host] mutableCopy]; - if (_serverName.length != 0) { - callOptions.serverAuthority = _serverName; - } - if (_timeout > 0) { - callOptions.timeout = _timeout; - } - uint32_t callFlags = [GRPCCall callFlagsForHost:_host path:_path]; - if (callFlags != 0) { - if (callFlags == GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { - _callSafety = GRPCCallSafetyIdempotentRequest; - } else if (callFlags == GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) { - _callSafety = GRPCCallSafetyCacheableRequest; - } - } - - id<GRPCAuthorizationProtocol> tokenProvider = self.tokenProvider; - if (tokenProvider != nil) { - callOptions.authTokenProvider = tokenProvider; - } - _callOptions = callOptions; - } - - NSAssert(_callOptions.authTokenProvider == nil || _callOptions.oauth2AccessToken == nil, - @"authTokenProvider and oauth2AccessToken cannot be set at the same time"); - - tokenProvider = _callOptions.authTokenProvider; - } - - if (tokenProvider != nil) { - __weak typeof(self) weakSelf = self; - [tokenProvider getTokenWithHandler:^(NSString *token) { - __strong typeof(self) strongSelf = weakSelf; - if (strongSelf) { - BOOL startCall = NO; - @synchronized(strongSelf) { - if (strongSelf->_state != GRXWriterStateFinished) { - startCall = YES; - if (token) { - strongSelf->_fetchedOauth2AccessToken = [token copy]; - } - } - } - if (startCall) { - [strongSelf startCallWithWriteable:writeable]; - } - } - }]; - } else { - [self startCallWithWriteable:writeable]; - } -} - -- (void)setState:(GRXWriterState)newState { - @synchronized(self) { - // Manual transitions are only allowed from the started or paused states. - if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { - return; - } - - switch (newState) { - case GRXWriterStateFinished: - _state = newState; - // Per GRXWriter's contract, setting the state to Finished manually - // means one doesn't wish the writeable to be messaged anymore. - [_responseWriteable cancelSilently]; - _responseWriteable = nil; - return; - case GRXWriterStatePaused: - _state = newState; - return; - case GRXWriterStateStarted: - if (_state == GRXWriterStatePaused) { - _state = newState; - [self maybeStartNextRead]; - } - return; - case GRXWriterStateNotStarted: - return; - } - } -} - @end diff --git a/src/objective-c/GRPCClient/GRPCCallLegacy.h b/src/objective-c/GRPCClient/GRPCCallLegacy.h new file mode 100644 index 0000000000000000000000000000000000000000..51dd7da3440169de57664bd616e07a5c430a8706 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCallLegacy.h @@ -0,0 +1,136 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This is the legacy interface of this gRPC library. This API is deprecated and users should use + * the API in GRPCCall.h. This API exists solely for the purpose of backwards compatibility. + */ + +#import <RxLibrary/GRXWriter.h> +#import "GRPCTypes.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnullability-completeness" + +/** + * This interface is deprecated. Please use \a GRPCCall2. + * + * Represents a single gRPC remote call. + */ +@interface GRPCCall : GRXWriter + +- (instancetype)init NS_UNAVAILABLE; + +/** + * The container of the request headers of an RPC conforms to this protocol, which is a subset of + * NSMutableDictionary's interface. It will become a NSMutableDictionary later on. + * The keys of this container are the header names, which per the HTTP standard are case- + * insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and + * can only consist of ASCII characters. + * A header value is a NSString object (with only ASCII characters), unless the header name has the + * suffix "-bin", in which case the value has to be a NSData object. + */ +/** + * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a + * name-value pair with string names and either string or binary values. + * + * The passed dictionary has to use NSString keys, corresponding to the header names. The value + * associated to each can be a NSString object or a NSData object. E.g.: + * + * call.requestHeaders = @{@"authorization": @"Bearer ..."}; + * + * call.requestHeaders[@"my-header-bin"] = someData; + * + * After the call is started, trying to modify this property is an error. + * + * The property is initialized to an empty NSMutableDictionary. + */ +@property(atomic, readonly) NSMutableDictionary *requestHeaders; + +/** + * This dictionary is populated with the HTTP headers received from the server. This happens before + * any response message is received from the server. It has the same structure as the request + * headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a + * NSData value; the others have a NSString value. + * + * The value of this property is nil until all response headers are received, and will change before + * any of -writeValue: or -writesFinishedWithError: are sent to the writeable. + */ +@property(atomic, readonly) NSDictionary *responseHeaders; + +/** + * Same as responseHeaders, but populated with the HTTP trailers received from the server before the + * call finishes. + * + * The value of this property is nil until all response trailers are received, and will change + * before -writesFinishedWithError: is sent to the writeable. + */ +@property(atomic, readonly) NSDictionary *responseTrailers; + +/** + * The request writer has to write NSData objects into the provided Writeable. The server will + * receive each of those separately and in order as distinct messages. + * A gRPC call might not complete until the request writer finishes. On the other hand, the request + * finishing doesn't necessarily make the call to finish, as the server might continue sending + * messages to the response side of the call indefinitely (depending on the semantics of the + * specific remote method called). + * To finish a call right away, invoke cancel. + * host parameter should not contain the scheme (http:// or https://), only the name or IP addr + * and the port number, for example @"localhost:5050". + */ +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestWriter; + +/** + * Finishes the request side of this call, notifies the server that the RPC should be cancelled, and + * finishes the response side of the call with an error of code CANCELED. + */ +- (void)cancel; + +/** + * The following methods are deprecated. + */ ++ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path; +@property(atomic, copy, readwrite) NSString *serverName; +@property NSTimeInterval timeout; +- (void)setResponseDispatchQueue:(dispatch_queue_t)queue; + +@end + +#pragma mark Backwards compatibiity + +/** This protocol is kept for backwards compatibility with existing code. */ +DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") +@protocol GRPCRequestHeaders<NSObject> +@property(nonatomic, readonly) NSUInteger count; + +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)obj forKeyedSubscript:(id)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(id)key; +@end + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +/** This is only needed for backwards-compatibility. */ +@interface NSMutableDictionary (GRPCRequestHeaders)<GRPCRequestHeaders> +@end +#pragma clang diagnostic pop +#pragma clang diagnostic pop diff --git a/src/objective-c/GRPCClient/GRPCCallLegacy.m b/src/objective-c/GRPCClient/GRPCCallLegacy.m new file mode 100644 index 0000000000000000000000000000000000000000..d06048c3a89b3c08bd40fe33965a3eb62ede1e06 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCallLegacy.m @@ -0,0 +1,677 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCCallLegacy.h" + +#import "GRPCCall+OAuth2.h" +#import "GRPCCallOptions.h" +#import "GRPCTypes.h" + +#import "private/GRPCCore/GRPCChannelPool.h" +#import "private/GRPCCore/GRPCCompletionQueue.h" +#import "private/GRPCCore/GRPCHost.h" +#import "private/GRPCCore/GRPCWrappedCall.h" +#import "private/GRPCCore/NSData+GRPC.h" + +#import <RxLibrary/GRXBufferedPipe.h> +#import <RxLibrary/GRXConcurrentWriteable.h> +#import <RxLibrary/GRXImmediateSingleWriter.h> +#import <RxLibrary/GRXWriter+Immediate.h> + +#include <grpc/grpc.h> + +const char *kCFStreamVarName = "grpc_cfstream"; +static NSMutableDictionary *callFlags; + +// At most 6 ops can be in an op batch for a client: SEND_INITIAL_METADATA, +// SEND_MESSAGE, SEND_CLOSE_FROM_CLIENT, RECV_INITIAL_METADATA, RECV_MESSAGE, +// and RECV_STATUS_ON_CLIENT. +NSInteger kMaxClientBatch = 6; + +static NSString *const kAuthorizationHeader = @"authorization"; +static NSString *const kBearerPrefix = @"Bearer "; + +@interface GRPCCall ()<GRXWriteable> +// Make them read-write. +@property(atomic, strong) NSDictionary *responseHeaders; +@property(atomic, strong) NSDictionary *responseTrailers; + +- (void)receiveNextMessages:(NSUInteger)numberOfMessages; + +@end + +// The following methods of a C gRPC call object aren't reentrant, and thus +// calls to them must be serialized: +// - start_batch +// - destroy +// +// start_batch with a SEND_MESSAGE argument can only be called after the +// OP_COMPLETE event for any previous write is received. This is achieved by +// pausing the requests writer immediately every time it writes a value, and +// resuming it again when OP_COMPLETE is received. +// +// Similarly, start_batch with a RECV_MESSAGE argument can only be called after +// the OP_COMPLETE event for any previous read is received.This is easier to +// enforce, as we're writing the received messages into the writeable: +// start_batch is enqueued once upon receiving the OP_COMPLETE event for the +// RECV_METADATA batch, and then once after receiving each OP_COMPLETE event for +// each RECV_MESSAGE batch. +@implementation GRPCCall { + dispatch_queue_t _callQueue; + + NSString *_host; + NSString *_path; + GRPCCallSafety _callSafety; + GRPCCallOptions *_callOptions; + GRPCWrappedCall *_wrappedCall; + + // The C gRPC library has less guarantees on the ordering of events than we + // do. Particularly, in the face of errors, there's no ordering guarantee at + // all. This wrapper over our actual writeable ensures thread-safety and + // correct ordering. + GRXConcurrentWriteable *_responseWriteable; + + // The network thread wants the requestWriter to resume (when the server is ready for more input), + // or to stop (on errors), concurrently with user threads that want to start it, pause it or stop + // it. Because a writer isn't thread-safe, we'll synchronize those operations on it. + // We don't use a dispatch queue for that purpose, because the writer can call writeValue: or + // writesFinishedWithError: on this GRPCCall as part of those operations. We want to be able to + // pause the writer immediately on writeValue:, so we need our locking to be recursive. + GRXWriter *_requestWriter; + + // To create a retain cycle when a call is started, up until it finishes. See + // |startWithWriteable:| and |finishWithError:|. This saves users from having to retain a + // reference to the call object if all they're interested in is the handler being executed when + // the response arrives. + GRPCCall *_retainSelf; + + GRPCRequestHeaders *_requestHeaders; + + // In the case that the call is a unary call (i.e. the writer to GRPCCall is of type + // GRXImmediateSingleWriter), GRPCCall will delay sending ops (not send them to C core + // immediately) and buffer them into a batch _unaryOpBatch. The batch is sent to C core when + // the SendClose op is added. + BOOL _unaryCall; + NSMutableArray *_unaryOpBatch; + + // The dispatch queue to be used for enqueuing responses to user. Defaulted to the main dispatch + // queue + dispatch_queue_t _responseQueue; + + // The OAuth2 token fetched from a token provider. + NSString *_fetchedOauth2AccessToken; + + // The callback to be called when a write message op is done. + void (^_writeDone)(void); + + // Indicate a read request to core is pending. + BOOL _pendingCoreRead; + + // Indicate pending read message request from user. + NSUInteger _pendingReceiveNextMessages; +} + +@synthesize state = _state; + ++ (void)initialize { + // Guarantees the code in {} block is invoked only once. See ref at: + // https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?language=objc + if (self == [GRPCCall self]) { + grpc_init(); + callFlags = [NSMutableDictionary dictionary]; + } +} + ++ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path { + if (host.length == 0 || path.length == 0) { + return; + } + NSString *hostAndPath = [NSString stringWithFormat:@"%@/%@", host, path]; + @synchronized(callFlags) { + switch (callSafety) { + case GRPCCallSafetyDefault: + callFlags[hostAndPath] = @0; + break; + case GRPCCallSafetyIdempotentRequest: + callFlags[hostAndPath] = @GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + break; + case GRPCCallSafetyCacheableRequest: + callFlags[hostAndPath] = @GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + break; + default: + break; + } + } +} + ++ (uint32_t)callFlagsForHost:(NSString *)host path:(NSString *)path { + NSString *hostAndPath = [NSString stringWithFormat:@"%@/%@", host, path]; + @synchronized(callFlags) { + return [callFlags[hostAndPath] intValue]; + } +} + +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestWriter { + return [self initWithHost:host + path:path + callSafety:GRPCCallSafetyDefault + requestsWriter:requestWriter + callOptions:nil + writeDone:nil]; +} + +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + callSafety:(GRPCCallSafety)safety + requestsWriter:(GRXWriter *)requestsWriter + callOptions:(GRPCCallOptions *)callOptions + writeDone:(void (^)(void))writeDone { + // Purposely using pointer rather than length (host.length == 0) for backwards compatibility. + NSAssert(host != nil && path != nil, @"Neither host nor path can be nil."); + NSAssert(safety <= GRPCCallSafetyCacheableRequest, @"Invalid call safety value."); + NSAssert(requestsWriter.state == GRXWriterStateNotStarted, + @"The requests writer can't be already started."); + if (!host || !path) { + return nil; + } + if (safety > GRPCCallSafetyCacheableRequest) { + return nil; + } + if (requestsWriter.state != GRXWriterStateNotStarted) { + return nil; + } + + if ((self = [super init])) { + _host = [host copy]; + _path = [path copy]; + _callSafety = safety; + _callOptions = [callOptions copy]; + + // Serial queue to invoke the non-reentrant methods of the grpc_call object. + _callQueue = dispatch_queue_create("io.grpc.call", DISPATCH_QUEUE_SERIAL); + + _requestWriter = requestsWriter; + _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; + _writeDone = writeDone; + + if ([requestsWriter isKindOfClass:[GRXImmediateSingleWriter class]]) { + _unaryCall = YES; + _unaryOpBatch = [NSMutableArray arrayWithCapacity:kMaxClientBatch]; + } + + _responseQueue = dispatch_get_main_queue(); + + // do not start a read until initial metadata is received + _pendingReceiveNextMessages = 0; + _pendingCoreRead = YES; + } + return self; +} + +- (void)setResponseDispatchQueue:(dispatch_queue_t)queue { + @synchronized(self) { + if (_state != GRXWriterStateNotStarted) { + return; + } + _responseQueue = queue; + } +} + +#pragma mark Finish + +// This function should support being called within a @synchronized(self) block in another function +// Should not manipulate _requestWriter for deadlock prevention. +- (void)finishWithError:(NSError *)errorOrNil { + @synchronized(self) { + if (_state == GRXWriterStateFinished) { + return; + } + _state = GRXWriterStateFinished; + + if (errorOrNil) { + [_responseWriteable cancelWithError:errorOrNil]; + } else { + [_responseWriteable enqueueSuccessfulCompletion]; + } + + // If the call isn't retained anywhere else, it can be deallocated now. + _retainSelf = nil; + } +} + +- (void)cancel { + @synchronized(self) { + if (_state == GRXWriterStateFinished) { + return; + } + [self finishWithError:[NSError + errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeCancelled + userInfo:@{NSLocalizedDescriptionKey : @"Canceled by app"}]]; + [_wrappedCall cancel]; + } + _requestWriter.state = GRXWriterStateFinished; +} + +- (void)dealloc { + __block GRPCWrappedCall *wrappedCall = _wrappedCall; + dispatch_async(_callQueue, ^{ + wrappedCall = nil; + }); +} + +#pragma mark Read messages + +// Only called from the call queue. +// The handler will be called from the network queue. +- (void)startReadWithHandler:(void (^)(grpc_byte_buffer *))handler { + // TODO(jcanizales): Add error handlers for async failures + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpRecvMessage alloc] initWithHandler:handler] ]]; +} + +// Called initially from the network queue once response headers are received, +// then "recursively" from the responseWriteable queue after each response from the +// server has been written. +// If the call is currently paused, this is a noop. Restarting the call will invoke this +// method. +// TODO(jcanizales): Rename to readResponseIfNotPaused. +- (void)maybeStartNextRead { + @synchronized(self) { + if (_state != GRXWriterStateStarted) { + return; + } + if (_callOptions.flowControlEnabled && (_pendingCoreRead || _pendingReceiveNextMessages == 0)) { + return; + } + _pendingCoreRead = YES; + _pendingReceiveNextMessages--; + } + + dispatch_async(_callQueue, ^{ + __weak GRPCCall *weakSelf = self; + [self startReadWithHandler:^(grpc_byte_buffer *message) { + if (message == NULL) { + // No more messages from the server + return; + } + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf == nil) { + grpc_byte_buffer_destroy(message); + return; + } + NSData *data = [NSData grpc_dataWithByteBuffer:message]; + grpc_byte_buffer_destroy(message); + if (!data) { + // The app doesn't have enough memory to hold the server response. We + // don't want to throw, because the app shouldn't crash for a behavior + // that's on the hands of any server to have. Instead we finish and ask + // the server to cancel. + @synchronized(strongSelf) { + strongSelf->_pendingCoreRead = NO; + [strongSelf + finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeResourceExhausted + userInfo:@{ + NSLocalizedDescriptionKey : + @"Client does not have enough memory to " + @"hold the server response." + }]]; + [strongSelf->_wrappedCall cancel]; + } + strongSelf->_requestWriter.state = GRXWriterStateFinished; + } else { + @synchronized(strongSelf) { + [strongSelf->_responseWriteable enqueueValue:data + completionHandler:^{ + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + @synchronized(strongSelf) { + strongSelf->_pendingCoreRead = NO; + [strongSelf maybeStartNextRead]; + } + } + }]; + } + } + }]; + }); +} + +#pragma mark Send headers + +- (void)sendHeaders { + // TODO (mxyan): Remove after deprecated methods are removed + uint32_t callSafetyFlags = 0; + switch (_callSafety) { + case GRPCCallSafetyDefault: + callSafetyFlags = 0; + break; + case GRPCCallSafetyIdempotentRequest: + callSafetyFlags = GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + break; + case GRPCCallSafetyCacheableRequest: + callSafetyFlags = GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + break; + } + + NSMutableDictionary *headers = [_requestHeaders mutableCopy]; + NSString *fetchedOauth2AccessToken; + @synchronized(self) { + fetchedOauth2AccessToken = _fetchedOauth2AccessToken; + } + if (fetchedOauth2AccessToken != nil) { + headers[@"authorization"] = [kBearerPrefix stringByAppendingString:fetchedOauth2AccessToken]; + } else if (_callOptions.oauth2AccessToken != nil) { + headers[@"authorization"] = + [kBearerPrefix stringByAppendingString:_callOptions.oauth2AccessToken]; + } + + // TODO(jcanizales): Add error handlers for async failures + GRPCOpSendMetadata *op = [[GRPCOpSendMetadata alloc] + initWithMetadata:headers + flags:callSafetyFlags + handler:nil]; // No clean-up needed after SEND_INITIAL_METADATA + dispatch_async(_callQueue, ^{ + if (!self->_unaryCall) { + [self->_wrappedCall startBatchWithOperations:@[ op ]]; + } else { + [self->_unaryOpBatch addObject:op]; + } + }); +} + +- (void)receiveNextMessages:(NSUInteger)numberOfMessages { + if (numberOfMessages == 0) { + return; + } + @synchronized(self) { + _pendingReceiveNextMessages += numberOfMessages; + + if (_state != GRXWriterStateStarted || !_callOptions.flowControlEnabled) { + return; + } + [self maybeStartNextRead]; + } +} + +#pragma mark GRXWriteable implementation + +// Only called from the call queue. The error handler will be called from the +// network queue if the write didn't succeed. +// If the call is a unary call, parameter \a errorHandler will be ignored and +// the error handler of GRPCOpSendClose will be executed in case of error. +- (void)writeMessage:(NSData *)message withErrorHandler:(void (^)(void))errorHandler { + __weak GRPCCall *weakSelf = self; + void (^resumingHandler)(void) = ^{ + // Resume the request writer. + GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_requestWriter.state = GRXWriterStateStarted; + if (strongSelf->_writeDone) { + strongSelf->_writeDone(); + } + } + }; + GRPCOpSendMessage *op = + [[GRPCOpSendMessage alloc] initWithMessage:message handler:resumingHandler]; + if (!_unaryCall) { + [_wrappedCall startBatchWithOperations:@[ op ] errorHandler:errorHandler]; + } else { + // Ignored errorHandler since it is the same as the one for GRPCOpSendClose. + // TODO (mxyan): unify the error handlers of all Ops into a single closure. + [_unaryOpBatch addObject:op]; + } +} + +- (void)writeValue:(id)value { + NSAssert([value isKindOfClass:[NSData class]], @"value must be of type NSData"); + + @synchronized(self) { + if (_state == GRXWriterStateFinished) { + return; + } + } + + // Pause the input and only resume it when the C layer notifies us that writes + // can proceed. + _requestWriter.state = GRXWriterStatePaused; + + dispatch_async(_callQueue, ^{ + // Write error is not processed here. It is handled by op batch of GRPC_OP_RECV_STATUS_ON_CLIENT + [self writeMessage:value withErrorHandler:nil]; + }); +} + +// Only called from the call queue. The error handler will be called from the +// network queue if the requests stream couldn't be closed successfully. +- (void)finishRequestWithErrorHandler:(void (^)(void))errorHandler { + if (!_unaryCall) { + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpSendClose alloc] init] ] + errorHandler:errorHandler]; + } else { + [_unaryOpBatch addObject:[[GRPCOpSendClose alloc] init]]; + [_wrappedCall startBatchWithOperations:_unaryOpBatch errorHandler:errorHandler]; + } +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + if (errorOrNil) { + [self cancel]; + } else { + dispatch_async(_callQueue, ^{ + // EOS error is not processed here. It is handled by op batch of GRPC_OP_RECV_STATUS_ON_CLIENT + [self finishRequestWithErrorHandler:nil]; + }); + } +} + +#pragma mark Invoke + +// Both handlers will eventually be called, from the network queue. Writes can start immediately +// after this. +// The first one (headersHandler), when the response headers are received. +// The second one (completionHandler), whenever the RPC finishes for any reason. +- (void)invokeCallWithHeadersHandler:(void (^)(NSDictionary *))headersHandler + completionHandler:(void (^)(NSError *, NSDictionary *))completionHandler { + dispatch_async(_callQueue, ^{ + // TODO(jcanizales): Add error handlers for async failures + [self->_wrappedCall + startBatchWithOperations:@[ [[GRPCOpRecvMetadata alloc] initWithHandler:headersHandler] ]]; + [self->_wrappedCall + startBatchWithOperations:@[ [[GRPCOpRecvStatus alloc] initWithHandler:completionHandler] ]]; + }); +} + +- (void)invokeCall { + __weak GRPCCall *weakSelf = self; + [self invokeCallWithHeadersHandler:^(NSDictionary *headers) { + // Response headers received. + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + @synchronized(strongSelf) { + // it is ok to set nil because headers are only received once + strongSelf.responseHeaders = nil; + // copy the header so that the GRPCOpRecvMetadata object may be dealloc'ed + NSDictionary *copiedHeaders = + [[NSDictionary alloc] initWithDictionary:headers copyItems:YES]; + strongSelf.responseHeaders = copiedHeaders; + strongSelf->_pendingCoreRead = NO; + [strongSelf maybeStartNextRead]; + } + } + } + completionHandler:^(NSError *error, NSDictionary *trailers) { + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf.responseTrailers = trailers; + + if (error) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (error.userInfo) { + [userInfo addEntriesFromDictionary:error.userInfo]; + } + userInfo[kGRPCTrailersKey] = strongSelf.responseTrailers; + // Since gRPC core does not guarantee the headers block being called before this block, + // responseHeaders might be nil. + userInfo[kGRPCHeadersKey] = strongSelf.responseHeaders; + error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; + } + [strongSelf finishWithError:error]; + strongSelf->_requestWriter.state = GRXWriterStateFinished; + } + }]; +} + +#pragma mark GRXWriter implementation + +// Lock acquired inside startWithWriteable: +- (void)startCallWithWriteable:(id<GRXWriteable>)writeable { + @synchronized(self) { + if (_state == GRXWriterStateFinished) { + return; + } + + _responseWriteable = + [[GRXConcurrentWriteable alloc] initWithWriteable:writeable dispatchQueue:_responseQueue]; + + GRPCPooledChannel *channel = + [[GRPCChannelPool sharedInstance] channelWithHost:_host callOptions:_callOptions]; + _wrappedCall = [channel wrappedCallWithPath:_path + completionQueue:[GRPCCompletionQueue completionQueue] + callOptions:_callOptions]; + + if (_wrappedCall == nil) { + [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to create call or channel." + }]]; + return; + } + + [self sendHeaders]; + [self invokeCall]; + } + + // Now that the RPC has been initiated, request writes can start. + [_requestWriter startWithWriteable:self]; +} + +- (void)startWithWriteable:(id<GRXWriteable>)writeable { + id<GRPCAuthorizationProtocol> tokenProvider = nil; + @synchronized(self) { + _state = GRXWriterStateStarted; + + // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). + // This makes RPCs in which the call isn't externally retained possible (as long as it is + // started before being autoreleased). Care is taken not to retain self strongly in any of the + // blocks used in this implementation, so that the life of the instance is determined by this + // retain cycle. + _retainSelf = self; + + // If _callOptions is nil, people must be using the deprecated v1 interface. In this case, + // generate the call options from the corresponding GRPCHost configs and apply other options + // that are not covered by GRPCHost. + if (_callOptions == nil) { + GRPCMutableCallOptions *callOptions = [[GRPCHost callOptionsForHost:_host] mutableCopy]; + if (_serverName.length != 0) { + callOptions.serverAuthority = _serverName; + } + if (_timeout > 0) { + callOptions.timeout = _timeout; + } + uint32_t callFlags = [GRPCCall callFlagsForHost:_host path:_path]; + if (callFlags != 0) { + if (callFlags == GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { + _callSafety = GRPCCallSafetyIdempotentRequest; + } else if (callFlags == GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) { + _callSafety = GRPCCallSafetyCacheableRequest; + } + } + + id<GRPCAuthorizationProtocol> tokenProvider = self.tokenProvider; + if (tokenProvider != nil) { + callOptions.authTokenProvider = tokenProvider; + } + _callOptions = callOptions; + } + + NSAssert(_callOptions.authTokenProvider == nil || _callOptions.oauth2AccessToken == nil, + @"authTokenProvider and oauth2AccessToken cannot be set at the same time"); + + tokenProvider = _callOptions.authTokenProvider; + } + + if (tokenProvider != nil) { + __weak typeof(self) weakSelf = self; + [tokenProvider getTokenWithHandler:^(NSString *token) { + __strong typeof(self) strongSelf = weakSelf; + if (strongSelf) { + BOOL startCall = NO; + @synchronized(strongSelf) { + if (strongSelf->_state != GRXWriterStateFinished) { + startCall = YES; + if (token) { + strongSelf->_fetchedOauth2AccessToken = [token copy]; + } + } + } + if (startCall) { + [strongSelf startCallWithWriteable:writeable]; + } + } + }]; + } else { + [self startCallWithWriteable:writeable]; + } +} + +- (void)setState:(GRXWriterState)newState { + @synchronized(self) { + // Manual transitions are only allowed from the started or paused states. + if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + return; + } + + switch (newState) { + case GRXWriterStateFinished: + _state = newState; + // Per GRXWriter's contract, setting the state to Finished manually + // means one doesn't wish the writeable to be messaged anymore. + [_responseWriteable cancelSilently]; + _responseWriteable = nil; + return; + case GRXWriterStatePaused: + _state = newState; + return; + case GRXWriterStateStarted: + if (_state == GRXWriterStatePaused) { + _state = newState; + [self maybeStartNextRead]; + } + return; + case GRXWriterStateNotStarted: + return; + } + } +} + +@end diff --git a/src/objective-c/GRPCClient/GRPCCallOptions.h b/src/objective-c/GRPCClient/GRPCCallOptions.h index 98511e3f5cb3d00277a494e8bb67906e92d76ff9..e4261b5b5f9dec7adbc9b80a3352bc5cddb37129 100644 --- a/src/objective-c/GRPCClient/GRPCCallOptions.h +++ b/src/objective-c/GRPCClient/GRPCCallOptions.h @@ -18,57 +18,11 @@ #import <Foundation/Foundation.h> -NS_ASSUME_NONNULL_BEGIN - -/** - * Safety remark of a gRPC method as defined in RFC 2616 Section 9.1 - */ -typedef NS_ENUM(NSUInteger, GRPCCallSafety) { - /** Signal that there is no guarantees on how the call affects the server state. */ - GRPCCallSafetyDefault = 0, - /** Signal that the call is idempotent. gRPC is free to use PUT verb. */ - GRPCCallSafetyIdempotentRequest = 1, - /** - * Signal that the call is cacheable and will not affect server state. gRPC is free to use GET - * verb. - */ - GRPCCallSafetyCacheableRequest = 2, -}; - -// Compression algorithm to be used by a gRPC call -typedef NS_ENUM(NSUInteger, GRPCCompressionAlgorithm) { - GRPCCompressNone = 0, - GRPCCompressDeflate, - GRPCCompressGzip, - GRPCStreamCompressGzip, -}; - -// GRPCCompressAlgorithm is deprecated; use GRPCCompressionAlgorithm -typedef GRPCCompressionAlgorithm GRPCCompressAlgorithm; +#import "GRPCTypes.h" -/** The transport to be used by a gRPC call */ -typedef NS_ENUM(NSUInteger, GRPCTransportType) { - GRPCTransportTypeDefault = 0, - /** gRPC internal HTTP/2 stack with BoringSSL */ - GRPCTransportTypeChttp2BoringSSL = 0, - /** Cronet stack */ - GRPCTransportTypeCronet, - /** Insecure channel. FOR TEST ONLY! */ - GRPCTransportTypeInsecure, -}; - -/** - * Implement this protocol to provide a token to gRPC when a call is initiated. - */ -@protocol GRPCAuthorizationProtocol - -/** - * This method is called when gRPC is about to start the call. When OAuth token is acquired, - * \a handler is expected to be called with \a token being the new token to be used for this call. - */ -- (void)getTokenWithHandler:(void (^)(NSString *_Nullable token))handler; +NS_ASSUME_NONNULL_BEGIN -@end +@protocol GRPCInterceptorFactory; @interface GRPCCallOptions : NSObject<NSCopying, NSMutableCopying> @@ -104,7 +58,7 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) { * this array. This parameter should not be modified by any interceptor and will * not take effect if done so. */ -@property(copy, readonly) NSArray *interceptorFactories; +@property(copy, readonly) NSArray<id<GRPCInterceptorFactory>> *interceptorFactories; // OAuth2 parameters. Users of gRPC may specify one of the following two parameters. @@ -192,10 +146,23 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) { @property(copy, readonly, nullable) NSString *PEMCertificateChain; /** + * Deprecated: this option is deprecated. Please use the property \a transport + * instead. + * * Select the transport type to be used for this call. */ @property(readonly) GRPCTransportType transportType; +/** + * The transport to be used for this call. Users may choose a native transport + * identifier defined in \a GRPCTransport or provided by a non-native transport + * implementation. If the option is left to be NULL, gRPC will use its default + * transport. + * + * This is currently an experimental option. + */ +@property(readonly) GRPCTransportId transport; + /** * Override the hostname during the TLS hostname validation process. */ @@ -267,7 +234,7 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) { * this array. This parameter should not be modified by any interceptor and will * not take effect if done so. */ -@property(copy, readwrite) NSArray *interceptorFactories; +@property(copy, readwrite) NSArray<id<GRPCInterceptorFactory>> *interceptorFactories; // OAuth2 parameters. Users of gRPC may specify one of the following two parameters. @@ -357,10 +324,23 @@ typedef NS_ENUM(NSUInteger, GRPCTransportType) { @property(copy, readwrite, nullable) NSString *PEMCertificateChain; /** + * Deprecated: this option is deprecated. Please use the property \a transport + * instead. + * * Select the transport type to be used for this call. */ @property(readwrite) GRPCTransportType transportType; +/** + * The transport to be used for this call. Users may choose a native transport + * identifier defined in \a GRPCTransport or provided by a non-native ttransport + * implementation. If the option is left to be NULL, gRPC will use its default + * transport. + * + * An interceptor must not change the value of this option. + */ +@property(readwrite) GRPCTransportId transport; + /** * Override the hostname during the TLS hostname validation process. */ diff --git a/src/objective-c/GRPCClient/GRPCCallOptions.m b/src/objective-c/GRPCClient/GRPCCallOptions.m index 392e42a9d47d2a2884a2f6db7b8e8409a80ccd7f..7f88098eb6fe1856edfdd4ec5c50493f492b5baa 100644 --- a/src/objective-c/GRPCClient/GRPCCallOptions.m +++ b/src/objective-c/GRPCClient/GRPCCallOptions.m @@ -17,13 +17,14 @@ */ #import "GRPCCallOptions.h" +#import "GRPCTransport.h" #import "internal/GRPCCallOptions+Internal.h" // The default values for the call options. static NSString *const kDefaultServerAuthority = nil; static const NSTimeInterval kDefaultTimeout = 0; static const BOOL kDefaultFlowControlEnabled = NO; -static NSArray *const kDefaultInterceptorFactories = nil; +static NSArray<id<GRPCInterceptorFactory>> *const kDefaultInterceptorFactories = nil; static NSDictionary *const kDefaultInitialMetadata = nil; static NSString *const kDefaultUserAgentPrefix = nil; static const NSUInteger kDefaultResponseSizeLimit = 0; @@ -41,6 +42,7 @@ static NSString *const kDefaultPEMCertificateChain = nil; static NSString *const kDefaultOauth2AccessToken = nil; static const id<GRPCAuthorizationProtocol> kDefaultAuthTokenProvider = nil; static const GRPCTransportType kDefaultTransportType = GRPCTransportTypeChttp2BoringSSL; +static const GRPCTransportId kDefaultTransport = NULL; static NSString *const kDefaultHostNameOverride = nil; static const id kDefaultLogContext = nil; static NSString *const kDefaultChannelPoolDomain = nil; @@ -62,7 +64,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { NSString *_serverAuthority; NSTimeInterval _timeout; BOOL _flowControlEnabled; - NSArray *_interceptorFactories; + NSArray<id<GRPCInterceptorFactory>> *_interceptorFactories; NSString *_oauth2AccessToken; id<GRPCAuthorizationProtocol> _authTokenProvider; NSDictionary *_initialMetadata; @@ -80,6 +82,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { NSString *_PEMPrivateKey; NSString *_PEMCertificateChain; GRPCTransportType _transportType; + GRPCTransportId _transport; NSString *_hostNameOverride; id<NSObject> _logContext; NSString *_channelPoolDomain; @@ -107,6 +110,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { @synthesize PEMPrivateKey = _PEMPrivateKey; @synthesize PEMCertificateChain = _PEMCertificateChain; @synthesize transportType = _transportType; +@synthesize transport = _transport; @synthesize hostNameOverride = _hostNameOverride; @synthesize logContext = _logContext; @synthesize channelPoolDomain = _channelPoolDomain; @@ -134,6 +138,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:kDefaultPEMPrivateKey PEMCertificateChain:kDefaultPEMCertificateChain transportType:kDefaultTransportType + transport:kDefaultTransport hostNameOverride:kDefaultHostNameOverride logContext:kDefaultLogContext channelPoolDomain:kDefaultChannelPoolDomain @@ -143,7 +148,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { - (instancetype)initWithServerAuthority:(NSString *)serverAuthority timeout:(NSTimeInterval)timeout flowControlEnabled:(BOOL)flowControlEnabled - interceptorFactories:(NSArray *)interceptorFactories + interceptorFactories:(NSArray<id<GRPCInterceptorFactory>> *)interceptorFactories oauth2AccessToken:(NSString *)oauth2AccessToken authTokenProvider:(id<GRPCAuthorizationProtocol>)authTokenProvider initialMetadata:(NSDictionary *)initialMetadata @@ -161,6 +166,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:(NSString *)PEMPrivateKey PEMCertificateChain:(NSString *)PEMCertificateChain transportType:(GRPCTransportType)transportType + transport:(GRPCTransportId)transport hostNameOverride:(NSString *)hostNameOverride logContext:(id)logContext channelPoolDomain:(NSString *)channelPoolDomain @@ -193,6 +199,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { _PEMPrivateKey = [PEMPrivateKey copy]; _PEMCertificateChain = [PEMCertificateChain copy]; _transportType = transportType; + _transport = transport; _hostNameOverride = [hostNameOverride copy]; _logContext = logContext; _channelPoolDomain = [channelPoolDomain copy]; @@ -224,6 +231,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:_PEMPrivateKey PEMCertificateChain:_PEMCertificateChain transportType:_transportType + transport:_transport hostNameOverride:_hostNameOverride logContext:_logContext channelPoolDomain:_channelPoolDomain @@ -256,6 +264,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:[_PEMPrivateKey copy] PEMCertificateChain:[_PEMCertificateChain copy] transportType:_transportType + transport:_transport hostNameOverride:[_hostNameOverride copy] logContext:_logContext channelPoolDomain:[_channelPoolDomain copy] @@ -280,6 +289,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { if (!areObjectsEqual(callOptions.PEMCertificateChain, _PEMCertificateChain)) return NO; if (!areObjectsEqual(callOptions.hostNameOverride, _hostNameOverride)) return NO; if (!(callOptions.transportType == _transportType)) return NO; + if (!(TransportIdIsEqual(callOptions.transport, _transport))) return NO; if (!areObjectsEqual(callOptions.logContext, _logContext)) return NO; if (!areObjectsEqual(callOptions.channelPoolDomain, _channelPoolDomain)) return NO; if (!(callOptions.channelID == _channelID)) return NO; @@ -304,6 +314,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { result ^= _PEMCertificateChain.hash; result ^= _hostNameOverride.hash; result ^= _transportType; + result ^= TransportIdHash(_transport); result ^= _logContext.hash; result ^= _channelPoolDomain.hash; result ^= _channelID; @@ -336,6 +347,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { @dynamic PEMPrivateKey; @dynamic PEMCertificateChain; @dynamic transportType; +@dynamic transport; @dynamic hostNameOverride; @dynamic logContext; @dynamic channelPoolDomain; @@ -363,6 +375,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:kDefaultPEMPrivateKey PEMCertificateChain:kDefaultPEMCertificateChain transportType:kDefaultTransportType + transport:kDefaultTransport hostNameOverride:kDefaultHostNameOverride logContext:kDefaultLogContext channelPoolDomain:kDefaultChannelPoolDomain @@ -392,6 +405,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:_PEMPrivateKey PEMCertificateChain:_PEMCertificateChain transportType:_transportType + transport:_transport hostNameOverride:_hostNameOverride logContext:_logContext channelPoolDomain:_channelPoolDomain @@ -422,6 +436,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { PEMPrivateKey:_PEMPrivateKey PEMCertificateChain:_PEMCertificateChain transportType:_transportType + transport:_transport hostNameOverride:_hostNameOverride logContext:_logContext channelPoolDomain:_channelPoolDomain @@ -445,7 +460,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) { _flowControlEnabled = flowControlEnabled; } -- (void)setInterceptorFactories:(NSArray *)interceptorFactories { +- (void)setInterceptorFactories:(NSArray<id<GRPCInterceptorFactory>> *)interceptorFactories { _interceptorFactories = interceptorFactories; } @@ -538,6 +553,10 @@ static BOOL areObjectsEqual(id obj1, id obj2) { _transportType = transportType; } +- (void)setTransport:(GRPCTransportId)transport { + _transport = transport; +} + - (void)setHostNameOverride:(NSString *)hostNameOverride { _hostNameOverride = [hostNameOverride copy]; } diff --git a/src/objective-c/GRPCClient/GRPCDispatchable.h b/src/objective-c/GRPCClient/GRPCDispatchable.h new file mode 100644 index 0000000000000000000000000000000000000000..650103603d4304e599d6e6d9f3f4c0c3c8764cce --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCDispatchable.h @@ -0,0 +1,30 @@ + +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * An object that processes its methods with a dispatch queue. + */ +@protocol GRPCDispatchable + +/** + * The dispatch queue where the object's methods should be run on. + */ +@property(atomic, readonly) dispatch_queue_t dispatchQueue; + +@end diff --git a/src/objective-c/GRPCClient/GRPCInterceptor.h b/src/objective-c/GRPCClient/GRPCInterceptor.h index 3b62c1b3ec03fe07fe97da95c99a4464755e5bd9..509749769b333c4619d0236decd28bbe51e99619 100644 --- a/src/objective-c/GRPCClient/GRPCInterceptor.h +++ b/src/objective-c/GRPCClient/GRPCInterceptor.h @@ -106,22 +106,20 @@ */ #import "GRPCCall.h" +#import "GRPCDispatchable.h" NS_ASSUME_NONNULL_BEGIN @class GRPCInterceptorManager; @class GRPCInterceptor; +@class GRPCRequestOptions; +@class GRPCCallOptions; +@protocol GRPCResponseHandler; /** * The GRPCInterceptorInterface defines the request events that can occur to an interceptr. */ -@protocol GRPCInterceptorInterface<NSObject> - -/** - * The queue on which all methods of this interceptor should be dispatched on. The queue must be a - * serial queue. - */ -@property(readonly) dispatch_queue_t requestDispatchQueue; +@protocol GRPCInterceptorInterface<NSObject, GRPCDispatchable> /** * To start the call. This method will only be called once for each instance. @@ -171,19 +169,20 @@ NS_ASSUME_NONNULL_BEGIN * invoke shutDown method of its corresponding manager so that references to other interceptors can * be released. */ -@interface GRPCInterceptorManager : NSObject +@interface GRPCInterceptorManager : NSObject<GRPCInterceptorInterface, GRPCResponseHandler> - (instancetype)init NS_UNAVAILABLE; + (instancetype) new NS_UNAVAILABLE; -- (nullable instancetype)initWithNextInterceptor:(id<GRPCInterceptorInterface>)nextInterceptor - NS_DESIGNATED_INITIALIZER; - -/** Set the previous interceptor in the chain. Can only be set once. */ -- (void)setPreviousInterceptor:(id<GRPCResponseHandler>)previousInterceptor; +- (nullable instancetype)initWithFactories:(nullable NSArray<id<GRPCInterceptorFactory>> *)factories + previousInterceptor:(nullable id<GRPCResponseHandler>)previousInterceptor + transportId:(GRPCTransportId)transportId; -/** Indicate shutdown of the interceptor; release the reference to other interceptors */ +/** + * Notify the manager that the interceptor has shut down and the manager should release references + * to other interceptors and stop forwarding requests/responses. + */ - (void)shutDown; // Methods to forward GRPCInterceptorInterface calls to the next interceptor @@ -235,7 +234,6 @@ NS_ASSUME_NONNULL_BEGIN @interface GRPCInterceptor : NSObject<GRPCInterceptorInterface, GRPCResponseHandler> - (instancetype)init NS_UNAVAILABLE; - + (instancetype) new NS_UNAVAILABLE; /** @@ -243,9 +241,7 @@ NS_ASSUME_NONNULL_BEGIN * that this interceptor's methods are dispatched onto. */ - (nullable instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager - requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue - NS_DESIGNATED_INITIALIZER; + dispatchQueue:(dispatch_queue_t)dispatchQueue; // Default implementation of GRPCInterceptorInterface diff --git a/src/objective-c/GRPCClient/GRPCInterceptor.m b/src/objective-c/GRPCClient/GRPCInterceptor.m index a385ecd781330b5de3620d2ece66059b346e68ed..a7ffe05bddcd91d098cd7b8d6eed280af3b73f90 100644 --- a/src/objective-c/GRPCClient/GRPCInterceptor.m +++ b/src/objective-c/GRPCClient/GRPCInterceptor.m @@ -19,117 +19,253 @@ #import <Foundation/Foundation.h> #import "GRPCInterceptor.h" +#import "private/GRPCTransport+Private.h" + +@interface GRPCInterceptorManager ()<GRPCInterceptorInterface, GRPCResponseHandler> + +@end @implementation GRPCInterceptorManager { id<GRPCInterceptorInterface> _nextInterceptor; id<GRPCResponseHandler> _previousInterceptor; + GRPCInterceptor *_thisInterceptor; + dispatch_queue_t _dispatchQueue; + NSArray<id<GRPCInterceptorFactory>> *_factories; + GRPCTransportId _transportId; + BOOL _shutDown; } -- (instancetype)initWithNextInterceptor:(id<GRPCInterceptorInterface>)nextInterceptor { +- (instancetype)initWithFactories:(NSArray<id<GRPCInterceptorFactory>> *)factories + previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor + transportId:(nonnull GRPCTransportId)transportId { if ((self = [super init])) { - _nextInterceptor = nextInterceptor; + if (factories.count == 0) { + [NSException raise:NSInternalInconsistencyException + format:@"Interceptor manager must have factories"]; + } + _thisInterceptor = [factories[0] createInterceptorWithManager:self]; + if (_thisInterceptor == nil) { + return nil; + } + _previousInterceptor = previousInterceptor; + _factories = factories; + // Generate interceptor +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + if (@available(iOS 8.0, macOS 10.10, *)) { + _dispatchQueue = dispatch_queue_create( + NULL, + dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); + } else { +#else + { +#endif + _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + } + dispatch_set_target_queue(_dispatchQueue, _thisInterceptor.dispatchQueue); + _transportId = transportId; } - return self; } -- (void)setPreviousInterceptor:(id<GRPCResponseHandler>)previousInterceptor { - _previousInterceptor = previousInterceptor; +- (void)shutDown { + dispatch_async(_dispatchQueue, ^{ + self->_nextInterceptor = nil; + self->_previousInterceptor = nil; + self->_thisInterceptor = nil; + self->_shutDown = YES; + }); } -- (void)shutDown { - _nextInterceptor = nil; - _previousInterceptor = nil; +- (void)createNextInterceptor { + NSAssert(_nextInterceptor == nil, @"Starting the next interceptor more than once"); + NSAssert(_factories.count > 0, @"Interceptor manager of transport cannot start next interceptor"); + if (_nextInterceptor != nil) { + NSLog(@"Starting the next interceptor more than once"); + return; + } + NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories = [NSMutableArray + arrayWithArray:[_factories subarrayWithRange:NSMakeRange(1, _factories.count - 1)]]; + while (_nextInterceptor == nil) { + if (interceptorFactories.count == 0) { + _nextInterceptor = + [[GRPCTransportManager alloc] initWithTransportId:_transportId previousInterceptor:self]; + break; + } else { + _nextInterceptor = [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories + previousInterceptor:self + transportId:_transportId]; + if (_nextInterceptor == nil) { + [interceptorFactories removeObjectAtIndex:0]; + } + } + } + NSAssert(_nextInterceptor != nil, @"Failed to create interceptor or transport."); + if (_nextInterceptor == nil) { + NSLog(@"Failed to create interceptor or transport."); + } } - (void)startNextInterceptorWithRequest:(GRPCRequestOptions *)requestOptions callOptions:(GRPCCallOptions *)callOptions { - if (_nextInterceptor != nil) { - id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; - dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{ - [copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; - }); + if (_nextInterceptor == nil && !_shutDown) { + [self createNextInterceptor]; } + if (_nextInterceptor == nil) { + return; + } + id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; + dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ + [copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; + }); } - (void)writeNextInterceptorWithData:(id)data { - if (_nextInterceptor != nil) { - id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; - dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{ - [copiedNextInterceptor writeData:data]; - }); + if (_nextInterceptor == nil && !_shutDown) { + [self createNextInterceptor]; + } + if (_nextInterceptor == nil) { + return; } + id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; + dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ + [copiedNextInterceptor writeData:data]; + }); } - (void)finishNextInterceptor { - if (_nextInterceptor != nil) { - id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; - dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{ - [copiedNextInterceptor finish]; - }); + if (_nextInterceptor == nil && !_shutDown) { + [self createNextInterceptor]; + } + if (_nextInterceptor == nil) { + return; } + id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; + dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ + [copiedNextInterceptor finish]; + }); } - (void)cancelNextInterceptor { - if (_nextInterceptor != nil) { - id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; - dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{ - [copiedNextInterceptor cancel]; - }); + if (_nextInterceptor == nil && !_shutDown) { + [self createNextInterceptor]; + } + if (_nextInterceptor == nil) { + return; } + id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; + dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ + [copiedNextInterceptor cancel]; + }); } /** Notify the next interceptor in the chain to receive more messages */ - (void)receiveNextInterceptorMessages:(NSUInteger)numberOfMessages { - if (_nextInterceptor != nil) { - id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; - dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{ - [copiedNextInterceptor receiveNextMessages:numberOfMessages]; - }); + if (_nextInterceptor == nil && !_shutDown) { + [self createNextInterceptor]; + } + if (_nextInterceptor == nil) { + return; } + id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor; + dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ + [copiedNextInterceptor receiveNextMessages:numberOfMessages]; + }); } // Methods to forward GRPCResponseHandler callbacks to the previous object /** Forward initial metadata to the previous interceptor in the chain */ -- (void)forwardPreviousInterceptorWithInitialMetadata:(nullable NSDictionary *)initialMetadata { - if ([_previousInterceptor respondsToSelector:@selector(didReceiveInitialMetadata:)]) { - id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; - dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ - [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata]; - }); +- (void)forwardPreviousInterceptorWithInitialMetadata:(NSDictionary *)initialMetadata { + if (_previousInterceptor == nil) { + return; } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata]; + }); } /** Forward a received message to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorWithData:(id)data { - if ([_previousInterceptor respondsToSelector:@selector(didReceiveData:)]) { - id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; - dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ - [copiedPreviousInterceptor didReceiveData:data]; - }); + if (_previousInterceptor == nil) { + return; } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didReceiveData:data]; + }); } /** Forward call close and trailing metadata to the previous interceptor in the chain */ -- (void)forwardPreviousInterceptorCloseWithTrailingMetadata: - (nullable NSDictionary *)trailingMetadata - error:(nullable NSError *)error { - if ([_previousInterceptor respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { - id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; - dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ - [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; - }); +- (void)forwardPreviousInterceptorCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata + error:(NSError *)error { + if (_previousInterceptor == nil) { + return; } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; + }); } /** Forward write completion to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorDidWriteData { - if ([_previousInterceptor respondsToSelector:@selector(didWriteData)]) { - id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; - dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ - [copiedPreviousInterceptor didWriteData]; - }); + if (_previousInterceptor == nil) { + return; + } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didWriteData]; + }); +} + +- (dispatch_queue_t)dispatchQueue { + return _dispatchQueue; +} + +- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions + callOptions:(GRPCCallOptions *)callOptions { + [_thisInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; +} + +- (void)writeData:(id)data { + [_thisInterceptor writeData:data]; +} + +- (void)finish { + [_thisInterceptor finish]; +} + +- (void)cancel { + [_thisInterceptor cancel]; +} + +- (void)receiveNextMessages:(NSUInteger)numberOfMessages { + [_thisInterceptor receiveNextMessages:numberOfMessages]; +} + +- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata { + if ([_thisInterceptor respondsToSelector:@selector(didReceiveInitialMetadata:)]) { + [_thisInterceptor didReceiveInitialMetadata:initialMetadata]; + } +} + +- (void)didReceiveData:(id)data { + if ([_thisInterceptor respondsToSelector:@selector(didReceiveData:)]) { + [_thisInterceptor didReceiveData:data]; + } +} + +- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata + error:(nullable NSError *)error { + if ([_thisInterceptor respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { + [_thisInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; + } +} + +- (void)didWriteData { + if ([_thisInterceptor respondsToSelector:@selector(didWriteData)]) { + [_thisInterceptor didWriteData]; } } @@ -137,28 +273,21 @@ @implementation GRPCInterceptor { GRPCInterceptorManager *_manager; - dispatch_queue_t _requestDispatchQueue; - dispatch_queue_t _responseDispatchQueue; + dispatch_queue_t _dispatchQueue; } - (instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager - requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue { + dispatchQueue:(dispatch_queue_t)dispatchQueue { if ((self = [super init])) { _manager = interceptorManager; - _requestDispatchQueue = requestDispatchQueue; - _responseDispatchQueue = responseDispatchQueue; + _dispatchQueue = dispatchQueue; } return self; } -- (dispatch_queue_t)requestDispatchQueue { - return _requestDispatchQueue; -} - - (dispatch_queue_t)dispatchQueue { - return _responseDispatchQueue; + return _dispatchQueue; } - (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions diff --git a/src/objective-c/GRPCClient/GRPCTransport.h b/src/objective-c/GRPCClient/GRPCTransport.h new file mode 100644 index 0000000000000000000000000000000000000000..d5637922152bb64b861314b96979e04ca39a8f02 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCTransport.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// The interface for a transport implementation + +#import "GRPCInterceptor.h" + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Transport ID + +/** + * The default transport implementations available in gRPC. These implementations will be provided + * by gRPC by default unless explicitly excluded. + */ +extern const struct GRPCDefaultTransportImplList { + const GRPCTransportId core_secure; + const GRPCTransportId core_insecure; +} GRPCDefaultTransportImplList; + +/** Returns whether two transport id's are identical. */ +BOOL TransportIdIsEqual(GRPCTransportId lhs, GRPCTransportId rhs); + +/** Returns the hash value of a transport id. */ +NSUInteger TransportIdHash(GRPCTransportId); + +#pragma mark Transport and factory + +@protocol GRPCInterceptorInterface; +@protocol GRPCResponseHandler; +@class GRPCTransportManager; +@class GRPCRequestOptions; +@class GRPCCallOptions; +@class GRPCTransport; + +/** The factory method to create a transport. */ +@protocol GRPCTransportFactory<NSObject> + +- (GRPCTransport *)createTransportWithManager:(GRPCTransportManager *)transportManager; + +@end + +/** The registry of transport implementations. */ +@interface GRPCTransportRegistry : NSObject + ++ (instancetype)sharedInstance; + +/** + * Register a transport implementation with the registry. All transport implementations to be used + * in a process must register with the registry on process start-up in its +load: class method. + * Parameter \a transportId is the identifier of the implementation, and \a factory is the factory + * object to create the corresponding transport instance. + */ +- (void)registerTransportWithId:(GRPCTransportId)transportId + factory:(id<GRPCTransportFactory>)factory; + +@end + +/** + * Base class for transport implementations. All transport implementation should inherit from this + * class. + */ +@interface GRPCTransport : NSObject<GRPCInterceptorInterface> + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/objective-c/GRPCClient/GRPCTransport.m b/src/objective-c/GRPCClient/GRPCTransport.m new file mode 100644 index 0000000000000000000000000000000000000000..439acfb9cc231a3d2b51bbd48bc4882b8d062123 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCTransport.m @@ -0,0 +1,142 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCTransport.h" + +static const GRPCTransportId gGRPCCoreSecureId = "io.grpc.transport.core.secure"; +static const GRPCTransportId gGRPCCoreInsecureId = "io.grpc.transport.core.insecure"; + +const struct GRPCDefaultTransportImplList GRPCDefaultTransportImplList = { + .core_secure = gGRPCCoreSecureId, .core_insecure = gGRPCCoreInsecureId}; + +static const GRPCTransportId gDefaultTransportId = gGRPCCoreSecureId; + +static GRPCTransportRegistry *gTransportRegistry = nil; +static dispatch_once_t initTransportRegistry; + +BOOL TransportIdIsEqual(GRPCTransportId lhs, GRPCTransportId rhs) { + // Directly comparing pointers works because we require users to use the id provided by each + // implementation, not coming up with their own string. + return lhs == rhs; +} + +NSUInteger TransportIdHash(GRPCTransportId transportId) { + if (transportId == NULL) { + transportId = gDefaultTransportId; + } + return [NSString stringWithCString:transportId encoding:NSUTF8StringEncoding].hash; +} + +@implementation GRPCTransportRegistry { + NSMutableDictionary<NSString *, id<GRPCTransportFactory>> *_registry; + id<GRPCTransportFactory> _defaultFactory; +} + ++ (instancetype)sharedInstance { + dispatch_once(&initTransportRegistry, ^{ + gTransportRegistry = [[GRPCTransportRegistry alloc] init]; + NSAssert(gTransportRegistry != nil, @"Unable to initialize transport registry."); + if (gTransportRegistry == nil) { + NSLog(@"Unable to initialize transport registry."); + [NSException raise:NSGenericException format:@"Unable to initialize transport registry."]; + } + }); + return gTransportRegistry; +} + +- (instancetype)init { + if ((self = [super init])) { + _registry = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)registerTransportWithId:(GRPCTransportId)transportId + factory:(id<GRPCTransportFactory>)factory { + NSString *nsTransportId = [NSString stringWithCString:transportId encoding:NSUTF8StringEncoding]; + NSAssert(_registry[nsTransportId] == nil, @"The transport %@ has already been registered.", + nsTransportId); + if (_registry[nsTransportId] != nil) { + NSLog(@"The transport %@ has already been registered.", nsTransportId); + return; + } + _registry[nsTransportId] = factory; + + // if the default transport is registered, mark it. + if (0 == strcmp(transportId, gDefaultTransportId)) { + _defaultFactory = factory; + } +} + +- (id<GRPCTransportFactory>)getTransportFactoryWithId:(GRPCTransportId)transportId { + if (transportId == NULL) { + if (_defaultFactory == nil) { + [NSException raise:NSInvalidArgumentException + format:@"Unable to get default transport factory"]; + return nil; + } + return _defaultFactory; + } + NSString *nsTransportId = [NSString stringWithCString:transportId encoding:NSUTF8StringEncoding]; + id<GRPCTransportFactory> transportFactory = _registry[nsTransportId]; + if (transportFactory == nil) { + // User named a transport id that was not registered with the registry. + [NSException raise:NSInvalidArgumentException + format:@"Unable to get transport factory with id %s", transportId]; + return nil; + } + return transportFactory; +} + +@end + +@implementation GRPCTransport + +- (dispatch_queue_t)dispatchQueue { + [NSException raise:NSGenericException + format:@"Implementations should override the dispatch queue"]; + return nil; +} + +- (void)startWithRequestOptions:(nonnull GRPCRequestOptions *)requestOptions + callOptions:(nonnull GRPCCallOptions *)callOptions { + [NSException raise:NSGenericException + format:@"Implementations should override the methods of GRPCTransport"]; +} + +- (void)writeData:(nonnull id)data { + [NSException raise:NSGenericException + format:@"Implementations should override the methods of GRPCTransport"]; +} + +- (void)cancel { + [NSException raise:NSGenericException + format:@"Implementations should override the methods of GRPCTransport"]; +} + +- (void)finish { + [NSException raise:NSGenericException + format:@"Implementations should override the methods of GRPCTransport"]; +} + +- (void)receiveNextMessages:(NSUInteger)numberOfMessages { + [NSException raise:NSGenericException + format:@"Implementations should override the methods of GRPCTransport"]; +} + +@end diff --git a/src/objective-c/GRPCClient/GRPCTypes.h b/src/objective-c/GRPCClient/GRPCTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..c804bca4eaaf0a6845106abd749562282c906ad4 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCTypes.h @@ -0,0 +1,187 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * gRPC error codes. + * Note that a few of these are never produced by the gRPC libraries, but are of + * general utility for server applications to produce. + */ +typedef NS_ENUM(NSUInteger, GRPCErrorCode) { + /** The operation was cancelled (typically by the caller). */ + GRPCErrorCodeCancelled = 1, + + /** + * Unknown error. Errors raised by APIs that do not return enough error + * information may be converted to this error. + */ + GRPCErrorCodeUnknown = 2, + + /** + * The client specified an invalid argument. Note that this differs from + * FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are + * problematic regardless of the state of the server (e.g., a malformed file + * name). + */ + GRPCErrorCodeInvalidArgument = 3, + + /** + * Deadline expired before operation could complete. For operations that + * change the state of the server, this error may be returned even if the + * operation has completed successfully. For example, a successful response + * from the server could have been delayed long enough for the deadline to + * expire. + */ + GRPCErrorCodeDeadlineExceeded = 4, + + /** Some requested entity (e.g., file or directory) was not found. */ + GRPCErrorCodeNotFound = 5, + + /** Some entity that we attempted to create (e.g., file or directory) already + exists. */ + GRPCErrorCodeAlreadyExists = 6, + + /** + * The caller does not have permission to execute the specified operation. + * PERMISSION_DENIED isn't used for rejections caused by exhausting some + * resource (RESOURCE_EXHAUSTED is used instead for those errors). + * PERMISSION_DENIED doesn't indicate a failure to identify the caller + * (UNAUTHENTICATED is used instead for those errors). + */ + GRPCErrorCodePermissionDenied = 7, + + /** + * The request does not have valid authentication credentials for the + * operation (e.g. the caller's identity can't be verified). + */ + GRPCErrorCodeUnauthenticated = 16, + + /** Some resource has been exhausted, perhaps a per-user quota. */ + GRPCErrorCodeResourceExhausted = 8, + + /** + * The RPC was rejected because the server is not in a state required for the + * procedure's execution. For example, a directory to be deleted may be + * non-empty, etc. The client should not retry until the server state has been + * explicitly fixed (e.g. by performing another RPC). The details depend on + * the service being called, and should be found in the NSError's userInfo. + */ + GRPCErrorCodeFailedPrecondition = 9, + + /** + * The RPC was aborted, typically due to a concurrency issue like sequencer + * check failures, transaction aborts, etc. The client should retry at a + * higher-level (e.g., restarting a read- modify-write sequence). + */ + GRPCErrorCodeAborted = 10, + + /** + * The RPC was attempted past the valid range. E.g., enumerating past the end + * of a list. Unlike INVALID_ARGUMENT, this error indicates a problem that may + * be fixed if the system state changes. For example, an RPC to get elements + * of a list will generate INVALID_ARGUMENT if asked to return the element at + * a negative index, but it will generate OUT_OF_RANGE if asked to return the + * element at an index past the current size of the list. + */ + GRPCErrorCodeOutOfRange = 11, + + /** The procedure is not implemented or not supported/enabled in this server. + */ + GRPCErrorCodeUnimplemented = 12, + + /** + * Internal error. Means some invariant expected by the server application or + * the gRPC library has been broken. + */ + GRPCErrorCodeInternal = 13, + + /** + * The server is currently unavailable. This is most likely a transient + * condition and may be corrected by retrying with a backoff. Note that it is + * not always safe to retry non-idempotent operations. + */ + GRPCErrorCodeUnavailable = 14, + + /** Unrecoverable data loss or corruption. */ + GRPCErrorCodeDataLoss = 15, +}; + +/** + * Safety remark of a gRPC method as defined in RFC 2616 Section 9.1 + */ +typedef NS_ENUM(NSUInteger, GRPCCallSafety) { + /** + * Signal that there is no guarantees on how the call affects the server + * state. + */ + GRPCCallSafetyDefault = 0, + /** Signal that the call is idempotent. gRPC is free to use PUT verb. */ + GRPCCallSafetyIdempotentRequest = 1, + /** + * Signal that the call is cacheable and will not affect server state. gRPC is + * free to use GET verb. + */ + GRPCCallSafetyCacheableRequest = 2, +}; + +// Compression algorithm to be used by a gRPC call +typedef NS_ENUM(NSUInteger, GRPCCompressionAlgorithm) { + GRPCCompressNone = 0, + GRPCCompressDeflate, + GRPCCompressGzip, + GRPCStreamCompressGzip, +}; + +// GRPCCompressAlgorithm is deprecated; use GRPCCompressionAlgorithm +typedef GRPCCompressionAlgorithm GRPCCompressAlgorithm; + +/** The transport to be used by a gRPC call */ +typedef NS_ENUM(NSUInteger, GRPCTransportType) { + GRPCTransportTypeDefault = 0, + /** gRPC internal HTTP/2 stack with BoringSSL */ + GRPCTransportTypeChttp2BoringSSL = 0, + /** Cronet stack */ + GRPCTransportTypeCronet, + /** Insecure channel. FOR TEST ONLY! */ + GRPCTransportTypeInsecure, +}; + +/** Domain of NSError objects produced by gRPC. */ +extern NSString* _Nonnull const kGRPCErrorDomain; + +/** + * Keys used in |NSError|'s |userInfo| dictionary to store the response headers + * and trailers sent by the server. + */ +extern NSString* _Nonnull const kGRPCHeadersKey; +extern NSString* _Nonnull const kGRPCTrailersKey; + +/** The id of a transport implementation. */ +typedef char* _Nonnull GRPCTransportId; + +/** + * Implement this protocol to provide a token to gRPC when a call is initiated. + */ +@protocol GRPCAuthorizationProtocol + +/** + * This method is called when gRPC is about to start the call. When OAuth token is acquired, + * \a handler is expected to be called with \a token being the new token to be used for this call. + */ +- (void)getTokenWithHandler:(void (^_Nonnull)(NSString* _Nullable token))handler; + +@end diff --git a/src/objective-c/GRPCClient/internal_testing/GRPCCall+InternalTests.m b/src/objective-c/GRPCClient/internal_testing/GRPCCall+InternalTests.m index 1bb352f0be2f3eb0f2111d50f02079a8b03e77bc..8f98daa6348679d409f1db3f419e42124824b5f5 100644 --- a/src/objective-c/GRPCClient/internal_testing/GRPCCall+InternalTests.m +++ b/src/objective-c/GRPCClient/internal_testing/GRPCCall+InternalTests.m @@ -20,7 +20,7 @@ #import "GRPCCall+InternalTests.h" -#import "../private/GRPCOpBatchLog.h" +#import "../private/GRPCCore/GRPCOpBatchLog.h" @implementation GRPCCall (InternalTests) diff --git a/src/objective-c/GRPCClient/private/ChannelArgsUtil.h b/src/objective-c/GRPCClient/private/GRPCCore/ChannelArgsUtil.h similarity index 100% rename from src/objective-c/GRPCClient/private/ChannelArgsUtil.h rename to src/objective-c/GRPCClient/private/GRPCCore/ChannelArgsUtil.h diff --git a/src/objective-c/GRPCClient/private/ChannelArgsUtil.m b/src/objective-c/GRPCClient/private/GRPCCore/ChannelArgsUtil.m similarity index 100% rename from src/objective-c/GRPCClient/private/ChannelArgsUtil.m rename to src/objective-c/GRPCClient/private/GRPCCore/ChannelArgsUtil.m diff --git a/src/objective-c/GRPCClient/private/GRPCCall+V2API.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCall+V2API.h similarity index 79% rename from src/objective-c/GRPCClient/private/GRPCCall+V2API.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCall+V2API.h index 22bf16962c6a7afb290c2000ad02bf2da14a8e3f..f6db3023cac376b4ead3f752145161c2364d9790 100644 --- a/src/objective-c/GRPCClient/private/GRPCCall+V2API.h +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCall+V2API.h @@ -18,12 +18,6 @@ @interface GRPCCall (V2API) -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - callSafety:(GRPCCallSafety)safety - requestsWriter:(GRXWriter *)requestsWriter - callOptions:(GRPCCallOptions *)callOptions; - - (instancetype)initWithHost:(NSString *)host path:(NSString *)path callSafety:(GRPCCallSafety)safety diff --git a/src/objective-c/GRPCClient/private/GRPCCallInternal.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.h similarity index 70% rename from src/objective-c/GRPCClient/private/GRPCCallInternal.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.h index ac2d1cba2ec8ab20bc56bc9720aab43bfe0b0be2..641b1fb2e8a9968f42accb36bcda677087749da3 100644 --- a/src/objective-c/GRPCClient/private/GRPCCallInternal.h +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.h @@ -16,20 +16,22 @@ * */ -#import <GRPCClient/GRPCInterceptor.h> +#import <GRPCClient/GRPCTransport.h> NS_ASSUME_NONNULL_BEGIN -@interface GRPCCall2Internal : NSObject<GRPCInterceptorInterface> +@protocol GRPCResponseHandler; +@class GRPCCallOptions; +@protocol GRPCChannelFactory; -- (instancetype)init; +@interface GRPCCall2Internal : GRPCTransport -- (void)setResponseHandler:(id<GRPCResponseHandler>)responseHandler; +- (instancetype)initWithTransportManager:(GRPCTransportManager *)transportManager; - (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions - callOptions:(nullable GRPCCallOptions *)callOptions; + callOptions:(GRPCCallOptions *)callOptions; -- (void)writeData:(NSData *)data; +- (void)writeData:(id)data; - (void)finish; diff --git a/src/objective-c/GRPCClient/private/GRPCCallInternal.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.m similarity index 69% rename from src/objective-c/GRPCClient/private/GRPCCallInternal.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.m index 32e38158fad5b2264fcce829a45f519db13995a8..ea01fcaf5944f25042ebf623a6d270e7f06e9d3c 100644 --- a/src/objective-c/GRPCClient/private/GRPCCallInternal.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCallInternal.m @@ -19,8 +19,10 @@ #import "GRPCCallInternal.h" #import <GRPCClient/GRPCCall.h> +#import <GRPCClient/GRPCInterceptor.h> #import <RxLibrary/GRXBufferedPipe.h> +#import "../GRPCTransport+Private.h" #import "GRPCCall+V2API.h" @implementation GRPCCall2Internal { @@ -28,8 +30,8 @@ GRPCRequestOptions *_requestOptions; /** Options for the call. */ GRPCCallOptions *_callOptions; - /** The handler of responses. */ - id<GRPCResponseHandler> _handler; + /** The interceptor manager to process responses. */ + GRPCTransportManager *_transportManager; /** * Make use of legacy GRPCCall to make calls. Nullified when call is finished. @@ -51,40 +53,28 @@ NSUInteger _pendingReceiveNextMessages; } -- (instancetype)init { - if ((self = [super init])) { +- (instancetype)initWithTransportManager:(GRPCTransportManager *)transportManager { + dispatch_queue_t dispatchQueue; // Set queue QoS only when iOS version is 8.0 or above and Xcode version is 9.0 or above #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 - if (@available(iOS 8.0, macOS 10.10, *)) { - _dispatchQueue = dispatch_queue_create( - NULL, - dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); - } else { + if (@available(iOS 8.0, macOS 10.10, *)) { + dispatchQueue = dispatch_queue_create( + NULL, dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); + } else { #else - { + { #endif - _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); - } + dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + } + if ((self = [super init])) { _pipe = [GRXBufferedPipe pipe]; + _transportManager = transportManager; + _dispatchQueue = dispatchQueue; } return self; } -- (void)setResponseHandler:(id<GRPCResponseHandler>)responseHandler { - @synchronized(self) { - NSAssert(!_started, @"Call already started."); - if (_started) { - return; - } - _handler = responseHandler; - _initialMetadataPublished = NO; - _started = NO; - _canceled = NO; - _finished = NO; - } -} - -- (dispatch_queue_t)requestDispatchQueue { +- (dispatch_queue_t)dispatchQueue { return _dispatchQueue; } @@ -102,26 +92,15 @@ return; } + GRPCCall *copiedCall = nil; @synchronized(self) { - NSAssert(_handler != nil, @"Response handler required."); - if (_handler == nil) { - NSLog(@"Invalid response handler."); - return; - } _requestOptions = requestOptions; if (callOptions == nil) { _callOptions = [[GRPCCallOptions alloc] init]; } else { _callOptions = [callOptions copy]; } - } - [self start]; -} - -- (void)start { - GRPCCall *copiedCall = nil; - @synchronized(self) { NSAssert(!_started, @"Call already started."); NSAssert(!_canceled, @"Call already canceled."); if (_started) { @@ -140,7 +119,7 @@ callOptions:_callOptions writeDone:^{ @synchronized(self) { - if (self->_handler) { + if (self->_transportManager) { [self issueDidWriteData]; } } @@ -158,7 +137,7 @@ void (^valueHandler)(id value) = ^(id value) { @synchronized(self) { - if (self->_handler) { + if (self->_transportManager) { if (!self->_initialMetadataPublished) { self->_initialMetadataPublished = YES; [self issueInitialMetadata:self->_call.responseHeaders]; @@ -171,7 +150,7 @@ }; void (^completionHandler)(NSError *errorOrNil) = ^(NSError *errorOrNil) { @synchronized(self) { - if (self->_handler) { + if (self->_transportManager) { if (!self->_initialMetadataPublished) { self->_initialMetadataPublished = YES; [self issueInitialMetadata:self->_call.responseHeaders]; @@ -207,20 +186,19 @@ _call = nil; _pipe = nil; - if ([_handler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - _handler = nil; - dispatch_async(copiedHandler.dispatchQueue, ^{ - [copiedHandler didCloseWithTrailingMetadata:nil - error:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeCancelled - userInfo:@{ - NSLocalizedDescriptionKey : - @"Canceled by app" - }]]; - }); - } else { - _handler = nil; + if (_transportManager != nil) { + [_transportManager + forwardPreviousInterceptorCloseWithTrailingMetadata:nil + error: + [NSError + errorWithDomain:kGRPCErrorDomain + code: + GRPCErrorCodeCancelled + userInfo:@{ + NSLocalizedDescriptionKey : + @"Canceled by app" + }]]; + [_transportManager shutDown]; } } [copiedCall cancel]; @@ -271,59 +249,25 @@ } - (void)issueInitialMetadata:(NSDictionary *)initialMetadata { - @synchronized(self) { - if (initialMetadata != nil && - [_handler respondsToSelector:@selector(didReceiveInitialMetadata:)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - dispatch_async(_handler.dispatchQueue, ^{ - [copiedHandler didReceiveInitialMetadata:initialMetadata]; - }); - } + if (initialMetadata != nil) { + [_transportManager forwardPreviousInterceptorWithInitialMetadata:initialMetadata]; } } - (void)issueMessage:(id)message { - @synchronized(self) { - if (message != nil) { - if ([_handler respondsToSelector:@selector(didReceiveData:)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - dispatch_async(_handler.dispatchQueue, ^{ - [copiedHandler didReceiveData:message]; - }); - } else if ([_handler respondsToSelector:@selector(didReceiveRawMessage:)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - dispatch_async(_handler.dispatchQueue, ^{ - [copiedHandler didReceiveRawMessage:message]; - }); - } - } + if (message != nil) { + [_transportManager forwardPreviousInterceptorWithData:message]; } } - (void)issueClosedWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { - @synchronized(self) { - if ([_handler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - // Clean up _handler so that no more responses are reported to the handler. - _handler = nil; - dispatch_async(copiedHandler.dispatchQueue, ^{ - [copiedHandler didCloseWithTrailingMetadata:trailingMetadata error:error]; - }); - } else { - _handler = nil; - } - } + [_transportManager forwardPreviousInterceptorCloseWithTrailingMetadata:trailingMetadata + error:error]; + [_transportManager shutDown]; } - (void)issueDidWriteData { - @synchronized(self) { - if (_callOptions.flowControlEnabled && [_handler respondsToSelector:@selector(didWriteData)]) { - id<GRPCResponseHandler> copiedHandler = _handler; - dispatch_async(copiedHandler.dispatchQueue, ^{ - [copiedHandler didWriteData]; - }); - } - } + [_transportManager forwardPreviousInterceptorDidWriteData]; } - (void)receiveNextMessages:(NSUInteger)numberOfMessages { diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannel.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCChannel.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannel.h diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannel.m similarity index 78% rename from src/objective-c/GRPCClient/private/GRPCChannel.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannel.m index 1a79fb04a0d5cae04cff1aee09e07b03cb86f338..cd2b4313acd646bec31be7297bcfe3ced04491ed 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannel.m @@ -20,18 +20,19 @@ #include <grpc/support/log.h> -#import "../internal/GRPCCallOptions+Internal.h" +#import "../../internal/GRPCCallOptions+Internal.h" +#import "../GRPCTransport+Private.h" #import "ChannelArgsUtil.h" #import "GRPCChannelFactory.h" #import "GRPCChannelPool.h" #import "GRPCCompletionQueue.h" -#import "GRPCCronetChannelFactory.h" +#import "GRPCCoreFactory.h" #import "GRPCInsecureChannelFactory.h" #import "GRPCSecureChannelFactory.h" -#import "version.h" #import <GRPCClient/GRPCCall+Cronet.h> #import <GRPCClient/GRPCCallOptions.h> +#import <GRPCClient/version.h> @implementation GRPCChannelConfiguration @@ -50,32 +51,48 @@ } - (id<GRPCChannelFactory>)channelFactory { - GRPCTransportType type = _callOptions.transportType; - switch (type) { - case GRPCTransportTypeChttp2BoringSSL: - // TODO (mxyan): Remove when the API is deprecated -#ifdef GRPC_COMPILE_WITH_CRONET - if (![GRPCCall isUsingCronet]) { -#else - { -#endif - NSError *error; - id<GRPCChannelFactory> factory = [GRPCSecureChannelFactory - factoryWithPEMRootCertificates:_callOptions.PEMRootCertificates - privateKey:_callOptions.PEMPrivateKey - certChain:_callOptions.PEMCertificateChain - error:&error]; - NSAssert(factory != nil, @"Failed to create secure channel factory"); - if (factory == nil) { - NSLog(@"Error creating secure channel factory: %@", error); + if (_callOptions.transport != NULL) { + id<GRPCTransportFactory> transportFactory = + [[GRPCTransportRegistry sharedInstance] getTransportFactoryWithId:_callOptions.transport]; + if (! + [transportFactory respondsToSelector:@selector(createCoreChannelFactoryWithCallOptions:)]) { + // impossible because we are using GRPCCore now + [NSException raise:NSInternalInconsistencyException + format:@"Transport factory type is wrong"]; + } + id<GRPCCoreTransportFactory> coreTransportFactory = + (id<GRPCCoreTransportFactory>)transportFactory; + return [coreTransportFactory createCoreChannelFactoryWithCallOptions:_callOptions]; + } else { + // To maintain backwards compatibility with tranportType + GRPCTransportType type = _callOptions.transportType; + switch (type) { + case GRPCTransportTypeChttp2BoringSSL: + // TODO (mxyan): Remove when the API is deprecated + { + NSError *error; + id<GRPCChannelFactory> factory = [GRPCSecureChannelFactory + factoryWithPEMRootCertificates:_callOptions.PEMRootCertificates + privateKey:_callOptions.PEMPrivateKey + certChain:_callOptions.PEMCertificateChain + error:&error]; + NSAssert(factory != nil, @"Failed to create secure channel factory"); + if (factory == nil) { + NSLog(@"Error creating secure channel factory: %@", error); + } + return factory; } - return factory; + case GRPCTransportTypeCronet: { + id<GRPCCoreTransportFactory> transportFactory = (id<GRPCCoreTransportFactory>)[ + [GRPCTransportRegistry sharedInstance] getTransportFactoryWithId:gGRPCCoreCronetId]; + return [transportFactory createCoreChannelFactoryWithCallOptions:_callOptions]; } - // fallthrough - case GRPCTransportTypeCronet: - return [GRPCCronetChannelFactory sharedInstance]; - case GRPCTransportTypeInsecure: - return [GRPCInsecureChannelFactory sharedInstance]; + case GRPCTransportTypeInsecure: + return [GRPCInsecureChannelFactory sharedInstance]; + default: + NSLog(@"Unrecognized transport type"); + return nil; + } } } @@ -198,6 +215,7 @@ } else { channelArgs = channelConfiguration.channelArgs; } + id<GRPCChannelFactory> factory = channelConfiguration.channelFactory; _unmanagedChannel = [factory createChannelWithHost:host channelArgs:channelArgs]; NSAssert(_unmanagedChannel != NULL, @"Failed to create channel"); diff --git a/src/objective-c/GRPCClient/private/GRPCChannelFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelFactory.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCChannelFactory.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelFactory.h diff --git a/src/objective-c/GRPCClient/private/GRPCChannelPool+Test.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool+Test.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCChannelPool+Test.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool+Test.h diff --git a/src/objective-c/GRPCClient/private/GRPCChannelPool.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.h similarity index 98% rename from src/objective-c/GRPCClient/private/GRPCChannelPool.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.h index e00ee69e63ab99480aeb7b843476c7e5ebf773db..b83a28a236878408a3a86f9fb45e7d06e18722d7 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannelPool.h +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.h @@ -18,8 +18,6 @@ #import <GRPCClient/GRPCCallOptions.h> -#import "GRPCChannelFactory.h" - NS_ASSUME_NONNULL_BEGIN @protocol GRPCChannel; diff --git a/src/objective-c/GRPCClient/private/GRPCChannelPool.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.m similarity index 98% rename from src/objective-c/GRPCClient/private/GRPCChannelPool.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.m index d545793fccec14cea438850b9cde3dbb7d205912..92f52e67b754a98394bb6cdc70a0763a323903a4 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannelPool.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCChannelPool.m @@ -18,19 +18,16 @@ #import <Foundation/Foundation.h> -#import "../internal/GRPCCallOptions+Internal.h" +#import "../../internal/GRPCCallOptions+Internal.h" #import "GRPCChannel.h" #import "GRPCChannelFactory.h" #import "GRPCChannelPool+Test.h" #import "GRPCChannelPool.h" #import "GRPCCompletionQueue.h" -#import "GRPCCronetChannelFactory.h" #import "GRPCInsecureChannelFactory.h" #import "GRPCSecureChannelFactory.h" #import "GRPCWrappedCall.h" -#import "version.h" -#import <GRPCClient/GRPCCall+Cronet.h> #include <grpc/support/log.h> extern const char *kCFStreamVarName; diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCompletionQueue.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCCompletionQueue.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCompletionQueue.h diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCompletionQueue.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCCompletionQueue.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCompletionQueue.m diff --git a/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..83d279d2c72e573a007be33d03fb50c1d89380d4 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "../GRPCCoreFactory.h" + +/** + * The factory for gRPC Core + Cronet transport implementation. The + * implementation is not part of the default transports of gRPC and is for + * testing purpose only on Github. + * + * To use this transport, a user must include the GRPCCoreCronet module as a + * dependency of the project and use gGRPCCoreCronetId in call options to + * specify that this is the transport to be used for a call. + */ +@interface GRPCCoreCronetFactory : NSObject<GRPCCoreTransportFactory> + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.m new file mode 100644 index 0000000000000000000000000000000000000000..5772694fc5402465e97f78beba54e20b83971052 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.m @@ -0,0 +1,54 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCCoreCronetFactory.h" + +#import <GRPCClient/GRPCCall+Cronet.h> +#import <GRPCClient/GRPCTransport.h> + +#import "../GRPCCallInternal.h" +#import "../GRPCCoreFactory.h" +#import "GRPCCronetChannelFactory.h" + +static GRPCCoreCronetFactory *gGRPCCoreCronetFactory = nil; +static dispatch_once_t gInitGRPCCoreCronetFactory; + +@implementation GRPCCoreCronetFactory + ++ (instancetype)sharedInstance { + dispatch_once(&gInitGRPCCoreCronetFactory, ^{ + gGRPCCoreCronetFactory = [[GRPCCoreCronetFactory alloc] init]; + }); + return gGRPCCoreCronetFactory; +} + ++ (void)load { + [[GRPCTransportRegistry sharedInstance] + registerTransportWithId:gGRPCCoreCronetId + factory:[GRPCCoreCronetFactory sharedInstance]]; +} + +- (GRPCTransport *)createTransportWithManager:(GRPCTransportManager *)transportManager { + return [[GRPCCall2Internal alloc] initWithTransportManager:transportManager]; +} + +- (id<GRPCChannelFactory>)createCoreChannelFactoryWithCallOptions:(GRPCCallOptions *)callOptions { + return [GRPCCronetChannelFactory sharedInstance]; +} + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.h similarity index 96% rename from src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.h index 738dfdb737029115642ce18ebf330275a213b58b..138ddf1f73010df8506bc38917f6b619be6902f0 100644 --- a/src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.h +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.h @@ -15,7 +15,7 @@ * limitations under the License. * */ -#import "GRPCChannelFactory.h" +#import "../GRPCChannelFactory.h" @class GRPCChannel; typedef struct stream_engine stream_engine; diff --git a/src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.m similarity index 76% rename from src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.m index 5bcb021dc4bc1f37d5b4ab0339688832933adedc..da3f3afd8558f88a79ebbec16acc9785a55dea76 100644 --- a/src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCronetChannelFactory.m @@ -18,10 +18,8 @@ #import "GRPCCronetChannelFactory.h" -#import "ChannelArgsUtil.h" -#import "GRPCChannel.h" - -#ifdef GRPC_COMPILE_WITH_CRONET +#import "../ChannelArgsUtil.h" +#import "../GRPCChannel.h" #import <Cronet/Cronet.h> #include <grpc/grpc_cronet.h> @@ -59,21 +57,3 @@ } @end - -#else - -@implementation GRPCCronetChannelFactory - -+ (instancetype)sharedInstance { - NSAssert(NO, @"Must enable macro GRPC_COMPILE_WITH_CRONET to build Cronet channel."); - return nil; -} - -- (grpc_channel *)createChannelWithHost:(NSString *)host channelArgs:(NSDictionary *)args { - NSAssert(NO, @"Must enable macro GRPC_COMPILE_WITH_CRONET to build Cronet channel."); - return NULL; -} - -@end - -#endif diff --git a/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..3cd312d4ad045aee2c383c3a4453f170b4b5850e --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import <GRPCClient/GRPCTransport.h> + +NS_ASSUME_NONNULL_BEGIN + +@protocol GRPCChannelFactory; +@protocol GRPCCallOptions; + +/** The interface for transport implementations that are based on Core. */ +@protocol GRPCCoreTransportFactory<GRPCTransportFactory> + +/** Get the channel factory for GRPCChannel from call options. */ +- (nullable id<GRPCChannelFactory>)createCoreChannelFactoryWithCallOptions: + (GRPCCallOptions *)callOptions; + +@end + +/** The factory for gRPC Core + CFStream + TLS secure channel transport implementation. */ +@interface GRPCCoreSecureFactory : NSObject<GRPCCoreTransportFactory> + +@end + +/** The factory for gRPC Core + CFStream + insecure channel transport implementation. */ +@interface GRPCCoreInsecureFactory : NSObject<GRPCCoreTransportFactory> + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.m new file mode 100644 index 0000000000000000000000000000000000000000..19d7231a203816d7a5e33b8dcb2f4a0a73a37369 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.m @@ -0,0 +1,90 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCCoreFactory.h" + +#import <GRPCClient/GRPCTransport.h> + +#import "GRPCCallInternal.h" +#import "GRPCInsecureChannelFactory.h" +#import "GRPCSecureChannelFactory.h" + +static GRPCCoreSecureFactory *gGRPCCoreSecureFactory = nil; +static GRPCCoreInsecureFactory *gGRPCCoreInsecureFactory = nil; +static dispatch_once_t gInitGRPCCoreSecureFactory; +static dispatch_once_t gInitGRPCCoreInsecureFactory; + +@implementation GRPCCoreSecureFactory + ++ (instancetype)sharedInstance { + dispatch_once(&gInitGRPCCoreSecureFactory, ^{ + gGRPCCoreSecureFactory = [[GRPCCoreSecureFactory alloc] init]; + }); + return gGRPCCoreSecureFactory; +} + ++ (void)load { + [[GRPCTransportRegistry sharedInstance] + registerTransportWithId:GRPCDefaultTransportImplList.core_secure + factory:[self sharedInstance]]; +} + +- (GRPCTransport *)createTransportWithManager:(GRPCTransportManager *)transportManager { + return [[GRPCCall2Internal alloc] initWithTransportManager:transportManager]; +} + +- (id<GRPCChannelFactory>)createCoreChannelFactoryWithCallOptions:(GRPCCallOptions *)callOptions { + NSError *error; + id<GRPCChannelFactory> factory = + [GRPCSecureChannelFactory factoryWithPEMRootCertificates:callOptions.PEMRootCertificates + privateKey:callOptions.PEMPrivateKey + certChain:callOptions.PEMCertificateChain + error:&error]; + if (error != nil) { + NSLog(@"Unable to create secure channel factory"); + return nil; + } + return factory; +} + +@end + +@implementation GRPCCoreInsecureFactory + ++ (instancetype)sharedInstance { + dispatch_once(&gInitGRPCCoreInsecureFactory, ^{ + gGRPCCoreInsecureFactory = [[GRPCCoreInsecureFactory alloc] init]; + }); + return gGRPCCoreInsecureFactory; +} + ++ (void)load { + [[GRPCTransportRegistry sharedInstance] + registerTransportWithId:GRPCDefaultTransportImplList.core_insecure + factory:[self sharedInstance]]; +} + +- (GRPCTransport *)createTransportWithManager:(GRPCTransportManager *)transportManager { + return [[GRPCCall2Internal alloc] initWithTransportManager:transportManager]; +} + +- (id<GRPCChannelFactory>)createCoreChannelFactoryWithCallOptions:(GRPCCallOptions *)callOptions { + return [GRPCInsecureChannelFactory sharedInstance]; +} + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCHost.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCHost.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCHost.h diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCHost.m similarity index 82% rename from src/objective-c/GRPCClient/private/GRPCHost.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCHost.m index 63ffc92741134b5bcea64a23a08f29d0eef77d35..1f6a25ff78f066aa84db39d859b0de559390ba75 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCHost.m @@ -21,17 +21,16 @@ #import <GRPCClient/GRPCCall+Cronet.h> #import <GRPCClient/GRPCCall.h> #import <GRPCClient/GRPCCallOptions.h> +#import <GRPCClient/GRPCTransport.h> #include <grpc/grpc.h> #include <grpc/grpc_security.h> -#import "../internal/GRPCCallOptions+Internal.h" +#import "../../internal/GRPCCallOptions+Internal.h" #import "GRPCChannelFactory.h" #import "GRPCCompletionQueue.h" -#import "GRPCCronetChannelFactory.h" #import "GRPCSecureChannelFactory.h" #import "NSDictionary+GRPC.h" -#import "version.h" NS_ASSUME_NONNULL_BEGIN @@ -113,20 +112,12 @@ static NSMutableDictionary *gHostCache; options.PEMPrivateKey = _PEMPrivateKey; options.PEMCertificateChain = _PEMCertificateChain; options.hostNameOverride = _hostNameOverride; -#ifdef GRPC_COMPILE_WITH_CRONET - // By old API logic, insecure channel precedes Cronet channel; Cronet channel preceeds default - // channel. - if ([GRPCCall isUsingCronet]) { - if (_transportType == GRPCTransportTypeInsecure) { - options.transportType = GRPCTransportTypeInsecure; - } else { - NSAssert(_transportType == GRPCTransportTypeDefault, @"Invalid transport type"); - options.transportType = GRPCTransportTypeCronet; - } - } else -#endif - { - options.transportType = _transportType; + if (_transportType == GRPCTransportTypeInsecure) { + options.transport = GRPCDefaultTransportImplList.core_insecure; + } else if ([GRPCCall isUsingCronet]) { + options.transport = gGRPCCoreCronetId; + } else { + options.transport = GRPCDefaultTransportImplList.core_secure; } options.logContext = _logContext; @@ -135,16 +126,14 @@ static NSMutableDictionary *gHostCache; + (GRPCCallOptions *)callOptionsForHost:(NSString *)host { // TODO (mxyan): Remove when old API is deprecated - NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:host]]; - if (hostURL.host && hostURL.port == nil) { - host = [hostURL.host stringByAppendingString:@":443"]; - } - GRPCCallOptions *callOptions = nil; @synchronized(gHostCache) { - callOptions = [gHostCache[host] callOptions]; + GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; + callOptions = [hostConfig callOptions]; } + NSAssert(callOptions != nil, @"Unable to create call options object"); if (callOptions == nil) { + NSLog(@"Unable to create call options object"); callOptions = [[GRPCCallOptions alloc] init]; } return callOptions; diff --git a/src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCInsecureChannelFactory.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCInsecureChannelFactory.h diff --git a/src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCInsecureChannelFactory.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCInsecureChannelFactory.m diff --git a/src/objective-c/GRPCClient/private/GRPCOpBatchLog.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCOpBatchLog.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCOpBatchLog.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCOpBatchLog.h diff --git a/src/objective-c/GRPCClient/private/GRPCOpBatchLog.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCOpBatchLog.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCOpBatchLog.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCOpBatchLog.m diff --git a/src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCReachabilityFlagNames.xmacro.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCReachabilityFlagNames.xmacro.h diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCRequestHeaders.h similarity index 95% rename from src/objective-c/GRPCClient/private/GRPCRequestHeaders.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCRequestHeaders.h index 545ff1cea820ce0a47d11da0fb8f81fe296b6399..0fced0c385a77c454a4f99f6bb95a50a1855a788 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/private/GRPCCore/GRPCRequestHeaders.h @@ -18,7 +18,7 @@ #import <Foundation/Foundation.h> -#import "../GRPCCall.h" +#import <GRPCClient/GRPCCallLegacy.h> @interface GRPCRequestHeaders : NSMutableDictionary diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCRequestHeaders.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCRequestHeaders.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCRequestHeaders.m diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCSecureChannelFactory.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCSecureChannelFactory.h diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCSecureChannelFactory.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCSecureChannelFactory.m diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCCore/GRPCWrappedCall.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCWrappedCall.h rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCWrappedCall.h diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCCore/GRPCWrappedCall.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCWrappedCall.m rename to src/objective-c/GRPCClient/private/GRPCCore/GRPCWrappedCall.m diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.h b/src/objective-c/GRPCClient/private/GRPCCore/NSData+GRPC.h similarity index 100% rename from src/objective-c/GRPCClient/private/NSData+GRPC.h rename to src/objective-c/GRPCClient/private/GRPCCore/NSData+GRPC.h diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.m b/src/objective-c/GRPCClient/private/GRPCCore/NSData+GRPC.m similarity index 100% rename from src/objective-c/GRPCClient/private/NSData+GRPC.m rename to src/objective-c/GRPCClient/private/GRPCCore/NSData+GRPC.m diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.h b/src/objective-c/GRPCClient/private/GRPCCore/NSDictionary+GRPC.h similarity index 100% rename from src/objective-c/GRPCClient/private/NSDictionary+GRPC.h rename to src/objective-c/GRPCClient/private/GRPCCore/NSDictionary+GRPC.h diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/GRPCCore/NSDictionary+GRPC.m similarity index 100% rename from src/objective-c/GRPCClient/private/NSDictionary+GRPC.m rename to src/objective-c/GRPCClient/private/GRPCCore/NSDictionary+GRPC.m diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/GRPCCore/NSError+GRPC.h similarity index 100% rename from src/objective-c/GRPCClient/private/NSError+GRPC.h rename to src/objective-c/GRPCClient/private/GRPCCore/NSError+GRPC.h diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.m b/src/objective-c/GRPCClient/private/GRPCCore/NSError+GRPC.m similarity index 96% rename from src/objective-c/GRPCClient/private/NSError+GRPC.m rename to src/objective-c/GRPCClient/private/GRPCCore/NSError+GRPC.m index 3eefed88d638e90959406b738ff292317679465c..5b5fc6263c5dea20fc8ba4ebf1d8ff637fcf794e 100644 --- a/src/objective-c/GRPCClient/private/NSError+GRPC.m +++ b/src/objective-c/GRPCClient/private/GRPCCore/NSError+GRPC.m @@ -18,10 +18,9 @@ #import "NSError+GRPC.h" +#import <GRPCClient/GRPCTypes.h> #include <grpc/grpc.h> -NSString *const kGRPCErrorDomain = @"io.grpc"; - @implementation NSError (GRPC) + (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(const char *)details diff --git a/src/objective-c/GRPCClient/private/GRPCTransport+Private.h b/src/objective-c/GRPCClient/private/GRPCTransport+Private.h new file mode 100644 index 0000000000000000000000000000000000000000..2dc7357c363b6d8ccd1740172219f0fa74171994 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCTransport+Private.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import <GRPCClient/GRPCInterceptor.h> +#import <GRPCClient/GRPCTransport.h> + +NS_ASSUME_NONNULL_BEGIN + +/** + * Private interfaces of the transport registry. + */ +@interface GRPCTransportRegistry (Private) + +/** + * Get a transport implementation's factory by its transport id. If the transport id was not + * registered with the registry, the default transport factory (core + secure) is returned. If the + * default transport does not exist, an exception is thrown. + */ +- (id<GRPCTransportFactory>)getTransportFactoryWithId:(GRPCTransportId)transportId; + +@end + +@interface GRPCTransportManager : NSObject<GRPCInterceptorInterface> + +- (instancetype)initWithTransportId:(GRPCTransportId)transportId + previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor; + +/** + * Notify the manager that the transport has shut down and the manager should release references to + * its response handler and stop forwarding requests/responses. + */ +- (void)shutDown; + +/** Forward initial metadata to the previous interceptor in the interceptor chain */ +- (void)forwardPreviousInterceptorWithInitialMetadata:(nullable NSDictionary *)initialMetadata; + +/** Forward a received message to the previous interceptor in the interceptor chain */ +- (void)forwardPreviousInterceptorWithData:(nullable id)data; + +/** Forward call close and trailing metadata to the previous interceptor in the interceptor chain */ +- (void)forwardPreviousInterceptorCloseWithTrailingMetadata: + (nullable NSDictionary *)trailingMetadata + error:(nullable NSError *)error; + +/** Forward write completion to the previous interceptor in the interceptor chain */ +- (void)forwardPreviousInterceptorDidWriteData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/objective-c/GRPCClient/private/GRPCTransport+Private.m b/src/objective-c/GRPCClient/private/GRPCTransport+Private.m new file mode 100644 index 0000000000000000000000000000000000000000..9072f7afbe2d4786850e77904b2c6348d0d2b32c --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCTransport+Private.m @@ -0,0 +1,131 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCTransport+Private.h" + +#import <GRPCClient/GRPCTransport.h> + +@implementation GRPCTransportManager { + GRPCTransportId _transportId; + GRPCTransport *_transport; + id<GRPCResponseHandler> _previousInterceptor; + dispatch_queue_t _dispatchQueue; +} + +- (instancetype)initWithTransportId:(GRPCTransportId)transportId + previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor { + if ((self = [super init])) { + id<GRPCTransportFactory> factory = + [[GRPCTransportRegistry sharedInstance] getTransportFactoryWithId:transportId]; + + _transport = [factory createTransportWithManager:self]; + NSAssert(_transport != nil, @"Failed to create transport with id: %s", transportId); + if (_transport == nil) { + NSLog(@"Failed to create transport with id: %s", transportId); + return nil; + } + _previousInterceptor = previousInterceptor; + _dispatchQueue = _transport.dispatchQueue; + _transportId = transportId; + } + return self; +} + +- (void)shutDown { + dispatch_async(_dispatchQueue, ^{ + self->_transport = nil; + self->_previousInterceptor = nil; + }); +} + +- (dispatch_queue_t)dispatchQueue { + return _dispatchQueue; +} + +- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions + callOptions:(GRPCCallOptions *)callOptions { + if (_transportId != callOptions.transport) { + [NSException raise:NSInvalidArgumentException + format:@"Interceptors cannot change the call option 'transport'"]; + return; + } + [_transport startWithRequestOptions:requestOptions callOptions:callOptions]; +} + +- (void)writeData:(id)data { + [_transport writeData:data]; +} + +- (void)finish { + [_transport finish]; +} + +- (void)cancel { + [_transport cancel]; +} + +- (void)receiveNextMessages:(NSUInteger)numberOfMessages { + [_transport receiveNextMessages:numberOfMessages]; +} + +/** Forward initial metadata to the previous interceptor in the chain */ +- (void)forwardPreviousInterceptorWithInitialMetadata:(NSDictionary *)initialMetadata { + if (initialMetadata == nil || _previousInterceptor == nil) { + return; + } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata]; + }); +} + +/** Forward a received message to the previous interceptor in the chain */ +- (void)forwardPreviousInterceptorWithData:(id)data { + if (data == nil || _previousInterceptor == nil) { + return; + } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didReceiveData:data]; + }); +} + +/** Forward call close and trailing metadata to the previous interceptor in the chain */ +- (void)forwardPreviousInterceptorCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata + error:(NSError *)error { + if (_previousInterceptor == nil) { + return; + } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; + }); +} + +/** Forward write completion to the previous interceptor in the chain */ +- (void)forwardPreviousInterceptorDidWriteData { + if (_previousInterceptor == nil) { + return; + } + id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; + dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ + [copiedPreviousInterceptor didWriteData]; + }); +} + +@end diff --git a/src/objective-c/GRPCClient/private/version.h b/src/objective-c/GRPCClient/version.h similarity index 100% rename from src/objective-c/GRPCClient/private/version.h rename to src/objective-c/GRPCClient/version.h diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h index 12db46adeda5cc076b00d88e06192977ffdf96fc..c91adc7b7cdbc1ec314ab66b4e0437961462f10a 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.h +++ b/src/objective-c/ProtoRPC/ProtoRPC.h @@ -17,12 +17,16 @@ */ #import <Foundation/Foundation.h> -#import <GRPCClient/GRPCCall.h> + +// import legacy header for compatibility with users using the ProtoRPC interface +#import "ProtoRPCLegacy.h" #import "ProtoMethod.h" NS_ASSUME_NONNULL_BEGIN +@class GRPCRequestOptions; +@class GRPCCallOptions; @class GPBMessage; /** An object can implement this protocol to receive responses from server from a call. */ @@ -160,35 +164,3 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnullability-completeness" - -__attribute__((deprecated("Please use GRPCProtoCall."))) @interface ProtoRPC - : GRPCCall - - /** - * host parameter should not contain the scheme (http:// or https://), only the name or IP - * addr and the port number, for example @"localhost:5050". - */ - - - (instancetype)initWithHost : (NSString *)host method - : (GRPCProtoMethod *)method requestsWriter : (GRXWriter *)requestsWriter responseClass - : (Class)responseClass responsesWriteable - : (id<GRXWriteable>)responsesWriteable NS_DESIGNATED_INITIALIZER; - -- (void)start; -@end - -/** - * This subclass is empty now. Eventually we'll remove ProtoRPC class - * to avoid potential naming conflict - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - @interface GRPCProtoCall : ProtoRPC -#pragma clang diagnostic pop - - @end - -#pragma clang diagnostic pop diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index 4700fdd1124044b40375263d7dd81132515edbdf..dbfa3c0f23da4a25adfea8fb65599cbe00358588 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -27,24 +27,6 @@ #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter+Transformations.h> -/** - * Generate an NSError object that represents a failure in parsing a proto class. - */ -static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) { - NSDictionary *info = @{ - NSLocalizedDescriptionKey : @"Unable to parse response from the server", - NSLocalizedRecoverySuggestionErrorKey : - @"If this RPC is idempotent, retry " - @"with exponential backoff. Otherwise, query the server status before " - @"retrying.", - NSUnderlyingErrorKey : parsingError, - @"Expected class" : expectedClass, - @"Received value" : proto, - }; - // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public. - return [NSError errorWithDomain:@"io.grpc" code:13 userInfo:info]; -} - @implementation GRPCUnaryProtoCall { GRPCStreamingProtoCall *_call; GPBMessage *_message; @@ -291,76 +273,3 @@ static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsing } @end - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -@implementation ProtoRPC { -#pragma clang diagnostic pop - id<GRXWriteable> _responseWriteable; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path - requestsWriter:(GRXWriter *)requestsWriter { - [NSException raise:NSInvalidArgumentException - format:@"Please use ProtoRPC's designated initializer instead."]; - return nil; -} -#pragma clang diagnostic pop - -// Designated initializer -- (instancetype)initWithHost:(NSString *)host - method:(GRPCProtoMethod *)method - requestsWriter:(GRXWriter *)requestsWriter - responseClass:(Class)responseClass - responsesWriteable:(id<GRXWriteable>)responsesWriteable { - // Because we can't tell the type system to constrain the class, we need to check at runtime: - if (![responseClass respondsToSelector:@selector(parseFromData:error:)]) { - [NSException raise:NSInvalidArgumentException - format:@"A protobuf class to parse the responses must be provided."]; - } - // A writer that serializes the proto messages to send. - GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) { - if (![proto isKindOfClass:[GPBMessage class]]) { - [NSException raise:NSInvalidArgumentException - format:@"Request must be a proto message: %@", proto]; - } - return [proto data]; - }]; - if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { - __weak ProtoRPC *weakSelf = self; - - // A writeable that parses the proto messages received. - _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { - // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread. - NSError *error = nil; - id parsed = [responseClass parseFromData:value error:&error]; - if (parsed) { - [responsesWriteable writeValue:parsed]; - } else { - [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)]; - } - } - completionHandler:^(NSError *errorOrNil) { - [responsesWriteable writesFinishedWithError:errorOrNil]; - }]; - } - return self; -} - -- (void)start { - [self startWithWriteable:_responseWriteable]; -} - -- (void)startWithWriteable:(id<GRXWriteable>)writeable { - [super startWithWriteable:writeable]; - // Break retain cycles. - _responseWriteable = nil; -} -@end - -@implementation GRPCProtoCall - -@end diff --git a/src/objective-c/ProtoRPC/ProtoRPCLegacy.h b/src/objective-c/ProtoRPC/ProtoRPCLegacy.h new file mode 100644 index 0000000000000000000000000000000000000000..b8d4003f6957a8f6aaabeeef02e039ff399b325f --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoRPCLegacy.h @@ -0,0 +1,67 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import <GRPCClient/GRPCCallLegacy.h> + +// Import category headers for Swift build +#import <GRPCClient/GRPCCall+ChannelArg.h> +#import <GRPCClient/GRPCCall+ChannelCredentials.h> +#import <GRPCClient/GRPCCall+Cronet.h> +#import <GRPCClient/GRPCCall+OAuth2.h> +#import <GRPCClient/GRPCCall+Tests.h> +#import <RxLibrary/GRXWriteable.h> + +@class GRPCProtoMethod; +@class GRXWriter; +@protocol GRXWriteable; + +__attribute__((deprecated("Please use GRPCProtoCall."))) @interface ProtoRPC + : GRPCCall + + /** + * host parameter should not contain the scheme (http:// or https://), only the name or IP + * addr and the port number, for example @"localhost:5050". + */ + - + (instancetype)initWithHost : (NSString *)host method + : (GRPCProtoMethod *)method requestsWriter : (GRXWriter *)requestsWriter responseClass + : (Class)responseClass responsesWriteable + : (id<GRXWriteable>)responsesWriteable NS_DESIGNATED_INITIALIZER; + +- (void)start; + +@end + +/** + * This subclass is empty now. Eventually we'll remove ProtoRPC class + * to avoid potential naming conflict + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + @interface GRPCProtoCall + : ProtoRPC +#pragma clang diagnostic pop + + @end + + /** + * Generate an NSError object that represents a failure in parsing a proto class. For gRPC + * internal use only. + */ + NSError * + ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError); diff --git a/src/objective-c/ProtoRPC/ProtoRPCLegacy.m b/src/objective-c/ProtoRPC/ProtoRPCLegacy.m new file mode 100644 index 0000000000000000000000000000000000000000..4ba93674063fbffaabfa8f16fd4819bc5bd01406 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoRPCLegacy.m @@ -0,0 +1,121 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "ProtoRPCLegacy.h" + +#import "ProtoMethod.h" + +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS +#import <Protobuf/GPBProtocolBuffers.h> +#else +#import <GPBProtocolBuffers.h> +#endif +#import <GRPCClient/GRPCCall.h> +#import <RxLibrary/GRXWriteable.h> +#import <RxLibrary/GRXWriter+Transformations.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +@implementation ProtoRPC { +#pragma clang diagnostic pop + id<GRXWriteable> _responseWriteable; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestsWriter { + [NSException raise:NSInvalidArgumentException + format:@"Please use ProtoRPC's designated initializer instead."]; + return nil; +} +#pragma clang diagnostic pop + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host + method:(GRPCProtoMethod *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id<GRXWriteable>)responsesWriteable { + // Because we can't tell the type system to constrain the class, we need to check at runtime: + if (![responseClass respondsToSelector:@selector(parseFromData:error:)]) { + [NSException raise:NSInvalidArgumentException + format:@"A protobuf class to parse the responses must be provided."]; + } + // A writer that serializes the proto messages to send. + GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) { + if (![proto isKindOfClass:[GPBMessage class]]) { + [NSException raise:NSInvalidArgumentException + format:@"Request must be a proto message: %@", proto]; + } + return [proto data]; + }]; + if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { + __weak ProtoRPC *weakSelf = self; + + // A writeable that parses the proto messages received. + _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread. + NSError *error = nil; + id parsed = [responseClass parseFromData:value error:&error]; + if (parsed) { + [responsesWriteable writeValue:parsed]; + } else { + [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)]; + } + } + completionHandler:^(NSError *errorOrNil) { + [responsesWriteable writesFinishedWithError:errorOrNil]; + }]; + } + return self; +} + +- (void)start { + [self startWithWriteable:_responseWriteable]; +} + +- (void)startWithWriteable:(id<GRXWriteable>)writeable { + [super startWithWriteable:writeable]; + // Break retain cycles. + _responseWriteable = nil; +} +@end + +@implementation GRPCProtoCall + +@end + +/** + * Generate an NSError object that represents a failure in parsing a proto class. + */ +NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) { + NSDictionary *info = @{ + NSLocalizedDescriptionKey : @"Unable to parse response from the server", + NSLocalizedRecoverySuggestionErrorKey : + @"If this RPC is idempotent, retry " + @"with exponential backoff. Otherwise, query the server status before " + @"retrying.", + NSUnderlyingErrorKey : parsingError, + @"Expected class" : expectedClass, + @"Received value" : proto, + }; + // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public. + return [NSError errorWithDomain:@"io.grpc" code:13 userInfo:info]; +} diff --git a/src/objective-c/ProtoRPC/ProtoService.h b/src/objective-c/ProtoRPC/ProtoService.h index 900ec8d0e1ea7d69b2e0674506cf11c3800bf531..fd8a86bb2b29c2d8956fa728ee5e91e61e2988e6 100644 --- a/src/objective-c/ProtoRPC/ProtoService.h +++ b/src/objective-c/ProtoRPC/ProtoService.h @@ -18,14 +18,12 @@ #import <Foundation/Foundation.h> -@class GRPCProtoCall; +#import <GRPCClient/GRPCCallOptions.h> +#import "ProtoRPC.h" + @protocol GRXWriteable; @class GRXWriter; @class GRPCCallOptions; -@class GRPCProtoCall; -@class GRPCUnaryProtoCall; -@class GRPCStreamingProtoCall; -@protocol GRPCProtoResponseHandler; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnullability-completeness" @@ -38,15 +36,6 @@ __attribute__((deprecated("Please use GRPCProtoService."))) @interface ProtoServ : (nonnull NSString *)packageName serviceName : (nonnull NSString *)serviceName callOptions : (nullable GRPCCallOptions *)callOptions NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithHost:(NSString *)host - packageName:(NSString *)packageName - serviceName:(NSString *)serviceName; - -- (GRPCProtoCall *)RPCToMethod:(NSString *)method - requestsWriter:(GRXWriter *)requestsWriter - responseClass:(Class)responseClass - responsesWriteable:(id<GRXWriteable>)responsesWriteable; - - (nullable GRPCUnaryProtoCall *)RPCToMethod:(nonnull NSString *)method message:(nonnull id)message responseHandler:(nonnull id<GRPCProtoResponseHandler>)handler @@ -58,6 +47,18 @@ __attribute__((deprecated("Please use GRPCProtoService."))) @interface ProtoServ callOptions:(nullable GRPCCallOptions *)callOptions responseClass:(nonnull Class)responseClass; +@end + + @interface ProtoService(Legacy) + + - (instancetype)initWithHost : (NSString *)host packageName + : (NSString *)packageName serviceName : (NSString *)serviceName; + +- (GRPCProtoCall *)RPCToMethod:(NSString *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id<GRXWriteable>)responsesWriteable; + @end #pragma clang diagnostic pop diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m index 80a1f2f226c34cc8815dbfc5f1c3ddd6beac7eaa..e84cab8903f9610a23d6c4c945fccc165bf32eae 100644 --- a/src/objective-c/ProtoRPC/ProtoService.m +++ b/src/objective-c/ProtoRPC/ProtoService.m @@ -29,15 +29,21 @@ #pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation ProtoService { #pragma clang diagnostic pop + + GRPCCallOptions *_callOptions; NSString *_host; NSString *_packageName; NSString *_serviceName; - GRPCCallOptions *_callOptions; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" +// Do not call the default init method - (instancetype)init { - return [self initWithHost:nil packageName:nil serviceName:nil]; + [NSException raise:NSGenericException format:@"Do not call init method of ProtoService"]; + return [self initWithHost:nil packageName:nil serviceName:nil callOptions:nil]; } +#pragma clang diagnostic pop // Designated initializer - (instancetype)initWithHost:(NSString *)host @@ -58,38 +64,6 @@ return self; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -// Do not call designated initializer here due to nullability incompatibility. This method is from -// old API and does not assert on nullability of the parameters. - -- (instancetype)initWithHost:(NSString *)host - packageName:(NSString *)packageName - serviceName:(NSString *)serviceName { - if ((self = [super init])) { - _host = [host copy]; - _packageName = [packageName copy]; - _serviceName = [serviceName copy]; - _callOptions = nil; - } - return self; -} - -#pragma clang diagnostic pop - -- (GRPCProtoCall *)RPCToMethod:(NSString *)method - requestsWriter:(GRXWriter *)requestsWriter - responseClass:(Class)responseClass - responsesWriteable:(id<GRXWriteable>)responsesWriteable { - GRPCProtoMethod *methodName = - [[GRPCProtoMethod alloc] initWithPackage:_packageName service:_serviceName method:method]; - return [[GRPCProtoCall alloc] initWithHost:_host - method:methodName - requestsWriter:requestsWriter - responseClass:responseClass - responsesWriteable:responsesWriteable]; -} - - (GRPCUnaryProtoCall *)RPCToMethod:(NSString *)method message:(id)message responseHandler:(id<GRPCProtoResponseHandler>)handler diff --git a/src/objective-c/ProtoRPC/ProtoServiceLegacy.h b/src/objective-c/ProtoRPC/ProtoServiceLegacy.h new file mode 100644 index 0000000000000000000000000000000000000000..7b0b7a4de7e3814de01fb4123ba7f215b824bdaa --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoServiceLegacy.h @@ -0,0 +1,23 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "ProtoService.h" + +@class GRPCProtoCall; +@class GRXWriter; +@protocol GRXWriteable; diff --git a/src/objective-c/ProtoRPC/ProtoServiceLegacy.m b/src/objective-c/ProtoRPC/ProtoServiceLegacy.m new file mode 100644 index 0000000000000000000000000000000000000000..6aa64955381119add6c538d515c3214094c6bfd9 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoServiceLegacy.m @@ -0,0 +1,71 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import <objc/runtime.h> + +#import "ProtoMethod.h" +#import "ProtoRPCLegacy.h" +#import "ProtoService.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +@implementation ProtoService (Legacy) +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +// Do not call designated initializer here due to nullability incompatibility. This method is from +// old API and does not assert on nullability of the parameters. + +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName { + if ((self = [super init])) { + Ivar hostIvar = class_getInstanceVariable([ProtoService class], "_host"); + Ivar packageNameIvar = class_getInstanceVariable([ProtoService class], "_packageName"); + Ivar serviceNameIvar = class_getInstanceVariable([ProtoService class], "_serviceName"); + + object_setIvar(self, hostIvar, [host copy]); + object_setIvar(self, packageNameIvar, [packageName copy]); + object_setIvar(self, serviceNameIvar, [serviceName copy]); + } + return self; +} +#pragma clang diagnostic pop + +- (GRPCProtoCall *)RPCToMethod:(NSString *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id<GRXWriteable>)responsesWriteable { + Ivar hostIvar = class_getInstanceVariable([ProtoService class], "_host"); + Ivar packageNameIvar = class_getInstanceVariable([ProtoService class], "_packageName"); + Ivar serviceNameIvar = class_getInstanceVariable([ProtoService class], "_serviceName"); + NSString *host = object_getIvar(self, hostIvar); + NSString *packageName = object_getIvar(self, packageNameIvar); + NSString *serviceName = object_getIvar(self, serviceNameIvar); + + GRPCProtoMethod *methodName = + [[GRPCProtoMethod alloc] initWithPackage:packageName service:serviceName method:method]; + return [[GRPCProtoCall alloc] initWithHost:host + method:methodName + requestsWriter:requestsWriter + responseClass:responseClass + responsesWriteable:responsesWriteable]; +} + +@end diff --git a/src/objective-c/examples/tvOS-sample/tvOS-sample/ViewController.m b/src/objective-c/examples/tvOS-sample/tvOS-sample/ViewController.m index c94ee8c256977346074e13307302330928265543..9c46626012161df775b166068624c848644c8e04 100644 --- a/src/objective-c/examples/tvOS-sample/tvOS-sample/ViewController.m +++ b/src/objective-c/examples/tvOS-sample/tvOS-sample/ViewController.m @@ -25,6 +25,8 @@ #import "src/objective-c/examples/RemoteTestClient/Messages.pbobjc.h" #import "src/objective-c/examples/RemoteTestClient/Test.pbrpc.h" #endif +#import <GRPCClient/GRPCCallOptions.h> +#import <ProtoRPC/ProtoRPC.h> @interface ViewController ()<GRPCProtoResponseHandler> diff --git a/src/objective-c/examples/watchOS-sample/WatchKit-Extension/InterfaceController.m b/src/objective-c/examples/watchOS-sample/WatchKit-Extension/InterfaceController.m index 3e97767cf0cffd3bae072f1cdc81b7e62965a722..ce204a9c1462e92a52ff6fd071e2d6287b5ac146 100644 --- a/src/objective-c/examples/watchOS-sample/WatchKit-Extension/InterfaceController.m +++ b/src/objective-c/examples/watchOS-sample/WatchKit-Extension/InterfaceController.m @@ -24,6 +24,8 @@ #import "src/objective-c/examples/RemoteTestClient/Messages.pbobjc.h" #import "src/objective-c/examples/RemoteTestClient/Test.pbrpc.h" #endif +#import <GRPCClient/GRPCCallOptions.h> +#import <ProtoRPC/ProtoRPC.h> @interface InterfaceController ()<GRPCProtoResponseHandler> diff --git a/src/objective-c/tests/BUILD b/src/objective-c/tests/BUILD index fed92596b17d2a640c73820571d4d0f82a667179..65a32d742a910f4f6cb41075c510c231be0ec24c 100644 --- a/src/objective-c/tests/BUILD +++ b/src/objective-c/tests/BUILD @@ -95,19 +95,12 @@ tvos_application( deps = ["host-lib"], ) -grpc_objc_testing_library( - name = "CronetConfig", - srcs = ["ConfigureCronet.m"], - hdrs = ["ConfigureCronet.h"], -) - grpc_objc_testing_library( name = "InteropTests-lib", hdrs = ["InteropTests/InteropTests.h"], srcs = ["InteropTests/InteropTests.m"], deps = [ ":InteropTestsBlockCallbacks-lib", - ":CronetConfig", ], ) @@ -200,7 +193,6 @@ ios_unit_test( ":InteropTestsRemote-lib", ":InteropTestsLocalSSL-lib", ":InteropTestsLocalCleartext-lib", - # ":InteropTestsMulitpleChannels-lib", # needs Cronet ], test_host = ":ios-host", ) diff --git a/src/objective-c/tests/ConfigureCronet.h b/src/objective-c/tests/ConfigureCronet.h index cc5c038f3c61db280d20509297356b5d44dd9c74..ba0efc4df663dca679955f7febc258319ee541b6 100644 --- a/src/objective-c/tests/ConfigureCronet.h +++ b/src/objective-c/tests/ConfigureCronet.h @@ -16,8 +16,6 @@ * */ -#ifdef GRPC_COMPILE_WITH_CRONET - #ifdef __cplusplus extern "C" { #endif @@ -30,5 +28,3 @@ void configureCronet(void); #ifdef __cplusplus } #endif - -#endif diff --git a/src/objective-c/tests/ConfigureCronet.m b/src/objective-c/tests/ConfigureCronet.m index ab137e28cad0875438bb0a0d38c3d4e93024ee0f..6fc7e0ee9f59a4fa3d89f2e7ebb3ad4507877a69 100644 --- a/src/objective-c/tests/ConfigureCronet.m +++ b/src/objective-c/tests/ConfigureCronet.m @@ -16,8 +16,6 @@ * */ -#ifdef GRPC_COMPILE_WITH_CRONET - #import "ConfigureCronet.h" #import <Cronet/Cronet.h> @@ -35,5 +33,3 @@ void configureCronet(void) { [Cronet startNetLogToFile:@"cronet_netlog.json" logBytes:YES]; }); } - -#endif diff --git a/src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m b/src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m index a2a79c463167d851df013e1501f2b1544f7eed34..aa1af301b960795fae74b35eea1092c77b38346f 100644 --- a/src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m +++ b/src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m @@ -22,6 +22,7 @@ #import <Cronet/Cronet.h> #import <GRPCClient/GRPCCall+Cronet.h> +#import "../ConfigureCronet.h" #import "InteropTests.h" // The server address is derived from preprocessor macro, which is @@ -40,12 +41,19 @@ static int32_t kRemoteInteropServerOverhead = 12; @implementation InteropTestsRemoteWithCronet ++ (void)setUp { + configureCronet(); + [GRPCCall useCronetWithEngine:[Cronet getGlobalEngine]]; + + [super setUp]; +} + + (NSString *)host { return kRemoteSSLHost; } -+ (BOOL)useCronet { - return YES; ++ (GRPCTransportId)transport { + return gGRPCCoreCronetId; } - (int32_t)encodingOverhead { diff --git a/src/objective-c/tests/InteropTests/InteropTests.h b/src/objective-c/tests/InteropTests/InteropTests.h index 28fcbff9695a7dafaffd066244cf1946b5e31e0a..a4adecd541574ac1e8da434ed124582ca2f2676d 100644 --- a/src/objective-c/tests/InteropTests/InteropTests.h +++ b/src/objective-c/tests/InteropTests/InteropTests.h @@ -48,11 +48,19 @@ - (int32_t)encodingOverhead; /** + * DEPRECATED: \a transportType is a deprecated option. Please use \a transport instead. + * * The type of transport to be used. The base implementation returns default. Subclasses should * override to appropriate settings. */ + (GRPCTransportType)transportType; +/* + * The transport to be used. The base implementation returns NULL. Subclasses should override to + * appropriate settings. + */ ++ (GRPCTransportId)transport; + /** * The root certificates to be used. The base implementation returns nil. Subclasses should override * to appropriate settings. @@ -65,9 +73,4 @@ */ + (NSString *)hostNameOverride; -/** - * Whether to use Cronet for all the v1 API tests in the test suite. - */ -+ (BOOL)useCronet; - @end diff --git a/src/objective-c/tests/InteropTests/InteropTests.m b/src/objective-c/tests/InteropTests/InteropTests.m index a8f7db7ee9343bad1af9961e027fe366fb216c58..21198f7bad9762b6e8ceb7b5b29ef4ff656c0c18 100644 --- a/src/objective-c/tests/InteropTests/InteropTests.m +++ b/src/objective-c/tests/InteropTests/InteropTests.m @@ -20,9 +20,6 @@ #include <grpc/status.h> -#ifdef GRPC_COMPILE_WITH_CRONET -#import <Cronet/Cronet.h> -#endif #import <GRPCClient/GRPCCall+ChannelArg.h> #import <GRPCClient/GRPCCall+Cronet.h> #import <GRPCClient/GRPCCall+Interceptor.h> @@ -38,7 +35,6 @@ #import "src/objective-c/tests/RemoteTestClient/Test.pbobjc.h" #import "src/objective-c/tests/RemoteTestClient/Test.pbrpc.h" -#import "../ConfigureCronet.h" #import "InteropTestsBlockCallbacks.h" #define TEST_TIMEOUT 32 @@ -91,9 +87,8 @@ BOOL isRemoteInteropTest(NSString *host) { - (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager { dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); - return [[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager - requestDispatchQueue:queue - responseDispatchQueue:queue]; + return + [[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager dispatchQueue:queue]; } @end @@ -101,21 +96,19 @@ BOOL isRemoteInteropTest(NSString *host) { @interface HookInterceptorFactory : NSObject<GRPCInterceptorFactory> - (instancetype) -initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue - startHook:(void (^)(GRPCRequestOptions *requestOptions, - GRPCCallOptions *callOptions, - GRPCInterceptorManager *manager))startHook - writeDataHook:(void (^)(id data, GRPCInterceptorManager *manager))writeDataHook - finishHook:(void (^)(GRPCInterceptorManager *manager))finishHook - receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages, - GRPCInterceptorManager *manager))receiveNextMessagesHook - responseHeaderHook:(void (^)(NSDictionary *initialMetadata, - GRPCInterceptorManager *manager))responseHeaderHook - responseDataHook:(void (^)(id data, GRPCInterceptorManager *manager))responseDataHook - responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error, - GRPCInterceptorManager *manager))responseCloseHook - didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook; + initWithDispatchQueue:(dispatch_queue_t)dispatchQueue + startHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, + GRPCInterceptorManager *manager))startHook + writeDataHook:(void (^)(id data, GRPCInterceptorManager *manager))writeDataHook + finishHook:(void (^)(GRPCInterceptorManager *manager))finishHook +receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages, + GRPCInterceptorManager *manager))receiveNextMessagesHook + responseHeaderHook:(void (^)(NSDictionary *initialMetadata, + GRPCInterceptorManager *manager))responseHeaderHook + responseDataHook:(void (^)(id data, GRPCInterceptorManager *manager))responseDataHook + responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error, + GRPCInterceptorManager *manager))responseCloseHook + didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook; - (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager; @@ -125,8 +118,7 @@ initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - (instancetype) initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager - requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue + dispatchQueue:(dispatch_queue_t)dispatchQueue startHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager))startHook @@ -155,29 +147,25 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager void (^_responseCloseHook)(NSDictionary *trailingMetadata, NSError *error, GRPCInterceptorManager *manager); void (^_didWriteDataHook)(GRPCInterceptorManager *manager); - dispatch_queue_t _requestDispatchQueue; - dispatch_queue_t _responseDispatchQueue; + dispatch_queue_t _dispatchQueue; } - (instancetype) -initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue - startHook:(void (^)(GRPCRequestOptions *requestOptions, - GRPCCallOptions *callOptions, - GRPCInterceptorManager *manager))startHook - writeDataHook:(void (^)(id data, GRPCInterceptorManager *manager))writeDataHook - finishHook:(void (^)(GRPCInterceptorManager *manager))finishHook - receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages, - GRPCInterceptorManager *manager))receiveNextMessagesHook - responseHeaderHook:(void (^)(NSDictionary *initialMetadata, - GRPCInterceptorManager *manager))responseHeaderHook - responseDataHook:(void (^)(id data, GRPCInterceptorManager *manager))responseDataHook - responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error, - GRPCInterceptorManager *manager))responseCloseHook - didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook { + initWithDispatchQueue:(dispatch_queue_t)dispatchQueue + startHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, + GRPCInterceptorManager *manager))startHook + writeDataHook:(void (^)(id data, GRPCInterceptorManager *manager))writeDataHook + finishHook:(void (^)(GRPCInterceptorManager *manager))finishHook +receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages, + GRPCInterceptorManager *manager))receiveNextMessagesHook + responseHeaderHook:(void (^)(NSDictionary *initialMetadata, + GRPCInterceptorManager *manager))responseHeaderHook + responseDataHook:(void (^)(id data, GRPCInterceptorManager *manager))responseDataHook + responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error, + GRPCInterceptorManager *manager))responseCloseHook + didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook { if ((self = [super init])) { - _requestDispatchQueue = requestDispatchQueue; - _responseDispatchQueue = responseDispatchQueue; + _dispatchQueue = dispatchQueue; _startHook = startHook; _writeDataHook = writeDataHook; _finishHook = finishHook; @@ -192,8 +180,7 @@ initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager { return [[HookInterceptor alloc] initWithInterceptorManager:interceptorManager - requestDispatchQueue:_requestDispatchQueue - responseDispatchQueue:_responseDispatchQueue + dispatchQueue:_dispatchQueue startHook:_startHook writeDataHook:_writeDataHook finishHook:_finishHook @@ -218,22 +205,16 @@ initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue GRPCInterceptorManager *manager); void (^_didWriteDataHook)(GRPCInterceptorManager *manager); GRPCInterceptorManager *_manager; - dispatch_queue_t _requestDispatchQueue; - dispatch_queue_t _responseDispatchQueue; -} - -- (dispatch_queue_t)requestDispatchQueue { - return _requestDispatchQueue; + dispatch_queue_t _dispatchQueue; } - (dispatch_queue_t)dispatchQueue { - return _responseDispatchQueue; + return _dispatchQueue; } - (instancetype) initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager - requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue + dispatchQueue:(dispatch_queue_t)dispatchQueue startHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager))startHook @@ -247,9 +228,7 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error, GRPCInterceptorManager *manager))responseCloseHook didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook { - if ((self = [super initWithInterceptorManager:interceptorManager - requestDispatchQueue:requestDispatchQueue - responseDispatchQueue:responseDispatchQueue])) { + if ((self = [super initWithInterceptorManager:interceptorManager dispatchQueue:dispatchQueue])) { _startHook = startHook; _writeDataHook = writeDataHook; _finishHook = finishHook; @@ -258,8 +237,7 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager _responseDataHook = responseDataHook; _responseCloseHook = responseCloseHook; _didWriteDataHook = didWriteDataHook; - _requestDispatchQueue = requestDispatchQueue; - _responseDispatchQueue = responseDispatchQueue; + _dispatchQueue = dispatchQueue; _manager = interceptorManager; } return self; @@ -320,8 +298,7 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager @property BOOL enabled; -- (instancetype)initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue; +- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue; - (void)setStartHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager))startHook @@ -340,26 +317,23 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager @implementation GlobalInterceptorFactory -- (instancetype)initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue - responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue { +- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue { _enabled = NO; - return [super initWithRequestDispatchQueue:requestDispatchQueue - responseDispatchQueue:responseDispatchQueue - startHook:nil - writeDataHook:nil - finishHook:nil - receiveNextMessagesHook:nil - responseHeaderHook:nil - responseDataHook:nil - responseCloseHook:nil - didWriteDataHook:nil]; + return [super initWithDispatchQueue:dispatchQueue + startHook:nil + writeDataHook:nil + finishHook:nil + receiveNextMessagesHook:nil + responseHeaderHook:nil + responseDataHook:nil + responseCloseHook:nil + didWriteDataHook:nil]; } - (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager { if (_enabled) { return [[HookInterceptor alloc] initWithInterceptorManager:interceptorManager - requestDispatchQueue:_requestDispatchQueue - responseDispatchQueue:_responseDispatchQueue + dispatchQueue:_dispatchQueue startHook:_startHook writeDataHook:_writeDataHook finishHook:_finishHook @@ -425,10 +399,15 @@ static dispatch_once_t initGlobalInterceptorFactory; return 0; } +// For backwards compatibility + (GRPCTransportType)transportType { return GRPCTransportTypeChttp2BoringSSL; } ++ (GRPCTransportId)transport { + return NULL; +} + + (NSString *)PEMRootCertificates { return nil; } @@ -437,26 +416,11 @@ static dispatch_once_t initGlobalInterceptorFactory; return nil; } -+ (BOOL)useCronet { - return NO; -} - + (void)setUp { -#ifdef GRPC_COMPILE_WITH_CRONET - configureCronet(); - if ([self useCronet]) { - [GRPCCall useCronetWithEngine:[Cronet getGlobalEngine]]; - } -#endif -#ifdef GRPC_CFSTREAM - setenv(kCFStreamVarName, "1", 1); -#endif - dispatch_once(&initGlobalInterceptorFactory, ^{ dispatch_queue_t globalInterceptorQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); globalInterceptorFactory = - [[GlobalInterceptorFactory alloc] initWithRequestDispatchQueue:globalInterceptorQueue - responseDispatchQueue:globalInterceptorQueue]; + [[GlobalInterceptorFactory alloc] initWithDispatchQueue:globalInterceptorQueue]; [GRPCCall2 registerGlobalInterceptor:globalInterceptorFactory]; }); } @@ -502,7 +466,9 @@ static dispatch_once_t initGlobalInterceptorFactory; GPBEmpty *request = [GPBEmpty message]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; @@ -531,7 +497,9 @@ static dispatch_once_t initGlobalInterceptorFactory; GPBEmpty *request = [GPBEmpty message]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; @@ -608,7 +576,9 @@ static dispatch_once_t initGlobalInterceptorFactory; request.payload.body = [NSMutableData dataWithLength:271828]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; @@ -656,7 +626,9 @@ static dispatch_once_t initGlobalInterceptorFactory; request.responseStatus.code = GRPC_STATUS_CANCELLED; } GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; @@ -958,7 +930,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; @@ -1010,7 +984,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.flowControlEnabled = YES; @@ -1167,7 +1143,9 @@ static dispatch_once_t initGlobalInterceptorFactory; __block BOOL receivedResponse = NO; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = self.class.transportType; + options.transport = [[self class] transport]; options.PEMRootCertificates = self.class.PEMRootCertificates; options.hostNameOverride = [[self class] hostNameOverride]; @@ -1200,7 +1178,9 @@ static dispatch_once_t initGlobalInterceptorFactory; [self expectationWithDescription:@"Call completed."]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = self.class.transportType; + options.transport = [[self class] transport]; options.PEMRootCertificates = self.class.PEMRootCertificates; options.hostNameOverride = [[self class] hostNameOverride]; @@ -1286,48 +1266,47 @@ static dispatch_once_t initGlobalInterceptorFactory; [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; } -#ifndef GRPC_COMPILE_WITH_CRONET -- (void)testKeepalive { +- (void)testKeepaliveWithV2API { XCTAssertNotNil([[self class] host]); + if ([[self class] transport] == gGRPCCoreCronetId) { + // Cronet does not support keepalive + return; + } __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Keepalive"]; - [GRPCCall setKeepaliveWithInterval:1500 timeout:0 forHost:[[self class] host]]; - - NSArray *requests = @[ @27182, @8 ]; - NSArray *responses = @[ @31415, @9 ]; - - GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; - - __block int index = 0; + NSNumber *kRequestSize = @27182; + NSNumber *kResponseSize = @31415; - id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] - requestedResponseSize:responses[index]]; - [requestsBuffer writeValue:request]; + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:kRequestSize + requestedResponseSize:kResponseSize]; + GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; + options.PEMRootCertificates = [[self class] PEMRootCertificates]; + options.hostNameOverride = [[self class] hostNameOverride]; + options.keepaliveInterval = 1.5; + options.keepaliveTimeout = 0; - [_service - fullDuplexCallWithRequestsWriter:requestsBuffer - eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, - NSError *error) { - if (index == 0) { - XCTAssertNil(error, @"Finished with unexpected error: %@", error); - XCTAssertTrue(response, @"Event handler called without an event."); - XCTAssertFalse(done); - index++; - } else { - // Keepalive should kick after 1s elapsed and fails the call. - XCTAssertNotNil(error); - XCTAssertEqual(error.code, GRPC_STATUS_UNAVAILABLE); - XCTAssertEqualObjects( - error.localizedDescription, @"keepalive watchdog timeout", - @"Unexpected failure that is not keepalive watchdog timeout."); - XCTAssertTrue(done); - [expectation fulfill]; - } - }]; + __block GRPCStreamingProtoCall *call = [_service + fullDuplexCallWithResponseHandler: + [[InteropTestsBlockCallbacks alloc] + initWithInitialMetadataCallback:nil + messageCallback:nil + closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqual( + error.code, GRPC_STATUS_UNAVAILABLE, + @"Received status %ld instead of UNAVAILABLE (14).", + error.code); + [expectation fulfill]; + }] + callOptions:options]; + [call writeMessage:request]; + [call start]; [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; + [call finish]; } -#endif - (void)testDefaultInterceptor { XCTAssertNotNil([[self class] host]); @@ -1342,7 +1321,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.interceptorFactories = @[ [[DefaultInterceptorFactory alloc] init] ]; @@ -1397,8 +1378,7 @@ static dispatch_once_t initGlobalInterceptorFactory; __block NSUInteger responseCloseCount = 0; __block NSUInteger didWriteDataCount = 0; id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc] - initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) - responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) + initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager) { startCount++; @@ -1446,7 +1426,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.flowControlEnabled = YES; @@ -1524,8 +1506,7 @@ static dispatch_once_t initGlobalInterceptorFactory; __block NSUInteger responseDataCount = 0; __block NSUInteger responseCloseCount = 0; id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc] - initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) - responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) + initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager) { startCount++; @@ -1552,6 +1533,7 @@ static dispatch_once_t initGlobalInterceptorFactory; // finish must happen after the hijacking, so directly reply with a close [manager forwardPreviousInterceptorCloseWithTrailingMetadata:@{@"grpc-status" : @"0"} error:nil]; + [manager shutDown]; } receiveNextMessagesHook:nil responseHeaderHook:^(NSDictionary *initialMetadata, GRPCInterceptorManager *manager) { @@ -1578,7 +1560,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.interceptorFactories = @[ [[DefaultInterceptorFactory alloc] init], factory ]; @@ -1687,7 +1671,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.flowControlEnabled = YES; @@ -1742,16 +1728,15 @@ static dispatch_once_t initGlobalInterceptorFactory; - (void)testConflictingGlobalInterceptors { id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc] - initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) - responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) - startHook:nil - writeDataHook:nil - finishHook:nil - receiveNextMessagesHook:nil - responseHeaderHook:nil - responseDataHook:nil - responseCloseHook:nil - didWriteDataHook:nil]; + initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) + startHook:nil + writeDataHook:nil + finishHook:nil + receiveNextMessagesHook:nil + responseHeaderHook:nil + responseDataHook:nil + responseCloseHook:nil + didWriteDataHook:nil]; @try { [GRPCCall2 registerGlobalInterceptor:factory]; XCTFail(@"Did not receive an exception when registering global interceptor the second time"); @@ -1775,8 +1760,7 @@ static dispatch_once_t initGlobalInterceptorFactory; __block NSUInteger didWriteDataCount = 0; id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc] - initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) - responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) + initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions, GRPCInterceptorManager *manager) { startCount++; @@ -1872,7 +1856,9 @@ static dispatch_once_t initGlobalInterceptorFactory; id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] requestedResponseSize:responses[index]]; GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; + // For backwards compatibility options.transportType = [[self class] transportType]; + options.transport = [[self class] transport]; options.PEMRootCertificates = [[self class] PEMRootCertificates]; options.hostNameOverride = [[self class] hostNameOverride]; options.flowControlEnabled = YES; diff --git a/src/objective-c/tests/InteropTests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTests/InteropTestsLocalCleartext.m index a9c6918333229e261b3a57692dde6297ae604f92..2e638099e1e7b2fe24133248da47afaede6a005c 100644 --- a/src/objective-c/tests/InteropTests/InteropTestsLocalCleartext.m +++ b/src/objective-c/tests/InteropTests/InteropTestsLocalCleartext.m @@ -17,6 +17,7 @@ */ #import <GRPCClient/GRPCCall+Tests.h> +#import <GRPCClient/GRPCTransport.h> #import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> #import "InteropTests.h" @@ -60,8 +61,8 @@ static int32_t kLocalInteropServerOverhead = 10; [GRPCCall useInsecureConnectionsForHost:kLocalCleartextHost]; } -+ (GRPCTransportType)transportType { - return GRPCTransportTypeInsecure; ++ (GRPCTransportId)transport { + return GRPCDefaultTransportImplList.core_insecure; } @end diff --git a/src/objective-c/tests/InteropTests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTests/InteropTestsLocalSSL.m index e8222f602f4a30510881925fc65442b7d59a14d0..30d8f4c34af0db6fd3127f9ec547904b32f2f19b 100644 --- a/src/objective-c/tests/InteropTests/InteropTestsLocalSSL.m +++ b/src/objective-c/tests/InteropTests/InteropTestsLocalSSL.m @@ -17,6 +17,7 @@ */ #import <GRPCClient/GRPCCall+Tests.h> +#import <GRPCClient/GRPCTransport.h> #import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> #import "InteropTests.h" @@ -56,8 +57,8 @@ static int32_t kLocalInteropServerOverhead = 10; return kLocalInteropServerOverhead; // bytes } -+ (GRPCTransportType)transportType { - return GRPCTransportTypeChttp2BoringSSL; ++ (GRPCTransportId)transport { + return GRPCDefaultTransportImplList.core_secure; } - (void)setUp { diff --git a/src/objective-c/tests/InteropTests/InteropTestsMultipleChannels.m b/src/objective-c/tests/InteropTests/InteropTestsMultipleChannels.m index 98893a466bd8bead3015b232a1069af0bf94409c..dc48391cbccb9d2a56f364da070fcf82d158fa3d 100644 --- a/src/objective-c/tests/InteropTests/InteropTestsMultipleChannels.m +++ b/src/objective-c/tests/InteropTests/InteropTestsMultipleChannels.m @@ -18,9 +18,8 @@ #import <XCTest/XCTest.h> -#ifdef GRPC_COMPILE_WITH_CRONET #import <Cronet/Cronet.h> -#endif +#import <GRPCClient/GRPCCallOptions.h> #import <RxLibrary/GRXBufferedPipe.h> #import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h" #import "src/objective-c/tests/RemoteTestClient/Test.pbobjc.h" diff --git a/src/objective-c/tests/InteropTests/InteropTestsRemote.m b/src/objective-c/tests/InteropTests/InteropTestsRemote.m index c1cd9b81efc7da2695b40c7916ac3b9cb7312aa2..2dd8f0aed89e7e3d2d01a2eb1e90d2f120c2c929 100644 --- a/src/objective-c/tests/InteropTests/InteropTestsRemote.m +++ b/src/objective-c/tests/InteropTests/InteropTestsRemote.m @@ -53,14 +53,8 @@ static int32_t kRemoteInteropServerOverhead = 12; return kRemoteInteropServerOverhead; // bytes } -#ifdef GRPC_COMPILE_WITH_CRONET -+ (GRPCTransportType)transportType { - return GRPCTransportTypeCronet; -} -#else + (GRPCTransportType)transportType { return GRPCTransportTypeChttp2BoringSSL; } -#endif @end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index c2297aa00fdbbcabdcc52ccf827f53f1fb1618ca..c83e8861e933b40e09085e4fcaaef365ee815a7d 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -30,25 +30,25 @@ target 'MacTests' do grpc_deps end -target 'UnitTests' do - platform :ios, '8.0' - grpc_deps -end - %w( + UnitTests InteropTests - CronetTests ).each do |target_name| target target_name do platform :ios, '8.0' grpc_deps - - pod 'gRPC-Core/Cronet-Implementation', :path => GRPC_LOCAL_SRC - pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" - pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC, :inhibit_warnings => true end end +target 'CronetTests' do + platform :ios, '8.0' + grpc_deps + + pod 'gRPC/GRPCCoreCronet', :path => GRPC_LOCAL_SRC + pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" + pod 'gRPC-Core/Tests', :path => GRPC_LOCAL_SRC, :inhibit_warnings => true +end + # gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's # pre_install hook lets us do that. The block passed to it runs after the podspecs are downloaded # and before they are installed in the user project. @@ -103,7 +103,7 @@ post_install do |installer| # the test target 'InteropTestsRemoteWithCronet' # Activate GRPCCall+InternalTests functions for the dedicated build configuration 'Test', which will # be used by all test targets using it. - if /gRPC-(mac|i|tv)OS/.match(target.name) + if /gRPC(-macOS|-iOS|-tvOS|\.|-[0-9a-f])/.match(target.name) target.build_configurations.each do |config| if config.name == 'Cronet' config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) COCOAPODS=1 GRPC_COMPILE_WITH_CRONET=1 GRPC_TEST_OBJC=1' @@ -114,7 +114,7 @@ post_install do |installer| end # Enable NSAssert on gRPC - if /(gRPC|ProtoRPC|RxLibrary)-(mac|i|tv)OS/.match(target.name) + if /(gRPC|ProtoRPC|RxLibrary)/.match(target.name) target.build_configurations.each do |config| if config.name != 'Release' config.build_settings['ENABLE_NS_ASSERTIONS'] = 'YES' diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index a88838fdc8b779547e733eb3bd28221c044e3aa4..6cb9f5560b90e02679ffbdeecfb00450fd8244c0 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -8,15 +8,14 @@ /* Begin PBXBuildFile section */ 5E0282E9215AA697007AC99D /* NSErrorUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0282E8215AA697007AC99D /* NSErrorUnitTests.m */; }; + 5E08D07023021E3B006D76EA /* InteropTestsMultipleChannels.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F487722778226006656AD /* InteropTestsMultipleChannels.m */; }; 5E3F14842278B461007C6D90 /* InteropTestsBlockCallbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3F14832278B461007C6D90 /* InteropTestsBlockCallbacks.m */; }; 5E3F14852278BF5D007C6D90 /* InteropTestsBlockCallbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3F14832278B461007C6D90 /* InteropTestsBlockCallbacks.m */; }; 5E3F14862278BFFF007C6D90 /* InteropTestsBlockCallbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3F14832278B461007C6D90 /* InteropTestsBlockCallbacks.m */; }; 5E3F148D22792856007C6D90 /* ConfigureCronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3F1487227918AA007C6D90 /* ConfigureCronet.m */; }; - 5E3F148E22792AF5007C6D90 /* ConfigureCronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3F1487227918AA007C6D90 /* ConfigureCronet.m */; }; 5E7F486422775B37006656AD /* InteropTestsRemoteWithCronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EE84BF31D4717E40050C6CC /* InteropTestsRemoteWithCronet.m */; }; 5E7F486522775B41006656AD /* CronetUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD6D261E27047400002378 /* CronetUnitTests.mm */; }; 5E7F486E22778086006656AD /* CoreCronetEnd2EndTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F486D22778086006656AD /* CoreCronetEnd2EndTests.mm */; }; - 5E7F487922778226006656AD /* InteropTestsMultipleChannels.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F487722778226006656AD /* InteropTestsMultipleChannels.m */; }; 5E7F487D22778256006656AD /* ChannelPoolTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F487B22778256006656AD /* ChannelPoolTest.m */; }; 5E7F487E22778256006656AD /* ChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F487C22778256006656AD /* ChannelTests.m */; }; 5E7F4880227782C1006656AD /* APIv2Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E7F487F227782C1006656AD /* APIv2Tests.m */; }; @@ -34,6 +33,7 @@ 5EA4770322736178000F72FC /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; }; 5EA477042273617B000F72FC /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; }; 5EA4770522736AC4000F72FC /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; }; + 5ECFED8623030DCC00626501 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; }; 65EB19E418B39A8374D407BB /* libPods-CronetTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B1511C20E16A8422B58D61A /* libPods-CronetTests.a */; }; 903163C7FE885838580AEC7A /* libPods-InteropTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D457AD9797664CFA191C3280 /* libPods-InteropTests.a */; }; 953CD2942A3A6D6CE695BE87 /* libPods-MacTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 276873A05AC5479B60DF6079 /* libPods-MacTests.a */; }; @@ -515,7 +515,6 @@ 5EA476F12272816A000F72FC /* Frameworks */, 5EA476F22272816A000F72FC /* Resources */, D11CB94CF56A1E53760D29D8 /* [CP] Copy Pods Resources */, - 0FEFD5FC6B323AC95549AE4A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -630,6 +629,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5ECFED8623030DCC00626501 /* TestCertificates.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -678,24 +678,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 0FEFD5FC6B323AC95549AE4A /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-InteropTests/Pods-InteropTests-frameworks.sh", - "${PODS_ROOT}/CronetFramework/Cronet.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cronet.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-InteropTests/Pods-InteropTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 292EA42A76AC7933A37235FD /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -721,7 +703,7 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-CronetTests/Pods-CronetTests-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC.default-GRPCCoreCronet/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -898,6 +880,7 @@ files = ( 5E3F14852278BF5D007C6D90 /* InteropTestsBlockCallbacks.m in Sources */, 5E3F148D22792856007C6D90 /* ConfigureCronet.m in Sources */, + 5E08D07023021E3B006D76EA /* InteropTestsMultipleChannels.m in Sources */, 5E7F486E22778086006656AD /* CoreCronetEnd2EndTests.mm in Sources */, 5E7F488522778A88006656AD /* InteropTests.m in Sources */, 5E7F486422775B37006656AD /* InteropTestsRemoteWithCronet.m in Sources */, @@ -910,9 +893,7 @@ buildActionMask = 2147483647; files = ( 5E3F14842278B461007C6D90 /* InteropTestsBlockCallbacks.m in Sources */, - 5E3F148E22792AF5007C6D90 /* ConfigureCronet.m in Sources */, 5E7F488922778B04006656AD /* InteropTestsRemote.m in Sources */, - 5E7F487922778226006656AD /* InteropTestsMultipleChannels.m in Sources */, 5EA477042273617B000F72FC /* InteropTestsLocalCleartext.m in Sources */, 5EA4770322736178000F72FC /* InteropTestsLocalSSL.m in Sources */, 5E7F488422778A88006656AD /* InteropTests.m in Sources */, diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme index adb3c366af2d06b46b474f7ccab2d8dc4e664ccb..cbde360a33810f31e00c97ab9a5a72b1b85ad5ff 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme @@ -23,7 +23,7 @@ </BuildActionEntries> </BuildAction> <TestAction - buildConfiguration = "Cronet" + buildConfiguration = "Test" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,7 +48,7 @@ </AdditionalOptions> </TestAction> <LaunchAction - buildConfiguration = "Cronet" + buildConfiguration = "Test" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" @@ -70,7 +70,7 @@ </AdditionalOptions> </LaunchAction> <ProfileAction - buildConfiguration = "Release" + buildConfiguration = "Test" shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" @@ -86,7 +86,7 @@ </MacroExpansion> </ProfileAction> <AnalyzeAction - buildConfiguration = "Cronet"> + buildConfiguration = "Test"> </AnalyzeAction> <ArchiveAction buildConfiguration = "Release" diff --git a/src/objective-c/tests/UnitTests/ChannelPoolTest.m b/src/objective-c/tests/UnitTests/ChannelPoolTest.m index eab8c5193fb9832bc2b109e1fb79673cfffbdd28..0b1eca389b93d65069d9febbbd50b1da2a3c99c2 100644 --- a/src/objective-c/tests/UnitTests/ChannelPoolTest.m +++ b/src/objective-c/tests/UnitTests/ChannelPoolTest.m @@ -18,9 +18,9 @@ #import <XCTest/XCTest.h> -#import "../../GRPCClient/private/GRPCChannel.h" -#import "../../GRPCClient/private/GRPCChannelPool+Test.h" -#import "../../GRPCClient/private/GRPCCompletionQueue.h" +#import "../../GRPCClient/private/GRPCCore/GRPCChannel.h" +#import "../../GRPCClient/private/GRPCCore/GRPCChannelPool+Test.h" +#import "../../GRPCClient/private/GRPCCore/GRPCCompletionQueue.h" #define TEST_TIMEOUT 32 diff --git a/src/objective-c/tests/UnitTests/ChannelTests.m b/src/objective-c/tests/UnitTests/ChannelTests.m index df78e8b11629a45f8f0c42423613326b2ab95d50..1ed0f16ecafb23643262ee2e2a29a1388908a79d 100644 --- a/src/objective-c/tests/UnitTests/ChannelTests.m +++ b/src/objective-c/tests/UnitTests/ChannelTests.m @@ -19,11 +19,11 @@ #import <XCTest/XCTest.h> #import "../../GRPCClient/GRPCCallOptions.h" -#import "../../GRPCClient/private/GRPCChannel.h" -#import "../../GRPCClient/private/GRPCChannelPool+Test.h" -#import "../../GRPCClient/private/GRPCChannelPool.h" -#import "../../GRPCClient/private/GRPCCompletionQueue.h" -#import "../../GRPCClient/private/GRPCWrappedCall.h" +#import "../../GRPCClient/private/GRPCCore/GRPCChannel.h" +#import "../../GRPCClient/private/GRPCCore/GRPCChannelPool+Test.h" +#import "../../GRPCClient/private/GRPCCore/GRPCChannelPool.h" +#import "../../GRPCClient/private/GRPCCore/GRPCCompletionQueue.h" +#import "../../GRPCClient/private/GRPCCore/GRPCWrappedCall.h" static NSString *kDummyHost = @"dummy.host"; static NSString *kDummyPath = @"/dummy/path"; diff --git a/src/objective-c/tests/UnitTests/NSErrorUnitTests.m b/src/objective-c/tests/UnitTests/NSErrorUnitTests.m index 8a4f03a44603b13e978355f0887f586ec7c61cc1..74e5794c48006916d01fea14691451bd8179402c 100644 --- a/src/objective-c/tests/UnitTests/NSErrorUnitTests.m +++ b/src/objective-c/tests/UnitTests/NSErrorUnitTests.m @@ -20,7 +20,7 @@ #import <GRPCClient/GRPCCall.h> -#import "../../GRPCClient/private/NSError+GRPC.h" +#import "../../GRPCClient/private/GRPCCore/NSError+GRPC.h" @interface NSErrorUnitTests : XCTestCase diff --git a/templates/gRPC-ProtoRPC.podspec.template b/templates/gRPC-ProtoRPC.podspec.template index 457d2988036235c5bfc1adf3fcebc447941c0e00..e94f149ee4aa2cd8ea4ac411bbdf240b025bf6e5 100644 --- a/templates/gRPC-ProtoRPC.podspec.template +++ b/templates/gRPC-ProtoRPC.podspec.template @@ -44,22 +44,40 @@ s.module_name = name s.header_dir = name - src_dir = 'src/objective-c/ProtoRPC' + s.default_subspec = 'Main', 'Legacy', 'Legacy-Header' - s.default_subspec = 'Main' + s.subspec 'Legacy-Header' do |ss| + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.public_header_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.h" + ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.h" + end s.subspec 'Main' do |ss| - ss.header_mappings_dir = "#{src_dir}" - ss.dependency 'gRPC', version + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.dependency "#{s.name}/Legacy-Header", version + ss.dependency 'gRPC/Interface', version + ss.dependency 'Protobuf', '~> 3.0' + + ss.source_files = "src/objective-c/ProtoRPC/ProtoMethod.{h,m}", + "src/objective-c/ProtoRPC/ProtoRPC.{h,m}", + "src/objective-c/ProtoRPC/ProtoService.{h,m}" + end + + s.subspec 'Legacy' do |ss| + ss.header_mappings_dir = "src/objective-c/ProtoRPC" + ss.dependency "#{s.name}/Main", version + ss.dependency "#{s.name}/Legacy-Header", version + ss.dependency 'gRPC/GRPCCore', version ss.dependency 'gRPC-RxLibrary', version ss.dependency 'Protobuf', '~> 3.0' - ss.source_files = "#{src_dir}/*.{h,m}" + ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.m", + "src/objective-c/ProtoRPC/ProtoServiceLegacy.m" end # CFStream is now default. Leaving this subspec only for compatibility purpose. s.subspec 'CFStream' do |ss| - ss.dependency "#{s.name}/Main", version + ss.dependency "#{s.name}/Legacy", version end s.pod_target_xcconfig = { diff --git a/templates/gRPC-RxLibrary.podspec.template b/templates/gRPC-RxLibrary.podspec.template index 9389c5ecd8d2fe0d616c1463cac97866eda68fac..772265dadda134b481fd29c6fa4d0eb709db83a3 100644 --- a/templates/gRPC-RxLibrary.podspec.template +++ b/templates/gRPC-RxLibrary.podspec.template @@ -44,6 +44,23 @@ s.module_name = name s.header_dir = name + s.default_subspec = 'Interface', 'Implementation' + + src_dir = 'src/objective-c/RxLibrary' + s.subspec 'Interface' do |ss| + ss.header_mappings_dir = "#{src_dir}" + ss.source_files = "#{src_dir}/*.h" + ss.public_header_files = "#{src_dir}/*.h" + end + + s.subspec 'Implementation' do |ss| + ss.header_mappings_dir = "#{src_dir}" + ss.source_files = "#{src_dir}/*.m", "#{src_dir}/**/*.{h,m}" + ss.private_header_files = "#{src_dir}/**/*.h" + + ss.dependency "#{s.name}/Interface" + end + src_dir = 'src/objective-c/RxLibrary' s.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" s.private_header_files = "#{src_dir}/private/*.h" diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template index 8cb380ede03da2270b02ae0511dc63db58381021..e705edc1748acc256499e29e062677ff6c4390fd 100644 --- a/templates/gRPC.podspec.template +++ b/templates/gRPC.podspec.template @@ -43,13 +43,7 @@ s.module_name = name s.header_dir = name - src_dir = 'src/objective-c/GRPCClient' - - s.dependency 'gRPC-RxLibrary', version - s.default_subspec = 'Main' - - # Certificates, to be able to establish TLS connections: - s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + s.default_subspec = 'Interface', 'GRPCCore', 'Interface-Legacy' s.pod_target_xcconfig = { # This is needed by all pods that depend on gRPC-RxLibrary: @@ -57,29 +51,103 @@ 'CLANG_WARN_STRICT_PROTOTYPES' => 'NO', } - s.subspec 'Main' do |ss| - ss.header_mappings_dir = "#{src_dir}" - - ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" - ss.exclude_files = "#{src_dir}/GRPCCall+GID.{h,m}" - ss.private_header_files = "#{src_dir}/private/*.h", "#{src_dir}/internal/*.h" - - ss.dependency 'gRPC-Core', version + s.subspec 'Interface-Legacy' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "src/objective-c/GRPCClient/GRPCCallLegacy.h", + "src/objective-c/GRPCClient/GRPCTypes.h" + + ss.source_files = "GRPCClient/GRPCCall+ChannelArg.h", + "GRPCClient/GRPCCall+ChannelCredentials.h", + "GRPCClient/GRPCCall+Cronet.h", + "GRPCClient/GRPCCall+OAuth2.h", + "GRPCClient/GRPCCall+Tests.h", + "src/objective-c/GRPCClient/GRPCCallLegacy.h", + "src/objective-c/GRPCClient/GRPCTypes.h" + ss.dependency "gRPC-RxLibrary/Interface", version end - # CFStream is now default. Leaving this subspec only for compatibility purpose. - s.subspec 'CFStream' do |ss| - ss.dependency "#{s.name}/Main", version + s.subspec 'Interface' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = 'src/objective-c/GRPCClient/GRPCCall.h', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.h', + 'src/objective-c/GRPCClient/GRPCCallOptions.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.h', + 'src/objective-c/GRPCClient/GRPCTransport.h', + 'src/objective-c/GRPCClient/GRPCDispatchable.h', + 'src/objective-c/GRPCClient/version.h' + + ss.source_files = 'src/objective-c/GRPCClient/GRPCCall.h', + 'src/objective-c/GRPCClient/GRPCCall.m', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.h', + 'src/objective-c/GRPCClient/GRPCCall+Interceptor.m', + 'src/objective-c/GRPCClient/GRPCCallOptions.h', + 'src/objective-c/GRPCClient/GRPCCallOptions.m', + 'src/objective-c/GRPCClient/GRPCDispatchable.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.h', + 'src/objective-c/GRPCClient/GRPCInterceptor.m', + 'src/objective-c/GRPCClient/GRPCTransport.h', + 'src/objective-c/GRPCClient/GRPCTransport.m', + 'src/objective-c/GRPCClient/internal/*.h', + 'src/objective-c/GRPCClient/private/GRPCTransport+Private.h', + 'src/objective-c/GRPCClient/private/GRPCTransport+Private.m', + 'src/objective-c/GRPCClient/version.h' + + ss.dependency "#{s.name}/Interface-Legacy", version end - s.subspec 'GID' do |ss| - ss.ios.deployment_target = '7.0' + s.subspec 'GRPCCore' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' + + ss.public_header_files = 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.h', + 'src/objective-c/GRPCClient/GRPCCall+Tests.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.h', + 'src/objective-c/GRPCClient/internal_testing/*.h' + ss.private_header_files = 'src/objective-c/GRPCClient/private/GRPCCore/*.h' + ss.source_files = 'src/objective-c/GRPCClient/internal_testing/*.{h,m}', + 'src/objective-c/GRPCClient/private/GRPCCore/*.{h,m}', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelArg.m', + 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h', + 'src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.m', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.h', + 'src/objective-c/GRPCClient/GRPCCall+OAuth2.m', + 'src/objective-c/GRPCClient/GRPCCall+Tests.h', + 'src/objective-c/GRPCClient/GRPCCall+Tests.m', + 'src/objective-c/GRPCClient/GRPCCallLegacy.m' + + # Certificates, to be able to establish TLS connections: + ss.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + + ss.dependency "#{s.name}/Interface-Legacy", version + ss.dependency "#{s.name}/Interface", version + ss.dependency 'gRPC-Core', version + ss.dependency 'gRPC-RxLibrary', version + end - ss.header_mappings_dir = "#{src_dir}" + s.subspec 'GRPCCoreCronet' do |ss| + ss.header_mappings_dir = 'src/objective-c/GRPCClient' - ss.source_files = "#{src_dir}/GRPCCall+GID.{h,m}" + ss.source_files = 'src/objective-c/GRPCClient/GRPCCall+Cronet.h', + 'src/objective-c/GRPCClient/GRPCCall+Cronet.m', + 'src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/*.{h,m}' + ss.dependency "#{s.name}/GRPCCore", version + ss.dependency 'gRPC-Core/Cronet-Implementation', version + ss.dependency 'CronetFramework' + end - ss.dependency "#{s.name}/Main", version - ss.dependency 'Google/SignIn' + # CFStream is now default. Leaving this subspec only for compatibility purpose. + s.subspec 'CFStream' do |ss| + ss.dependency "#{s.name}/GRPCCore", version end end diff --git a/templates/src/objective-c/GRPCClient/private/version.h.template b/templates/src/objective-c/GRPCClient/version.h.template similarity index 100% rename from templates/src/objective-c/GRPCClient/private/version.h.template rename to templates/src/objective-c/GRPCClient/version.h.template