Revert "Merge pull request #19704 from muxi/isolate-call-implementation-2"

This reverts commit ac5f8062dd, reversing
changes made to 8ae549431c.
pull/20103/head
Muxi Yan 6 years ago
parent f29aee7fa9
commit d1f4456dc6
  1. 11
      bazel/grpc_build_system.bzl
  2. 30
      gRPC-ProtoRPC.podspec
  3. 17
      gRPC-RxLibrary.podspec
  4. 116
      gRPC.podspec
  5. 97
      src/compiler/objective_c_generator.cc
  6. 14
      src/compiler/objective_c_generator.h
  7. 60
      src/compiler/objective_c_plugin.cc
  8. 244
      src/objective-c/BUILD
  9. 2
      src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
  10. 4
      src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
  11. 2
      src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h
  12. 2
      src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.m
  13. 13
      src/objective-c/GRPCClient/GRPCCall+Cronet.h
  14. 4
      src/objective-c/GRPCClient/GRPCCall+Cronet.m
  15. 2
      src/objective-c/GRPCClient/GRPCCall+GID.h
  16. 4
      src/objective-c/GRPCClient/GRPCCall+OAuth2.h
  17. 2
      src/objective-c/GRPCClient/GRPCCall+Tests.h
  18. 2
      src/objective-c/GRPCClient/GRPCCall+Tests.m
  19. 240
      src/objective-c/GRPCClient/GRPCCall.h
  20. 867
      src/objective-c/GRPCClient/GRPCCall.m
  21. 136
      src/objective-c/GRPCClient/GRPCCallLegacy.h
  22. 677
      src/objective-c/GRPCClient/GRPCCallLegacy.m
  23. 82
      src/objective-c/GRPCClient/GRPCCallOptions.h
  24. 27
      src/objective-c/GRPCClient/GRPCCallOptions.m
  25. 30
      src/objective-c/GRPCClient/GRPCDispatchable.h
  26. 32
      src/objective-c/GRPCClient/GRPCInterceptor.h
  27. 265
      src/objective-c/GRPCClient/GRPCInterceptor.m
  28. 82
      src/objective-c/GRPCClient/GRPCTransport.h
  29. 142
      src/objective-c/GRPCClient/GRPCTransport.m
  30. 187
      src/objective-c/GRPCClient/GRPCTypes.h
  31. 2
      src/objective-c/GRPCClient/internal_testing/GRPCCall+InternalTests.m
  32. 0
      src/objective-c/GRPCClient/private/ChannelArgsUtil.h
  33. 0
      src/objective-c/GRPCClient/private/ChannelArgsUtil.m
  34. 6
      src/objective-c/GRPCClient/private/GRPCCall+V2API.h
  35. 14
      src/objective-c/GRPCClient/private/GRPCCallInternal.h
  36. 140
      src/objective-c/GRPCClient/private/GRPCCallInternal.m
  37. 0
      src/objective-c/GRPCClient/private/GRPCChannel.h
  38. 72
      src/objective-c/GRPCClient/private/GRPCChannel.m
  39. 0
      src/objective-c/GRPCClient/private/GRPCChannelFactory.h
  40. 0
      src/objective-c/GRPCClient/private/GRPCChannelPool+Test.h
  41. 2
      src/objective-c/GRPCClient/private/GRPCChannelPool.h
  42. 5
      src/objective-c/GRPCClient/private/GRPCChannelPool.m
  43. 0
      src/objective-c/GRPCClient/private/GRPCCompletionQueue.h
  44. 0
      src/objective-c/GRPCClient/private/GRPCCompletionQueue.m
  45. 32
      src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.h
  46. 54
      src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreCronet/GRPCCoreCronetFactory.m
  47. 45
      src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.h
  48. 90
      src/objective-c/GRPCClient/private/GRPCCore/GRPCCoreFactory.m
  49. 2
      src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.h
  50. 24
      src/objective-c/GRPCClient/private/GRPCCronetChannelFactory.m
  51. 0
      src/objective-c/GRPCClient/private/GRPCHost.h
  52. 35
      src/objective-c/GRPCClient/private/GRPCHost.m
  53. 0
      src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.h
  54. 0
      src/objective-c/GRPCClient/private/GRPCInsecureChannelFactory.m
  55. 0
      src/objective-c/GRPCClient/private/GRPCOpBatchLog.h
  56. 0
      src/objective-c/GRPCClient/private/GRPCOpBatchLog.m
  57. 0
      src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h
  58. 2
      src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
  59. 0
      src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
  60. 0
      src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.h
  61. 0
      src/objective-c/GRPCClient/private/GRPCSecureChannelFactory.m
  62. 65
      src/objective-c/GRPCClient/private/GRPCTransport+Private.h
  63. 131
      src/objective-c/GRPCClient/private/GRPCTransport+Private.m
  64. 0
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  65. 0
      src/objective-c/GRPCClient/private/GRPCWrappedCall.m
  66. 0
      src/objective-c/GRPCClient/private/NSData+GRPC.h
  67. 0
      src/objective-c/GRPCClient/private/NSData+GRPC.m
  68. 0
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.h
  69. 0
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
  70. 0
      src/objective-c/GRPCClient/private/NSError+GRPC.h
  71. 3
      src/objective-c/GRPCClient/private/NSError+GRPC.m
  72. 0
      src/objective-c/GRPCClient/private/version.h
  73. 38
      src/objective-c/ProtoRPC/ProtoRPC.h
  74. 91
      src/objective-c/ProtoRPC/ProtoRPC.m
  75. 67
      src/objective-c/ProtoRPC/ProtoRPCLegacy.h
  76. 121
      src/objective-c/ProtoRPC/ProtoRPCLegacy.m
  77. 29
      src/objective-c/ProtoRPC/ProtoService.h
  78. 42
      src/objective-c/ProtoRPC/ProtoService.m
  79. 23
      src/objective-c/ProtoRPC/ProtoServiceLegacy.h
  80. 71
      src/objective-c/ProtoRPC/ProtoServiceLegacy.m
  81. 2
      src/objective-c/examples/tvOS-sample/tvOS-sample/ViewController.m
  82. 2
      src/objective-c/examples/watchOS-sample/WatchKit-Extension/InterfaceController.m
  83. 8
      src/objective-c/tests/BUILD
  84. 4
      src/objective-c/tests/ConfigureCronet.h
  85. 4
      src/objective-c/tests/ConfigureCronet.m
  86. 12
      src/objective-c/tests/CronetTests/InteropTestsRemoteWithCronet.m
  87. 13
      src/objective-c/tests/InteropTests/InteropTests.h
  88. 268
      src/objective-c/tests/InteropTests/InteropTests.m
  89. 5
      src/objective-c/tests/InteropTests/InteropTestsLocalCleartext.m
  90. 5
      src/objective-c/tests/InteropTests/InteropTestsLocalSSL.m
  91. 3
      src/objective-c/tests/InteropTests/InteropTestsMultipleChannels.m
  92. 6
      src/objective-c/tests/InteropTests/InteropTestsRemote.m
  93. 24
      src/objective-c/tests/Podfile
  94. 29
      src/objective-c/tests/Tests.xcodeproj/project.pbxproj
  95. 8
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTests.xcscheme
  96. 6
      src/objective-c/tests/UnitTests/ChannelPoolTest.m
  97. 10
      src/objective-c/tests/UnitTests/ChannelTests.m
  98. 2
      src/objective-c/tests/UnitTests/NSErrorUnitTests.m
  99. 30
      templates/gRPC-ProtoRPC.podspec.template
  100. 17
      templates/gRPC-RxLibrary.podspec.template
  101. Some files were not shown because too many files have changed in this diff Show More

@ -24,6 +24,7 @@
#
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")
@ -238,13 +239,19 @@ 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_generate_objc_one_off_targets():
def grpc_objc_use_cronet_config():
pass
def grpc_sh_test(name, srcs, args = [], data = []):
@ -289,7 +296,7 @@ def grpc_package(name, visibility = "private", features = []):
def grpc_objc_library(
name,
srcs = [],
srcs,
hdrs = [],
textual_hdrs = [],
data = [],

@ -42,40 +42,22 @@ Pod::Spec.new do |s|
s.module_name = name
s.header_dir = name
s.default_subspec = 'Main', 'Legacy', 'Legacy-Header'
src_dir = 'src/objective-c/ProtoRPC'
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.default_subspec = 'Main'
s.subspec 'Main' do |ss|
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.header_mappings_dir = "#{src_dir}"
ss.dependency 'gRPC', version
ss.dependency 'gRPC-RxLibrary', version
ss.dependency 'Protobuf', '~> 3.0'
ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.m",
"src/objective-c/ProtoRPC/ProtoServiceLegacy.m"
ss.source_files = "#{src_dir}/*.{h,m}"
end
# CFStream is now default. Leaving this subspec only for compatibility purpose.
s.subspec 'CFStream' do |ss|
ss.dependency "#{s.name}/Legacy", version
ss.dependency "#{s.name}/Main", version
end
s.pod_target_xcconfig = {

@ -42,23 +42,6 @@ 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"

@ -41,7 +41,13 @@ Pod::Spec.new do |s|
s.module_name = name
s.header_dir = name
s.default_subspec = 'Interface', 'GRPCCore', 'Interface-Legacy'
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.pod_target_xcconfig = {
# This is needed by all pods that depend on gRPC-RxLibrary:
@ -49,103 +55,29 @@ Pod::Spec.new do |s|
'CLANG_WARN_STRICT_PROTOTYPES' => 'NO',
}
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
s.subspec 'Main' do |ss|
ss.header_mappings_dir = "#{src_dir}"
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
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"
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
s.subspec 'GRPCCoreCronet' do |ss|
ss.header_mappings_dir = 'src/objective-c/GRPCClient'
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
# CFStream is now default. Leaving this subspec only for compatibility purpose.
s.subspec 'CFStream' do |ss|
ss.dependency "#{s.name}/GRPCCore", version
ss.dependency "#{s.name}/Main", version
end
s.subspec 'GID' do |ss|
ss.ios.deployment_target = '7.0'
ss.header_mappings_dir = "#{src_dir}"
ss.source_files = "#{src_dir}/GRPCCall+GID.{h,m}"
ss.dependency "#{s.name}/Main", version
ss.dependency 'Google/SignIn'
end
end

@ -238,21 +238,19 @@ void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
}
void PrintMethodImplementations(Printer* printer,
const MethodDescriptor* method,
const Parameters& generator_params) {
const MethodDescriptor* method) {
map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
if (!generator_params.no_v1_compatibility) {
// TODO(jcanizales): Print documentation from the method.
PrintSimpleSignature(printer, method, vars);
PrintSimpleImplementation(printer, method, vars);
// TODO(jcanizales): Print documentation from the method.
printer->Print("// Deprecated methods.\n");
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);
@ -278,12 +276,9 @@ void PrintMethodImplementations(Printer* printer,
return output;
}
::grpc::string GetProtocol(const ServiceDescriptor* service,
const Parameters& generator_params) {
::grpc::string GetProtocol(const ServiceDescriptor* service) {
::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, '$');
@ -326,8 +321,7 @@ void PrintMethodImplementations(Printer* printer,
return output;
}
::grpc::string GetInterface(const ServiceDescriptor* service,
const Parameters& generator_params) {
::grpc::string GetInterface(const ServiceDescriptor* service) {
::grpc::string output;
// Scope the output stream so it closes and finalizes output to the string.
@ -344,11 +338,7 @@ void PrintMethodImplementations(Printer* printer,
" */\n");
printer.Print(vars,
"@interface $service_class$ :"
" GRPCProtoService<$service_class$2");
if (!generator_params.no_v1_compatibility) {
printer.Print(vars, ", $service_class$");
}
printer.Print(">\n");
" GRPCProtoService<$service_class$, $service_class$2>\n");
printer.Print(
"- (instancetype)initWithHost:(NSString *)host "
"callOptions:(GRPCCallOptions "
@ -357,20 +347,17 @@ void PrintMethodImplementations(Printer* printer,
printer.Print(
"+ (instancetype)serviceWithHost:(NSString *)host "
"callOptions:(GRPCCallOptions *_Nullable)callOptions;\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(
"// 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,
const Parameters& generator_params) {
::grpc::string GetSource(const ServiceDescriptor* service) {
::grpc::string output;
{
// Scope the output stream so it closes and finalizes output to the string.
@ -394,28 +381,22 @@ void PrintMethodImplementations(Printer* printer,
" packageName:@\"$package$\"\n"
" serviceName:@\"$service_name$\"\n"
" callOptions:callOptions];\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");
}
"}\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");
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"
@ -423,14 +404,11 @@ 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"
@ -439,8 +417,7 @@ 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),
generator_params);
PrintMethodImplementations(&printer, service->method(i));
}
printer.Print("@end\n");

@ -23,11 +23,6 @@
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;
@ -39,8 +34,7 @@ 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,
const Parameters& generator_params);
string GetProtocol(const ServiceDescriptor* service);
// Returns the content to be included defining the @protocol segment at the
// insertion point of the generated implementation file.
@ -48,13 +42,11 @@ 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,
const Parameters& generator_params);
string GetInterface(const ServiceDescriptor* service);
// Returns the content to be included in the "global_scope" insertion point of
// the generated implementation file.
string GetSource(const ServiceDescriptor* service,
const Parameters& generator_params);
string GetSource(const ServiceDescriptor* service);
} // namespace grpc_objective_c_generator

@ -111,22 +111,6 @@ 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
@ -137,25 +121,18 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
imports = FrameworkImport(file_name + ".pbobjc.h", framework);
}
::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 system_imports = SystemImport("ProtoRPC/ProtoService.h") +
SystemImport("ProtoRPC/ProtoRPC.h") +
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";
if (!generator_params.no_v1_compatibility) {
forward_declarations += "@class GRPCProtoCall;\n";
}
forward_declarations += "\n";
"@protocol GRPCProtoResponseHandler;\n"
"\n";
::grpc::string class_declarations =
grpc_objective_c_generator::GetAllMessageClasses(file);
@ -175,15 +152,13 @@ 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, generator_params);
protocols += grpc_objective_c_generator::GetProtocol(service);
}
::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, generator_params);
interfaces += grpc_objective_c_generator::GetInterface(service);
}
Write(context, file_name + ".pbrpc.h",
@ -203,16 +178,14 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
::grpc::string imports;
if (framework.empty()) {
imports = LocalImport(file_name + ".pbrpc.h") +
LocalImport(file_name + ".pbobjc.h");
LocalImport(file_name + ".pbobjc.h") +
SystemImport("ProtoRPC/ProtoRPC.h") +
SystemImport("RxLibrary/GRXWriter+Immediate.h");
} else {
imports = FrameworkImport(file_name + ".pbrpc.h", framework) +
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");
FrameworkImport(file_name + ".pbobjc.h", framework) +
SystemImport("ProtoRPC/ProtoRPC.h") +
SystemImport("RxLibrary/GRXWriter+Immediate.h");
}
::grpc::string class_imports;
@ -223,8 +196,7 @@ 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, generator_params);
definitions += grpc_objective_c_generator::GetSource(service);
}
Write(context, file_name + ".pbrpc.m",

@ -18,30 +18,24 @@ licenses(["notice"]) # Apache v2
package(default_visibility = ["//visibility:public"])
load("//bazel:grpc_build_system.bzl", "grpc_objc_library", "grpc_generate_objc_one_off_targets")
load("//bazel:grpc_build_system.bzl", "grpc_objc_library", "grpc_objc_use_cronet_config")
exports_files(["LICENSE"])
grpc_generate_objc_one_off_targets()
grpc_objc_library(
name = "rx_library_headers",
hdrs = glob([
"RxLibrary/*.h",
]),
includes = ["."],
)
grpc_objc_use_cronet_config()
grpc_objc_library(
name = "rx_library",
srcs = glob([
"RxLibrary/*.m",
"RxLibrary/transformations/*.m",
]),
hdrs = glob([
"RxLibrary/*.h",
"RxLibrary/transformations/*.h",
]),
includes = ["."],
deps = [
":rx_library_headers",
":rx_library_private",
],
deps = [":rx_library_private"],
)
grpc_objc_library(
@ -56,196 +50,84 @@ grpc_objc_library(
)
grpc_objc_library(
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 = [
"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"],
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"],
includes = ["."],
textual_hdrs = glob([
"GRPCClient/private/*.h",
]),
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_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_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",
],
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
defines = ["GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=0"],
deps = [
":grpc_objc_client",
":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_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"],
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"],
),
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",
)

@ -15,7 +15,7 @@
* limitations under the License.
*
*/
#import "GRPCCallLegacy.h"
#import "GRPCCall.h"
#include <AvailabilityMacros.h>

@ -18,8 +18,8 @@
#import "GRPCCall+ChannelArg.h"
#import "private/GRPCCore/GRPCChannelPool.h"
#import "private/GRPCCore/GRPCHost.h"
#import "private/GRPCChannelPool.h"
#import "private/GRPCHost.h"
#import <grpc/impl/codegen/compression_types.h>

@ -16,7 +16,7 @@
*
*/
#import "GRPCCallLegacy.h"
#import "GRPCCall.h"
// Deprecated interface. Please use GRPCCallOptions instead.
@interface GRPCCall (ChannelCredentials)

@ -18,7 +18,7 @@
#import "GRPCCall+ChannelCredentials.h"
#import "private/GRPCCore/GRPCHost.h"
#import "private/GRPCHost.h"
@implementation GRPCCall (ChannelCredentials)

@ -15,16 +15,12 @@
* limitations under the License.
*
*/
#ifdef GRPC_COMPILE_WITH_CRONET
#import <Cronet/Cronet.h>
#import "GRPCCallLegacy.h"
#import "GRPCTypes.h"
#import "GRPCCall.h"
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.
// Deprecated interface. Please use GRPCCallOptions instead.
@interface GRPCCall (Cronet)
+ (void)useCronetWithEngine:(stream_engine*)engine;
@ -32,3 +28,4 @@ extern const GRPCTransportId gGRPCCoreCronetId;
+ (BOOL)isUsingCronet;
@end
#endif

@ -18,8 +18,7 @@
#import "GRPCCall+Cronet.h"
const GRPCTransportId gGRPCCoreCronetId = "io.grpc.transport.core.cronet";
#ifdef GRPC_COMPILE_WITH_CRONET
static BOOL useCronet = NO;
static stream_engine *globalCronetEngine;
@ -39,3 +38,4 @@ static stream_engine *globalCronetEngine;
}
@end
#endif

@ -17,7 +17,7 @@
*/
#import "GRPCCall+OAuth2.h"
#import "GRPCCallLegacy.h"
#import "GRPCCall.h"
#import <Google/SignIn.h>

@ -16,9 +16,9 @@
*
*/
#import "GRPCCallLegacy.h"
#import "GRPCCall.h"
@protocol GRPCAuthorizationProtocol;
#import "GRPCCallOptions.h"
// Deprecated interface. Please use GRPCCallOptions instead.
@interface GRPCCall (OAuth2)

@ -16,7 +16,7 @@
*
*/
#import "GRPCCallLegacy.h"
#import "GRPCCall.h"
// Deprecated interface. Please use GRPCCallOptions instead.
@interface GRPCCall (Tests)

@ -18,7 +18,7 @@
#import "GRPCCall+Tests.h"
#import "private/GRPCCore/GRPCHost.h"
#import "private/GRPCHost.h"
#import "GRPCCallOptions.h"

@ -33,19 +33,134 @@
*/
#import <Foundation/Foundation.h>
#import <RxLibrary/GRXWriter.h>
#import "GRPCCallOptions.h"
#import "GRPCDispatchable.h"
#import "GRPCTypes.h"
#include <AvailabilityMacros.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"
#include "GRPCCallOptions.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, GRPCDispatchable>
@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;
@optional
@ -187,3 +302,114 @@ NS_ASSUME_NONNULL_BEGIN
@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

@ -17,86 +17,56 @@
*/
#import "GRPCCall.h"
#import "GRPCCall+Interceptor.h"
#import "GRPCCall+OAuth2.h"
#import "GRPCCallOptions.h"
#import "GRPCInterceptor.h"
#import "private/GRPCTransport+Private.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;
NSString *const kGRPCHeadersKey = @"io.grpc.HeadersKey";
NSString *const kGRPCTrailersKey = @"io.grpc.TrailersKey";
static NSMutableDictionary *callFlags;
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
static NSString *const kAuthorizationHeader = @"authorization";
static NSString *const kBearerPrefix = @"Bearer ";
@implementation GRPCResponseDispatcher {
id<GRPCResponseHandler> _responseHandler;
dispatch_queue_t _dispatchQueue;
}
const char *kCFStreamVarName = "grpc_cfstream";
- (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);
}
@interface GRPCCall ()<GRXWriteable>
// Make them read-write.
@property(atomic, strong) NSDictionary *responseHeaders;
@property(atomic, strong) NSDictionary *responseTrailers;
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];
}
}
- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
- (void)didWriteData {
if ([_responseHandler respondsToSelector:@selector(didWriteData)]) {
[_responseHandler didWriteData];
}
}
- (instancetype)initWithHost:(NSString *)host
path:(NSString *)path
callSafety:(GRPCCallSafety)safety
requestsWriter:(GRXWriter *)requestsWriter
callOptions:(GRPCCallOptions *)callOptions
writeDone:(void (^)(void))writeDone;
@end
@ -170,39 +140,54 @@ NSString *const kGRPCErrorDomain = @"io.grpc";
}
_responseHandler = responseHandler;
GRPCResponseDispatcher *dispatcher =
[[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler];
NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories;
if (_actualCallOptions.interceptorFactories != nil) {
interceptorFactories =
[NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories];
} else {
interceptorFactories = [NSMutableArray array];
}
// 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
id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory];
if (globalInterceptorFactory != nil) {
[interceptorFactories addObject:globalInterceptorFactory];
if (globalInterceptorFactory) {
GRPCInterceptorManager *manager =
[[GRPCInterceptorManager alloc] initWithNextInterceptor:nextInterceptor];
GRPCInterceptor *interceptor =
[globalInterceptorFactory createInterceptorWithManager:manager];
if (interceptor != nil) {
[internalCall setResponseHandler:interceptor];
nextInterceptor = interceptor;
nextManager = manager;
}
}
// continuously create interceptor until one is successfully created
while (_firstInterceptor == nil) {
if (interceptorFactories.count == 0) {
_firstInterceptor = [[GRPCTransportManager alloc] initWithTransportId:_callOptions.transport
previousInterceptor:dispatcher];
break;
// 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];
} else {
_firstInterceptor =
[[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories
previousInterceptor:dispatcher
transportId:_callOptions.transport];
if (_firstInterceptor == nil) {
[interceptorFactories removeObjectAtIndex:0];
}
[nextManager setPreviousInterceptor:interceptor];
}
nextInterceptor = interceptor;
nextManager = manager;
}
NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport.");
if (_firstInterceptor == nil) {
NSLog(@"Failed to create interceptor or transport.");
if (nextManager == nil) {
[internalCall setResponseHandler:_responseHandler];
} else {
[nextManager setPreviousInterceptor:_responseHandler];
}
_firstInterceptor = nextInterceptor;
}
return self;
@ -215,42 +200,696 @@ NSString *const kGRPCErrorDomain = @"io.grpc";
}
- (void)start {
id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
GRPCRequestOptions *requestOptions = _requestOptions;
GRPCCallOptions *callOptions = _actualCallOptions;
dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
[copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
});
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];
});
}
}
- (void)cancel {
id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
if (copiedFirstInterceptor != nil) {
dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
id<GRPCInterceptorInterface> copiedFirstInterceptor;
@synchronized(self) {
copiedFirstInterceptor = _firstInterceptor;
}
if ([copiedFirstInterceptor respondsToSelector:@selector(cancel)]) {
dispatch_async(copiedFirstInterceptor.requestDispatchQueue, ^{
[copiedFirstInterceptor cancel];
});
}
}
- (void)writeData:(id)data {
id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
[copiedFirstInterceptor writeData: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 = _firstInterceptor;
dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
[copiedFirstInterceptor 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];
}
}
}];
}
}
}];
});
}
#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 {
id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
[copiedFirstInterceptor receiveNextMessages: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 == 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

@ -1,136 +0,0 @@
/*
*
* 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

@ -1,677 +0,0 @@
/*
*
* 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

@ -18,11 +18,57 @@
#import <Foundation/Foundation.h>
#import "GRPCTypes.h"
NS_ASSUME_NONNULL_BEGIN
@protocol GRPCInterceptorFactory;
/**
* 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,
};
/**
* 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;
@end
@interface GRPCCallOptions : NSObject<NSCopying, NSMutableCopying>
@ -58,7 +104,7 @@ NS_ASSUME_NONNULL_BEGIN
* this array. This parameter should not be modified by any interceptor and will
* not take effect if done so.
*/
@property(copy, readonly) NSArray<id<GRPCInterceptorFactory>> *interceptorFactories;
@property(copy, readonly) NSArray *interceptorFactories;
// OAuth2 parameters. Users of gRPC may specify one of the following two parameters.
@ -146,23 +192,10 @@ NS_ASSUME_NONNULL_BEGIN
@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.
*/
@ -234,7 +267,7 @@ NS_ASSUME_NONNULL_BEGIN
* this array. This parameter should not be modified by any interceptor and will
* not take effect if done so.
*/
@property(copy, readwrite) NSArray<id<GRPCInterceptorFactory>> *interceptorFactories;
@property(copy, readwrite) NSArray *interceptorFactories;
// OAuth2 parameters. Users of gRPC may specify one of the following two parameters.
@ -324,23 +357,10 @@ NS_ASSUME_NONNULL_BEGIN
@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.
*/

@ -17,14 +17,13 @@
*/
#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<id<GRPCInterceptorFactory>> *const kDefaultInterceptorFactories = nil;
static NSArray *const kDefaultInterceptorFactories = nil;
static NSDictionary *const kDefaultInitialMetadata = nil;
static NSString *const kDefaultUserAgentPrefix = nil;
static const NSUInteger kDefaultResponseSizeLimit = 0;
@ -42,7 +41,6 @@ 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;
@ -64,7 +62,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
NSString *_serverAuthority;
NSTimeInterval _timeout;
BOOL _flowControlEnabled;
NSArray<id<GRPCInterceptorFactory>> *_interceptorFactories;
NSArray *_interceptorFactories;
NSString *_oauth2AccessToken;
id<GRPCAuthorizationProtocol> _authTokenProvider;
NSDictionary *_initialMetadata;
@ -82,7 +80,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
NSString *_PEMPrivateKey;
NSString *_PEMCertificateChain;
GRPCTransportType _transportType;
GRPCTransportId _transport;
NSString *_hostNameOverride;
id<NSObject> _logContext;
NSString *_channelPoolDomain;
@ -110,7 +107,6 @@ 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;
@ -138,7 +134,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
PEMPrivateKey:kDefaultPEMPrivateKey
PEMCertificateChain:kDefaultPEMCertificateChain
transportType:kDefaultTransportType
transport:kDefaultTransport
hostNameOverride:kDefaultHostNameOverride
logContext:kDefaultLogContext
channelPoolDomain:kDefaultChannelPoolDomain
@ -148,7 +143,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
- (instancetype)initWithServerAuthority:(NSString *)serverAuthority
timeout:(NSTimeInterval)timeout
flowControlEnabled:(BOOL)flowControlEnabled
interceptorFactories:(NSArray<id<GRPCInterceptorFactory>> *)interceptorFactories
interceptorFactories:(NSArray *)interceptorFactories
oauth2AccessToken:(NSString *)oauth2AccessToken
authTokenProvider:(id<GRPCAuthorizationProtocol>)authTokenProvider
initialMetadata:(NSDictionary *)initialMetadata
@ -166,7 +161,6 @@ 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
@ -199,7 +193,6 @@ 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];
@ -231,7 +224,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
PEMPrivateKey:_PEMPrivateKey
PEMCertificateChain:_PEMCertificateChain
transportType:_transportType
transport:_transport
hostNameOverride:_hostNameOverride
logContext:_logContext
channelPoolDomain:_channelPoolDomain
@ -264,7 +256,6 @@ 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]
@ -289,7 +280,6 @@ 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;
@ -314,7 +304,6 @@ 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;
@ -347,7 +336,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
@dynamic PEMPrivateKey;
@dynamic PEMCertificateChain;
@dynamic transportType;
@dynamic transport;
@dynamic hostNameOverride;
@dynamic logContext;
@dynamic channelPoolDomain;
@ -375,7 +363,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
PEMPrivateKey:kDefaultPEMPrivateKey
PEMCertificateChain:kDefaultPEMCertificateChain
transportType:kDefaultTransportType
transport:kDefaultTransport
hostNameOverride:kDefaultHostNameOverride
logContext:kDefaultLogContext
channelPoolDomain:kDefaultChannelPoolDomain
@ -405,7 +392,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
PEMPrivateKey:_PEMPrivateKey
PEMCertificateChain:_PEMCertificateChain
transportType:_transportType
transport:_transport
hostNameOverride:_hostNameOverride
logContext:_logContext
channelPoolDomain:_channelPoolDomain
@ -436,7 +422,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
PEMPrivateKey:_PEMPrivateKey
PEMCertificateChain:_PEMCertificateChain
transportType:_transportType
transport:_transport
hostNameOverride:_hostNameOverride
logContext:_logContext
channelPoolDomain:_channelPoolDomain
@ -460,7 +445,7 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
_flowControlEnabled = flowControlEnabled;
}
- (void)setInterceptorFactories:(NSArray<id<GRPCInterceptorFactory>> *)interceptorFactories {
- (void)setInterceptorFactories:(NSArray *)interceptorFactories {
_interceptorFactories = interceptorFactories;
}
@ -553,10 +538,6 @@ static BOOL areObjectsEqual(id obj1, id obj2) {
_transportType = transportType;
}
- (void)setTransport:(GRPCTransportId)transport {
_transport = transport;
}
- (void)setHostNameOverride:(NSString *)hostNameOverride {
_hostNameOverride = [hostNameOverride copy];
}

@ -1,30 +0,0 @@
/*
*
* 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

@ -106,20 +106,22 @@
*/
#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, GRPCDispatchable>
@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;
/**
* To start the call. This method will only be called once for each instance.
@ -169,20 +171,19 @@ NS_ASSUME_NONNULL_BEGIN
* invoke shutDown method of its corresponding manager so that references to other interceptors can
* be released.
*/
@interface GRPCInterceptorManager : NSObject<GRPCInterceptorInterface, GRPCResponseHandler>
@interface GRPCInterceptorManager : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype) new NS_UNAVAILABLE;
- (nullable instancetype)initWithFactories:(nullable NSArray<id<GRPCInterceptorFactory>> *)factories
previousInterceptor:(nullable id<GRPCResponseHandler>)previousInterceptor
transportId:(GRPCTransportId)transportId;
- (nullable instancetype)initWithNextInterceptor:(id<GRPCInterceptorInterface>)nextInterceptor
NS_DESIGNATED_INITIALIZER;
/**
* Notify the manager that the interceptor has shut down and the manager should release references
* to other interceptors and stop forwarding requests/responses.
*/
/** Set the previous interceptor in the chain. Can only be set once. */
- (void)setPreviousInterceptor:(id<GRPCResponseHandler>)previousInterceptor;
/** Indicate shutdown of the interceptor; release the reference to other interceptors */
- (void)shutDown;
// Methods to forward GRPCInterceptorInterface calls to the next interceptor
@ -234,6 +235,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface GRPCInterceptor : NSObject<GRPCInterceptorInterface, GRPCResponseHandler>
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype) new NS_UNAVAILABLE;
/**
@ -241,7 +243,9 @@ NS_ASSUME_NONNULL_BEGIN
* that this interceptor's methods are dispatched onto.
*/
- (nullable instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
dispatchQueue:(dispatch_queue_t)dispatchQueue;
requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue
NS_DESIGNATED_INITIALIZER;
// Default implementation of GRPCInterceptorInterface

@ -19,253 +19,117 @@
#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)initWithFactories:(NSArray<id<GRPCInterceptorFactory>> *)factories
previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor
transportId:(nonnull GRPCTransportId)transportId {
- (instancetype)initWithNextInterceptor:(id<GRPCInterceptorInterface>)nextInterceptor {
if ((self = [super init])) {
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;
_nextInterceptor = nextInterceptor;
}
return self;
}
- (void)shutDown {
dispatch_async(_dispatchQueue, ^{
self->_nextInterceptor = nil;
self->_previousInterceptor = nil;
self->_thisInterceptor = nil;
self->_shutDown = YES;
});
- (void)setPreviousInterceptor:(id<GRPCResponseHandler>)previousInterceptor {
_previousInterceptor = previousInterceptor;
}
- (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)shutDown {
_nextInterceptor = nil;
_previousInterceptor = nil;
}
- (void)startNextInterceptorWithRequest:(GRPCRequestOptions *)requestOptions
callOptions:(GRPCCallOptions *)callOptions {
if (_nextInterceptor == nil && !_shutDown) {
[self createNextInterceptor];
}
if (_nextInterceptor == nil) {
return;
if (_nextInterceptor != nil) {
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{
[copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
});
}
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
[copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
});
}
- (void)writeNextInterceptorWithData:(id)data {
if (_nextInterceptor == nil && !_shutDown) {
[self createNextInterceptor];
}
if (_nextInterceptor == nil) {
return;
if (_nextInterceptor != nil) {
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{
[copiedNextInterceptor writeData:data];
});
}
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
[copiedNextInterceptor writeData:data];
});
}
- (void)finishNextInterceptor {
if (_nextInterceptor == nil && !_shutDown) {
[self createNextInterceptor];
}
if (_nextInterceptor == nil) {
return;
if (_nextInterceptor != nil) {
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{
[copiedNextInterceptor finish];
});
}
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
[copiedNextInterceptor finish];
});
}
- (void)cancelNextInterceptor {
if (_nextInterceptor == nil && !_shutDown) {
[self createNextInterceptor];
}
if (_nextInterceptor == nil) {
return;
if (_nextInterceptor != nil) {
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{
[copiedNextInterceptor cancel];
});
}
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 && !_shutDown) {
[self createNextInterceptor];
}
if (_nextInterceptor == nil) {
return;
if (_nextInterceptor != nil) {
id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
dispatch_async(copiedNextInterceptor.requestDispatchQueue, ^{
[copiedNextInterceptor receiveNextMessages:numberOfMessages];
});
}
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:(NSDictionary *)initialMetadata {
if (_previousInterceptor == nil) {
return;
- (void)forwardPreviousInterceptorWithInitialMetadata:(nullable NSDictionary *)initialMetadata {
if ([_previousInterceptor respondsToSelector:@selector(didReceiveInitialMetadata:)]) {
id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
[copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata];
});
}
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 == nil) {
return;
if ([_previousInterceptor respondsToSelector:@selector(didReceiveData:)]) {
id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
[copiedPreviousInterceptor didReceiveData:data];
});
}
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;
- (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];
});
}
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];
});
}
- (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];
if ([_previousInterceptor respondsToSelector:@selector(didWriteData)]) {
id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
[copiedPreviousInterceptor didWriteData];
});
}
}
@ -273,21 +137,28 @@
@implementation GRPCInterceptor {
GRPCInterceptorManager *_manager;
dispatch_queue_t _dispatchQueue;
dispatch_queue_t _requestDispatchQueue;
dispatch_queue_t _responseDispatchQueue;
}
- (instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
dispatchQueue:(dispatch_queue_t)dispatchQueue {
requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue {
if ((self = [super init])) {
_manager = interceptorManager;
_dispatchQueue = dispatchQueue;
_requestDispatchQueue = requestDispatchQueue;
_responseDispatchQueue = responseDispatchQueue;
}
return self;
}
- (dispatch_queue_t)requestDispatchQueue {
return _requestDispatchQueue;
}
- (dispatch_queue_t)dispatchQueue {
return _dispatchQueue;
return _responseDispatchQueue;
}
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions

@ -1,82 +0,0 @@
/*
*
* 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

@ -1,142 +0,0 @@
/*
*
* 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

@ -1,187 +0,0 @@
/*
*
* 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

@ -20,7 +20,7 @@
#import "GRPCCall+InternalTests.h"
#import "../private/GRPCCore/GRPCOpBatchLog.h"
#import "../private/GRPCOpBatchLog.h"
@implementation GRPCCall (InternalTests)

@ -18,6 +18,12 @@
@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

@ -16,22 +16,20 @@
*
*/
#import <GRPCClient/GRPCTransport.h>
#import <GRPCClient/GRPCInterceptor.h>
NS_ASSUME_NONNULL_BEGIN
@protocol GRPCResponseHandler;
@class GRPCCallOptions;
@protocol GRPCChannelFactory;
@interface GRPCCall2Internal : NSObject<GRPCInterceptorInterface>
@interface GRPCCall2Internal : GRPCTransport
- (instancetype)init;
- (instancetype)initWithTransportManager:(GRPCTransportManager *)transportManager;
- (void)setResponseHandler:(id<GRPCResponseHandler>)responseHandler;
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions
callOptions:(GRPCCallOptions *)callOptions;
callOptions:(nullable GRPCCallOptions *)callOptions;
- (void)writeData:(id)data;
- (void)writeData:(NSData *)data;
- (void)finish;

@ -19,10 +19,8 @@
#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 {
@ -30,8 +28,8 @@
GRPCRequestOptions *_requestOptions;
/** Options for the call. */
GRPCCallOptions *_callOptions;
/** The interceptor manager to process responses. */
GRPCTransportManager *_transportManager;
/** The handler of responses. */
id<GRPCResponseHandler> _handler;
/**
* Make use of legacy GRPCCall to make calls. Nullified when call is finished.
@ -53,28 +51,40 @@
NSUInteger _pendingReceiveNextMessages;
}
- (instancetype)initWithTransportManager:(GRPCTransportManager *)transportManager {
dispatch_queue_t dispatchQueue;
- (instancetype)init {
if ((self = [super init])) {
// 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);
}
if ((self = [super init])) {
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
}
_pipe = [GRXBufferedPipe pipe];
_transportManager = transportManager;
_dispatchQueue = dispatchQueue;
}
return self;
}
- (dispatch_queue_t)dispatchQueue {
- (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 {
return _dispatchQueue;
}
@ -92,15 +102,26 @@
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) {
@ -119,7 +140,7 @@
callOptions:_callOptions
writeDone:^{
@synchronized(self) {
if (self->_transportManager) {
if (self->_handler) {
[self issueDidWriteData];
}
}
@ -137,7 +158,7 @@
void (^valueHandler)(id value) = ^(id value) {
@synchronized(self) {
if (self->_transportManager) {
if (self->_handler) {
if (!self->_initialMetadataPublished) {
self->_initialMetadataPublished = YES;
[self issueInitialMetadata:self->_call.responseHeaders];
@ -150,7 +171,7 @@
};
void (^completionHandler)(NSError *errorOrNil) = ^(NSError *errorOrNil) {
@synchronized(self) {
if (self->_transportManager) {
if (self->_handler) {
if (!self->_initialMetadataPublished) {
self->_initialMetadataPublished = YES;
[self issueInitialMetadata:self->_call.responseHeaders];
@ -186,19 +207,20 @@
_call = nil;
_pipe = nil;
if (_transportManager != nil) {
[_transportManager
forwardPreviousInterceptorCloseWithTrailingMetadata:nil
error:
[NSError
errorWithDomain:kGRPCErrorDomain
code:
GRPCErrorCodeCancelled
userInfo:@{
NSLocalizedDescriptionKey :
@"Canceled by app"
}]];
[_transportManager shutDown];
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;
}
}
[copiedCall cancel];
@ -249,25 +271,59 @@
}
- (void)issueInitialMetadata:(NSDictionary *)initialMetadata {
if (initialMetadata != nil) {
[_transportManager forwardPreviousInterceptorWithInitialMetadata:initialMetadata];
@synchronized(self) {
if (initialMetadata != nil &&
[_handler respondsToSelector:@selector(didReceiveInitialMetadata:)]) {
id<GRPCResponseHandler> copiedHandler = _handler;
dispatch_async(_handler.dispatchQueue, ^{
[copiedHandler didReceiveInitialMetadata:initialMetadata];
});
}
}
}
- (void)issueMessage:(id)message {
if (message != nil) {
[_transportManager forwardPreviousInterceptorWithData: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];
});
}
}
}
}
- (void)issueClosedWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
[_transportManager forwardPreviousInterceptorCloseWithTrailingMetadata:trailingMetadata
error:error];
[_transportManager shutDown];
@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;
}
}
}
- (void)issueDidWriteData {
[_transportManager forwardPreviousInterceptorDidWriteData];
@synchronized(self) {
if (_callOptions.flowControlEnabled && [_handler respondsToSelector:@selector(didWriteData)]) {
id<GRPCResponseHandler> copiedHandler = _handler;
dispatch_async(copiedHandler.dispatchQueue, ^{
[copiedHandler didWriteData];
});
}
}
}
- (void)receiveNextMessages:(NSUInteger)numberOfMessages {

@ -20,19 +20,18 @@
#include <grpc/support/log.h>
#import "../../internal/GRPCCallOptions+Internal.h"
#import "../GRPCTransport+Private.h"
#import "../internal/GRPCCallOptions+Internal.h"
#import "ChannelArgsUtil.h"
#import "GRPCChannelFactory.h"
#import "GRPCChannelPool.h"
#import "GRPCCompletionQueue.h"
#import "GRPCCoreFactory.h"
#import "GRPCCronetChannelFactory.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
@ -51,48 +50,32 @@
}
- (id<GRPCChannelFactory>)channelFactory {
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;
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);
}
case GRPCTransportTypeCronet: {
id<GRPCCoreTransportFactory> transportFactory = (id<GRPCCoreTransportFactory>)[
[GRPCTransportRegistry sharedInstance] getTransportFactoryWithId:gGRPCCoreCronetId];
return [transportFactory createCoreChannelFactoryWithCallOptions:_callOptions];
return factory;
}
case GRPCTransportTypeInsecure:
return [GRPCInsecureChannelFactory sharedInstance];
default:
NSLog(@"Unrecognized transport type");
return nil;
}
// fallthrough
case GRPCTransportTypeCronet:
return [GRPCCronetChannelFactory sharedInstance];
case GRPCTransportTypeInsecure:
return [GRPCInsecureChannelFactory sharedInstance];
}
}
@ -215,7 +198,6 @@
} else {
channelArgs = channelConfiguration.channelArgs;
}
id<GRPCChannelFactory> factory = channelConfiguration.channelFactory;
_unmanagedChannel = [factory createChannelWithHost:host channelArgs:channelArgs];
NSAssert(_unmanagedChannel != NULL, @"Failed to create channel");

@ -18,6 +18,8 @@
#import <GRPCClient/GRPCCallOptions.h>
#import "GRPCChannelFactory.h"
NS_ASSUME_NONNULL_BEGIN
@protocol GRPCChannel;

@ -18,16 +18,19 @@
#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;

@ -1,32 +0,0 @@
/*
*
* 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

@ -1,54 +0,0 @@
/*
*
* 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

@ -1,45 +0,0 @@
/*
*
* 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

@ -1,90 +0,0 @@
/*
*
* 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

@ -15,7 +15,7 @@
* limitations under the License.
*
*/
#import "../GRPCChannelFactory.h"
#import "GRPCChannelFactory.h"
@class GRPCChannel;
typedef struct stream_engine stream_engine;

@ -18,8 +18,10 @@
#import "GRPCCronetChannelFactory.h"
#import "../ChannelArgsUtil.h"
#import "../GRPCChannel.h"
#import "ChannelArgsUtil.h"
#import "GRPCChannel.h"
#ifdef GRPC_COMPILE_WITH_CRONET
#import <Cronet/Cronet.h>
#include <grpc/grpc_cronet.h>
@ -57,3 +59,21 @@
}
@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

@ -21,16 +21,17 @@
#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
@ -112,12 +113,20 @@ static NSMutableDictionary *gHostCache;
options.PEMPrivateKey = _PEMPrivateKey;
options.PEMCertificateChain = _PEMCertificateChain;
options.hostNameOverride = _hostNameOverride;
if (_transportType == GRPCTransportTypeInsecure) {
options.transport = GRPCDefaultTransportImplList.core_insecure;
} else if ([GRPCCall isUsingCronet]) {
options.transport = gGRPCCoreCronetId;
} else {
options.transport = GRPCDefaultTransportImplList.core_secure;
#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;
}
options.logContext = _logContext;
@ -126,14 +135,16 @@ 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) {
GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
callOptions = [hostConfig callOptions];
callOptions = [gHostCache[host] 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;

@ -18,7 +18,7 @@
#import <Foundation/Foundation.h>
#import <GRPCClient/GRPCCallLegacy.h>
#import "../GRPCCall.h"
@interface GRPCRequestHeaders : NSMutableDictionary

@ -1,65 +0,0 @@
/*
*
* 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

@ -1,131 +0,0 @@
/*
*
* 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

@ -18,9 +18,10 @@
#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

@ -17,16 +17,12 @@
*/
#import <Foundation/Foundation.h>
// import legacy header for compatibility with users using the ProtoRPC interface
#import "ProtoRPCLegacy.h"
#import <GRPCClient/GRPCCall.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. */
@ -164,3 +160,35 @@ 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

@ -27,6 +27,24 @@
#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;
@ -273,3 +291,76 @@
}
@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

@ -1,67 +0,0 @@
/*
*
* 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);

@ -1,121 +0,0 @@
/*
*
* 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];
}

@ -18,12 +18,14 @@
#import <Foundation/Foundation.h>
#import <GRPCClient/GRPCCallOptions.h>
#import "ProtoRPC.h"
@class GRPCProtoCall;
@protocol GRXWriteable;
@class GRXWriter;
@class GRPCCallOptions;
@class GRPCProtoCall;
@class GRPCUnaryProtoCall;
@class GRPCStreamingProtoCall;
@protocol GRPCProtoResponseHandler;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullability-completeness"
@ -36,6 +38,15 @@ __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
@ -47,18 +58,6 @@ __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

@ -29,21 +29,15 @@
#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 {
[NSException raise:NSGenericException format:@"Do not call init method of ProtoService"];
return [self initWithHost:nil packageName:nil serviceName:nil callOptions:nil];
return [self initWithHost:nil packageName:nil serviceName:nil];
}
#pragma clang diagnostic pop
// Designated initializer
- (instancetype)initWithHost:(NSString *)host
@ -64,6 +58,38 @@
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

@ -1,23 +0,0 @@
/*
*
* 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;

@ -1,71 +0,0 @@
/*
*
* 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

@ -25,8 +25,6 @@
#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>

@ -24,8 +24,6 @@
#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>

@ -95,12 +95,19 @@ 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",
],
)
@ -193,6 +200,7 @@ ios_unit_test(
":InteropTestsRemote-lib",
":InteropTestsLocalSSL-lib",
":InteropTestsLocalCleartext-lib",
# ":InteropTestsMulitpleChannels-lib", # needs Cronet
],
test_host = ":ios-host",
)

@ -16,6 +16,8 @@
*
*/
#ifdef GRPC_COMPILE_WITH_CRONET
#ifdef __cplusplus
extern "C" {
#endif
@ -28,3 +30,5 @@ void configureCronet(void);
#ifdef __cplusplus
}
#endif
#endif

@ -16,6 +16,8 @@
*
*/
#ifdef GRPC_COMPILE_WITH_CRONET
#import "ConfigureCronet.h"
#import <Cronet/Cronet.h>
@ -33,3 +35,5 @@ void configureCronet(void) {
[Cronet startNetLogToFile:@"cronet_netlog.json" logBytes:YES];
});
}
#endif

@ -22,7 +22,6 @@
#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
@ -41,19 +40,12 @@ static int32_t kRemoteInteropServerOverhead = 12;
@implementation InteropTestsRemoteWithCronet
+ (void)setUp {
configureCronet();
[GRPCCall useCronetWithEngine:[Cronet getGlobalEngine]];
[super setUp];
}
+ (NSString *)host {
return kRemoteSSLHost;
}
+ (GRPCTransportId)transport {
return gGRPCCoreCronetId;
+ (BOOL)useCronet {
return YES;
}
- (int32_t)encodingOverhead {

@ -48,19 +48,11 @@
- (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.
@ -73,4 +65,9 @@
*/
+ (NSString *)hostNameOverride;
/**
* Whether to use Cronet for all the v1 API tests in the test suite.
*/
+ (BOOL)useCronet;
@end

@ -20,6 +20,9 @@
#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>
@ -35,6 +38,7 @@
#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
@ -87,8 +91,9 @@ BOOL isRemoteInteropTest(NSString *host) {
- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager {
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
return
[[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager dispatchQueue:queue];
return [[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager
requestDispatchQueue:queue
responseDispatchQueue:queue];
}
@end
@ -96,19 +101,21 @@ BOOL isRemoteInteropTest(NSString *host) {
@interface HookInterceptorFactory : NSObject<GRPCInterceptorFactory>
- (instancetype)
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;
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;
- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager;
@ -118,7 +125,8 @@ receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages,
- (instancetype)
initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
dispatchQueue:(dispatch_queue_t)dispatchQueue
requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue
startHook:(void (^)(GRPCRequestOptions *requestOptions,
GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager))startHook
@ -147,25 +155,29 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
void (^_responseCloseHook)(NSDictionary *trailingMetadata, NSError *error,
GRPCInterceptorManager *manager);
void (^_didWriteDataHook)(GRPCInterceptorManager *manager);
dispatch_queue_t _dispatchQueue;
dispatch_queue_t _requestDispatchQueue;
dispatch_queue_t _responseDispatchQueue;
}
- (instancetype)
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 {
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 {
if ((self = [super init])) {
_dispatchQueue = dispatchQueue;
_requestDispatchQueue = requestDispatchQueue;
_responseDispatchQueue = responseDispatchQueue;
_startHook = startHook;
_writeDataHook = writeDataHook;
_finishHook = finishHook;
@ -180,7 +192,8 @@ receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages,
- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager {
return [[HookInterceptor alloc] initWithInterceptorManager:interceptorManager
dispatchQueue:_dispatchQueue
requestDispatchQueue:_requestDispatchQueue
responseDispatchQueue:_responseDispatchQueue
startHook:_startHook
writeDataHook:_writeDataHook
finishHook:_finishHook
@ -205,16 +218,22 @@ receiveNextMessagesHook:(void (^)(NSUInteger numberOfMessages,
GRPCInterceptorManager *manager);
void (^_didWriteDataHook)(GRPCInterceptorManager *manager);
GRPCInterceptorManager *_manager;
dispatch_queue_t _dispatchQueue;
dispatch_queue_t _requestDispatchQueue;
dispatch_queue_t _responseDispatchQueue;
}
- (dispatch_queue_t)requestDispatchQueue {
return _requestDispatchQueue;
}
- (dispatch_queue_t)dispatchQueue {
return _dispatchQueue;
return _responseDispatchQueue;
}
- (instancetype)
initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
dispatchQueue:(dispatch_queue_t)dispatchQueue
requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue
startHook:(void (^)(GRPCRequestOptions *requestOptions,
GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager))startHook
@ -228,7 +247,9 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
responseCloseHook:(void (^)(NSDictionary *trailingMetadata, NSError *error,
GRPCInterceptorManager *manager))responseCloseHook
didWriteDataHook:(void (^)(GRPCInterceptorManager *manager))didWriteDataHook {
if ((self = [super initWithInterceptorManager:interceptorManager dispatchQueue:dispatchQueue])) {
if ((self = [super initWithInterceptorManager:interceptorManager
requestDispatchQueue:requestDispatchQueue
responseDispatchQueue:responseDispatchQueue])) {
_startHook = startHook;
_writeDataHook = writeDataHook;
_finishHook = finishHook;
@ -237,7 +258,8 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
_responseDataHook = responseDataHook;
_responseCloseHook = responseCloseHook;
_didWriteDataHook = didWriteDataHook;
_dispatchQueue = dispatchQueue;
_requestDispatchQueue = requestDispatchQueue;
_responseDispatchQueue = responseDispatchQueue;
_manager = interceptorManager;
}
return self;
@ -298,7 +320,8 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
@property BOOL enabled;
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue;
- (instancetype)initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue;
- (void)setStartHook:(void (^)(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager))startHook
@ -317,23 +340,26 @@ initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
@implementation GlobalInterceptorFactory
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue {
- (instancetype)initWithRequestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue {
_enabled = NO;
return [super initWithDispatchQueue:dispatchQueue
startHook:nil
writeDataHook:nil
finishHook:nil
receiveNextMessagesHook:nil
responseHeaderHook:nil
responseDataHook:nil
responseCloseHook:nil
didWriteDataHook:nil];
return [super initWithRequestDispatchQueue:requestDispatchQueue
responseDispatchQueue:responseDispatchQueue
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
dispatchQueue:_dispatchQueue
requestDispatchQueue:_requestDispatchQueue
responseDispatchQueue:_responseDispatchQueue
startHook:_startHook
writeDataHook:_writeDataHook
finishHook:_finishHook
@ -399,15 +425,10 @@ static dispatch_once_t initGlobalInterceptorFactory;
return 0;
}
// For backwards compatibility
+ (GRPCTransportType)transportType {
return GRPCTransportTypeChttp2BoringSSL;
}
+ (GRPCTransportId)transport {
return NULL;
}
+ (NSString *)PEMRootCertificates {
return nil;
}
@ -416,11 +437,26 @@ 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] initWithDispatchQueue:globalInterceptorQueue];
[[GlobalInterceptorFactory alloc] initWithRequestDispatchQueue:globalInterceptorQueue
responseDispatchQueue:globalInterceptorQueue];
[GRPCCall2 registerGlobalInterceptor:globalInterceptorFactory];
});
}
@ -466,9 +502,7 @@ 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];
@ -497,9 +531,7 @@ 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];
@ -576,9 +608,7 @@ 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];
@ -626,9 +656,7 @@ 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];
@ -930,9 +958,7 @@ 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];
@ -984,9 +1010,7 @@ 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;
@ -1143,9 +1167,7 @@ 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];
@ -1178,9 +1200,7 @@ 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];
@ -1266,47 +1286,48 @@ static dispatch_once_t initGlobalInterceptorFactory;
[self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
}
- (void)testKeepaliveWithV2API {
#ifndef GRPC_COMPILE_WITH_CRONET
- (void)testKeepalive {
XCTAssertNotNil([[self class] host]);
if ([[self class] transport] == gGRPCCoreCronetId) {
// Cronet does not support keepalive
return;
}
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"Keepalive"];
NSNumber *kRequestSize = @27182;
NSNumber *kResponseSize = @31415;
[GRPCCall setKeepaliveWithInterval:1500 timeout:0 forHost:[[self class] host]];
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;
NSArray *requests = @[ @27182, @8 ];
NSArray *responses = @[ @31415, @9 ];
__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];
GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init];
__block int index = 0;
id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index]
requestedResponseSize:responses[index]];
[requestsBuffer writeValue:request];
[_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];
}
}];
[self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil];
[call finish];
}
#endif
- (void)testDefaultInterceptor {
XCTAssertNotNil([[self class] host]);
@ -1321,9 +1342,7 @@ 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] ];
@ -1378,7 +1397,8 @@ static dispatch_once_t initGlobalInterceptorFactory;
__block NSUInteger responseCloseCount = 0;
__block NSUInteger didWriteDataCount = 0;
id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc]
initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager) {
startCount++;
@ -1426,9 +1446,7 @@ 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;
@ -1506,7 +1524,8 @@ static dispatch_once_t initGlobalInterceptorFactory;
__block NSUInteger responseDataCount = 0;
__block NSUInteger responseCloseCount = 0;
id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc]
initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager) {
startCount++;
@ -1533,7 +1552,6 @@ 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) {
@ -1560,9 +1578,7 @@ 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 ];
@ -1671,9 +1687,7 @@ 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;
@ -1728,15 +1742,16 @@ static dispatch_once_t initGlobalInterceptorFactory;
- (void)testConflictingGlobalInterceptors {
id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc]
initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
startHook:nil
writeDataHook:nil
finishHook:nil
receiveNextMessagesHook:nil
responseHeaderHook:nil
responseDataHook:nil
responseCloseHook:nil
didWriteDataHook:nil];
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];
@try {
[GRPCCall2 registerGlobalInterceptor:factory];
XCTFail(@"Did not receive an exception when registering global interceptor the second time");
@ -1760,7 +1775,8 @@ static dispatch_once_t initGlobalInterceptorFactory;
__block NSUInteger didWriteDataCount = 0;
id<GRPCInterceptorFactory> factory = [[HookInterceptorFactory alloc]
initWithDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
initWithRequestDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
responseDispatchQueue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL)
startHook:^(GRPCRequestOptions *requestOptions, GRPCCallOptions *callOptions,
GRPCInterceptorManager *manager) {
startCount++;
@ -1856,9 +1872,7 @@ 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;

@ -17,7 +17,6 @@
*/
#import <GRPCClient/GRPCCall+Tests.h>
#import <GRPCClient/GRPCTransport.h>
#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
#import "InteropTests.h"
@ -61,8 +60,8 @@ static int32_t kLocalInteropServerOverhead = 10;
[GRPCCall useInsecureConnectionsForHost:kLocalCleartextHost];
}
+ (GRPCTransportId)transport {
return GRPCDefaultTransportImplList.core_insecure;
+ (GRPCTransportType)transportType {
return GRPCTransportTypeInsecure;
}
@end

@ -17,7 +17,6 @@
*/
#import <GRPCClient/GRPCCall+Tests.h>
#import <GRPCClient/GRPCTransport.h>
#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h>
#import "InteropTests.h"
@ -57,8 +56,8 @@ static int32_t kLocalInteropServerOverhead = 10;
return kLocalInteropServerOverhead; // bytes
}
+ (GRPCTransportId)transport {
return GRPCDefaultTransportImplList.core_secure;
+ (GRPCTransportType)transportType {
return GRPCTransportTypeChttp2BoringSSL;
}
- (void)setUp {

@ -18,8 +18,9 @@
#import <XCTest/XCTest.h>
#ifdef GRPC_COMPILE_WITH_CRONET
#import <Cronet/Cronet.h>
#import <GRPCClient/GRPCCallOptions.h>
#endif
#import <RxLibrary/GRXBufferedPipe.h>
#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h"
#import "src/objective-c/tests/RemoteTestClient/Test.pbobjc.h"

@ -53,8 +53,14 @@ static int32_t kRemoteInteropServerOverhead = 12;
return kRemoteInteropServerOverhead; // bytes
}
#ifdef GRPC_COMPILE_WITH_CRONET
+ (GRPCTransportType)transportType {
return GRPCTransportTypeCronet;
}
#else
+ (GRPCTransportType)transportType {
return GRPCTransportTypeChttp2BoringSSL;
}
#endif
@end

@ -30,23 +30,23 @@ 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
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
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
# gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's
@ -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(-macOS|-iOS|-tvOS|\.|-[0-9a-f])/.match(target.name)
if /gRPC-(mac|i|tv)OS/.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)/.match(target.name)
if /(gRPC|ProtoRPC|RxLibrary)-(mac|i|tv)OS/.match(target.name)
target.build_configurations.each do |config|
if config.name != 'Release'
config.build_settings['ENABLE_NS_ASSERTIONS'] = 'YES'

@ -8,14 +8,15 @@
/* 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 */; };
@ -33,7 +34,6 @@
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,6 +515,7 @@
5EA476F12272816A000F72FC /* Frameworks */,
5EA476F22272816A000F72FC /* Resources */,
D11CB94CF56A1E53760D29D8 /* [CP] Copy Pods Resources */,
0FEFD5FC6B323AC95549AE4A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -629,7 +630,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5ECFED8623030DCC00626501 /* TestCertificates.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -678,6 +678,24 @@
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;
@ -703,7 +721,7 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CronetTests/Pods-CronetTests-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/gRPC.default-GRPCCoreCronet/gRPCCertificates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
@ -880,7 +898,6 @@
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 */,
@ -893,7 +910,9 @@
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 */,

@ -23,7 +23,7 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Test"
buildConfiguration = "Cronet"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
@ -48,7 +48,7 @@
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Test"
buildConfiguration = "Cronet"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
@ -70,7 +70,7 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Test"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
@ -86,7 +86,7 @@
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Test">
buildConfiguration = "Cronet">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"

@ -18,9 +18,9 @@
#import <XCTest/XCTest.h>
#import "../../GRPCClient/private/GRPCCore/GRPCChannel.h"
#import "../../GRPCClient/private/GRPCCore/GRPCChannelPool+Test.h"
#import "../../GRPCClient/private/GRPCCore/GRPCCompletionQueue.h"
#import "../../GRPCClient/private/GRPCChannel.h"
#import "../../GRPCClient/private/GRPCChannelPool+Test.h"
#import "../../GRPCClient/private/GRPCCompletionQueue.h"
#define TEST_TIMEOUT 32

@ -19,11 +19,11 @@
#import <XCTest/XCTest.h>
#import "../../GRPCClient/GRPCCallOptions.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"
#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"
static NSString *kDummyHost = @"dummy.host";
static NSString *kDummyPath = @"/dummy/path";

@ -20,7 +20,7 @@
#import <GRPCClient/GRPCCall.h>
#import "../../GRPCClient/private/GRPCCore/NSError+GRPC.h"
#import "../../GRPCClient/private/NSError+GRPC.h"
@interface NSErrorUnitTests : XCTestCase

@ -44,40 +44,22 @@
s.module_name = name
s.header_dir = name
s.default_subspec = 'Main', 'Legacy', 'Legacy-Header'
src_dir = 'src/objective-c/ProtoRPC'
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.default_subspec = 'Main'
s.subspec 'Main' do |ss|
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.header_mappings_dir = "#{src_dir}"
ss.dependency 'gRPC', version
ss.dependency 'gRPC-RxLibrary', version
ss.dependency 'Protobuf', '~> 3.0'
ss.source_files = "src/objective-c/ProtoRPC/ProtoRPCLegacy.m",
"src/objective-c/ProtoRPC/ProtoServiceLegacy.m"
ss.source_files = "#{src_dir}/*.{h,m}"
end
# CFStream is now default. Leaving this subspec only for compatibility purpose.
s.subspec 'CFStream' do |ss|
ss.dependency "#{s.name}/Legacy", version
ss.dependency "#{s.name}/Main", version
end
s.pod_target_xcconfig = {

@ -44,23 +44,6 @@
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"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save