Merge remote-tracking branch 'upstream/master' into protosplit

reviewable/pr3905/r6
vjpai 9 years ago
commit 11537dc71c
  1. 3
      .gitignore
  2. 66
      .travis.yml
  3. 6
      BUILD
  4. 1445
      Makefile
  5. 1
      README.md
  6. 28
      binding.gyp
  7. 34
      build.yaml
  8. 2
      examples/README.md
  9. 2
      examples/node/greeter_client.js
  10. 10
      examples/objective-c/auth_sample/AuthTestService.podspec
  11. 9
      examples/objective-c/auth_sample/MakeRPCViewController.m
  12. 3
      examples/objective-c/auth_sample/Podfile
  13. 188
      examples/objective-c/auth_sample/README.md
  14. 6
      examples/objective-c/helloworld/README.md
  15. 2
      examples/objective-c/route_guide/Podfile
  16. 358
      examples/objective-c/route_guide/README.md
  17. 10
      examples/objective-c/route_guide/RouteGuide.podspec
  18. 16
      examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
  19. 63
      examples/objective-c/route_guide/ViewControllers.m
  20. 11
      gRPC.podspec
  21. 1
      include/grpc++/support/byte_buffer.h
  22. 4
      include/grpc++/support/sync_stream.h
  23. 1
      package.json
  24. 572
      src/core/channel/client_uchannel.c
  25. 70
      src/core/channel/client_uchannel.h
  26. 47
      src/core/client_config/subchannel.c
  27. 11
      src/core/client_config/subchannel.h
  28. 62
      src/core/surface/channel_connectivity.c
  29. 1
      src/csharp/.gitignore
  30. 2
      src/csharp/.nuget/packages.config
  31. 6
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  32. 4
      src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
  33. 2
      src/csharp/Grpc.IntegrationTesting/InteropServer.cs
  34. 17
      src/csharp/Grpc.IntegrationTesting/TestCredentials.cs
  35. 1
      src/csharp/README.md
  36. 1
      src/node/README.md
  37. 6
      src/node/interop/interop_client.js
  38. 11
      src/node/src/client.js
  39. 2
      src/node/src/common.js
  40. 9
      src/node/src/metadata.js
  41. 15
      src/node/src/server.js
  42. 16
      src/objective-c/GRPCClient/GRPCCall+OAuth2.h
  43. 28
      src/objective-c/GRPCClient/GRPCCall+Tests.h
  44. 3
      src/objective-c/GRPCClient/GRPCCall+Tests.m
  45. 236
      src/objective-c/GRPCClient/GRPCCall.h
  46. 12
      src/objective-c/GRPCClient/private/GRPCChannel.h
  47. 20
      src/objective-c/GRPCClient/private/GRPCCompletionQueue.h
  48. 6
      src/objective-c/GRPCClient/private/GRPCHost.h
  49. 8
      src/objective-c/GRPCClient/private/GRPCSecureChannel.h
  50. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  51. 6
      src/objective-c/GRPCClient/private/NSError+GRPC.h
  52. 6
      src/objective-c/ProtoRPC/ProtoMethod.h
  53. 1
      src/objective-c/README.md
  54. 36
      src/objective-c/RxLibrary/GRXBufferedPipe.h
  55. 54
      src/objective-c/RxLibrary/GRXConcurrentWriteable.h
  56. 24
      src/objective-c/RxLibrary/GRXForwardingWriter.h
  57. 70
      src/objective-c/RxLibrary/GRXImmediateWriter.h
  58. 22
      src/objective-c/RxLibrary/GRXWriteable.h
  59. 44
      src/objective-c/RxLibrary/GRXWriter+Immediate.h
  60. 6
      src/objective-c/RxLibrary/GRXWriter+Transformations.h
  61. 116
      src/objective-c/RxLibrary/GRXWriter.h
  62. 22
      src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h
  63. 12
      src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h
  64. 14
      src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h
  65. 8
      src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h
  66. 2
      src/objective-c/RxLibrary/transformations/GRXMappingWriter.h
  67. 128
      src/objective-c/change-comments.py
  68. 3
      src/objective-c/format-all-comments.sh
  69. 36
      src/objective-c/tests/GRPCClientTests.m
  70. 16
      src/objective-c/tests/InteropTests.h
  71. 14
      src/objective-c/tests/InteropTests.m
  72. 4
      src/objective-c/tests/InteropTestsLocalCleartext.m
  73. 4
      src/objective-c/tests/InteropTestsLocalSSL.m
  74. 50
      src/objective-c/tests/InteropTestsRemote.m
  75. 164
      src/objective-c/tests/LocalClearTextTests.m
  76. 18
      src/objective-c/tests/Podfile
  77. 622
      src/objective-c/tests/Tests.xcodeproj/project.pbxproj
  78. 14
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
  79. 101
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme
  80. 95
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme
  81. 95
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme
  82. 90
      src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme
  83. 12
      src/php/bin/run_php_cs_fixer.sh
  84. 10
      src/php/ext/grpc/credentials.c
  85. 116
      src/php/lib/Grpc/AbstractCall.php
  86. 469
      src/php/lib/Grpc/BaseStub.php
  87. 123
      src/php/lib/Grpc/BidiStreamingCall.php
  88. 84
      src/php/lib/Grpc/ClientStreamingCall.php
  89. 93
      src/php/lib/Grpc/ServerStreamingCall.php
  90. 67
      src/php/lib/Grpc/UnaryCall.php
  91. 384
      src/php/tests/generated_code/AbstractGeneratedCodeTest.php
  92. 19
      src/php/tests/generated_code/GeneratedCodeTest.php
  93. 34
      src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php
  94. 55
      src/php/tests/generated_code/math_client.php
  95. 599
      src/php/tests/interop/interop_client.php
  96. 26
      src/php/tests/interop/message_set.php
  97. 98
      src/php/tests/unit_tests/CallTest.php
  98. 415
      src/php/tests/unit_tests/EndToEndTest.php
  99. 365
      src/php/tests/unit_tests/SecureEndToEndTest.php
  100. 100
      src/php/tests/unit_tests/TimevalTest.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -46,6 +46,9 @@ portlog.txt
*-gyp.mk
out
# YCM config files
.ycm_extra_conf.py
# XCode
build/
*.pbxuser

@ -1,42 +1,32 @@
language: cpp
before_install:
- sudo add-apt-repository ppa:yjwong/gflags -y
- sudo add-apt-repository ppa:h-rayflood/llvm -y
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
- echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
- echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
- sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5
- sudo pip install --upgrade virtualenv
- sudo pip install cpp-coveralls mako simplejson
- sudo apt-get install -qq mono-devel nunit
- wget www.nuget.org/NuGet.exe -O nuget.exe
language: objective-c
osx_image: xcode7.1
env:
global:
- RUBY_VERSION=2.1
- COVERALLS_PARALLEL=true
- CPPFLAGS=-I/tmp/prebuilt/include
- NUGET="mono nuget.exe"
matrix:
- CONFIG=opt TEST=sanity JOBS=1
- CONFIG=gcov TEST=c JOBS=16
- CONFIG=gcov TEST=c++ JOBS=16
- CONFIG=opt TEST=c JOBS=16
- CONFIG=opt TEST=c++ JOBS=16
- CONFIG=opt TEST=node JOBS=16
- CONFIG=opt TEST=ruby JOBS=16
- CONFIG=opt TEST=python JOBS=1
- CONFIG=opt TEST=csharp JOBS=16
- USE_GCC=4.4 CONFIG=opt TEST=build JOBS=16
script:
- rvm use $RUBY_VERSION
- gem install bundler
- ./tools/run_tests/prepare_travis.sh
- if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi
- ./tools/run_tests/run_tests.py -l $TEST -t -j $JOBS -c $CONFIG -s 4.0
after_success:
- if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens --exclude test --exclude tools --exclude src/compiler -b. --gcov-options '\-p' ; fi
- CONFIG=opt
- TEST=objc
- JOBS=1
before_install:
- brew install gflags
# Pod install does this too, but we don't want the output.
- pod repo update --silent
install:
- make grpc_objective_c_plugin
- pushd src/objective-c/tests
# Needs to be verbose, or otherwise OpenSSL's prepare_command makes Travis
# time out:
- pod install --verbose
- popd
before_script:
- make interop_server
- bins/$CONFIG/interop_server --port=5050 &
- bins/$CONFIG/interop_server --port=5051 --use_tls &
xcode_workspace: src/objective-c/tests/Tests.xcworkspace
xcode_scheme:
- RxLibraryUnitTests
- InteropTestsLocalSSL
- InteropTestsLocalCleartext
# TODO(jcanizales): Investigate why they time out:
# - InteropTestsRemote
xcode_sdk: iphonesimulator9.1
notifications:
email: false
webhooks:
- https://coveralls.io/webhook?repo_token=54IxAHPjJNdQJzJAhPU0MFpCtg7KvcydQ

@ -153,6 +153,7 @@ cc_library(
"src/core/channel/channel_args.h",
"src/core/channel/channel_stack.h",
"src/core/channel/client_channel.h",
"src/core/channel/client_uchannel.h",
"src/core/channel/compress_filter.h",
"src/core/channel/connected_channel.h",
"src/core/channel/context.h",
@ -289,6 +290,7 @@ cc_library(
"src/core/channel/channel_args.c",
"src/core/channel/channel_stack.c",
"src/core/channel/client_channel.c",
"src/core/channel/client_uchannel.c",
"src/core/channel/compress_filter.c",
"src/core/channel/connected_channel.c",
"src/core/channel/http_client_filter.c",
@ -439,6 +441,7 @@ cc_library(
"src/core/channel/channel_args.h",
"src/core/channel/channel_stack.h",
"src/core/channel/client_channel.h",
"src/core/channel/client_uchannel.h",
"src/core/channel/compress_filter.h",
"src/core/channel/connected_channel.h",
"src/core/channel/context.h",
@ -555,6 +558,7 @@ cc_library(
"src/core/channel/channel_args.c",
"src/core/channel/channel_stack.c",
"src/core/channel/client_channel.c",
"src/core/channel/client_uchannel.c",
"src/core/channel/compress_filter.c",
"src/core/channel/connected_channel.c",
"src/core/channel/http_client_filter.c",
@ -1082,6 +1086,7 @@ objc_library(
"src/core/channel/channel_args.c",
"src/core/channel/channel_stack.c",
"src/core/channel/client_channel.c",
"src/core/channel/client_uchannel.c",
"src/core/channel/compress_filter.c",
"src/core/channel/connected_channel.c",
"src/core/channel/http_client_filter.c",
@ -1229,6 +1234,7 @@ objc_library(
"src/core/channel/channel_args.h",
"src/core/channel/channel_stack.h",
"src/core/channel/client_channel.h",
"src/core/channel/client_uchannel.h",
"src/core/channel/compress_filter.h",
"src/core/channel/connected_channel.h",
"src/core/channel/context.h",

1445
Makefile

File diff suppressed because one or more lines are too long

@ -1,5 +1,4 @@
[![Build Status](https://grpc-testing.appspot.com/job/gRPC_master/badge/icon)](https://grpc-testing.appspot.com/job/gRPC_master)
[![Coverage Status](https://img.shields.io/coveralls/grpc/grpc.svg)](https://coveralls.io/r/grpc/grpc?branch=master)
[gRPC - An RPC library and framework](http://github.com/grpc/grpc)
===================================

@ -54,7 +54,9 @@
],
'include_dirs': [
'.',
'include'
'include',
'<(node_root_dir)/deps/openssl/openssl/include',
'<(node_root_dir)/deps/zlib'
],
'conditions': [
['OS != "win"', {
@ -73,6 +75,15 @@
]
]
}],
["target_arch=='ia32'", {
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
}],
["target_arch=='x64'", {
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
}],
["target_arch=='arm'", {
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
}]
]
},
'targets': [
@ -124,6 +135,13 @@
'src/core/support/time_win32.c',
'src/core/support/tls_pthread.c',
],
"conditions": [
['OS == "mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.9'
}
}]
],
},
{
'target_name': 'grpc',
@ -159,6 +177,7 @@
'src/core/channel/channel_args.c',
'src/core/channel/channel_stack.c',
'src/core/channel/client_channel.c',
'src/core/channel/client_uchannel.c',
'src/core/channel/compress_filter.c',
'src/core/channel/connected_channel.c',
'src/core/channel/http_client_filter.c',
@ -281,6 +300,13 @@
'src/core/census/operation.c',
'src/core/census/tracing.c',
],
"conditions": [
['OS == "mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.9'
}
}]
],
},
{
'include_dirs': [

@ -109,6 +109,7 @@ filegroups:
- src/core/channel/channel_args.h
- src/core/channel/channel_stack.h
- src/core/channel/client_channel.h
- src/core/channel/client_uchannel.h
- src/core/channel/compress_filter.h
- src/core/channel/connected_channel.h
- src/core/channel/context.h
@ -222,6 +223,7 @@ filegroups:
- src/core/channel/channel_args.c
- src/core/channel/channel_stack.c
- src/core/channel/client_channel.c
- src/core/channel/client_uchannel.c
- src/core/channel/compress_filter.c
- src/core/channel/connected_channel.c
- src/core/channel/http_client_filter.c
@ -972,6 +974,14 @@ targets:
deps:
- gpr_test_util
- gpr
- name: gpr_cpu_test
build: test
language: c
src:
- test/core/support/cpu_test.c
deps:
- gpr_test_util
- gpr
- name: gpr_env_test
build: test
language: c
@ -1930,6 +1940,7 @@ targets:
- posix
- name: qps_openloop_test
build: test
run: false
language: c++
src:
- test/cpp/qps/qps_openloop_test.cc
@ -2098,6 +2109,29 @@ targets:
- mac
- linux
- posix
- name: stress_test
build: test
run: false
language: c++
headers:
- test/cpp/interop/client_helper.h
- test/cpp/interop/interop_client.h
- test/cpp/interop/stress_interop_client.h
src:
- test/proto/empty.proto
- test/proto/messages.proto
- test/proto/test.proto
- test/cpp/interop/interop_client.cc
- test/cpp/interop/stress_interop_client.cc
- test/cpp/interop/stress_test.cc
deps:
- grpc++_test_util
- grpc_test_util
- grpc++
- grpc
- gpr_test_util
- gpr
- grpc++_test_config
- name: sync_streaming_ping_pong_test
build: test
language: c++

@ -18,7 +18,7 @@ You can find quick start guides for each language, including installation instru
* [Android Java](https://github.com/grpc/grpc-java/tree/master/examples/android)
* [Python](python/helloworld)
* [C#](csharp)
* [Objective-C](objective-c/route_guide)
* [Objective-C](objective-c/helloworld)
* [PHP](php)
## What's in this repository?

@ -38,7 +38,7 @@ var hello_proto = grpc.load(PROTO_PATH).helloworld;
function main() {
var client = new hello_proto.Greeter('localhost:50051',
grpc.Credentials.createInsecure());
grpc.credentials.createInsecure());
var user;
if (process.argv.length >= 3) {
user = process.argv[2];

@ -3,13 +3,13 @@ Pod::Spec.new do |s|
s.version = "0.0.1"
s.license = "New BSD"
s.ios.deployment_target = "6.0"
s.osx.deployment_target = "10.8"
s.ios.deployment_target = "7.1"
s.osx.deployment_target = "10.9"
# Base directory where the .proto files are.
src = "../../protos"
# Directory where the generated files will be place.
# Directory where the generated files will be placed.
dir = "Pods/" + s.name
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
@ -22,14 +22,14 @@ Pod::Spec.new do |s|
ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
ms.header_mappings_dir = dir
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
ms.dependency "Protobuf", "~> 3.0.0-alpha-4"
end
s.subspec "Services" do |ss|
ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
ss.header_mappings_dir = dir
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.6"
ss.dependency "gRPC", "~> 0.11"
ss.dependency "#{s.name}/Messages"
end
end

@ -35,7 +35,6 @@
#import <AuthTestService/AuthSample.pbrpc.h>
#import <Google/SignIn.h>
#include <grpc/status.h>
#import <ProtoRPC/ProtoRPC.h>
NSString * const kTestScope = @"https://www.googleapis.com/auth/xapi.zoo";
@ -49,10 +48,10 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com";
@implementation NSError (AuthSample)
- (NSString *)UIDescription {
if (self.code == GRPC_STATUS_UNAUTHENTICATED) {
if (self.code == GRPCErrorCodeUnauthenticated) {
// Authentication error. OAuth2 specifies we'll receive a challenge header.
// |userInfo[kGRPCStatusMetadataKey]| is the dictionary of response metadata.
NSString *challengeHeader = self.userInfo[kGRPCStatusMetadataKey][@"www-authenticate"] ?: @"";
// |userInfo[kGRPCHeadersKey]| is the dictionary of response headers.
NSString *challengeHeader = self.userInfo[kGRPCHeadersKey][@"www-authenticate"] ?: @"";
return [@"Invalid credentials. Server challenge:\n" stringByAppendingString:challengeHeader];
} else {
// Any other error.
@ -89,7 +88,7 @@ static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com";
// Set the access token to be used.
NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
call.requestHeaders[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
// Start the RPC.
[call start];

@ -1,6 +1,9 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'gRPC', :path => "../../.."
target 'AuthSample' do
# Depend on the generated AuthTestService library.
pod 'AuthTestService', :path => '.'

@ -1,189 +1,3 @@
#OAuth2 on gRPC: Objective-C
This example application demostrates how to use OAuth2 on gRPC to make authenticated API calls on
behalf of a user. By walking through it you'll learn how to use the Objective-C gRPC API to:
- Initialize and configure a remote call object before the RPC is started.
- Set request metadata elements on a call, which are semantically equivalent to HTTP request
headers.
- Read response metadata from a call, which is equivalent to HTTP response headers and trailers.
It assumes you know the basics on how to make gRPC API calls using the Objective-C client library,
as shown in the [Hello World](../helloworld)
or [Route Guide](../route_guide) tutorials,
and are familiar with OAuth2 concepts like _access token_.
- [Example code and setup](#setup)
- [Try it out!](#try)
- [Create an RPC object and start it later](#rpc-object)
- [Set request metadata of a call: Authorization header with an access token](#request-metadata)
- [Get response metadata of a call: Auth challenge header](#response-metadata)
<a name="setup"></a>
## Example code and setup
The example code for our tutorial is in [examples/objective-c/auth_sample](.).
To download the example, clone this repository by running the following command:
```shell
$ git clone https://github.com/grpc/grpc.git
```
Then change your current directory to `examples/objective-c/auth_sample`:
```shell
$ cd examples/objective-c/auth_sample
```
Our example is a simple application with two views. The first one lets a user sign in and out using
the OAuth2 flow of Google's [iOS SignIn library](https://developers.google.com/identity/sign-in/ios/).
(Google's library is used in this example because the test gRPC service we are going to call expects
Google account credentials, but neither gRPC nor the Objective-C client library is tied to any
specific OAuth2 provider). The second view makes a gRPC request to the test server, using the
access token obtained by the first view.
Note: OAuth2 libraries need the application to register and obtain an ID from the identity provider
(in the case of this example app, Google). The app's XCode project is configured using that ID, so
you shouldn't copy this project "as is" for your own app: it would result in your app being
identified in the consent screen as "gRPC-AuthSample", and not having access to real Google
services. Instead, configure your own XCode project following the [instructions here](https://developers.google.com/identity/sign-in/ios/).
As with the other examples, you also should have [Cocoapods](https://cocoapods.org/#install)
installed, as well as the relevant tools to generate the client library code. You can obtain the
latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc).
<a name="try"></a>
## Try it out!
To try the sample app, first have Cocoapods generate and install the client library for our .proto
files:
```shell
$ pod install
```
(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet
on your computer's cache).
Finally, open the XCode workspace created by Cocoapods, and run the app.
The first view, `SelectUserViewController.h/m`, asks you to sign in with your Google account, and to
give the "gRPC-AuthSample" app the following permissions:
- View your email address.
- View your basic profile info.
- "Test scope for access to the Zoo service".
This last permission, corresponding to the scope `https://www.googleapis.com/auth/xapi.zoo` doesn't
grant any real capability: it's only used for testing. You can log out at any time.
The second view, `MakeRPCViewController.h/m`, makes a gRPC request to a test server at
https://grpc-test.sandbox.google.com, sending the access token along with the request. The test
service simply validates the token and writes in its response which user it belongs to, and which
scopes it gives access to. (The client application already knows those two values; it's a way to
verify that everything went as expected).
The next sections guide you step-by-step through how the gRPC call in `MakeRPCViewController` is
performed.
<a name="rpc-object"></a>
## Create an RPC object and start it later
The other basic tutorials show how to invoke an RPC by calling an asynchronous method in a generated
client object. This shows how to initialize an object that represents the RPC, and configure it
before starting the network request.
Assume you have a proto service definition like this:
```protobuf
option objc_class_prefix = "AUTH";
service TestService {
rpc UnaryCall(Request) returns (Response);
}
```
A `unaryCallWithRequest:handler:` method, with which you're already familiar, is generated for the
`AUTHTestService` class:
```objective-c
[client unaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
...
}];
```
In addition, an `RPCToUnaryCallWithRequest:handler:` method is generated, which returns a
not-yet-started RPC object:
```objective-c
#import <ProtoRPC/ProtoRPC.h>
ProtoRPC *call =
[client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
...
}];
```
The RPC represented by this object can be started at any later time like this:
```objective-c
[call start];
```
<a name="request-metadata"></a>
## Set request metadata of a call: Authorization header with an access token
The `ProtoRPC` class has a `requestMetadata` property (inherited from `GRPCCall`) defined like this:
```objective-c
- (NSMutableDictionary *)requestMetadata; // nonatomic
- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy
```
Setting it to a dictionary of metadata keys and values will have them sent on the wire when the call
is started. gRPC metadata are pieces of information about the call sent by the client to the server
(and vice versa). They take the form of key-value pairs and are essentially opaque to gRPC itself.
```objective-c
call.requestMetadata = @{@"My-Header": @"Value for this header",
@"Another-Header": @"Its value"};
```
For convenience, the property is initialized with an empty `NSMutableDictionary`, so that request
metadata elements can be set like this:
```objective-c
call.requestMetadata[@"My-Header"] = @"Value for this header";
```
If you have an access token, OAuth2 specifies it is to be sent in this format:
```objective-c
call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
```
<a name="response-metadata"></a>
## Get response metadata of a call: Auth challenge header
The `ProtoRPC` class also inherits a `responseMetadata` property, analogous to the request metadata
we just looked at. It's defined like this:
```objective-c
@property(atomic, readonly) NSDictionary *responseMetadata;
```
To access OAuth2's authentication challenge header you write:
```objective-c
call.responseMetadata[@"www-authenticate"]
```
Note that, as gRPC metadata elements are mapped to HTTP/2 headers (or trailers), the keys of the
response metadata are always ASCII strings in lowercase.
Many uses cases of response metadata are getting more details about an RPC error. For convenience,
when a `NSError` instance is passed to an RPC handler block, the response metadata dictionary can
also be accessed this way:
```objective-c
error.userInfo[kGRPCStatusMetadataKey]
```
This is the supporting code for the tutorial "[OAuth2 on gRPC: Objective-C](http://www.grpc.io/docs/tutorials/auth/oauth2-objective-c.html)."

@ -12,11 +12,13 @@ Here's how to build and run the Objective-C implementation of the [Hello World](
example used in [Getting started](https://github.com/grpc/grpc/tree/master/examples).
The example code for this and our other examples lives in the `examples` directory. Clone
this repository to your local machine by running the following command:
this repository to your local machine by running the following commands:
```sh
$ git clone https://github.com/grpc/grpc.git
$ cd grpc
$ git submodule update --init
```
Change your current directory to `examples/objective-c/helloworld`
@ -53,4 +55,4 @@ responds with a `HLWHelloResponse`, which contains a string that is then output
## Tutorial
You can find a more detailed tutorial in [gRPC Basics: Objective-C](../route_guide/README.md).
You can find a more detailed tutorial in [gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html).

@ -2,6 +2,8 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
target 'RouteGuideClient' do
pod 'Protobuf', :path => "../../../third_party/protobuf"
pod 'gRPC', :path => "../../.."
# Depend on the generated RouteGuide library.
pod 'RouteGuide', :path => '.'
end

@ -1,360 +1,4 @@
#gRPC Basics: Objective-C
This tutorial provides a basic Objective-C programmer's introduction to working with gRPC. By
walking through this example you'll learn how to:
This is the supporting code for the tutorial "[gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html)."
- Define a service in a .proto file.
- Generate client code using the protocol buffer compiler.
- Use the Objective-C gRPC API to write a simple client for your service.
It assumes a passing familiarity with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
Note that the example in this tutorial uses the proto3 version of the protocol buffers language,
which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3)
and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the
protocol buffers Github repository.
This isn't a comprehensive guide to using gRPC in Objective-C: more reference documentation is
coming soon.
- [Why use gRPC?](#why-grpc)
- [Example code and setup](#setup)
- [Try it out!](#try)
- [Defining the service](#proto)
- [Generating client code](#protoc)
- [Creating the client](#client)
<a name="why-grpc"></a>
## Why use gRPC?
With gRPC you can define your service once in a .proto file and implement clients and servers in any
of gRPC's supported languages, which in turn can be run in environments ranging from servers inside
Google to your own tablet - all the complexity of communication between different languages and
environments is handled for you by gRPC. You also get all the advantages of working with protocol
buffers, including efficient serialization, a simple IDL, and easy interface updating.
gRPC and proto3 are specially suited for mobile clients: gRPC is implemented on top of HTTP/2, which
results in network bandwidth savings over using HTTP/1.1. Serialization and parsing of the proto
binary format is more efficient than the equivalent JSON, resulting in CPU and battery savings. And
proto3 uses a runtime that has been optimized over the years at Google to keep code size to a
minimum. The latter is important in Objective-C, because the ability of the compiler to strip unused
code is limited by the dynamic nature of the language.
<a name="setup"></a>
## Example code and setup
The example code for our tutorial is in [examples/objective-c/route_guide](.).
To download the example, clone this repository by running the following command:
```shell
$ git clone https://github.com/grpc/grpc.git
```
Then change your current directory to `examples/objective-c/route_guide`:
```shell
$ cd examples/objective-c/route_guide
```
Our example is a simple route mapping application that lets clients get information about features
on their route, create a summary of their route, and exchange route information such as traffic
updates with the server and other clients.
You also should have [Cocoapods](https://cocoapods.org/#install) installed, as well as the relevant
tools to generate the client library code (and a server in another language, for testing). You can
obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc).
<a name="try"></a>
## Try it out!
To try the sample app, we need a gRPC server running locally. Let's compile and run, for example,
the C++ server in this repository:
```shell
$ pushd ../../cpp/route_guide
$ make
$ ./route_guide_server &
$ popd
```
Now have Cocoapods generate and install the client library for our .proto files:
```shell
$ pod install
```
(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet
on your computer's cache).
Finally, open the XCode workspace created by Cocoapods, and run the app. You can check the calling
code in `ViewControllers.m` and see the results in XCode's log console.
The next sections guide you step-by-step through how this proto service is defined, how to generate
a client library from it, and how to create an app that uses that library.
<a name="proto"></a>
## Defining the service
First let's look at how the service we're using is defined. A gRPC *service* and its method
*request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview).
You can see the complete .proto file for our example in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto).
To define a service, you specify a named `service` in your .proto file:
```protobuf
service RouteGuide {
...
}
```
Then you define `rpc` methods inside your service definition, specifying their request and response
types. Protocol buffers let you define four kinds of service method, all of which are used in the
`RouteGuide` service:
- A *simple RPC* where the client sends a request to the server and receives a response later, just
like a normal remote procedure call.
```protobuf
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
```
- A *response-streaming RPC* where the client sends a request to the server and gets back a stream
of response messages. You specify a response-streaming method by placing the `stream` keyword before
the *response* type.
```protobuf
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
```
- A *request-streaming RPC* where the client sends a sequence of messages to the server. Once the
client has finished writing the messages, it waits for the server to read them all and return its
response. You specify a request-streaming method by placing the `stream` keyword before the
*request* type.
```protobuf
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
```
- A *bidirectional streaming RPC* where both sides send a sequence of messages to the other. The two
streams operate independently, so clients and servers can read and write in whatever order they
like: for example, the server could wait to receive all the client messages before writing its
responses, or it could alternately read a message then write a message, or some other combination of
reads and writes. The order of messages in each stream is preserved. You specify this type of method
by placing the `stream` keyword before both the request and the response.
```protobuf
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
```
Our .proto file also contains protocol buffer message type definitions for all the request and
response types used in our service methods - for example, here's the `Point` message type:
```protobuf
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
```
You can specify a prefix to be used for your generated classes by adding the `objc_class_prefix`
option at the top of the file. For example:
```protobuf
option objc_class_prefix = "RTG";
```
<a name="protoc"></a>
## Generating client code
Next we need to generate the gRPC client interfaces from our .proto service definition. We do this
using the protocol buffer compiler (`protoc`) with a special gRPC Objective-C plugin.
For simplicity, we've provided a [Podspec file](RouteGuide.podspec)
that runs `protoc` for you with the appropriate plugin, input, and output, and describes how to
compile the generated files. You just need to run in this directory (`examples/objective-c/route_guide`):
```shell
$ pod install
```
which, before installing the generated library in the XCode project of this sample, runs:
```shell
$ protoc -I ../../protos --objc_out=Pods/RouteGuide --objcgrpc_out=Pods/RouteGuide ../../protos/route_guide.proto
```
Running this command generates the following files under `Pods/RouteGuide/`:
- `RouteGuide.pbobjc.h`, the header which declares your generated message classes.
- `RouteGuide.pbobjc.m`, which contains the implementation of your message classes.
- `RouteGuide.pbrpc.h`, the header which declares your generated service classes.
- `RouteGuide.pbrpc.m`, which contains the implementation of your service classes.
These contain:
- All the protocol buffer code to populate, serialize, and retrieve our request and response message
types.
- A class called `RTGRouteGuide` that lets clients call the methods defined in the `RouteGuide`
service.
You can also use the provided Podspec file to generate client code from any other proto service
definition; just replace the name (matching the file name), version, and other metadata.
<a name="client"></a>
## Creating the client
In this section, we'll look at creating an Objective-C client for our `RouteGuide` service. You can
see our complete example client code in [ViewControllers.m](ViewControllers.m).
(Note: In your apps, for maintainability and readability reasons, you shouldn't put all of your view
controllers in a single file; it's done here only to simplify the learning process).
### Constructing a client object
To call service methods, we first need to create a client object, an instance of the generated
`RTGRouteGuide` class. The designated initializer of the class expects a `NSString *` with the
server address and port we want to connect to:
```objective-c
#import <RouteGuide/RouteGuide.pbrpc.h>
static NSString * const kHostAddress = @"http://localhost:50051";
...
RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
```
Notice that we've specified the HTTP scheme in the host address. This is because the server we will
be using to test our client doesn't use [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security).
This is fine because it will be running locally on our development machine. The most common case,
though, is connecting with a gRPC server on the internet, running gRPC over TLS. For that case, the
HTTPS scheme can be specified (or no scheme at all, as HTTPS is the default value). The default
value of the port is that of the scheme selected: 443 for HTTPS and 80 for HTTP.
### Calling service methods
Now let's look at how we call our service methods. As you will see, all these methods are
asynchronous, so you can call them from the main thread of your app without worrying about freezing
your UI or the OS killing your app.
#### Simple RPC
Calling the simple RPC `GetFeature` is nearly as straightforward as calling any other asynchronous
method on Cocoa.
```objective-c
RTGPoint *point = [RTGPoint message];
point.latitude = 40E7;
point.longitude = -74E7;
[client getFeatureWithRequest:point handler:^(RTGFeature *response, NSError *error) {
if (response) {
// Successful response received
} else {
// RPC error
}
}];
```
As you can see, we create and populate a request protocol buffer object (in our case `RTGPoint`).
Then, we call the method on the client object, passing it the request, and a block to handle the
response (or any RPC error). If the RPC finishes successfully, the handler block is called with a
`nil` error argument, and we can read the response information from the server from the response
argument. If, instead, some RPC error happens, the handler block is called with a `nil` response
argument, and we can read the details of the problem from the error argument.
```objective-c
NSLog(@"Found feature called %@ at %@.", response.name, response.location);
```
#### Streaming RPCs
Now let's look at our streaming methods. Here's where we call the response-streaming method
`ListFeatures`, which results in our client receiving a stream of geographical `RTGFeature`s:
```objective-c
[client listFeaturesWithRequest:rectangle
eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
if (response) {
// Element of the stream of responses received
} else if (error) {
// RPC error; the stream is over.
}
if (done) {
// The stream is over (all the responses were received, or an error occured). Do any cleanup.
}
}];
```
Notice how the signature of the `eventHandler` block now includes a `BOOL done` parameter. The
`eventHandler` block can be called any number of times; only on the last call is the `done` argument
value set to `YES`. If an error occurs, the RPC finishes and the block is called with the arguments
`(YES, nil, error)`.
The request-streaming method `RecordRoute` expects a stream of `RTGPoint`s from the cient. This
stream is passed to the method as an object of class `GRXWriter`. The simplest way to create one is
to initialize one from a `NSArray` object:
```objective-c
#import <RxLibrary/GRXWriter+Immediate.h>
...
RTGPoint *point1 = [RTGPoint message];
point.latitude = 40E7;
point.longitude = -74E7;
RTGPoint *point2 = [RTGPoint message];
point.latitude = 40E7;
point.longitude = -74E7;
GRXWriter *locationsWriter = [GRXWriter writerWithContainer:@[point1, point2]];
[client recordRouteWithRequestsWriter:locationsWriter
handler:^(RTGRouteSummary *response, NSError *error) {
if (response) {
NSLog(@"Finished trip with %i points", response.pointCount);
NSLog(@"Passed %i features", response.featureCount);
NSLog(@"Travelled %i meters", response.distance);
NSLog(@"It took %i seconds", response.elapsedTime);
} else {
NSLog(@"RPC error: %@", error);
}
}];
```
The `GRXWriter` class is generic enough to allow for asynchronous streams, streams of future values,
or even infinite streams.
Finally, let's look at our bidirectional streaming RPC `RouteChat()`. The way to call a
bidirectional streaming RPC is just a combination of how to call request-streaming RPCs and
response-streaming RPCs.
```objective-c
[client routeChatWithRequestsWriter:notesWriter
eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
if (note) {
NSLog(@"Got message %@ at %@", note.message, note.location);
} else if (error) {
NSLog(@"RPC error: %@", error);
}
if (done) {
NSLog(@"Chat ended.");
}
}];
```
The semantics for the handler block and the `GRXWriter` argument here are exactly the same as for
our request-streaming and response-streaming methods. Although both client and server will always
get the other's messages in the order they were written, the two streams operate completely
independently.

@ -3,13 +3,13 @@ Pod::Spec.new do |s|
s.version = "0.0.1"
s.license = "New BSD"
s.ios.deployment_target = "6.0"
s.osx.deployment_target = "10.8"
s.ios.deployment_target = "7.1"
s.osx.deployment_target = "10.9"
# Base directory where the .proto files are.
src = "../../protos"
# Directory where the generated files will be place.
# Directory where the generated files will be placed.
dir = "Pods/" + s.name
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
@ -22,14 +22,14 @@ Pod::Spec.new do |s|
ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
ms.header_mappings_dir = dir
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
ms.dependency "Protobuf", "~> 3.0.0-alpha-4"
end
s.subspec "Services" do |ss|
ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
ss.header_mappings_dir = dir
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.6"
ss.dependency "gRPC", "~> 0.11"
ss.dependency "#{s.name}/Messages"
end
end

@ -121,6 +121,7 @@
6325277A1B1D0395003073D9 /* Frameworks */,
6325277B1B1D0395003073D9 /* Resources */,
FFE0BCF30339E7A50A989EAB /* Copy Pods Resources */,
B5388EC5A25E89021740B916 /* Embed Pods Frameworks */,
);
buildRules = (
);
@ -177,6 +178,21 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
B5388EC5A25E89021740B916 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C6FC30AD2376EC04317237C5 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

@ -32,13 +32,14 @@
*/
#import <UIKit/UIKit.h>
#import <GRPCClient/GRPCCall+Tests.h>
#import <RouteGuide/RouteGuide.pbrpc.h>
#import <RxLibrary/GRXWriter+Immediate.h>
#import <RxLibrary/GRXWriter+Transformations.h>
static NSString * const kHostAddress = @"http://localhost:50051";
static NSString * const kHostAddress = @"localhost:50051";
// Category to override RTGPoint's description.
/** Category to override RTGPoint's description. */
@interface RTGPoint (Description)
- (NSString *)description;
@end
@ -53,7 +54,7 @@ static NSString * const kHostAddress = @"http://localhost:50051";
}
@end
// Category to give RTGRouteNote a convenience constructor.
/** Category to give RTGRouteNote a convenience constructor. */
@interface RTGRouteNote (Constructors)
+ (instancetype)noteWithMessage:(NSString *)message
latitude:(float)latitude
@ -75,9 +76,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
#pragma mark Demo: Get Feature
// Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
// not to have a feature.
/**
* Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
* not to have a feature.
*/
@interface GetFeatureViewController : UIViewController
@end
@ -86,7 +88,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
- (void)viewDidLoad {
[super viewDidLoad];
RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
// This only needs to be done once per host, before creating service objects for that host.
[GRPCCall useInsecureConnectionsForHost:kHostAddress];
RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
void (^handler)(RTGFeature *response, NSError *error) = ^(RTGFeature *response, NSError *error) {
if (response.name.length) {
@ -102,8 +107,8 @@ static NSString * const kHostAddress = @"http://localhost:50051";
point.latitude = 409146138;
point.longitude = -746188906;
[client getFeatureWithRequest:point handler:handler];
[client getFeatureWithRequest:[RTGPoint message] handler:handler];
[service getFeatureWithRequest:point handler:handler];
[service getFeatureWithRequest:[RTGPoint message] handler:handler];
}
@end
@ -111,9 +116,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
#pragma mark Demo: List Features
// Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
// the pre-generated database. Prints each response as it comes in.
/**
* Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
* the pre-generated database. Prints each response as it comes in.
*/
@interface ListFeaturesViewController : UIViewController
@end
@ -122,7 +128,7 @@ static NSString * const kHostAddress = @"http://localhost:50051";
- (void)viewDidLoad {
[super viewDidLoad];
RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
RTGRectangle *rectangle = [RTGRectangle message];
rectangle.lo.latitude = 405E6;
@ -131,8 +137,8 @@ static NSString * const kHostAddress = @"http://localhost:50051";
rectangle.hi.longitude = -745E6;
NSLog(@"Looking for features between %@ and %@", rectangle.lo, rectangle.hi);
[client listFeaturesWithRequest:rectangle
eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
[service listFeaturesWithRequest:rectangle
eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
if (response) {
NSLog(@"Found feature at %@ called %@.", response.location, response.name);
} else if (error) {
@ -146,10 +152,11 @@ static NSString * const kHostAddress = @"http://localhost:50051";
#pragma mark Demo: Record Route
// Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
// database with a variable delay in between. Prints the statistics when they are sent from the
// server.
/**
* Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
* database with a variable delay in between. Prints the statistics when they are sent from the
* server.
*/
@interface RecordRouteViewController : UIViewController
@end
@ -171,9 +178,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
return location;
}];
RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
[client recordRouteWithRequestsWriter:locations handler:^(RTGRouteSummary *response, NSError *error) {
[service recordRouteWithRequestsWriter:locations
handler:^(RTGRouteSummary *response, NSError *error) {
if (response) {
NSLog(@"Finished trip with %i points", response.pointCount);
NSLog(@"Passed %i features", response.featureCount);
@ -190,9 +198,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
#pragma mark Demo: Route Chat
// Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
// the server.
/**
* Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
* the server.
*/
@interface RouteChatViewController : UIViewController
@end
@ -210,10 +219,10 @@ static NSString * const kHostAddress = @"http://localhost:50051";
return note;
}];
RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
[client routeChatWithRequestsWriter:notesWriter
eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
[service routeChatWithRequestsWriter:notesWriter
eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
if (note) {
NSLog(@"Got message %@ at %@", note.message, note.location);
} else if (error) {

@ -157,6 +157,7 @@ Pod::Spec.new do |s|
'src/core/channel/channel_args.h',
'src/core/channel/channel_stack.h',
'src/core/channel/client_channel.h',
'src/core/channel/client_uchannel.h',
'src/core/channel/compress_filter.h',
'src/core/channel/connected_channel.h',
'src/core/channel/context.h',
@ -300,6 +301,7 @@ Pod::Spec.new do |s|
'src/core/channel/channel_args.c',
'src/core/channel/channel_stack.c',
'src/core/channel/client_channel.c',
'src/core/channel/client_uchannel.c',
'src/core/channel/compress_filter.c',
'src/core/channel/connected_channel.c',
'src/core/channel/http_client_filter.c',
@ -449,6 +451,7 @@ Pod::Spec.new do |s|
'src/core/channel/channel_args.h',
'src/core/channel/channel_stack.h',
'src/core/channel/client_channel.h',
'src/core/channel/client_uchannel.h',
'src/core/channel/compress_filter.h',
'src/core/channel/connected_channel.h',
'src/core/channel/context.h',
@ -563,8 +566,12 @@ Pod::Spec.new do |s|
ss.header_mappings_dir = '.'
# This isn't officially supported in Cocoapods. We've asked for an alternative:
# https://github.com/CocoaPods/CocoaPods/issues/4386
ss.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
'"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
ss.xcconfig = {
'USE_HEADERMAP' => 'NO',
'ALWAYS_SEARCH_USER_PATHS' => 'NO',
'USER_HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC"',
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC/include"'
}
ss.requires_arc = false
ss.libraries = 'z'

@ -75,7 +75,6 @@ class ByteBuffer GRPC_FINAL {
// takes ownership
void set_buffer(grpc_byte_buffer* buf) {
if (buffer_) {
gpr_log(GPR_ERROR, "Overriding existing buffer");
Clear();
}
buffer_ = buf;

@ -131,7 +131,7 @@ class ClientReader GRPC_FINAL : public ClientReaderInterface<R> {
cq_.Pluck(&ops);
}
void WaitForInitialMetadata() {
void WaitForInitialMetadata() GRPC_OVERRIDE {
GPR_ASSERT(!context_->initial_metadata_received_);
CallOpSet<CallOpRecvInitialMetadata> ops;
@ -257,7 +257,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
cq_.Pluck(&ops);
}
void WaitForInitialMetadata() {
void WaitForInitialMetadata() GRPC_OVERRIDE {
GPR_ASSERT(!context_->initial_metadata_received_);
CallOpSet<CallOpRecvInitialMetadata> ops;

@ -38,6 +38,7 @@
"jshint": "^2.5.0",
"minimist": "^1.1.0",
"mocha": "~1.21.0",
"mocha-jenkins-reporter": "^0.1.9",
"mustache": "^2.0.0"
},
"engines": {

@ -0,0 +1,572 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/channel/client_uchannel.h"
#include <string.h>
#include "src/core/census/grpc_filter.h"
#include "src/core/channel/channel_args.h"
#include "src/core/channel/client_channel.h"
#include "src/core/channel/compress_filter.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/support/string.h"
#include "src/core/surface/channel.h"
#include "src/core/transport/connectivity_state.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/useful.h>
/** Microchannel (uchannel) implementation: a lightweight channel without any
* load-balancing mechanisms meant for communication from within the core. */
typedef struct call_data call_data;
typedef struct client_uchannel_channel_data {
/** metadata context for this channel */
grpc_mdctx *mdctx;
/** master channel - the grpc_channel instance that ultimately owns
this channel_data via its channel stack.
We occasionally use this to bump the refcount on the master channel
to keep ourselves alive through an asynchronous operation. */
grpc_channel *master;
/** connectivity state being tracked */
grpc_connectivity_state_tracker state_tracker;
/** the subchannel wrapped by the microchannel */
grpc_subchannel *subchannel;
/** the callback used to stay subscribed to subchannel connectivity
* notifications */
grpc_closure connectivity_cb;
/** the current connectivity state of the wrapped subchannel */
grpc_connectivity_state subchannel_connectivity;
gpr_mu mu_state;
} channel_data;
typedef enum {
CALL_CREATED,
CALL_WAITING_FOR_SEND,
CALL_WAITING_FOR_CALL,
CALL_ACTIVE,
CALL_CANCELLED
} call_state;
struct call_data {
/* owning element */
grpc_call_element *elem;
gpr_mu mu_state;
call_state state;
gpr_timespec deadline;
grpc_closure async_setup_task;
grpc_transport_stream_op waiting_op;
/* our child call stack */
grpc_subchannel_call *subchannel_call;
grpc_linked_mdelem status;
grpc_linked_mdelem details;
};
static grpc_closure *merge_into_waiting_op(grpc_call_element *elem,
grpc_transport_stream_op *new_op)
GRPC_MUST_USE_RESULT;
static void handle_op_after_cancellation(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
grpc_transport_stream_op *op) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
if (op->send_ops) {
grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops);
op->on_done_send->cb(exec_ctx, op->on_done_send->cb_arg, 0);
}
if (op->recv_ops) {
char status[GPR_LTOA_MIN_BUFSIZE];
grpc_metadata_batch mdb;
gpr_ltoa(GRPC_STATUS_CANCELLED, status);
calld->status.md =
grpc_mdelem_from_strings(chand->mdctx, "grpc-status", status);
calld->details.md =
grpc_mdelem_from_strings(chand->mdctx, "grpc-message", "Cancelled");
calld->status.prev = calld->details.next = NULL;
calld->status.next = &calld->details;
calld->details.prev = &calld->status;
mdb.list.head = &calld->status;
mdb.list.tail = &calld->details;
mdb.garbage.head = mdb.garbage.tail = NULL;
mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
grpc_sopb_add_metadata(op->recv_ops, mdb);
*op->recv_state = GRPC_STREAM_CLOSED;
op->on_done_recv->cb(exec_ctx, op->on_done_recv->cb_arg, 1);
}
if (op->on_consumed) {
op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 0);
}
}
typedef struct {
grpc_closure closure;
grpc_call_element *elem;
} waiting_call;
static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
grpc_transport_stream_op *op,
int continuation);
static int is_empty(void *p, int len) {
char *ptr = p;
int i;
for (i = 0; i < len; i++) {
if (ptr[i] != 0) return 0;
}
return 1;
}
static void monitor_subchannel(grpc_exec_ctx *exec_ctx, void *arg,
int iomgr_success) {
channel_data *chand = arg;
grpc_connectivity_state_set(exec_ctx, &chand->state_tracker,
chand->subchannel_connectivity,
"uchannel_monitor_subchannel");
grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel,
&chand->subchannel_connectivity,
&chand->connectivity_cb);
}
static void started_call_locked(grpc_exec_ctx *exec_ctx, void *arg,
int iomgr_success) {
call_data *calld = arg;
grpc_transport_stream_op op;
int have_waiting;
if (calld->state == CALL_CANCELLED && iomgr_success == 0) {
have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op));
gpr_mu_unlock(&calld->mu_state);
if (have_waiting) {
handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op);
}
} else if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) {
memset(&op, 0, sizeof(op));
op.cancel_with_status = GRPC_STATUS_CANCELLED;
gpr_mu_unlock(&calld->mu_state);
grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, &op);
} else if (calld->state == CALL_WAITING_FOR_CALL) {
have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op));
if (calld->subchannel_call != NULL) {
calld->state = CALL_ACTIVE;
gpr_mu_unlock(&calld->mu_state);
if (have_waiting) {
grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call,
&calld->waiting_op);
}
} else {
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&calld->mu_state);
if (have_waiting) {
handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op);
}
}
} else {
GPR_ASSERT(calld->state == CALL_CANCELLED);
gpr_mu_unlock(&calld->mu_state);
have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op));
if (have_waiting) {
handle_op_after_cancellation(exec_ctx, calld->elem, &calld->waiting_op);
}
}
}
static void started_call(grpc_exec_ctx *exec_ctx, void *arg,
int iomgr_success) {
call_data *calld = arg;
gpr_mu_lock(&calld->mu_state);
started_call_locked(exec_ctx, arg, iomgr_success);
}
static grpc_closure *merge_into_waiting_op(grpc_call_element *elem,
grpc_transport_stream_op *new_op) {
call_data *calld = elem->call_data;
grpc_closure *consumed_op = NULL;
grpc_transport_stream_op *waiting_op = &calld->waiting_op;
GPR_ASSERT((waiting_op->send_ops != NULL) + (new_op->send_ops != NULL) <= 1);
GPR_ASSERT((waiting_op->recv_ops != NULL) + (new_op->recv_ops != NULL) <= 1);
if (new_op->send_ops != NULL) {
waiting_op->send_ops = new_op->send_ops;
waiting_op->is_last_send = new_op->is_last_send;
waiting_op->on_done_send = new_op->on_done_send;
}
if (new_op->recv_ops != NULL) {
waiting_op->recv_ops = new_op->recv_ops;
waiting_op->recv_state = new_op->recv_state;
waiting_op->on_done_recv = new_op->on_done_recv;
}
if (new_op->on_consumed != NULL) {
if (waiting_op->on_consumed != NULL) {
consumed_op = waiting_op->on_consumed;
}
waiting_op->on_consumed = new_op->on_consumed;
}
if (new_op->cancel_with_status != GRPC_STATUS_OK) {
waiting_op->cancel_with_status = new_op->cancel_with_status;
}
return consumed_op;
}
static char *cuc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
grpc_subchannel_call *subchannel_call;
char *result;
gpr_mu_lock(&calld->mu_state);
if (calld->state == CALL_ACTIVE) {
subchannel_call = calld->subchannel_call;
GRPC_SUBCHANNEL_CALL_REF(subchannel_call, "get_peer");
gpr_mu_unlock(&calld->mu_state);
result = grpc_subchannel_call_get_peer(exec_ctx, subchannel_call);
GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "get_peer");
return result;
} else {
gpr_mu_unlock(&calld->mu_state);
return grpc_channel_get_target(chand->master);
}
}
static void perform_transport_stream_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
grpc_transport_stream_op *op,
int continuation) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
grpc_subchannel_call *subchannel_call;
grpc_transport_stream_op op2;
GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter);
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
gpr_mu_lock(&calld->mu_state);
/* make sure the wrapped subchannel has been set (see
* grpc_client_uchannel_set_subchannel) */
GPR_ASSERT(chand->subchannel != NULL);
switch (calld->state) {
case CALL_ACTIVE:
GPR_ASSERT(!continuation);
subchannel_call = calld->subchannel_call;
gpr_mu_unlock(&calld->mu_state);
grpc_subchannel_call_process_op(exec_ctx, subchannel_call, op);
break;
case CALL_CANCELLED:
gpr_mu_unlock(&calld->mu_state);
handle_op_after_cancellation(exec_ctx, elem, op);
break;
case CALL_WAITING_FOR_SEND:
GPR_ASSERT(!continuation);
grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1);
if (!calld->waiting_op.send_ops &&
calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) {
gpr_mu_unlock(&calld->mu_state);
break;
}
*op = calld->waiting_op;
memset(&calld->waiting_op, 0, sizeof(calld->waiting_op));
continuation = 1;
/* fall through */
case CALL_WAITING_FOR_CALL:
if (!continuation) {
if (op->cancel_with_status != GRPC_STATUS_OK) {
calld->state = CALL_CANCELLED;
op2 = calld->waiting_op;
memset(&calld->waiting_op, 0, sizeof(calld->waiting_op));
if (op->on_consumed) {
calld->waiting_op.on_consumed = op->on_consumed;
op->on_consumed = NULL;
} else if (op2.on_consumed) {
calld->waiting_op.on_consumed = op2.on_consumed;
op2.on_consumed = NULL;
}
gpr_mu_unlock(&calld->mu_state);
handle_op_after_cancellation(exec_ctx, elem, op);
handle_op_after_cancellation(exec_ctx, elem, &op2);
grpc_subchannel_cancel_waiting_call(exec_ctx, chand->subchannel, 1);
} else {
grpc_exec_ctx_enqueue(exec_ctx, merge_into_waiting_op(elem, op), 1);
gpr_mu_unlock(&calld->mu_state);
}
break;
}
/* fall through */
case CALL_CREATED:
if (op->cancel_with_status != GRPC_STATUS_OK) {
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&calld->mu_state);
handle_op_after_cancellation(exec_ctx, elem, op);
} else {
calld->waiting_op = *op;
if (op->send_ops == NULL) {
calld->state = CALL_WAITING_FOR_SEND;
gpr_mu_unlock(&calld->mu_state);
} else {
grpc_subchannel_call_create_status call_creation_status;
grpc_pollset *pollset = calld->waiting_op.bind_pollset;
calld->state = CALL_WAITING_FOR_CALL;
grpc_closure_init(&calld->async_setup_task, started_call, calld);
call_creation_status = grpc_subchannel_create_call(
exec_ctx, chand->subchannel, pollset, &calld->subchannel_call,
&calld->async_setup_task);
if (call_creation_status == GRPC_SUBCHANNEL_CALL_CREATE_READY) {
started_call_locked(exec_ctx, calld, 1);
} else {
gpr_mu_unlock(&calld->mu_state);
}
}
}
break;
}
}
static void cuc_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
grpc_transport_stream_op *op) {
perform_transport_stream_op(exec_ctx, elem, op, 0);
}
static void cuc_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_transport_op *op) {
channel_data *chand = elem->channel_data;
grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1);
GPR_ASSERT(op->set_accept_stream == NULL);
GPR_ASSERT(op->bind_pollset == NULL);
if (op->on_connectivity_state_change != NULL) {
grpc_connectivity_state_notify_on_state_change(
exec_ctx, &chand->state_tracker, op->connectivity_state,
op->on_connectivity_state_change);
op->on_connectivity_state_change = NULL;
op->connectivity_state = NULL;
}
if (op->disconnect) {
grpc_connectivity_state_set(exec_ctx, &chand->state_tracker,
GRPC_CHANNEL_FATAL_FAILURE, "disconnect");
}
}
/* Constructor for call_data */
static void cuc_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
const void *server_transport_data,
grpc_transport_stream_op *initial_op) {
call_data *calld = elem->call_data;
memset(calld, 0, sizeof(call_data));
/* TODO(ctiller): is there something useful we can do here? */
GPR_ASSERT(initial_op == NULL);
GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter);
GPR_ASSERT(server_transport_data == NULL);
gpr_mu_init(&calld->mu_state);
calld->elem = elem;
calld->state = CALL_CREATED;
calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
}
/* Destructor for call_data */
static void cuc_destroy_call_elem(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem) {
call_data *calld = elem->call_data;
grpc_subchannel_call *subchannel_call;
/* if the call got activated, we need to destroy the child stack also, and
remove it from the in-flight requests tracked by the child_entry we
picked */
gpr_mu_lock(&calld->mu_state);
switch (calld->state) {
case CALL_ACTIVE:
subchannel_call = calld->subchannel_call;
gpr_mu_unlock(&calld->mu_state);
GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, subchannel_call, "client_uchannel");
break;
case CALL_CREATED:
case CALL_CANCELLED:
gpr_mu_unlock(&calld->mu_state);
break;
case CALL_WAITING_FOR_CALL:
case CALL_WAITING_FOR_SEND:
GPR_UNREACHABLE_CODE(return );
}
}
/* Constructor for channel_data */
static void cuc_init_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_channel *master,
const grpc_channel_args *args,
grpc_mdctx *metadata_context, int is_first,
int is_last) {
channel_data *chand = elem->channel_data;
memset(chand, 0, sizeof(*chand));
grpc_closure_init(&chand->connectivity_cb, monitor_subchannel, chand);
GPR_ASSERT(is_last);
GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter);
chand->mdctx = metadata_context;
chand->master = master;
grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
"client_uchannel");
gpr_mu_init(&chand->mu_state);
}
/* Destructor for channel_data */
static void cuc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem) {
channel_data *chand = elem->channel_data;
grpc_subchannel_state_change_unsubscribe(exec_ctx, chand->subchannel,
&chand->connectivity_cb);
grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
gpr_mu_destroy(&chand->mu_state);
}
const grpc_channel_filter grpc_client_uchannel_filter = {
cuc_start_transport_stream_op,
cuc_start_transport_op,
sizeof(call_data),
cuc_init_call_elem,
cuc_destroy_call_elem,
sizeof(channel_data),
cuc_init_channel_elem,
cuc_destroy_channel_elem,
cuc_get_peer,
"client-uchannel",
};
grpc_connectivity_state grpc_client_uchannel_check_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) {
channel_data *chand = elem->channel_data;
grpc_connectivity_state out;
out = grpc_connectivity_state_check(&chand->state_tracker);
gpr_mu_lock(&chand->mu_state);
if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
grpc_connectivity_state_set(exec_ctx, &chand->state_tracker,
GRPC_CHANNEL_CONNECTING,
"uchannel_connecting_changed");
chand->subchannel_connectivity = out;
grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel,
&chand->subchannel_connectivity,
&chand->connectivity_cb);
}
gpr_mu_unlock(&chand->mu_state);
return out;
}
void grpc_client_uchannel_watch_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
grpc_connectivity_state *state, grpc_closure *on_complete) {
channel_data *chand = elem->channel_data;
gpr_mu_lock(&chand->mu_state);
grpc_connectivity_state_notify_on_state_change(
exec_ctx, &chand->state_tracker, state, on_complete);
gpr_mu_unlock(&chand->mu_state);
}
grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set(
grpc_channel_element *elem) {
channel_data *chand = elem->channel_data;
grpc_channel_element *parent_elem;
gpr_mu_lock(&chand->mu_state);
parent_elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack(
grpc_subchannel_get_master(chand->subchannel)));
gpr_mu_unlock(&chand->mu_state);
return grpc_client_channel_get_connecting_pollset_set(parent_elem);
}
void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_pollset *pollset) {
grpc_pollset_set *master_pollset_set =
grpc_client_uchannel_get_connecting_pollset_set(elem);
grpc_pollset_set_add_pollset(exec_ctx, master_pollset_set, pollset);
}
void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_pollset *pollset) {
grpc_pollset_set *master_pollset_set =
grpc_client_uchannel_get_connecting_pollset_set(elem);
grpc_pollset_set_del_pollset(exec_ctx, master_pollset_set, pollset);
}
grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
grpc_channel_args *args) {
grpc_channel *channel = NULL;
#define MAX_FILTERS 3
const grpc_channel_filter *filters[MAX_FILTERS];
grpc_mdctx *mdctx = grpc_subchannel_get_mdctx(subchannel);
grpc_channel *master = grpc_subchannel_get_master(subchannel);
char *target = grpc_channel_get_target(master);
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
size_t n = 0;
grpc_mdctx_ref(mdctx);
if (grpc_channel_args_is_census_enabled(args)) {
filters[n++] = &grpc_client_census_filter;
}
filters[n++] = &grpc_compress_filter;
filters[n++] = &grpc_client_uchannel_filter;
GPR_ASSERT(n <= MAX_FILTERS);
channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
args, mdctx, 1);
gpr_free(target);
return channel;
}
void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel,
grpc_subchannel *subchannel) {
grpc_channel_element *elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(uchannel));
channel_data *chand = elem->channel_data;
GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter);
gpr_mu_lock(&chand->mu_state);
chand->subchannel = subchannel;
gpr_mu_unlock(&chand->mu_state);
}

@ -0,0 +1,70 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H
#define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H
#include "src/core/channel/channel_stack.h"
#include "src/core/client_config/resolver.h"
#define GRPC_MICROCHANNEL_SUBCHANNEL_ARG "grpc.microchannel_subchannel_key"
/* A client microchannel (aka uchannel) is a channel wrapping a subchannel, for
* the purposes of lightweight RPC communications from within the core.*/
extern const grpc_channel_filter grpc_client_uchannel_filter;
grpc_connectivity_state grpc_client_uchannel_check_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect);
void grpc_client_uchannel_watch_connectivity_state(
grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
grpc_connectivity_state *state, grpc_closure *on_complete);
grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set(
grpc_channel_element *elem);
void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx,
grpc_channel_element *channel,
grpc_pollset *pollset);
void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx,
grpc_channel_element *channel,
grpc_pollset *pollset);
grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel,
grpc_channel_args *args);
void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel,
grpc_subchannel *subchannel);
#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H */

@ -312,6 +312,29 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
return c;
}
void grpc_subchannel_cancel_waiting_call(grpc_exec_ctx *exec_ctx,
grpc_subchannel *subchannel,
int iomgr_success) {
waiting_for_connect *w4c;
gpr_mu_lock(&subchannel->mu);
w4c = subchannel->waiting;
subchannel->waiting = NULL;
gpr_mu_unlock(&subchannel->mu);
while (w4c != NULL) {
waiting_for_connect *next = w4c->next;
grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel,
w4c->pollset);
if (w4c->notify) {
w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success);
}
GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect");
gpr_free(w4c);
w4c = next;
}
}
static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
grpc_connect_in_args args;
@ -659,24 +682,12 @@ static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) {
iomgr_success = 0;
}
connectivity_state_changed_locked(exec_ctx, c, "alarm");
gpr_mu_unlock(&c->mu);
if (iomgr_success) {
gpr_mu_unlock(&c->mu);
update_reconnect_parameters(c);
continue_connect(exec_ctx, c);
} else {
waiting_for_connect *w4c;
w4c = c->waiting;
c->waiting = NULL;
gpr_mu_unlock(&c->mu);
while (w4c != NULL) {
waiting_for_connect *next = w4c->next;
grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel,
w4c->pollset);
w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, 0);
GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect");
gpr_free(w4c);
w4c = next;
}
grpc_subchannel_cancel_waiting_call(exec_ctx, c, iomgr_success);
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting");
GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting");
}
@ -784,3 +795,11 @@ static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx,
grpc_call_stack_init(exec_ctx, chanstk, NULL, NULL, callstk);
return call;
}
grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel) {
return subchannel->mdctx;
}
grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) {
return subchannel->master;
}

@ -92,6 +92,11 @@ grpc_subchannel_call_create_status grpc_subchannel_create_call(
grpc_exec_ctx *exec_ctx, grpc_subchannel *subchannel, grpc_pollset *pollset,
grpc_subchannel_call **target, grpc_closure *notify);
/** cancel \a call in the waiting state. */
void grpc_subchannel_cancel_waiting_call(grpc_exec_ctx *exec_ctx,
grpc_subchannel *subchannel,
int iomgr_success);
/** process a transport level op */
void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx,
grpc_subchannel *subchannel,
@ -154,4 +159,10 @@ struct grpc_subchannel_args {
grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
grpc_subchannel_args *args);
/** Return the metadata context associated with the subchannel */
grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel);
/** Return the master channel associated with the subchannel */
grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel);
#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H */

@ -37,6 +37,7 @@
#include <grpc/support/log.h>
#include "src/core/channel/client_channel.h"
#include "src/core/channel/client_uchannel.h"
#include "src/core/iomgr/timer.h"
#include "src/core/surface/api_trace.h"
#include "src/core/surface/completion_queue.h"
@ -51,18 +52,24 @@ grpc_connectivity_state grpc_channel_check_connectivity_state(
GRPC_API_TRACE(
"grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2,
(channel, try_to_connect));
if (client_channel_elem->filter != &grpc_client_channel_filter) {
gpr_log(GPR_ERROR,
"grpc_channel_check_connectivity_state called on something that is "
"not a client channel, but '%s'",
client_channel_elem->filter->name);
if (client_channel_elem->filter == &grpc_client_channel_filter) {
state = grpc_client_channel_check_connectivity_state(
&exec_ctx, client_channel_elem, try_to_connect);
grpc_exec_ctx_finish(&exec_ctx);
return GRPC_CHANNEL_FATAL_FAILURE;
return state;
}
state = grpc_client_channel_check_connectivity_state(
&exec_ctx, client_channel_elem, try_to_connect);
if (client_channel_elem->filter == &grpc_client_uchannel_filter) {
state = grpc_client_uchannel_check_connectivity_state(
&exec_ctx, client_channel_elem, try_to_connect);
grpc_exec_ctx_finish(&exec_ctx);
return state;
}
gpr_log(GPR_ERROR,
"grpc_channel_check_connectivity_state called on something that is "
"not a (u)client channel, but '%s'",
client_channel_elem->filter->name);
grpc_exec_ctx_finish(&exec_ctx);
return state;
return GRPC_CHANNEL_FATAL_FAILURE;
}
typedef enum {
@ -87,7 +94,17 @@ typedef struct {
} state_watcher;
static void delete_state_watcher(grpc_exec_ctx *exec_ctx, state_watcher *w) {
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, "watch_connectivity");
grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(w->channel));
if (client_channel_elem->filter == &grpc_client_channel_filter) {
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel,
"watch_channel_connectivity");
} else if (client_channel_elem->filter == &grpc_client_uchannel_filter) {
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel,
"watch_uchannel_connectivity");
} else {
abort();
}
gpr_mu_destroy(&w->mu);
gpr_free(w);
}
@ -125,8 +142,13 @@ static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w,
w->removed = 1;
client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(w->channel));
grpc_client_channel_del_interested_party(exec_ctx, client_channel_elem,
grpc_cq_pollset(w->cq));
if (client_channel_elem->filter == &grpc_client_channel_filter) {
grpc_client_channel_del_interested_party(exec_ctx, client_channel_elem,
grpc_cq_pollset(w->cq));
} else {
grpc_client_uchannel_del_interested_party(exec_ctx, client_channel_elem,
grpc_cq_pollset(w->cq));
}
}
gpr_mu_unlock(&w->mu);
if (due_to_completion) {
@ -199,18 +221,18 @@ void grpc_channel_watch_connectivity_state(
gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
timeout_complete, w, gpr_now(GPR_CLOCK_MONOTONIC));
if (client_channel_elem->filter != &grpc_client_channel_filter) {
gpr_log(GPR_ERROR,
"grpc_channel_watch_connectivity_state called on something that is "
"not a client channel, but '%s'",
client_channel_elem->filter->name);
grpc_exec_ctx_enqueue(&exec_ctx, &w->on_complete, 1);
} else {
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_connectivity");
if (client_channel_elem->filter == &grpc_client_channel_filter) {
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
grpc_client_channel_add_interested_party(&exec_ctx, client_channel_elem,
grpc_cq_pollset(cq));
grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem,
&w->state, &w->on_complete);
} else if (client_channel_elem->filter == &grpc_client_uchannel_filter) {
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_uchannel_connectivity");
grpc_client_uchannel_add_interested_party(&exec_ctx, client_channel_elem,
grpc_cq_pollset(cq));
grpc_client_uchannel_watch_connectivity_state(
&exec_ctx, client_channel_elem, &w->state, &w->on_complete);
}
grpc_exec_ctx_finish(&exec_ctx);

@ -7,6 +7,7 @@ Grpc.v12.suo
Grpc.sdf
TestResult.xml
coverage_results.xml
/TestResults
.vs/
*.nupkg

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit.Runners" version="2.6.4" />
<package id="OpenCover" version="4.6.166" />
<package id="ReportGenerator" version="2.3.2.0" />
</packages>

@ -137,7 +137,11 @@ namespace Grpc.IntegrationTesting
private async Task<ChannelCredentials> CreateCredentialsAsync()
{
var credentials = options.UseTls.Value ? TestCredentials.CreateTestClientCredentials(options.UseTestCa.Value) : ChannelCredentials.Insecure;
var credentials = ChannelCredentials.Insecure;
if (options.UseTls.Value)
{
credentials = options.UseTestCa.Value ? TestCredentials.CreateSslCredentials() : new SslCredentials();
}
if (options.TestCase == "jwt_token_creds")
{

@ -59,7 +59,7 @@ namespace Grpc.IntegrationTesting
server = new Server
{
Services = { TestService.BindService(new TestServiceImpl()) },
Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } }
Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
};
server.Start();
@ -68,7 +68,7 @@ namespace Grpc.IntegrationTesting
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
int port = server.Ports.Single().BoundPort;
channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options);
channel = new Channel(Host, port, TestCredentials.CreateSslCredentials(), options);
client = TestService.NewClient(channel);
}

@ -102,7 +102,7 @@ namespace Grpc.IntegrationTesting
int port = options.Port;
if (options.UseTls.Value)
{
server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials());
server.Ports.Add(host, port, TestCredentials.CreateSslServerCredentials());
}
else
{

@ -51,26 +51,15 @@ namespace Grpc.IntegrationTesting
public const string DefaultHostOverride = "foo.test.google.fr";
public const string ClientCertAuthorityPath = "data/ca.pem";
public const string ClientCertAuthorityEnvName = "SSL_CERT_FILE";
public const string ServerCertChainPath = "data/server1.pem";
public const string ServerPrivateKeyPath = "data/server1.key";
public static SslCredentials CreateTestClientCredentials(bool useTestCa)
public static SslCredentials CreateSslCredentials()
{
string caPath = ClientCertAuthorityPath;
if (!useTestCa)
{
caPath = Environment.GetEnvironmentVariable(ClientCertAuthorityEnvName);
if (string.IsNullOrEmpty(caPath))
{
throw new ArgumentException("CA path environment variable is not set.");
}
}
return new SslCredentials(File.ReadAllText(caPath));
return new SslCredentials(File.ReadAllText(ClientCertAuthorityPath));
}
public static SslServerCredentials CreateTestServerCredentials()
public static SslServerCredentials CreateSslServerCredentials()
{
var keyCertPair = new KeyCertificatePair(
File.ReadAllText(ServerCertChainPath),

@ -1,3 +1,4 @@
[![Nuget](https://img.shields.io/nuget/v/Grpc.svg)](http://www.nuget.org/packages/Grpc/)
gRPC C#
=======

@ -1,3 +1,4 @@
[![npm](https://img.shields.io/npm/v/grpc.svg)](https://www.npmjs.com/package/grpc)
# Node.js gRPC Library
## Status

@ -562,11 +562,11 @@ function runTest(address, host_override, test_case, tls, test_ca, done, extra) {
var ca_path;
if (test_ca) {
ca_path = path.join(__dirname, '../test/data/ca.pem');
var ca_data = fs.readFileSync(ca_path);
creds = grpc.credentials.createSsl(ca_data);
} else {
ca_path = process.env.SSL_CERT_FILE;
creds = grpc.credentials.createSsl();
}
var ca_data = fs.readFileSync(ca_path);
creds = grpc.credentials.createSsl(ca_data);
if (host_override) {
options['grpc.ssl_target_name_override'] = host_override;
options['grpc.default_authority'] = host_override;

@ -33,6 +33,17 @@
/**
* Client module
*
* This module contains the factory method for creating Client classes, and the
* method calling code for all types of methods.
*
* For example, to create a client and call a method on it:
*
* var proto_obj = grpc.load(proto_file_path);
* var Client = proto_obj.package.subpackage.ServiceName;
* var client = new Client(server_address, client_credentials);
* var call = client.unaryMethod(arguments, callback);
*
* @module
*/

@ -32,6 +32,8 @@
*/
/**
* This module contains functions that are common to client and server
* code. None of them should be used directly by gRPC users.
* @module
*/

@ -33,6 +33,15 @@
/**
* Metadata module
*
* This module defines the Metadata class, which represents header and trailer
* metadata for gRPC calls. Here is an example of how to use it:
*
* var metadata = new metadata_module.Metadata();
* metadata.set('key1', 'value1');
* metadata.add('key1', 'value2');
* metadata.get('key1') // returns ['value1', 'value2']
*
* @module
*/

@ -33,6 +33,17 @@
/**
* Server module
*
* This module contains all the server code for Node gRPC: both the Server
* class itself and the method handler code for all types of methods.
*
* For example, to create a Server, add a service, and start it:
*
* var server = new server_module.Server();
* server.addProtoService(protobuf_service_descriptor, service_implementation);
* server.bind('address:port', server_credential);
* server.start();
*
* @module
*/
@ -746,8 +757,8 @@ Server.prototype.addProtoService = function(service, implementation) {
* Binds the server to the given port, with SSL enabled if creds is given
* @param {string} port The port that the server should bind on, in the format
* "address:port"
* @param {boolean=} creds Server credential object to be used for SSL. Pass
* nothing for an insecure port
* @param {ServerCredentials=} creds Server credential object to be used for
* SSL. Pass an insecure credentials object for an insecure port.
*/
Server.prototype.bind = function(port, creds) {
if (this.started) {

@ -33,17 +33,19 @@
#import "GRPCCall.h"
// Helpers for setting and reading headers compatible with OAuth2.
/** Helpers for setting and reading headers compatible with OAuth2. */
@interface GRPCCall (OAuth2)
// Setting this property is equivalent to setting "Bearer <passed token>" as the value of the
// request header with key "authorization" (the authorization header). Setting it to nil removes the
// authorization header from the request.
// The value obtained by getting the property is the OAuth2 bearer token if the authorization header
// of the request has the form "Bearer <token>", or nil otherwise.
/**
* Setting this property is equivalent to setting "Bearer <passed token>" as the value of the
* request header with key "authorization" (the authorization header). Setting it to nil removes the
* authorization header from the request.
* The value obtained by getting the property is the OAuth2 bearer token if the authorization header
* of the request has the form "Bearer <token>", or nil otherwise.
*/
@property(atomic, copy) NSString *oauth2AccessToken;
// Returns the value (if any) of the "www-authenticate" response header (the challenge header).
/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */
@property(atomic, readonly) NSString *oauth2ChallengeHeader;
@end

@ -33,22 +33,28 @@
#import "GRPCCall.h"
// Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be
// used in releases, but are sometimes needed for testing.
/**
* Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be
* used in releases, but are sometimes needed for testing.
*/
@interface GRPCCall (Tests)
// Establish all SSL connections to the provided host using the passed SSL target name and the root
// certificates found in the file at |certsPath|.
//
// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
// more than one invocation of the methods of this category.
/**
* Establish all SSL connections to the provided host using the passed SSL target name and the root
* certificates found in the file at |certsPath|.
*
* Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
* more than one invocation of the methods of this category.
*/
+ (void)useTestCertsPath:(NSString *)certsPath
testName:(NSString *)testName
forHost:(NSString *)host;
// Establish all connections to the provided host using cleartext instead of SSL.
//
// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
// more than one invocation of the methods of this category.
/**
* Establish all connections to the provided host using cleartext instead of SSL.
*
* Must be called before any gRPC call to that host is made. It's illegal to pass the same host to
* more than one invocation of the methods of this category.
*/
+ (void)useInsecureConnectionsForHost:(NSString *)host;
@end

@ -40,6 +40,9 @@
+ (void)useTestCertsPath:(NSString *)certsPath
testName:(NSString *)testName
forHost:(NSString *)host {
if (!host || !certsPath || !testName) {
[NSException raise:NSInvalidArgumentException format:@"host, path and name must be provided."];
}
GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
hostConfig.pathToCertificates = certsPath;
hostConfig.hostNameOverride = testName;

@ -31,117 +31,145 @@
*
*/
// The gRPC protocol is an RPC protocol on top of HTTP2.
//
// While the most common type of RPC receives only one request message and returns only one response
// message, the protocol also supports RPCs that return multiple individual messages in a streaming
// fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and
// responses.
//
// Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of
// the "non-streaming type" sending only one message in the corresponding direction (the protocol
// doesn't make any distinction).
//
// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed
// transparently on the same TCP connection.
/**
* The gRPC protocol is an RPC protocol on top of HTTP2.
*
* While the most common type of RPC receives only one request message and returns only one response
* message, the protocol also supports RPCs that return multiple individual messages in a streaming
* fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and
* responses.
*
* Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of
* the "non-streaming type" sending only one message in the corresponding direction (the protocol
* doesn't make any distinction).
*
* Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed
* transparently on the same TCP connection.
*/
#import <Foundation/Foundation.h>
#import <RxLibrary/GRXWriter.h>
#pragma mark gRPC errors
// Domain of NSError objects produced by gRPC.
/** 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.
/**
* 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).
/** 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.
/**
* 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).
/**
* 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.
/**
* 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.
/** 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.
/** 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).
/**
* 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).
/**
* 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.
/** 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.
/**
* 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).
/**
* 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.
/**
* 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.
/** 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.
/**
* 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.
/**
* The server is currently unavailable. This is most likely a transient condition and may be
* corrected by retrying with a backoff.
*/
GRPCErrorCodeUnavailable = 14,
// Unrecoverable data loss or corruption.
/** 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.
/**
* Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by
* the server.
*/
extern id const kGRPCHeadersKey;
extern id const kGRPCTrailersKey;
#pragma mark GRPCCall
// 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.
/**
* 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.
*/
@protocol GRPCRequestHeaders <NSObject>
@property(nonatomic, readonly) NSUInteger count;
@ -154,53 +182,63 @@ extern id const kGRPCTrailersKey;
@end
// Represents a single gRPC remote call.
/** Represents a single gRPC remote call. */
@interface GRPCCall : GRXWriter
// 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.
/**
* 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) id<GRPCRequestHeaders> 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.
/**
* 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.
/**
* 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.
/**
* 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.
*/
- (instancetype)initWithHost:(NSString *)host
path:(NSString *)path
requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER;
// 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.
/**
* 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;
// TODO(jcanizales): Let specify a deadline. As a category of GRXWriter?

@ -35,12 +35,16 @@
struct grpc_channel;
// Each separate instance of this class represents at least one TCP connection to the provided host.
// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
/**
* Each separate instance of this class represents at least one TCP connection to the provided host.
* Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
*/
@interface GRPCChannel : NSObject
@property(nonatomic, readonly) struct grpc_channel *unmanagedChannel;
// This initializer takes ownership of the passed channel, and will destroy it when this object is
// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
/**
* This initializer takes ownership of the passed channel, and will destroy it when this object is
* deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
*/
- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER;
@end

@ -36,15 +36,17 @@
typedef void(^GRPCQueueCompletionHandler)(bool success);
// This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the
// |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for
// every |grpc_call_*| method that accepts a tag, you can pass a block of type
// |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is
// guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is
// passed a |bool| that tells if the operation was successful.
//
// Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to
// the |grpc_call| that's using it.
/**
* This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the
* |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for
* every |grpc_call_*| method that accepts a tag, you can pass a block of type
* |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is
* guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is
* passed a |bool| that tells if the operation was successful.
*
* Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to
* the |grpc_call| that's using it.
*/
@interface GRPCCompletionQueue : NSObject
@property(nonatomic, readonly) grpc_completion_queue *unmanagedQueue;

@ -40,18 +40,18 @@ struct grpc_call;
@property(nonatomic, readonly) NSString *address;
// The following properties should only be modified for testing:
/** The following properties should only be modified for testing: */
@property(nonatomic, getter=isSecure) BOOL secure;
@property(nonatomic, copy) NSString *pathToCertificates;
@property(nonatomic, copy) NSString *hostNameOverride;
// Host objects initialized with the same address are the same.
/** Host objects initialized with the same address are the same. */
+ (instancetype)hostWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
// Create a grpc_call object to the provided path on this host.
/** Create a grpc_call object to the provided path on this host. */
- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue;

@ -40,13 +40,15 @@ struct grpc_credentials;
@interface GRPCSecureChannel : GRPCChannel
- (instancetype)initWithHost:(NSString *)host;
// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
// pathToCertificates results in using the default root certificates distributed with the library.
/**
* Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
* pathToCertificates results in using the default root certificates distributed with the library.
*/
- (instancetype)initWithHost:(NSString *)host
pathToCertificates:(NSString *)path
hostNameOverride:(NSString *)hostNameOverride;
// The passed arguments aren't required to be valid beyond the invocation of this initializer.
/** The passed arguments aren't required to be valid beyond the invocation of this initializer. */
- (instancetype)initWithHost:(NSString *)host
credentials:(struct grpc_credentials *)credentials
args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER;

@ -39,7 +39,7 @@
@interface GRPCOperation : NSObject
@property(nonatomic, readonly) grpc_op op;
// Guaranteed to be called when the operation has finished.
/** Guaranteed to be called when the operation has finished. */
- (void)finish;
@end

@ -35,7 +35,9 @@
#include <grpc/grpc.h>
@interface NSError (GRPC)
// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
// and whose domain is |kGRPCErrorDomain|.
/**
* Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
* and whose domain is |kGRPCErrorDomain|.
*/
+ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details;
@end

@ -33,8 +33,10 @@
#import <Foundation/Foundation.h>
// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint
// can implement multiple services.
/**
* A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint
* can implement multiple services.
*/
@interface ProtoMethod : NSObject
@property(nonatomic, readonly) NSString *package;
@property(nonatomic, readonly) NSString *service;

@ -1,3 +1,4 @@
[![Cocoapods](https://img.shields.io/cocoapods/v/gRPC.svg)](https://cocoapods.org/pods/gRPC)
# gRPC for Objective-C
- [Install protoc with the gRPC plugin](#install)

@ -36,25 +36,27 @@
#import "GRXWriteable.h"
#import "GRXWriter.h"
// A buffered pipe is a Writer that also acts as a Writeable.
// Once it is started, whatever values are written into it (via -writeValue:) will be propagated
// immediately, unless flow control prevents it.
// If it is throttled and keeps receiving values, as well as if it receives values before being
// started, it will buffer them and propagate them in order as soon as its state becomes Started.
// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and
// propagate the error immediately.
//
// Beware that a pipe of this type can't prevent receiving more values when it is paused (for
// example if used to write data to a congested network connection). Because in such situations the
// pipe will keep buffering all data written to it, your application could run out of memory and
// crash. If you want to react to flow control signals to prevent that, instead of using this class
// you can implement an object that conforms to GRXWriter.
//
// Thread-safety:
// The methods of an object of this class should not be called concurrently from different threads.
/**
* A buffered pipe is a Writer that also acts as a Writeable.
* Once it is started, whatever values are written into it (via -writeValue:) will be propagated
* immediately, unless flow control prevents it.
* If it is throttled and keeps receiving values, as well as if it receives values before being
* started, it will buffer them and propagate them in order as soon as its state becomes Started.
* If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and
* propagate the error immediately.
*
* Beware that a pipe of this type can't prevent receiving more values when it is paused (for
* example if used to write data to a congested network connection). Because in such situations the
* pipe will keep buffering all data written to it, your application could run out of memory and
* crash. If you want to react to flow control signals to prevent that, instead of using this class
* you can implement an object that conforms to GRXWriter.
*
* Thread-safety:
* The methods of an object of this class should not be called concurrently from different threads.
*/
@interface GRXBufferedPipe : GRXWriter<GRXWriteable>
// Convenience constructor.
/** Convenience constructor. */
+ (instancetype)pipe;
@end

@ -36,36 +36,48 @@
#import "GRXWriter.h"
#import "GRXWriteable.h"
// This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a
// GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last
// message sent to it (no matter what messages are sent to the wrapper, in what order, nor from
// which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g.
// by the app cancelling the writes), no further messages are sent to the writeable except
// writesFinishedWithError:.
//
// TODO(jcanizales): Let the user specify another queue for the writeable callbacks.
/**
* This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a
* GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last
* message sent to it (no matter what messages are sent to the wrapper, in what order, nor from
* which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g.
* by the app cancelling the writes), no further messages are sent to the writeable except
* writesFinishedWithError:.
*
* TODO(jcanizales): Let the user specify another queue for the writeable callbacks.
*/
@interface GRXConcurrentWriteable : NSObject
// The GRXWriteable passed is the wrapped writeable.
// The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released
// after that.
/**
* The GRXWriteable passed is the wrapped writeable.
* The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released
* after that.
*/
- (instancetype)initWithWriteable:(id<GRXWriteable>)writeable NS_DESIGNATED_INITIALIZER;
// Enqueues writeValue: to be sent to the writeable in the main thread.
// The passed handler is invoked from the main thread after writeValue: returns.
/**
* Enqueues writeValue: to be sent to the writeable in the main thread.
* The passed handler is invoked from the main thread after writeValue: returns.
*/
- (void)enqueueValue:(id)value completionHandler:(void (^)())handler;
// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that
// message is sent to the writeable, all other methods of this object are effectively noops.
/**
* Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that
* message is sent to the writeable, all other methods of this object are effectively noops.
*/
- (void)enqueueSuccessfulCompletion;
// If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one
// to be sent to it in the main thread, and cancel all other pending messages to the writeable
// enqueued by this object (both past and future).
// The error argument cannot be nil.
/**
* If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one
* to be sent to it in the main thread, and cancel all other pending messages to the writeable
* enqueued by this object (both past and future).
* The error argument cannot be nil.
*/
- (void)cancelWithError:(NSError *)error;
// Cancels all pending messages to the writeable enqueued by this object (both past and future).
// Because the writeable won't receive writesFinishedWithError:, this also releases the writeable.
/**
* Cancels all pending messages to the writeable enqueued by this object (both past and future).
* Because the writeable won't receive writesFinishedWithError:, this also releases the writeable.
*/
- (void)cancelSilently;
@end

@ -33,17 +33,19 @@
#import "GRXWriter.h"
// A "proxy" class that simply forwards values, completion, and errors from its input writer to its
// writeable.
// It is useful as a superclass for pipes that act as a transformation of their
// input writer, and for classes that represent objects with input and
// output sequences of values, like an RPC.
//
// Thread-safety:
// All messages sent to this object need to be serialized. When it is started, the writer it wraps
// is started in the same thread. Manual state changes are propagated to the wrapped writer in the
// same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be
// serialized with any message sent to this object.
/**
* A "proxy" class that simply forwards values, completion, and errors from its input writer to its
* writeable.
* It is useful as a superclass for pipes that act as a transformation of their
* input writer, and for classes that represent objects with input and
* output sequences of values, like an RPC.
*
* Thread-safety:
* All messages sent to this object need to be serialized. When it is started, the writer it wraps
* is started in the same thread. Manual state changes are propagated to the wrapped writer in the
* same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be
* serialized with any message sent to this object.
*/
@interface GRXForwardingWriter : GRXWriter
- (instancetype)initWithWriter:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER;
@end

@ -35,46 +35,60 @@
#import "GRXWriter.h"
// Utility to construct GRXWriter instances from values that are immediately available when
// required.
//
// Thread-safety:
//
// An object of this class shouldn't be messaged concurrently by more than one thread. It will start
// messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the
// only place where the writer can be paused or stopped prematurely.
//
// If a paused writer of this class is resumed, it will start messaging the writeable, in the same
// thread, before |setState:| returns. Because the object can't be legally accessed concurrently,
// that's the only place where it can be paused again (or stopped).
/**
* Utility to construct GRXWriter instances from values that are immediately available when
* required.
*
* Thread-safety:
*
* An object of this class shouldn't be messaged concurrently by more than one thread. It will start
* messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the
* only place where the writer can be paused or stopped prematurely.
*
* If a paused writer of this class is resumed, it will start messaging the writeable, in the same
* thread, before |setState:| returns. Because the object can't be legally accessed concurrently,
* that's the only place where it can be paused again (or stopped).
*/
@interface GRXImmediateWriter : GRXWriter
// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
// its writeable. The NSEnumerator is released when it finishes.
/**
* Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
* its writeable. The NSEnumerator is released when it finishes.
*/
+ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator;
// Returns a writer that pushes to its writeable the successive values returned by the passed
// block. When the block first returns nil, it is released.
/**
* Returns a writer that pushes to its writeable the successive values returned by the passed
* block. When the block first returns nil, it is released.
*/
+ (GRXWriter *)writerWithValueSupplier:(id (^)())block;
// Returns a writer that iterates over the values of the passed container and pushes them to
// its writeable. The container is released when the iteration is over.
//
// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
// call one method per element. Because GRXWriteable instances accept values one by one, that speed
// gain doesn't happen here.
/**
* Returns a writer that iterates over the values of the passed container and pushes them to
* its writeable. The container is released when the iteration is over.
*
* Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
* call one method per element. Because GRXWriteable instances accept values one by one, that speed
* gain doesn't happen here.
*/
+ (GRXWriter *)writerWithContainer:(id<NSFastEnumeration>)container;
// Returns a writer that sends the passed value to its writeable and then finishes (releasing the
// value).
/**
* Returns a writer that sends the passed value to its writeable and then finishes (releasing the
* value).
*/
+ (GRXWriter *)writerWithValue:(id)value;
// Returns a writer that, as part of its start method, sends the passed error to the writeable
// (then releasing the error).
/**
* Returns a writer that, as part of its start method, sends the passed error to the writeable
* (then releasing the error).
*/
+ (GRXWriter *)writerWithError:(NSError *)error;
// Returns a writer that, as part of its start method, finishes immediately without sending any
// values to its writeable.
/**
* Returns a writer that, as part of its start method, finishes immediately without sending any
* values to its writeable.
*/
+ (GRXWriter *)emptyWriter;
@end

@ -33,16 +33,20 @@
#import <Foundation/Foundation.h>
// A GRXWriteable is an object to which a sequence of values can be sent. The
// sequence finishes with an optional error.
/**
* A GRXWriteable is an object to which a sequence of values can be sent. The
* sequence finishes with an optional error.
*/
@protocol GRXWriteable <NSObject>
// Push the next value of the sequence to the receiving object.
/** Push the next value of the sequence to the receiving object. */
- (void)writeValue:(id)value;
// Signal that the sequence is completed, or that an error ocurred. After this
// message is sent to the instance, neither it nor writeValue: may be
// called again.
/**
* Signal that the sequence is completed, or that an error ocurred. After this
* message is sent to the instance, neither it nor writeValue: may be
* called again.
*/
- (void)writesFinishedWithError:(NSError *)errorOrNil;
@end
@ -51,8 +55,10 @@ typedef void (^GRXCompletionHandler)(NSError *errorOrNil);
typedef void (^GRXSingleHandler)(id value, NSError *errorOrNil);
typedef void (^GRXEventHandler)(BOOL done, id value, NSError *error);
// Utility to create objects that conform to the GRXWriteable protocol, from
// blocks that handle each of the two methods of the protocol.
/**
* Utility to create objects that conform to the GRXWriteable protocol, from
* blocks that handle each of the two methods of the protocol.
*/
@interface GRXWriteable : NSObject<GRXWriteable>
+ (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler;

@ -35,32 +35,44 @@
@interface GRXWriter (Immediate)
// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
// its writeable. The NSEnumerator is released when it finishes.
/**
* Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to
* its writeable. The NSEnumerator is released when it finishes.
*/
+ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator;
// Returns a writer that pushes to its writeable the successive values returned by the passed
// block. When the block first returns nil, it is released.
/**
* Returns a writer that pushes to its writeable the successive values returned by the passed
* block. When the block first returns nil, it is released.
*/
+ (instancetype)writerWithValueSupplier:(id (^)())block;
// Returns a writer that iterates over the values of the passed container and pushes them to
// its writeable. The container is released when the iteration is over.
//
// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
// call one method per element. Because GRXWriteable instances accept values one by one, that speed
// gain doesn't happen here.
/**
* Returns a writer that iterates over the values of the passed container and pushes them to
* its writeable. The container is released when the iteration is over.
*
* Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to
* call one method per element. Because GRXWriteable instances accept values one by one, that speed
* gain doesn't happen here.
*/
+ (instancetype)writerWithContainer:(id<NSFastEnumeration>)container;
// Returns a writer that sends the passed value to its writeable and then finishes (releasing the
// value).
/**
* Returns a writer that sends the passed value to its writeable and then finishes (releasing the
* value).
*/
+ (instancetype)writerWithValue:(id)value;
// Returns a writer that, as part of its start method, sends the passed error to the writeable
// (then releasing the error).
/**
* Returns a writer that, as part of its start method, sends the passed error to the writeable
* (then releasing the error).
*/
+ (instancetype)writerWithError:(NSError *)error;
// Returns a writer that, as part of its start method, finishes immediately without sending any
// values to its writeable.
/**
* Returns a writer that, as part of its start method, finishes immediately without sending any
* values to its writeable.
*/
+ (instancetype)emptyWriter;
@end

@ -35,8 +35,10 @@
@interface GRXWriter (Transformations)
// Returns a writer that wraps the receiver, and has all the values the receiver would write
// transformed by the provided mapping function.
/**
* Returns a writer that wraps the receiver, and has all the values the receiver would write
* transformed by the provided mapping function.
*/
- (GRXWriter *)map:(id (^)(id value))map;
@end

@ -35,73 +35,87 @@
#import "GRXWriteable.h"
// States of a writer.
/** States of a writer. */
typedef NS_ENUM(NSInteger, GRXWriterState) {
// The writer has not yet been given a writeable to which it can push its values. To have a writer
// transition to the Started state, send it a startWithWriteable: message.
//
// A writer's state cannot be manually set to this value.
/**
* The writer has not yet been given a writeable to which it can push its values. To have a writer
* transition to the Started state, send it a startWithWriteable: message.
*
* A writer's state cannot be manually set to this value.
*/
GRXWriterStateNotStarted,
// The writer might push values to the writeable at any moment.
/** The writer might push values to the writeable at any moment. */
GRXWriterStateStarted,
// The writer is temporarily paused, and won't send any more values to the writeable unless its
// state is set back to Started. The writer might still transition to the Finished state at any
// moment, and is allowed to send writesFinishedWithError: to its writeable.
/**
* The writer is temporarily paused, and won't send any more values to the writeable unless its
* state is set back to Started. The writer might still transition to the Finished state at any
* moment, and is allowed to send writesFinishedWithError: to its writeable.
*/
GRXWriterStatePaused,
// The writer has released its writeable and won't interact with it anymore.
//
// One seldomly wants to set a writer's state to this value, as its writeable isn't notified with
// a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make
// it notify the writeable and then transition to this state.
/**
* The writer has released its writeable and won't interact with it anymore.
*
* One seldomly wants to set a writer's state to this value, as its writeable isn't notified with
* a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make
* it notify the writeable and then transition to this state.
*/
GRXWriterStateFinished
};
// An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced
// asynchronously, and it may consist of any number of elements, including none or an infinite
// number.
//
// GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the
// object plays an active or passive role during usage: A user of NSEnumerator pulls values off it,
// and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and
// the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to
// represent a sequence of future values, as well as collections with internal iteration.
//
// An instance of GRXWriter can start producing values after a writeable is passed to it. It can
// also be commanded to finish the sequence immediately (with an optional error). Finally, it can be
// asked to pause, and resumed later. All GRXWriter objects support pausing and early termination.
//
// Thread-safety:
//
// State transitions take immediate effect if the object is used from a single thread. Subclasses
// might offer stronger guarantees.
//
// Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a
// GRXWriter. I.e., conforming classes aren't required to be thread-safe.
/**
* An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced
* asynchronously, and it may consist of any number of elements, including none or an infinite
* number.
*
* GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the
* object plays an active or passive role during usage: A user of NSEnumerator pulls values off it,
* and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and
* the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to
* represent a sequence of future values, as well as collections with internal iteration.
*
* An instance of GRXWriter can start producing values after a writeable is passed to it. It can
* also be commanded to finish the sequence immediately (with an optional error). Finally, it can be
* asked to pause, and resumed later. All GRXWriter objects support pausing and early termination.
*
* Thread-safety:
*
* State transitions take immediate effect if the object is used from a single thread. Subclasses
* might offer stronger guarantees.
*
* Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a
* GRXWriter. I.e., conforming classes aren't required to be thread-safe.
*/
@interface GRXWriter : NSObject
// This property can be used to query the current state of the writer, which determines how it might
// currently use its writeable. Some state transitions can be triggered by setting this property to
// the corresponding value, and that's useful for advanced use cases like pausing an writer. For
// more details, see the documentation of the enum further down.
/**
* This property can be used to query the current state of the writer, which determines how it might
* currently use its writeable. Some state transitions can be triggered by setting this property to
* the corresponding value, and that's useful for advanced use cases like pausing an writer. For
* more details, see the documentation of the enum further down.
*/
@property(nonatomic) GRXWriterState state;
// Transition to the Started state, and start sending messages to the writeable (a reference to it
// is retained). Messages to the writeable may be sent before the method returns, or they may be
// sent later in the future. See GRXWriteable.h for the different messages a writeable can receive.
//
// If this writer draws its values from an external source (e.g. from the filesystem or from a
// server), calling this method will commonly trigger side effects (like network connections).
//
// This method might only be called on writers in the NotStarted state.
/**
* Transition to the Started state, and start sending messages to the writeable (a reference to it
* is retained). Messages to the writeable may be sent before the method returns, or they may be
* sent later in the future. See GRXWriteable.h for the different messages a writeable can receive.
*
* If this writer draws its values from an external source (e.g. from the filesystem or from a
* server), calling this method will commonly trigger side effects (like network connections).
*
* This method might only be called on writers in the NotStarted state.
*/
- (void)startWithWriteable:(id<GRXWriteable>)writeable;
// Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and
// transition to the Finished state.
//
// This method might only be called on writers in the Started or Paused state.
/**
* Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and
* transition to the Finished state.
*
* This method might only be called on writers in the Started or Paused state.
*/
- (void)finishWithError:(NSError *)errorOrNil;
@end

@ -35,17 +35,23 @@
@interface NSEnumerator (GRXUtil)
// Returns a NSEnumerator instance that iterates through the elements of the passed container that
// supports fast enumeration. Note that this negates the speed benefits of fast enumeration over
// NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the
// former, e.g. for iteration that needs to be paused and resumed later.
/**
* Returns a NSEnumerator instance that iterates through the elements of the passed container that
* supports fast enumeration. Note that this negates the speed benefits of fast enumeration over
* NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the
* former, e.g. for iteration that needs to be paused and resumed later.
*/
+ (NSEnumerator *)grx_enumeratorWithContainer:(id<NSFastEnumeration>)container;
// Returns a NSEnumerator instance that provides a single object before finishing. The value is then
// released.
/**
* Returns a NSEnumerator instance that provides a single object before finishing. The value is then
* released.
*/
+ (NSEnumerator *)grx_enumeratorWithSingleValue:(id)value;
// Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block.
// When the block first returns nil, it is released.
/**
* Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block.
* When the block first returns nil, it is released.
*/
+ (NSEnumerator *)grx_enumeratorWithValueSupplier:(id (^)())block;
@end

@ -33,10 +33,14 @@
#import <Foundation/Foundation.h>
// Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed
// on initialization.
/**
* Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed
* on initialization.
*/
@interface GRXNSBlockEnumerator : NSEnumerator
// The first time the passed block returns nil, the enumeration will end and the block will be
// released.
/**
* The first time the passed block returns nil, the enumeration will end and the block will be
* released.
*/
- (instancetype)initWithValueSupplier:(id (^)())block;
@end

@ -33,11 +33,15 @@
#import <Foundation/Foundation.h>
// This is a bridge to interact through NSEnumerator's interface with objects that only conform to
// NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any
// speed by using this instead of a NSEnumerator provided by your container).
/**
* This is a bridge to interact through NSEnumerator's interface with objects that only conform to
* NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any
* speed by using this instead of a NSEnumerator provided by your container).
*/
@interface GRXNSFastEnumerator : NSEnumerator
// After the iteration of the container (via the NSFastEnumeration protocol) is over, the container
// is released. If the container is modified during enumeration, an exception is thrown.
/**
* After the iteration of the container (via the NSFastEnumeration protocol) is over, the container
* is released. If the container is modified during enumeration, an exception is thrown.
*/
- (instancetype)initWithContainer:(id<NSFastEnumeration>)container;
@end

@ -33,9 +33,11 @@
#import <Foundation/Foundation.h>
// Concrete subclass of NSEnumerator whose instances return a single object before finishing.
/** Concrete subclass of NSEnumerator whose instances return a single object before finishing. */
@interface GRXNSScalarEnumerator : NSEnumerator
// Param value: the single object this instance will produce. After the first invocation of
// nextObject, the value is released.
/**
* Param value: the single object this instance will produce. After the first invocation of
* nextObject, the value is released.
*/
- (instancetype)initWithValue:(id)value;
@end

@ -33,7 +33,7 @@
#import "RxLibrary/GRXForwardingWriter.h"
// A "proxy" writer that transforms all the values of its input writer by using a mapping function.
/** A "proxy" writer that transforms all the values of its input writer by using a mapping function. */
@interface GRXMappingWriter : GRXForwardingWriter
- (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map
NS_DESIGNATED_INITIALIZER;

@ -0,0 +1,128 @@
#!/usr/bin/env python2.7
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Change comments style of source files from // to /** */"""
import re
import sys
if len(sys.argv) < 2:
print("Please provide at least one source file name as argument.")
sys.exit()
for file_name in sys.argv[1:]:
print("Modifying format of {file} comments in place...".format(
file=file_name,
))
# Input
with open(file_name, "r") as input_file:
lines = input_file.readlines()
def peek():
return lines[0]
def read_line():
return lines.pop(0)
def more_input_available():
return lines
# Output
output_lines = []
def write(line):
output_lines.append(line)
def flush_output():
with open(file_name, "w") as output_file:
for line in output_lines:
output_file.write(line)
# Pattern matching
comment_regex = r'^(\s*)//\s(.*)$'
def is_comment(line):
return re.search(comment_regex, line)
def isnt_comment(line):
return not is_comment(line)
def next_line(predicate):
return more_input_available() and predicate(peek())
# Transformation
def indentation_of(line):
match = re.search(comment_regex, line)
return match.group(1)
def content(line):
match = re.search(comment_regex, line)
return match.group(2)
def format_as_block(comment_block):
if len(comment_block) == 0:
return []
indent = indentation_of(comment_block[0])
if len(comment_block) == 1:
return [indent + "/** " + content(comment_block[0]) + " */\n"]
block = ["/**"] + [" * " + content(line) for line in comment_block] + [" */"]
return [indent + line.rstrip() + "\n" for line in block]
# Main algorithm
while more_input_available():
while next_line(isnt_comment):
write(read_line())
comment_block = []
# Get all lines in the same comment block. We could restrict the indentation
# to be the same as the first line of the block, but it's probably ok.
while (next_line(is_comment)):
comment_block.append(read_line())
for line in format_as_block(comment_block):
write(line)
flush_output()

@ -1,3 +1,4 @@
#!/bin/bash
# Copyright 2015, Google Inc.
# All rights reserved.
#
@ -27,4 +28,4 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
find . -type f -name "*.h" ! -path "*/Pods/*" ! -path "./generated_libraries/*" ! -path "./examples/*" ! -path "./tests/*" | xargs ./change-comments.py

@ -42,9 +42,6 @@
#import <RxLibrary/GRXWriteable.h>
#import <RxLibrary/GRXWriter+Immediate.h>
// These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall)
// rather than a generated proto library on top of it.
static NSString * const kHostAddress = @"localhost:5050";
static NSString * const kPackage = @"grpc.testing";
static NSString * const kService = @"TestService";
@ -53,11 +50,10 @@ static ProtoMethod *kInexistentMethod;
static ProtoMethod *kEmptyCallMethod;
static ProtoMethod *kUnaryCallMethod;
// This is an observer class for testing that responseMetadata is KVO-compliant
/** Observer class for testing that responseMetadata is KVO-compliant */
@interface PassthroughObserver : NSObject
- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback;
- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback
NS_DESIGNATED_INITIALIZER;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
context:(void *)context;
@ -67,23 +63,38 @@ static ProtoMethod *kUnaryCallMethod;
void (^_callback)(NSString*, id, NSDictionary*);
}
- (instancetype)init {
return [self initWithCallback:nil];
}
- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
self = [super init];
if (self) {
if (!callback) {
return nil;
}
if ((self = [super init])) {
_callback = callback;
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
_callback(keyPath, object, change);
[object removeObserver:self forKeyPath:keyPath];
}
@end
# pragma mark Tests
/**
* A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
* a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
*
* TODO(jcanizales): Run them also against a local SSL server and against a remote server.
*/
@interface GRPCClientTests : XCTestCase
@end
@ -180,6 +191,7 @@ static ProtoMethod *kUnaryCallMethod;
[self waitForExpectationsWithTimeout:8 handler:nil];
}
// TODO(jcanizales): Activate this test against the remote server.
- (void)testMetadata {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];

@ -33,11 +33,17 @@
#import <XCTest/XCTest.h>
// Implements tests as described here:
// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
/**
* Implements tests as described here:
* https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
*
* This is an abstract class that needs to be subclassed. See |+host|.
*/
@interface InteropTests : XCTestCase
// Returns @"grpc-test.sandbox.google.com".
// Override in a subclass to perform the same tests against a different address.
/**
* Host to send the RPCs to. The base implementation returns nil, which would make all tests to
* fail.
* Override in a subclass to perform these tests against a specific address.
*/
+ (NSString *)host;
@end

@ -78,21 +78,20 @@
#pragma mark Tests
static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
@implementation InteropTests {
RMTTestService *_service;
}
+ (NSString *)host {
return kRemoteSSLHost;
return nil;
}
- (void)setUp {
_service = [RMTTestService serviceWithHost:self.class.host];
_service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil;
}
- (void)testEmptyUnaryRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"];
RMTEmpty *request = [RMTEmpty message];
@ -110,6 +109,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testLargeUnaryRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
RMTSimpleRequest *request = [RMTSimpleRequest message];
@ -132,6 +132,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testClientStreamingRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"];
RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message];
@ -164,6 +165,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testServerStreamingRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"];
NSArray *expectedSizes = @[@31415, @9, @2653, @58979];
@ -200,6 +202,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testPingPongRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"];
NSArray *requests = @[@27182, @8, @1828, @45904];
@ -243,6 +246,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testEmptyStreamRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"];
[_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter]
eventHandler:^(BOOL done,
@ -256,6 +260,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testCancelAfterBeginRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"];
// A buffered pipe to which we never write any value acts as a writer that just hangs.
@ -273,6 +278,7 @@ static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
}
- (void)testCancelAfterFirstResponseRPC {
XCTAssertNotNil(self.class.host);
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterFirstResponse"];
// A buffered pipe to which we write a single value but never close

@ -31,15 +31,13 @@
*
*/
// Repeat of the tests in InteropTests.m, but sending the RPCs to a local cleartext server instead
// of the remote SSL one.
#import <GRPCClient/GRPCCall+Tests.h>
#import "InteropTests.h"
static NSString * const kLocalCleartextHost = @"localhost:5050";
/** Tests in InteropTests.m, sending the RPCs to a local cleartext server. */
@interface InteropTestsLocalCleartext : InteropTests
@end

@ -31,15 +31,13 @@
*
*/
// Repeat of the tests in InteropTests.m, but sending the RPCs to a local SSL server instead of the
// remote one.
#import <GRPCClient/GRPCCall+Tests.h>
#import "InteropTests.h"
static NSString * const kLocalSSLHost = @"localhost:5051";
/** Tests in InteropTests.m, sending the RPCs to a local SSL server. */
@interface InteropTestsLocalSSL : InteropTests
@end

@ -0,0 +1,50 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <GRPCClient/GRPCCall+Tests.h>
#import "InteropTests.h"
static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
/** Tests in InteropTests.m, sending the RPCs to a remote SSL server. */
@interface InteropTestsRemote : InteropTests
@end
@implementation InteropTestsRemote
+ (NSString *)host {
return kRemoteSSLHost;
}
@end

@ -1,164 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <GRPCClient/GRPCCall.h>
#import <ProtoRPC/ProtoMethod.h>
#import <RouteGuide/RouteGuide.pbobjc.h>
#import <RouteGuide/RouteGuide.pbrpc.h>
#import <RxLibrary/GRXWriteable.h>
#import <RxLibrary/GRXWriter+Immediate.h>
// These tests require a gRPC "RouteGuide" sample server to be running locally. You can compile and
// run one by following the instructions here: https://github.com/grpc/grpc/blob/master/examples/cpp/cpptutorial.md#try-it-out
// Be sure to have the C gRPC library installed in your system (for example, by having followed the
// instructions at https://github.com/grpc/homebrew-grpc
static NSString * const kRouteGuideHost = @"http://localhost:50051";
static NSString * const kPackage = @"routeguide";
static NSString * const kService = @"RouteGuide";
@interface LocalClearTextTests : XCTestCase
@end
@implementation LocalClearTextTests
// This test currently fails: see Issue #1907.
//- (void)testConnectionToLocalServer {
// __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
//
// // This method isn't implemented by the local server.
// GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage
// interface:kService
// method:@"EmptyCall"];
//
// GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]];
//
// GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
// method:method
// requestsWriter:requestsWriter];
//
// id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
// XCTFail(@"Received unexpected response: %@", value);
// } completionHandler:^(NSError *errorOrNil) {
// XCTAssertNotNil(errorOrNil, @"Finished without error!");
// XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
// [expectation fulfill];
// }];
//
// [call startWithWriteable:responsesWriteable];
//
// [self waitForExpectationsWithTimeout:8.0 handler:nil];
//}
- (void)testEmptyRPC {
__weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
__weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
service:kService
method:@"RecordRoute"];
GRXWriter *requestsWriter = [GRXWriter emptyWriter];
GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
path:method.HTTPPath
requestsWriter:requestsWriter];
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTAssertNotNil(value, @"nil value received as response.");
XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
[response fulfill];
} completionHandler:^(NSError *errorOrNil) {
XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
[completion fulfill];
}];
[call startWithWriteable:responsesWriteable];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
}
- (void)testSimpleProtoRPC {
__weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."];
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
service:kService
method:@"GetFeature"];
RGDPoint *point = [RGDPoint message];
point.latitude = 28E7;
point.longitude = -15E7;
GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]];
GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
path:method.HTTPPath
requestsWriter:requestsWriter];
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTAssertNotNil(value, @"nil value received as response.");
RGDFeature *feature = [RGDFeature parseFromData:value error:NULL];
XCTAssertEqualObjects(point, feature.location);
XCTAssertNotNil(feature.name, @"Response's name is nil.");
[response fulfill];
} completionHandler:^(NSError *errorOrNil) {
XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
[completion fulfill];
}];
[call startWithWriteable:responsesWriteable];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
}
- (void)testSimpleProtoRPCUsingGeneratedService {
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
RGDPoint *point = [RGDPoint message];
point.latitude = 28E7;
point.longitude = -15E7;
RGDRouteGuide *service = [[RGDRouteGuide alloc] initWithHost:kRouteGuideHost];
[service getFeatureWithRequest:point handler:^(RGDFeature *response, NSError *error) {
XCTAssertNil(error, @"Finished with unexpected error: %@", error);
XCTAssertEqualObjects(point, response.location);
XCTAssertNotNil(response.name, @"Response's name is nil.");
[completion fulfill];
}];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
}
@end

@ -6,10 +6,26 @@ pod 'gRPC', :path => "../../.."
pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient"
pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient"
link_with 'AllTests'
link_with 'AllTests',
'RxLibraryUnitTests',
'InteropTests',
'InteropTestsLocalSSL',
'InteropTestsLocalCleartext'
target 'Tests' do
end
target 'AllTests' do
end
target 'RxLibraryUnitTests' do
end
target 'InteropTestsRemote' do
end
target 'InteropTestsLocalSSL' do
end
target 'InteropTestsLocalCleartext' do
end

@ -7,16 +7,33 @@
objects = {
/* Begin PBXBuildFile section */
036D953EE34B1FD523647ACD /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
50267643BA114A2A724D4FDF /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; };
63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */; };
63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; };
63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; };
635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; };
635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; };
63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; };
6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; settings = {ASSET_TAGS = (); }; };
63DC84181BE15179000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
63DC84281BE15267000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
63DC84391BE15294000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
63DC84481BE152B5000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; settings = {ASSET_TAGS = (); }; };
63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; settings = {ASSET_TAGS = (); }; };
63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; settings = {ASSET_TAGS = (); }; };
63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; };
63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; };
7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -27,6 +44,34 @@
remoteGlobalIDString = 635697C61B14FC11007A7283;
remoteInfo = Tests;
};
63DC84191BE15179000708E8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 635697C61B14FC11007A7283;
remoteInfo = Tests;
};
63DC84291BE15267000708E8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 635697C61B14FC11007A7283;
remoteInfo = Tests;
};
63DC843A1BE15294000708E8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 635697C61B14FC11007A7283;
remoteInfo = Tests;
};
63DC84491BE152B5000708E8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 635697C61B14FC11007A7283;
remoteInfo = Tests;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@ -45,7 +90,6 @@
0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = "<group>"; };
63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalClearTextTests.m; sourceTree = "<group>"; };
63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = "<group>"; };
635697C71B14FC11007A7283 /* libTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTests.a; sourceTree = BUILT_PRODUCTS_DIR; };
@ -53,6 +97,11 @@
635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = "<group>"; };
63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalCleartext.m; sourceTree = "<group>"; };
6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsRemote.m; sourceTree = "<group>"; };
63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxLibraryUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsRemote.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalSSL.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalCleartext.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; };
63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; };
63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; };
@ -76,6 +125,42 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84101BE15179000708E8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
63DC84181BE15179000708E8 /* libTests.a in Frameworks */,
036D953EE34B1FD523647ACD /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84201BE15267000708E8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
63DC84281BE15267000708E8 /* libTests.a in Frameworks */,
DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84311BE15294000708E8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
63DC84391BE15294000708E8 /* libTests.a in Frameworks */,
08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84401BE152B5000708E8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
63DC84481BE152B5000708E8 /* libTests.a in Frameworks */,
50267643BA114A2A724D4FDF /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -112,6 +197,10 @@
children = (
635697C71B14FC11007A7283 /* libTests.a */,
63423F441B150A5F006CF63C /* AllTests.xctest */,
63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */,
63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */,
63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */,
63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -122,10 +211,10 @@
6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */,
63E240CC1B6C4D3A005F3B0E /* InteropTests.h */,
635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */,
6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */,
63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */,
63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */,
63423F501B151B77006CF63C /* RxLibraryUnitTests.m */,
63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */,
635697CC1B14FC11007A7283 /* Tests.m */,
635697D71B14FC11007A7283 /* Supporting Files */,
);
@ -152,6 +241,7 @@
63423F411B150A5F006CF63C /* Frameworks */,
63423F421B150A5F006CF63C /* Resources */,
A441F71824DCB9D0CA297748 /* Copy Pods Resources */,
5F14F59509E10C2852014F9E /* Embed Pods Frameworks */,
);
buildRules = (
);
@ -180,6 +270,90 @@
productReference = 635697C71B14FC11007A7283 /* libTests.a */;
productType = "com.apple.product-type.library.static";
};
63DC84121BE15179000708E8 /* RxLibraryUnitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */;
buildPhases = (
B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */,
63DC840F1BE15179000708E8 /* Sources */,
63DC84101BE15179000708E8 /* Frameworks */,
63DC84111BE15179000708E8 /* Resources */,
4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */,
C977426A8727267BBAC7D48E /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
63DC841A1BE15179000708E8 /* PBXTargetDependency */,
);
name = RxLibraryUnitTests;
productName = RxLibraryUnitTests;
productReference = 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
63DC84221BE15267000708E8 /* InteropTestsRemote */ = {
isa = PBXNativeTarget;
buildConfigurationList = 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */;
buildPhases = (
4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */,
63DC841F1BE15267000708E8 /* Sources */,
63DC84201BE15267000708E8 /* Frameworks */,
63DC84211BE15267000708E8 /* Resources */,
900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */,
C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
63DC842A1BE15267000708E8 /* PBXTargetDependency */,
);
name = InteropTestsRemote;
productName = InteropTests;
productReference = 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
63DC84331BE15294000708E8 /* InteropTestsLocalSSL */ = {
isa = PBXNativeTarget;
buildConfigurationList = 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */;
buildPhases = (
5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */,
63DC84301BE15294000708E8 /* Sources */,
63DC84311BE15294000708E8 /* Frameworks */,
63DC84321BE15294000708E8 /* Resources */,
C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */,
693DD0B453431D64EA24FD66 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
63DC843B1BE15294000708E8 /* PBXTargetDependency */,
);
name = InteropTestsLocalSSL;
productName = InteropTestsLocalSSL;
productReference = 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */ = {
isa = PBXNativeTarget;
buildConfigurationList = 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */;
buildPhases = (
7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */,
63DC843F1BE152B5000708E8 /* Sources */,
63DC84401BE152B5000708E8 /* Frameworks */,
63DC84411BE152B5000708E8 /* Resources */,
A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */,
8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
63DC844A1BE152B5000708E8 /* PBXTargetDependency */,
);
name = InteropTestsLocalCleartext;
productName = InteropTestsLocalCleartext;
productReference = 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -195,6 +369,18 @@
635697C61B14FC11007A7283 = {
CreatedOnToolsVersion = 6.3.1;
};
63DC84121BE15179000708E8 = {
CreatedOnToolsVersion = 7.0.1;
};
63DC84221BE15267000708E8 = {
CreatedOnToolsVersion = 7.0.1;
};
63DC84331BE15294000708E8 = {
CreatedOnToolsVersion = 7.0.1;
};
63DC84421BE152B5000708E8 = {
CreatedOnToolsVersion = 7.0.1;
};
};
};
buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */;
@ -211,6 +397,10 @@
targets = (
635697C61B14FC11007A7283 /* Tests */,
63423F431B150A5F006CF63C /* AllTests */,
63DC84121BE15179000708E8 /* RxLibraryUnitTests */,
63DC84221BE15267000708E8 /* InteropTestsRemote */,
63DC84331BE15294000708E8 /* InteropTestsLocalSSL */,
63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */,
);
};
/* End PBXProject section */
@ -224,9 +414,158 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84111BE15179000708E8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84211BE15267000708E8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84321BE15294000708E8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84411BE152B5000708E8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
5F14F59509E10C2852014F9E /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
693DD0B453431D64EA24FD66 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -257,6 +596,81 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C977426A8727267BBAC7D48E /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -265,11 +679,11 @@
buildActionMask = 2147483647;
files = (
63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */,
63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */,
63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */,
6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */,
63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */,
6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */,
635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */,
63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -281,6 +695,42 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC840F1BE15179000708E8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC841F1BE15267000708E8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */,
6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC84301BE15294000708E8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */,
6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
63DC843F1BE152B5000708E8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */,
63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */,
6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@ -289,6 +739,26 @@
target = 635697C61B14FC11007A7283 /* Tests */;
targetProxy = 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */;
};
63DC841A1BE15179000708E8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 635697C61B14FC11007A7283 /* Tests */;
targetProxy = 63DC84191BE15179000708E8 /* PBXContainerItemProxy */;
};
63DC842A1BE15267000708E8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 635697C61B14FC11007A7283 /* Tests */;
targetProxy = 63DC84291BE15267000708E8 /* PBXContainerItemProxy */;
};
63DC843B1BE15294000708E8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 635697C61B14FC11007A7283 /* Tests */;
targetProxy = 63DC843A1BE15294000708E8 /* PBXContainerItemProxy */;
};
63DC844A1BE152B5000708E8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 635697C61B14FC11007A7283 /* Tests */;
targetProxy = 63DC84491BE152B5000708E8 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@ -418,6 +888,110 @@
};
name = Release;
};
63DC841C1BE15179000708E8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
63DC841D1BE15179000708E8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
63DC842C1BE15267000708E8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
63DC842D1BE15267000708E8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
63DC843D1BE15294000708E8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
63DC843E1BE15294000708E8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
63DC844C1BE152B5000708E8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
63DC844D1BE152B5000708E8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -448,6 +1022,42 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63DC841C1BE15179000708E8 /* Debug */,
63DC841D1BE15179000708E8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63DC842C1BE15267000708E8 /* Debug */,
63DC842D1BE15267000708E8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63DC843D1BE15294000708E8 /* Debug */,
63DC843E1BE15294000708E8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */ = {
isa = XCConfigurationList;
buildConfigurations = (
63DC844C1BE152B5000708E8 /* Debug */,
63DC844D1BE152B5000708E8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 635697BF1B14FC11007A7283 /* Project object */;

@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@ -44,6 +44,9 @@
<Test
Identifier = "GRPCClientTests/testMetadata">
</Test>
<Test
Identifier = "InteropTests">
</Test>
<Test
Identifier = "LocalClearTextTests">
</Test>
@ -62,15 +65,18 @@
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@ -85,10 +91,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84421BE152B5000708E8"
BuildableName = "InteropTestsLocalCleartext.xctest"
BlueprintName = "InteropTestsLocalCleartext"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84421BE152B5000708E8"
BuildableName = "InteropTestsLocalCleartext.xctest"
BlueprintName = "InteropTestsLocalCleartext"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "GRPCClientTests/testConnectionToRemoteServer">
</Test>
<Test
Identifier = "GRPCClientTests/testMetadata">
</Test>
<Test
Identifier = "InteropTests">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84421BE152B5000708E8"
BuildableName = "InteropTestsLocalCleartext.xctest"
BlueprintName = "InteropTestsLocalCleartext"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84421BE152B5000708E8"
BuildableName = "InteropTestsLocalCleartext.xctest"
BlueprintName = "InteropTestsLocalCleartext"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84331BE15294000708E8"
BuildableName = "InteropTestsLocalSSL.xctest"
BlueprintName = "InteropTestsLocalSSL"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84331BE15294000708E8"
BuildableName = "InteropTestsLocalSSL.xctest"
BlueprintName = "InteropTestsLocalSSL"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "InteropTests">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84331BE15294000708E8"
BuildableName = "InteropTestsLocalSSL.xctest"
BlueprintName = "InteropTestsLocalSSL"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84331BE15294000708E8"
BuildableName = "InteropTestsLocalSSL.xctest"
BlueprintName = "InteropTestsLocalSSL"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84221BE15267000708E8"
BuildableName = "InteropTestsRemote.xctest"
BlueprintName = "InteropTestsRemote"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84221BE15267000708E8"
BuildableName = "InteropTestsRemote.xctest"
BlueprintName = "InteropTestsRemote"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "InteropTests">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84221BE15267000708E8"
BuildableName = "InteropTestsRemote.xctest"
BlueprintName = "InteropTestsRemote"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84221BE15267000708E8"
BuildableName = "InteropTestsRemote.xctest"
BlueprintName = "InteropTestsRemote"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84121BE15179000708E8"
BuildableName = "RxLibraryUnitTests.xctest"
BlueprintName = "RxLibraryUnitTests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84121BE15179000708E8"
BuildableName = "RxLibraryUnitTests.xctest"
BlueprintName = "RxLibraryUnitTests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84121BE15179000708E8"
BuildableName = "RxLibraryUnitTests.xctest"
BlueprintName = "RxLibraryUnitTests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63DC84121BE15179000708E8"
BuildableName = "RxLibraryUnitTests.xctest"
BlueprintName = "RxLibraryUnitTests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -1,3 +1,4 @@
#!/bin/bash
# Copyright 2015, Google Inc.
# All rights reserved.
#
@ -27,4 +28,13 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
command -v php-cs-fixer > /dev/null || {
echo "Cannot find php-cs-fixer. Exiting..."
exit 1
}
cd $(dirname $0)/..
php-cs-fixer fix lib/Grpc || true
php-cs-fixer fix tests/generated_code || true
php-cs-fixer fix tests/interop || true
php-cs-fixer fix tests/unit_tests || true

@ -111,21 +111,21 @@ PHP_METHOD(Credentials, createDefault) {
* @return Credentials The new SSL credentials object
*/
PHP_METHOD(Credentials, createSsl) {
char *pem_root_certs;
char *pem_root_certs = NULL;
grpc_ssl_pem_key_cert_pair pem_key_cert_pair;
int root_certs_length, private_key_length = 0, cert_chain_length = 0;
int root_certs_length = 0, private_key_length = 0, cert_chain_length = 0;
pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL;
/* "s|s!s! == 1 string, 2 optional nullable strings */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!",
/* "|s!s!s! == 3 optional nullable strings */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!s!",
&pem_root_certs, &root_certs_length,
&pem_key_cert_pair.private_key, &private_key_length,
&pem_key_cert_pair.cert_chain,
&cert_chain_length) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"createSsl expects 1 to 3 strings", 1 TSRMLS_CC);
"createSsl expects 3 optional strings", 1 TSRMLS_CC);
return;
}
grpc_credentials *creds = grpc_ssl_credentials_create(

@ -31,65 +31,79 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
abstract class AbstractCall {
abstract class AbstractCall
{
protected $call;
protected $deserialize;
protected $metadata;
protected $call;
protected $deserialize;
protected $metadata;
/**
* Create a new Call wrapper object.
*
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the
* remote server
* @param callback $deserialize A callback function to deserialize
* the response
* @param (optional) long $timeout Timeout in microseconds
*/
public function __construct(Channel $channel,
$method,
$deserialize,
$timeout = false)
{
if ($timeout) {
$now = Timeval::now();
$delta = new Timeval($timeout);
$deadline = $now->add($delta);
} else {
$deadline = Timeval::infFuture();
}
$this->call = new Call($channel, $method, $deadline);
$this->deserialize = $deserialize;
$this->metadata = null;
}
/**
* Create a new Call wrapper object.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param callback $deserialize A callback function to deserialize
* the response
* @param (optional) long $timeout Timeout in microseconds
*/
public function __construct(Channel $channel, $method, $deserialize, $timeout = false) {
if ($timeout) {
$now = Timeval::now();
$delta = new Timeval($timeout);
$deadline = $now->add($delta);
} else {
$deadline = Timeval::infFuture();
/**
* @return The metadata sent by the server.
*/
public function getMetadata()
{
return $this->metadata;
}
$this->call = new Call($channel, $method, $deadline);
$this->deserialize = $deserialize;
$this->metadata = null;
}
/**
* @return The metadata sent by the server.
*/
public function getMetadata() {
return $this->metadata;
}
/**
* @return string The URI of the endpoint.
*/
public function getPeer()
{
return $this->call->getPeer();
}
/**
* @return string The URI of the endpoint.
*/
public function getPeer() {
return $this->call->getPeer();
}
/**
* Cancels the call.
*/
public function cancel()
{
$this->call->cancel();
}
/**
* Cancels the call
*/
public function cancel() {
$this->call->cancel();
}
/**
* Deserialize a response value to an object.
*
* @param string $value The binary value to deserialize
*
* @return The deserialized value
*/
protected function deserializeResponse($value)
{
if ($value === null) {
return;
}
/**
* Deserialize a response value to an object.
* @param string $value The binary value to deserialize
* @return The deserialized value
*/
protected function deserializeResponse($value) {
if ($value === null) {
return null;
return call_user_func($this->deserialize, $value);
}
return call_user_func($this->deserialize, $value);
}
}

@ -31,255 +31,308 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
/**
* Base class for generated client stubs. Stub methods are expected to call
* _simpleRequest or _streamRequest and return the result.
*/
class BaseStub {
class BaseStub
{
private $hostname;
private $channel;
private $hostname;
private $channel;
// a callback function
private $update_metadata;
// a callback function
private $update_metadata;
/**
* @param $hostname string
* @param $opts array
* - 'update_metadata': (optional) a callback function which takes in a
* metadata array, and returns an updated metadata array
*/
public function __construct($hostname, $opts)
{
$this->hostname = $hostname;
$this->update_metadata = null;
if (isset($opts['update_metadata'])) {
if (is_callable($opts['update_metadata'])) {
$this->update_metadata = $opts['update_metadata'];
}
unset($opts['update_metadata']);
}
$package_config = json_decode(
file_get_contents(dirname(__FILE__).'/../../composer.json'), true);
$opts['grpc.primary_user_agent'] =
'grpc-php/'.$package_config['version'];
$this->channel = new Channel($hostname, $opts);
}
/**
* @param $hostname string
* @param $opts array
* - 'update_metadata': (optional) a callback function which takes in a
* metadata array, and returns an updated metadata array
*/
public function __construct($hostname, $opts) {
$this->hostname = $hostname;
$this->update_metadata = null;
if (isset($opts['update_metadata'])) {
if (is_callable($opts['update_metadata'])) {
$this->update_metadata = $opts['update_metadata'];
}
unset($opts['update_metadata']);
/**
* @return string The URI of the endpoint.
*/
public function getTarget()
{
return $this->channel->getTarget();
}
$package_config = json_decode(
file_get_contents(dirname(__FILE__) . '/../../composer.json'), true);
$opts['grpc.primary_user_agent'] =
'grpc-php/' . $package_config['version'];
$this->channel = new Channel($hostname, $opts);
}
/**
* @return string The URI of the endpoint.
*/
public function getTarget() {
return $this->channel->getTarget();
}
/**
* @param $try_to_connect bool
*
* @return int The grpc connectivity state
*/
public function getConnectivityState($try_to_connect = false)
{
return $this->channel->getConnectivityState($try_to_connect);
}
/**
* @param $try_to_connect bool
* @return int The grpc connectivity state
*/
public function getConnectivityState($try_to_connect = false) {
return $this->channel->getConnectivityState($try_to_connect);
}
/**
* @param $timeout in microseconds
*
* @return bool true if channel is ready
* @throw Exception if channel is in FATAL_ERROR state
*/
public function waitForReady($timeout)
{
$new_state = $this->getConnectivityState(true);
if ($this->_checkConnectivityState($new_state)) {
return true;
}
/**
* @param $timeout in microseconds
* @return bool true if channel is ready
* @throw Exception if channel is in FATAL_ERROR state
*/
public function waitForReady($timeout) {
$new_state = $this->getConnectivityState(true);
if ($this->_checkConnectivityState($new_state)) {
return true;
}
$now = Timeval::now();
$delta = new Timeval($timeout);
$deadline = $now->add($delta);
$now = Timeval::now();
$delta = new Timeval($timeout);
$deadline = $now->add($delta);
while ($this->channel->watchConnectivityState($new_state, $deadline)) {
// state has changed before deadline
$new_state = $this->getConnectivityState();
if ($this->_checkConnectivityState($new_state)) {
return true;
}
}
// deadline has passed
$new_state = $this->getConnectivityState();
while ($this->channel->watchConnectivityState($new_state, $deadline)) {
// state has changed before deadline
$new_state = $this->getConnectivityState();
if ($this->_checkConnectivityState($new_state)) {
return true;
}
return $this->_checkConnectivityState($new_state);
}
// deadline has passed
$new_state = $this->getConnectivityState();
return $this->_checkConnectivityState($new_state);
}
private function _checkConnectivityState($new_state) {
if ($new_state == \Grpc\CHANNEL_READY) {
return true;
private function _checkConnectivityState($new_state)
{
if ($new_state == \Grpc\CHANNEL_READY) {
return true;
}
if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
throw new \Exception('Failed to connect to server');
}
return false;
}
if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
// @codeCoverageIgnoreStart
throw new \Exception('Failed to connect to server');
// @codeCoverageIgnoreEnd
/**
* Close the communication channel associated with this stub.
*/
public function close()
{
$this->channel->close();
}
return false;
}
/**
* Close the communication channel associated with this stub
*/
public function close() {
$this->channel->close();
}
/**
* constructs the auth uri for the jwt.
*/
private function _get_jwt_aud_uri($method)
{
$last_slash_idx = strrpos($method, '/');
if ($last_slash_idx === false) {
throw new \InvalidArgumentException(
'service name must have a slash');
}
$service_name = substr($method, 0, $last_slash_idx);
/**
* constructs the auth uri for the jwt
*/
private function _get_jwt_aud_uri($method) {
$last_slash_idx = strrpos($method, '/');
if ($last_slash_idx === false) {
throw new \InvalidArgumentException('service name must have a slash');
return 'https://'.$this->hostname.$service_name;
}
$service_name = substr($method, 0, $last_slash_idx);
return "https://" . $this->hostname . $service_name;
}
/**
* extract $timeout from $metadata
* @param $metadata The metadata map
* @return list($metadata_copy, $timeout)
*/
private function _extract_timeout_from_metadata($metadata) {
$timeout = false;
$metadata_copy = $metadata;
if (isset($metadata['timeout'])) {
$timeout = $metadata['timeout'];
unset($metadata_copy['timeout']);
/**
* extract $timeout from $metadata.
*
* @param $metadata The metadata map
*
* @return list($metadata_copy, $timeout)
*/
private function _extract_timeout_from_metadata($metadata)
{
$timeout = false;
$metadata_copy = $metadata;
if (isset($metadata['timeout'])) {
$timeout = $metadata['timeout'];
unset($metadata_copy['timeout']);
}
return [$metadata_copy, $timeout];
}
return array($metadata_copy, $timeout);
}
/**
* validate and normalize the metadata array
* @param $metadata The metadata map
* @return $metadata Validated and key-normalized metadata map
* @throw InvalidArgumentException if key contains invalid characters
*/
private function _validate_and_normalize_metadata($metadata) {
$metadata_copy = array();
foreach ($metadata as $key => $value) {
if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
throw new \InvalidArgumentException(
'Metadata keys must be nonempty strings containing only '.
'alphanumeric characters, hyphens and underscores');
}
$metadata_copy[strtolower($key)] = $value;
/**
* validate and normalize the metadata array.
*
* @param $metadata The metadata map
*
* @return $metadata Validated and key-normalized metadata map
* @throw InvalidArgumentException if key contains invalid characters
*/
private function _validate_and_normalize_metadata($metadata)
{
$metadata_copy = [];
foreach ($metadata as $key => $value) {
if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
throw new \InvalidArgumentException(
'Metadata keys must be nonempty strings containing only '.
'alphanumeric characters, hyphens and underscores');
}
$metadata_copy[strtolower($key)] = $value;
}
return $metadata_copy;
}
return $metadata_copy;
}
/* This class is intended to be subclassed by generated code, so all functions
begin with "_" to avoid name collisions. */
/* This class is intended to be subclassed by generated code, so
* all functions begin with "_" to avoid name collisions. */
/**
* Call a remote method that takes a single argument and has a single output
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
* @return SimpleSurfaceActiveCall The active call object
*/
public function _simpleRequest($method,
$argument,
callable $deserialize,
$metadata = array(),
$options = array()) {
list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata);
$call = new UnaryCall($this->channel, $method, $deserialize, $timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
/**
* Call a remote method that takes a single argument and has a
* single output.
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
*
* @return SimpleSurfaceActiveCall The active call object
*/
public function _simpleRequest($method,
$argument,
callable $deserialize,
$metadata = [],
$options = [])
{
list($actual_metadata, $timeout) =
$this->_extract_timeout_from_metadata($metadata);
$call = new UnaryCall($this->channel,
$method,
$deserialize,
$timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
$actual_metadata,
$jwt_aud_uri);
}
$actual_metadata = $this->_validate_and_normalize_metadata(
$actual_metadata);
$call->start($argument, $actual_metadata, $options);
return $call;
}
$actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
$call->start($argument, $actual_metadata, $options);
return $call;
}
/**
* Call a remote method that takes a stream of arguments and has a single
* output
*
* @param string $method The name of the method to call
* @param $arguments An array or Traversable of arguments to stream to the
* server
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
* @return ClientStreamingSurfaceActiveCall The active call object
*/
public function _clientStreamRequest($method,
callable $deserialize,
$metadata = array()) {
list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata);
$call = new ClientStreamingCall($this->channel, $method, $deserialize, $timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
/**
* Call a remote method that takes a stream of arguments and has a single
* output.
*
* @param string $method The name of the method to call
* @param $arguments An array or Traversable of arguments to stream to the
* server
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
*
* @return ClientStreamingSurfaceActiveCall The active call object
*/
public function _clientStreamRequest($method,
callable $deserialize,
$metadata = [])
{
list($actual_metadata, $timeout) =
$this->_extract_timeout_from_metadata($metadata);
$call = new ClientStreamingCall($this->channel,
$method,
$deserialize,
$timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
$actual_metadata,
$jwt_aud_uri);
}
$actual_metadata = $this->_validate_and_normalize_metadata(
$actual_metadata);
$call->start($actual_metadata);
return $call;
}
$actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
$call->start($actual_metadata);
return $call;
}
/**
* Call a remote method that takes a single argument and returns a stream of
* responses
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
* @return ServerStreamingSurfaceActiveCall The active call object
*/
public function _serverStreamRequest($method,
$argument,
callable $deserialize,
$metadata = array(),
$options = array()) {
list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata);
$call = new ServerStreamingCall($this->channel, $method, $deserialize, $timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
/**
* Call a remote method that takes a single argument and returns a stream of
* responses.
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
*
* @return ServerStreamingSurfaceActiveCall The active call object
*/
public function _serverStreamRequest($method,
$argument,
callable $deserialize,
$metadata = [],
$options = [])
{
list($actual_metadata, $timeout) =
$this->_extract_timeout_from_metadata($metadata);
$call = new ServerStreamingCall($this->channel,
$method,
$deserialize,
$timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
$actual_metadata,
$jwt_aud_uri);
}
$actual_metadata = $this->_validate_and_normalize_metadata(
$actual_metadata);
$call->start($argument, $actual_metadata, $options);
return $call;
}
$actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
$call->start($argument, $actual_metadata, $options);
return $call;
}
/**
* Call a remote method with messages streaming in both directions
*
* @param string $method The name of the method to call
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
* @return BidiStreamingSurfaceActiveCall The active call object
*/
public function _bidiRequest($method,
callable $deserialize,
$metadata = array()) {
list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata);
$call = new BidiStreamingCall($this->channel, $method, $deserialize, $timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
/**
* Call a remote method with messages streaming in both directions.
*
* @param string $method The name of the method to call
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
*
* @return BidiStreamingSurfaceActiveCall The active call object
*/
public function _bidiRequest($method,
callable $deserialize,
$metadata = [])
{
list($actual_metadata, $timeout) =
$this->_extract_timeout_from_metadata($metadata);
$call = new BidiStreamingCall($this->channel,
$method,
$deserialize,
$timeout);
$jwt_aud_uri = $this->_get_jwt_aud_uri($method);
if (is_callable($this->update_metadata)) {
$actual_metadata = call_user_func($this->update_metadata,
$actual_metadata,
$jwt_aud_uri);
}
$actual_metadata = $this->_validate_and_normalize_metadata(
$actual_metadata);
$call->start($actual_metadata);
return $call;
}
$actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
$call->start($actual_metadata);
return $call;
}
}

@ -31,68 +31,87 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
/**
* Represents an active call that allows for sending and recieving messages in
* streams in any order.
*/
class BidiStreamingCall extends AbstractCall {
/**
* Start the call
* @param array $metadata Metadata to send with the call, if applicable
*/
public function start($metadata = array()) {
$this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]);
}
class BidiStreamingCall extends AbstractCall
{
/**
* Start the call.
*
* @param array $metadata Metadata to send with the call, if applicable
*/
public function start($metadata = [])
{
$this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
]);
}
/**
* Reads the next value from the server.
*
* @return The next value from the server, or null if there is none
*/
public function read()
{
$batch = [OP_RECV_MESSAGE => true];
if ($this->metadata === null) {
$batch[OP_RECV_INITIAL_METADATA] = true;
}
$read_event = $this->call->startBatch($batch);
if ($this->metadata === null) {
$this->metadata = $read_event->metadata;
}
/**
* Reads the next value from the server.
* @return The next value from the server, or null if there is none
*/
public function read() {
$batch = [OP_RECV_MESSAGE => true];
if ($this->metadata === null) {
$batch[OP_RECV_INITIAL_METADATA] = true;
return $this->deserializeResponse($read_event->message);
}
$read_event = $this->call->startBatch($batch);
if ($this->metadata === null) {
$this->metadata = $read_event->metadata;
/**
* Write a single message to the server. This cannot be called after
* writesDone is called.
*
* @param ByteBuffer $data The data to write
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function write($data, $options = [])
{
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
}
$this->call->startBatch([
OP_SEND_MESSAGE => $message_array,
]);
}
return $this->deserializeResponse($read_event->message);
}
/**
* Write a single message to the server. This cannot be called after
* writesDone is called.
* @param ByteBuffer $data The data to write
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function write($data, $options = array()) {
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
/**
* Indicate that no more writes will be sent.
*/
public function writesDone()
{
$this->call->startBatch([
OP_SEND_CLOSE_FROM_CLIENT => true,
]);
}
$this->call->startBatch([OP_SEND_MESSAGE => $message_array]);
}
/**
* Indicate that no more writes will be sent.
*/
public function writesDone() {
$this->call->startBatch([OP_SEND_CLOSE_FROM_CLIENT => true]);
}
/**
* Wait for the server to send the status, and return it.
*
* @return object The status object, with integer $code, string $details,
* and array $metadata members
*/
public function getStatus()
{
$status_event = $this->call->startBatch([
OP_RECV_STATUS_ON_CLIENT => true,
]);
/**
* Wait for the server to send the status, and return it.
* @return object The status object, with integer $code, string $details,
* and array $metadata members
*/
public function getStatus() {
$status_event = $this->call->startBatch([
OP_RECV_STATUS_ON_CLIENT => true
]);
return $status_event->status;
}
}
return $status_event->status;
}
}

@ -31,47 +31,61 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
/**
* Represents an active call that sends a stream of messages and then gets a
* single response.
*/
class ClientStreamingCall extends AbstractCall {
/**
* Start the call.
* @param array $metadata Metadata to send with the call, if applicable
*/
public function start($metadata = array()) {
$this->call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]);
}
class ClientStreamingCall extends AbstractCall
{
/**
* Start the call.
*
* @param array $metadata Metadata to send with the call, if applicable
*/
public function start($metadata = [])
{
$this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
]);
}
/**
* Write a single message to the server. This cannot be called after
* wait is called.
* @param ByteBuffer $data The data to write
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function write($data, $options = array()) {
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
/**
* Write a single message to the server. This cannot be called after
* wait is called.
*
* @param ByteBuffer $data The data to write
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function write($data, $options = [])
{
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
}
$this->call->startBatch([
OP_SEND_MESSAGE => $message_array,
]);
}
$this->call->startBatch([OP_SEND_MESSAGE => $message_array]);
}
/**
* Wait for the server to respond with data and a status
* @return [response data, status]
*/
public function wait() {
$event = $this->call->startBatch([
OP_SEND_CLOSE_FROM_CLIENT => true,
OP_RECV_INITIAL_METADATA => true,
OP_RECV_MESSAGE => true,
OP_RECV_STATUS_ON_CLIENT => true]);
$this->metadata = $event->metadata;
return array($this->deserializeResponse($event->message), $event->status);
}
}
/**
* Wait for the server to respond with data and a status.
*
* @return [response data, status]
*/
public function wait()
{
$event = $this->call->startBatch([
OP_SEND_CLOSE_FROM_CLIENT => true,
OP_RECV_INITIAL_METADATA => true,
OP_RECV_MESSAGE => true,
OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->metadata = $event->metadata;
return [$this->deserializeResponse($event->message), $event->status];
}
}

@ -31,53 +31,66 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
/**
* Represents an active call that sends a single message and then gets a stream
* of reponses
* of reponses.
*/
class ServerStreamingCall extends AbstractCall {
/**
* Start the call
* @param $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function start($data, $metadata = array(), $options = array()) {
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
class ServerStreamingCall extends AbstractCall
{
/**
* Start the call.
*
* @param $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function start($data, $metadata = [], $options = [])
{
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
}
$event = $this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
OP_RECV_INITIAL_METADATA => true,
OP_SEND_MESSAGE => $message_array,
OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->metadata = $event->metadata;
}
$event = $this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
OP_RECV_INITIAL_METADATA => true,
OP_SEND_MESSAGE => $message_array,
OP_SEND_CLOSE_FROM_CLIENT => true]);
$this->metadata = $event->metadata;
}
/**
* @return An iterator of response values
*/
public function responses() {
$response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message;
while($response !== null) {
yield $this->deserializeResponse($response);
$response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message;
/**
* @return An iterator of response values
*/
public function responses()
{
$response = $this->call->startBatch([
OP_RECV_MESSAGE => true,
])->message;
while ($response !== null) {
yield $this->deserializeResponse($response);
$response = $this->call->startBatch([
OP_RECV_MESSAGE => true,
])->message;
}
}
}
/**
* Wait for the server to send the status, and return it.
* @return object The status object, with integer $code, string $details,
* and array $metadata members
*/
public function getStatus() {
$status_event = $this->call->startBatch([
OP_RECV_STATUS_ON_CLIENT => true
]);
return $status_event->status;
}
/**
* Wait for the server to send the status, and return it.
*
* @return object The status object, with integer $code, string $details,
* and array $metadata members
*/
public function getStatus()
{
$status_event = $this->call->startBatch([
OP_RECV_STATUS_ON_CLIENT => true,
]);
return $status_event->status;
}
}

@ -31,41 +31,50 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace Grpc;
/**
* Represents an active call that sends a single message and then gets a single
* response.
*/
class UnaryCall extends AbstractCall {
/**
* Start the call
* @param $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function start($data, $metadata = array(), $options = array()) {
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
class UnaryCall extends AbstractCall
{
/**
* Start the call.
*
* @param $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* @param array $options an array of options, possible keys:
* 'flags' => a number
*/
public function start($data, $metadata = [], $options = [])
{
$message_array = ['message' => $data->serialize()];
if (isset($options['flags'])) {
$message_array['flags'] = $options['flags'];
}
$event = $this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
OP_RECV_INITIAL_METADATA => true,
OP_SEND_MESSAGE => $message_array,
OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->metadata = $event->metadata;
}
$event = $this->call->startBatch([
OP_SEND_INITIAL_METADATA => $metadata,
OP_RECV_INITIAL_METADATA => true,
OP_SEND_MESSAGE => $message_array,
OP_SEND_CLOSE_FROM_CLIENT => true]);
$this->metadata = $event->metadata;
}
/**
* Wait for the server to respond with data and a status
* @return [response data, status]
*/
public function wait() {
$event = $this->call->startBatch([
OP_RECV_MESSAGE => true,
OP_RECV_STATUS_ON_CLIENT => true]);
return array($this->deserializeResponse($event->message), $event->status);
}
/**
* Wait for the server to respond with data and a status.
*
* @return [response data, status]
*/
public function wait()
{
$event = $this->call->startBatch([
OP_RECV_MESSAGE => true,
OP_RECV_STATUS_ON_CLIENT => true,
]);
return [$this->deserializeResponse($event->message), $event->status];
}
}

@ -31,184 +31,212 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
require_once dirname(__FILE__) . '/math.php';
abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase {
/* These tests require that a server exporting the math service must be
* running on $GRPC_TEST_HOST */
protected static $client;
protected static $timeout;
public function testWaitForNotReady() {
$this->assertFalse(self::$client->waitForReady(1));
}
public function testWaitForReady() {
$this->assertTrue(self::$client->waitForReady(250000));
}
public function testAlreadyReady() {
$this->assertTrue(self::$client->waitForReady(250000));
$this->assertTrue(self::$client->waitForReady(100));
}
public function testGetTarget() {
$this->assertTrue(is_string(self::$client->getTarget()));
}
/**
* @expectedException InvalidArgumentException
*/
public function testClose() {
self::$client->close();
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMetadata() {
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg, array(' ' => 'abc123'));
}
public function testGetCallMetadata() {
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
$this->assertTrue(is_array($call->getMetadata()));
}
public function testTimeout() {
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg, array('timeout' => 100));
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code);
}
public function testCancel() {
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
$call->cancel();
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_CANCELLED, $status->code);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMethodName() {
$invalid_client = new DummyInvalidClient('host', array());
$div_arg = new math\DivArgs();
$invalid_client->InvalidUnaryCall($div_arg);
}
public function testWriteFlags() {
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call = self::$client->Div($div_arg, array(), array('flags' => Grpc\WRITE_NO_COMPRESS));
$this->assertTrue(is_string($call->getPeer()));
list($response, $status) = $call->wait();
$this->assertSame(1, $response->getQuotient());
$this->assertSame(3, $response->getRemainder());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsServerStreaming() {
$fib_arg = new math\FibArgs();
$fib_arg->setLimit(7);
$call = self::$client->Fib($fib_arg, array(), array('flags' => Grpc\WRITE_NO_COMPRESS));
$result_array = iterator_to_array($call->responses());
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsClientStreaming() {
$call = self::$client->Sum();
$num = new math\Num();
$num->setNum(1);
$call->write($num, array('flags' => Grpc\WRITE_NO_COMPRESS));
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsBidiStreaming() {
$call = self::$client->DivMany();
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call->write($div_arg, array('flags' => Grpc\WRITE_NO_COMPRESS));
$response = $call->read();
$call->writesDone();
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testSimpleRequest() {
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call = self::$client->Div($div_arg);
$this->assertTrue(is_string($call->getPeer()));
list($response, $status) = $call->wait();
$this->assertSame(1, $response->getQuotient());
$this->assertSame(3, $response->getRemainder());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testServerStreaming() {
$fib_arg = new math\FibArgs();
$fib_arg->setLimit(7);
$call = self::$client->Fib($fib_arg);
$this->assertTrue(is_string($call->getPeer()));
$result_array = iterator_to_array($call->responses());
$extract_num = function($num){
return $num->getNum();
};
$values = array_map($extract_num, $result_array);
$this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testClientStreaming() {
$call = self::$client->Sum();
$this->assertTrue(is_string($call->getPeer()));
for ($i = 0; $i < 7; $i++) {
$num = new math\Num();
$num->setNum($i);
$call->write($num);
}
list($response, $status) = $call->wait();
$this->assertSame(21, $response->getNum());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testBidiStreaming() {
$call = self::$client->DivMany();
$this->assertTrue(is_string($call->getPeer()));
for ($i = 0; $i < 7; $i++) {
$div_arg = new math\DivArgs();
$div_arg->setDividend(2 * $i + 1);
$div_arg->setDivisor(2);
$call->write($div_arg);
$response = $call->read();
$this->assertSame($i, $response->getQuotient());
$this->assertSame(1, $response->getRemainder());
}
$call->writesDone();
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
require_once dirname(__FILE__).'/math.php';
abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase
{
/**
* These tests require that a server exporting the math service must be
* running on $GRPC_TEST_HOST.
*/
protected static $client;
protected static $timeout;
public function testWaitForNotReady()
{
$this->assertFalse(self::$client->waitForReady(1));
}
public function testWaitForReady()
{
$this->assertTrue(self::$client->waitForReady(250000));
}
public function testAlreadyReady()
{
$this->assertTrue(self::$client->waitForReady(250000));
$this->assertTrue(self::$client->waitForReady(100));
}
public function testGetTarget()
{
$this->assertTrue(is_string(self::$client->getTarget()));
}
/**
* @expectedException InvalidArgumentException
*/
public function testClose()
{
self::$client->close();
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMetadata()
{
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg, [' ' => 'abc123']);
}
public function testGetCallMetadata()
{
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
$this->assertTrue(is_array($call->getMetadata()));
}
public function testTimeout()
{
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg, ['timeout' => 100]);
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code);
}
public function testCancel()
{
$div_arg = new math\DivArgs();
$call = self::$client->Div($div_arg);
$call->cancel();
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_CANCELLED, $status->code);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidMethodName()
{
$invalid_client = new DummyInvalidClient('host', []);
$div_arg = new math\DivArgs();
$invalid_client->InvalidUnaryCall($div_arg);
}
public function testWriteFlags()
{
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call = self::$client->Div($div_arg, [],
['flags' => Grpc\WRITE_NO_COMPRESS]);
$this->assertTrue(is_string($call->getPeer()));
list($response, $status) = $call->wait();
$this->assertSame(1, $response->getQuotient());
$this->assertSame(3, $response->getRemainder());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsServerStreaming()
{
$fib_arg = new math\FibArgs();
$fib_arg->setLimit(7);
$call = self::$client->Fib($fib_arg, [],
['flags' => Grpc\WRITE_NO_COMPRESS]);
$result_array = iterator_to_array($call->responses());
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsClientStreaming()
{
$call = self::$client->Sum();
$num = new math\Num();
$num->setNum(1);
$call->write($num, ['flags' => Grpc\WRITE_NO_COMPRESS]);
list($response, $status) = $call->wait();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testWriteFlagsBidiStreaming()
{
$call = self::$client->DivMany();
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call->write($div_arg, ['flags' => Grpc\WRITE_NO_COMPRESS]);
$response = $call->read();
$call->writesDone();
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testSimpleRequest()
{
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
$call = self::$client->Div($div_arg);
$this->assertTrue(is_string($call->getPeer()));
list($response, $status) = $call->wait();
$this->assertSame(1, $response->getQuotient());
$this->assertSame(3, $response->getRemainder());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testServerStreaming()
{
$fib_arg = new math\FibArgs();
$fib_arg->setLimit(7);
$call = self::$client->Fib($fib_arg);
$this->assertTrue(is_string($call->getPeer()));
$result_array = iterator_to_array($call->responses());
$extract_num = function ($num) {
return $num->getNum();
};
$values = array_map($extract_num, $result_array);
$this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testClientStreaming()
{
$call = self::$client->Sum();
$this->assertTrue(is_string($call->getPeer()));
for ($i = 0; $i < 7; ++$i) {
$num = new math\Num();
$num->setNum($i);
$call->write($num);
}
list($response, $status) = $call->wait();
$this->assertSame(21, $response->getNum());
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
public function testBidiStreaming()
{
$call = self::$client->DivMany();
$this->assertTrue(is_string($call->getPeer()));
for ($i = 0; $i < 7; ++$i) {
$div_arg = new math\DivArgs();
$div_arg->setDividend(2 * $i + 1);
$div_arg->setDivisor(2);
$call->write($div_arg);
$response = $call->read();
$this->assertSame($i, $response->getQuotient());
$this->assertSame(1, $response->getRemainder());
}
$call->writesDone();
$status = $call->getStatus();
$this->assertSame(\Grpc\STATUS_OK, $status->code);
}
}
class DummyInvalidClient extends \Grpc\BaseStub {
public function InvalidUnaryCall(\math\DivArgs $argument,
$metadata = array(),
$options = array()) {
return $this->_simpleRequest('invalidMethodName', $argument,
function() {}, $metadata, $options);
}
class DummyInvalidClient extends \Grpc\BaseStub
{
public function InvalidUnaryCall(\math\DivArgs $argument,
$metadata = [],
$options = [])
{
return $this->_simpleRequest('invalidMethodName',
$argument,
function () {},
$metadata,
$options);
}
}

@ -31,15 +31,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once dirname(__FILE__) . '/AbstractGeneratedCodeTest.php';
require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php';
class GeneratedCodeTest extends AbstractGeneratedCodeTest {
public function setUp() {
self::$client = new math\MathClient(
class GeneratedCodeTest extends AbstractGeneratedCodeTest
{
public function setUp()
{
self::$client = new math\MathClient(
getenv('GRPC_TEST_HOST'), []);
}
}
public static function tearDownAfterClass() {
self::$client->close();
}
public static function tearDownAfterClass()
{
self::$client->close();
}
}

@ -31,21 +31,25 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once dirname(__FILE__) . '/AbstractGeneratedCodeTest.php';
require_once dirname(__FILE__).'/AbstractGeneratedCodeTest.php';
class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest {
public function setUp() {
self::$client = new math\MathClient(
getenv('GRPC_TEST_HOST'), ['update_metadata' =>
function($a_hash,
$client = array()) {
$a_copy = $a_hash;
$a_copy['foo'] = ['bar'];
return $a_copy;
}]);
}
class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest
{
public function setUp()
{
self::$client = new math\MathClient(
getenv('GRPC_TEST_HOST'),
['update_metadata' => function ($a_hash,
$client = []) {
$a_copy = $a_hash;
$a_copy['foo'] = ['bar'];
public static function tearDownAfterClass() {
self::$client->close();
}
return $a_copy;
}]);
}
public static function tearDownAfterClass()
{
self::$client->close();
}
}

@ -36,31 +36,32 @@
include 'vendor/autoload.php';
include 'tests/generated_code/math.php';
function p($line) {
print("$line<br/>\n");
function p($line)
{
print("$line<br/>\n");
}
$host = "localhost:50051";
$host = 'localhost:50051';
p("Connecting to host: $host");
$client = new math\MathClient($host, []);
p("Client class: ".get_class($client));
p('Client class: '.get_class($client));
p('');
p("Running unary call test:");
p('Running unary call test:');
$dividend = 7;
$divisor = 4;
$div_arg = new math\DivArgs();
$div_arg->setDividend($dividend);
$div_arg->setDivisor($divisor);
$call = $client->Div($div_arg);
p("Call peer: ".$call->getPeer());
p('Call peer: '.$call->getPeer());
p("Dividing $dividend by $divisor");
list($response, $status) = $call->wait();
p("quotient = ".$response->getQuotient());
p("remainder = ".$response->getRemainder());
p('quotient = '.$response->getQuotient());
p('remainder = '.$response->getRemainder());
p('');
p("Running server streaming test:");
p('Running server streaming test:');
$limit = 7;
$fib_arg = new math\FibArgs();
$fib_arg->setLimit($limit);
@ -68,35 +69,35 @@ $call = $client->Fib($fib_arg);
$result_array = iterator_to_array($call->responses());
$result = '';
foreach ($result_array as $num) {
$result .= ' '.$num->getNum();
$result .= ' '.$num->getNum();
}
p("The first $limit Fibonacci numbers are:".$result);
p('');
p("Running client streaming test:");
p('Running client streaming test:');
$call = $client->Sum();
for ($i = 0; $i <= $limit; $i++) {
$num = new math\Num();
$num->setNum($i);
$call->write($num);
for ($i = 0; $i <= $limit; ++$i) {
$num = new math\Num();
$num->setNum($i);
$call->write($num);
}
list($response, $status) = $call->wait();
p(sprintf("The first %d positive integers sum to: %d",
p(sprintf('The first %d positive integers sum to: %d',
$limit, $response->getNum()));
p('');
p("Running bidi-streaming test:");
p('Running bidi-streaming test:');
$call = $client->DivMany();
for ($i = 0; $i < 7; $i++) {
$div_arg = new math\DivArgs();
$dividend = 2 * $i + 1;
$divisor = 3;
$div_arg->setDividend($dividend);
$div_arg->setDivisor($divisor);
$call->write($div_arg);
p("client writing: $dividend / $divisor");
$response = $call->read();
p(sprintf("server writing: quotient = %d, remainder = %d",
for ($i = 0; $i < 7; ++$i) {
$div_arg = new math\DivArgs();
$dividend = 2 * $i + 1;
$divisor = 3;
$div_arg->setDividend($dividend);
$div_arg->setDivisor($divisor);
$call->write($div_arg);
p("client writing: $dividend / $divisor");
$response = $call->read();
p(sprintf('server writing: quotient = %d, remainder = %d',
$response->getQuotient(), $response->getRemainder()));
}
$call->writesDone();

@ -31,9 +31,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once realpath(dirname(__FILE__) . '/../../vendor/autoload.php');
require_once realpath(dirname(__FILE__).'/../../vendor/autoload.php');
require 'empty.php';
require 'message_set.php';
require 'messages.php';
require 'test.php';
use Google\Auth\CredentialsLoader;
@ -42,395 +41,427 @@ use GuzzleHttp\ClientInterface;
/**
* Assertion function that always exits with an error code if the assertion is
* falsy
* falsy.
*
* @param $value Assertion value. Should be true.
* @param $error_message Message to display if the assertion is false
*/
function hardAssert($value, $error_message) {
if (!$value) {
echo $error_message . "\n";
exit(1);
}
function hardAssert($value, $error_message)
{
if (!$value) {
echo $error_message."\n";
exit(1);
}
}
/**
* Run the empty_unary test.
*
* @param $stub Stub object that has service methods
*/
function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call completed with a null response');
function emptyUnary($stub)
{
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call completed with a null response');
}
/**
* Run the large_unary test.
*
* @param $stub Stub object that has service methods
*/
function largeUnary($stub) {
performLargeUnary($stub);
function largeUnary($stub)
{
performLargeUnary($stub);
}
/**
* Shared code between large unary test and auth test
* Shared code between large unary test and auth test.
*
* @param $stub Stub object that has service methods
* @param $fillUsername boolean whether to fill result with username
* @param $fillOauthScope boolean whether to fill result with oauth scope
*/
function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false,
$metadata = array()) {
$request_len = 271828;
$response_len = 314159;
$request = new grpc\testing\SimpleRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$request->setResponseSize($response_len);
$payload = new grpc\testing\Payload();
$payload->setType(grpc\testing\PayloadType::COMPRESSABLE);
$payload->setBody(str_repeat("\0", $request_len));
$request->setPayload($payload);
$request->setFillUsername($fillUsername);
$request->setFillOauthScope($fillOauthScope);
list($result, $status) = $stub->UnaryCall($request, $metadata)->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
$metadata = [])
{
$request_len = 271828;
$response_len = 314159;
$request = new grpc\testing\SimpleRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$request->setResponseSize($response_len);
$payload = new grpc\testing\Payload();
$payload->setType(grpc\testing\PayloadType::COMPRESSABLE);
$payload->setBody(str_repeat("\0", $request_len));
$request->setPayload($payload);
$request->setFillUsername($fillUsername);
$request->setFillOauthScope($fillOauthScope);
list($result, $status) = $stub->UnaryCall($request, $metadata)->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload had the wrong type');
hardAssert(strlen($payload->getBody()) === $response_len,
hardAssert(strlen($payload->getBody()) === $response_len,
'Payload had the wrong length');
hardAssert($payload->getBody() === str_repeat("\0", $response_len),
hardAssert($payload->getBody() === str_repeat("\0", $response_len),
'Payload had the wrong content');
return $result;
return $result;
}
/**
* Run the service account credentials auth test.
*
* @param $stub Stub object that has service methods
* @param $args array command line args
*/
function serviceAccountCreds($stub, $args) {
if (!array_key_exists('oauth_scope', $args)) {
throw new Exception('Missing oauth scope');
}
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
function serviceAccountCreds($stub, $args)
{
if (!array_key_exists('oauth_scope', $args)) {
throw new Exception('Missing oauth scope');
}
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
'invalid email returned');
hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false,
hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false,
'invalid oauth scope returned');
}
/**
* Run the compute engine credentials auth test.
* Has not been run from gcloud as of 2015-05-05
* Has not been run from gcloud as of 2015-05-05.
*
* @param $stub Stub object that has service methods
* @param $args array command line args
*/
function computeEngineCreds($stub, $args) {
if (!array_key_exists('oauth_scope', $args)) {
throw new Exception('Missing oauth scope');
}
if (!array_key_exists('default_service_account', $args)) {
throw new Exception('Missing default_service_account');
}
$result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
hardAssert($args['default_service_account'] == $result->getUsername(),
function computeEngineCreds($stub, $args)
{
if (!array_key_exists('oauth_scope', $args)) {
throw new Exception('Missing oauth scope');
}
if (!array_key_exists('default_service_account', $args)) {
throw new Exception('Missing default_service_account');
}
$result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
hardAssert($args['default_service_account'] == $result->getUsername(),
'invalid email returned');
}
/**
* Run the jwt token credentials auth test.
*
* @param $stub Stub object that has service methods
* @param $args array command line args
*/
function jwtTokenCreds($stub, $args) {
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
function jwtTokenCreds($stub, $args)
{
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
'invalid email returned');
}
/**
* Run the oauth2_auth_token auth test.
*
* @param $stub Stub object that has service methods
* @param $args array command line args
*/
function oauth2AuthToken($stub, $args) {
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
function oauth2AuthToken($stub, $args)
{
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true);
hardAssert($result->getUsername() == $jsonKey['client_email'],
'invalid email returned');
}
/**
* Run the per_rpc_creds auth test.
*
* @param $stub Stub object that has service methods
* @param $args array command line args
*/
function perRpcCreds($stub, $args) {
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
$token = $auth_credentials->fetchAuthToken();
$metadata = array(CredentialsLoader::AUTH_METADATA_KEY =>
array(sprintf("%s %s",
$token['token_type'],
$token['access_token'])));
$result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true,
function perRpcCreds($stub, $args)
{
$jsonKey = json_decode(
file_get_contents(getenv(CredentialsLoader::ENV_VAR)),
true);
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
$token = $auth_credentials->fetchAuthToken();
$metadata = [CredentialsLoader::AUTH_METADATA_KEY => [sprintf('%s %s',
$token['token_type'],
$token['access_token'])]];
$result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true,
$metadata);
hardAssert($result->getUsername() == $jsonKey['client_email'],
hardAssert($result->getUsername() == $jsonKey['client_email'],
'invalid email returned');
}
/**
* Run the client_streaming test.
*
* @param $stub Stub object that has service methods
*/
function clientStreaming($stub) {
$request_lengths = array(27182, 8, 1828, 45904);
$requests = array_map(
function($length) {
$request = new grpc\testing\StreamingInputCallRequest();
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $length));
$request->setPayload($payload);
return $request;
}, $request_lengths);
$call = $stub->StreamingInputCall();
foreach ($requests as $request) {
$call->write($request);
}
list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() === 74922,
function clientStreaming($stub)
{
$request_lengths = [27182, 8, 1828, 45904];
$requests = array_map(
function ($length) {
$request = new grpc\testing\StreamingInputCallRequest();
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $length));
$request->setPayload($payload);
return $request;
}, $request_lengths);
$call = $stub->StreamingInputCall();
foreach ($requests as $request) {
$call->write($request);
}
list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() === 74922,
'aggregated_payload_size was incorrect');
}
/**
* Run the server_streaming test.
*
* @param $stub Stub object that has service methods.
*/
function serverStreaming($stub) {
$sizes = array(31415, 9, 2653, 58979);
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
foreach($sizes as $size) {
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($size);
$request->addResponseParameters($response_parameters);
}
function serverStreaming($stub)
{
$sizes = [31415, 9, 2653, 58979];
$call = $stub->StreamingOutputCall($request);
$i = 0;
foreach($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) === $sizes[$i],
'Response ' . $i . ' had the wrong length');
$i += 1;
}
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
foreach ($sizes as $size) {
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($size);
$request->addResponseParameters($response_parameters);
}
$call = $stub->StreamingOutputCall($request);
$i = 0;
foreach ($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload '.$i.' had the wrong type');
hardAssert(strlen($payload->getBody()) === $sizes[$i],
'Response '.$i.' had the wrong length');
$i += 1;
}
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
}
/**
* Run the ping_pong test.
*
* @param $stub Stub object that has service methods.
*/
function pingPong($stub) {
$request_lengths = array(27182, 8, 1828, 45904);
$response_lengths = array(31415, 9, 2653, 58979);
$call = $stub->FullDuplexCall();
for($i = 0; $i < 4; $i++) {
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($response_lengths[$i]);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $request_lengths[$i]));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
function pingPong($stub)
{
$request_lengths = [27182, 8, 1828, 45904];
$response_lengths = [31415, 9, 2653, 58979];
$call = $stub->FullDuplexCall();
for ($i = 0; $i < 4; ++$i) {
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($response_lengths[$i]);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $request_lengths[$i]));
$request->setPayload($payload);
hardAssert($response !== null, 'Server returned too few responses');
$payload = $response->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
'Payload ' . $i . ' had the wrong length');
}
$call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
$call->write($request);
$response = $call->read();
hardAssert($response !== null, 'Server returned too few responses');
$payload = $response->getPayload();
hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload '.$i.' had the wrong type');
hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
'Payload '.$i.' had the wrong length');
}
$call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
}
/**
* Run the empty_stream test.
*
* @param $stub Stub object that has service methods.
*/
function emptyStream($stub) {
$call = $stub->FullDuplexCall();
$call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
function emptyStream($stub)
{
$call = $stub->FullDuplexCall();
$call->writesDone();
hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully');
}
/**
* Run the cancel_after_begin test.
*
* @param $stub Stub object that has service methods.
*/
function cancelAfterBegin($stub) {
$call = $stub->StreamingInputCall();
$call->cancel();
list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_CANCELLED,
function cancelAfterBegin($stub)
{
$call = $stub->StreamingInputCall();
$call->cancel();
list($result, $status) = $call->wait();
hardAssert($status->code === Grpc\STATUS_CANCELLED,
'Call status was not CANCELLED');
}
/**
* Run the cancel_after_first_response test.
*
* @param $stub Stub object that has service methods.
*/
function cancelAfterFirstResponse($stub) {
$call = $stub->FullDuplexCall();
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize(31415);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", 27182));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
$call->cancel();
hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
function cancelAfterFirstResponse($stub)
{
$call = $stub->FullDuplexCall();
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize(31415);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", 27182));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
$call->cancel();
hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
'Call status was not CANCELLED');
}
function timeoutOnSleepingServer($stub) {
$call = $stub->FullDuplexCall(array('timeout' => 1000));
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize(8);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", 9));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED,
function timeoutOnSleepingServer($stub)
{
$call = $stub->FullDuplexCall(['timeout' => 1000]);
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize(8);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", 9));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED,
'Call status was not DEADLINE_EXCEEDED');
}
$args = getopt('', array('server_host:', 'server_port:', 'test_case:',
'use_tls::', 'use_test_ca::',
'server_host_override:', 'oauth_scope:',
'default_service_account:'));
$args = getopt('', ['server_host:', 'server_port:', 'test_case:',
'use_tls::', 'use_test_ca::',
'server_host_override:', 'oauth_scope:',
'default_service_account:', ]);
if (!array_key_exists('server_host', $args)) {
throw new Exception('Missing argument: --server_host is required');
throw new Exception('Missing argument: --server_host is required');
}
if (!array_key_exists('server_port', $args)) {
throw new Exception('Missing argument: --server_port is required');
throw new Exception('Missing argument: --server_port is required');
}
if (!array_key_exists('test_case', $args)) {
throw new Exception('Missing argument: --test_case is required');
throw new Exception('Missing argument: --test_case is required');
}
if ($args['server_port'] == 443) {
$server_address = $args['server_host'];
$server_address = $args['server_host'];
} else {
$server_address = $args['server_host'] . ':' . $args['server_port'];
$server_address = $args['server_host'].':'.$args['server_port'];
}
$test_case = $args['test_case'];
$host_override = 'foo.test.google.fr';
if (array_key_exists('server_host_override', $args)) {
$host_override = $args['server_host_override'];
$host_override = $args['server_host_override'];
}
$use_tls = false;
if (array_key_exists('use_tls', $args) &&
$args['use_tls'] != 'false') {
$use_tls = true;
$use_tls = true;
}
$use_test_ca = false;
if (array_key_exists('use_test_ca', $args) &&
$args['use_test_ca'] != 'false') {
$use_test_ca = true;
$use_test_ca = true;
}
$opts = [];
if ($use_tls) {
if ($use_test_ca) {
$ssl_cert_file = dirname(__FILE__) . '/../data/ca.pem';
} else {
$ssl_cert_file = getenv('SSL_CERT_FILE');
}
$ssl_credentials = Grpc\Credentials::createSsl(
file_get_contents($ssl_cert_file));
$opts['credentials'] = $ssl_credentials;
$opts['grpc.ssl_target_name_override'] = $host_override;
if ($use_test_ca) {
$ssl_credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
} else {
$ssl_credentials = Grpc\Credentials::createSsl();
}
$opts['credentials'] = $ssl_credentials;
$opts['grpc.ssl_target_name_override'] = $host_override;
}
if (in_array($test_case, array('service_account_creds',
'compute_engine_creds', 'jwt_token_creds'))) {
if ($test_case == 'jwt_token_creds') {
$auth_credentials = ApplicationDefaultCredentials::getCredentials();
} else {
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
}
$opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc();
if (in_array($test_case, ['service_account_creds',
'compute_engine_creds', 'jwt_token_creds', ])) {
if ($test_case == 'jwt_token_creds') {
$auth_credentials = ApplicationDefaultCredentials::getCredentials();
} else {
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
}
$opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc();
}
if ($test_case == 'oauth2_auth_token') {
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
$token = $auth_credentials->fetchAuthToken();
$update_metadata =
function($metadata,
$authUri = null,
ClientInterface $client = null) use ($token) {
$metadata_copy = $metadata;
$metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] =
array(sprintf("%s %s",
$token['token_type'],
$token['access_token']));
return $metadata_copy;
};
$opts['update_metadata'] = $update_metadata;
$auth_credentials = ApplicationDefaultCredentials::getCredentials(
$args['oauth_scope']
);
$token = $auth_credentials->fetchAuthToken();
$update_metadata =
function ($metadata,
$authUri = null,
ClientInterface $client = null) use ($token) {
$metadata_copy = $metadata;
$metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] =
[sprintf('%s %s',
$token['token_type'],
$token['access_token'])];
return $metadata_copy;
};
$opts['update_metadata'] = $update_metadata;
}
$stub = new grpc\testing\TestServiceClient($server_address, $opts);
@ -439,49 +470,49 @@ echo "Connecting to $server_address\n";
echo "Running test case $test_case\n";
switch ($test_case) {
case 'empty_unary':
emptyUnary($stub);
break;
case 'large_unary':
largeUnary($stub);
break;
case 'client_streaming':
clientStreaming($stub);
break;
case 'server_streaming':
serverStreaming($stub);
break;
case 'ping_pong':
pingPong($stub);
break;
case 'empty_stream':
emptyStream($stub);
break;
case 'cancel_after_begin':
cancelAfterBegin($stub);
break;
case 'cancel_after_first_response':
cancelAfterFirstResponse($stub);
break;
case 'timeout_on_sleeping_server':
timeoutOnSleepingServer($stub);
break;
case 'service_account_creds':
serviceAccountCreds($stub, $args);
break;
case 'compute_engine_creds':
computeEngineCreds($stub, $args);
break;
case 'jwt_token_creds':
jwtTokenCreds($stub, $args);
break;
case 'oauth2_auth_token':
oauth2AuthToken($stub, $args);
break;
case 'per_rpc_creds':
perRpcCreds($stub, $args);
break;
default:
echo "Unsupported test case $test_case\n";
exit(1);
case 'empty_unary':
emptyUnary($stub);
break;
case 'large_unary':
largeUnary($stub);
break;
case 'client_streaming':
clientStreaming($stub);
break;
case 'server_streaming':
serverStreaming($stub);
break;
case 'ping_pong':
pingPong($stub);
break;
case 'empty_stream':
emptyStream($stub);
break;
case 'cancel_after_begin':
cancelAfterBegin($stub);
break;
case 'cancel_after_first_response':
cancelAfterFirstResponse($stub);
break;
case 'timeout_on_sleeping_server':
timeoutOnSleepingServer($stub);
break;
case 'service_account_creds':
serviceAccountCreds($stub, $args);
break;
case 'compute_engine_creds':
computeEngineCreds($stub, $args);
break;
case 'jwt_token_creds':
jwtTokenCreds($stub, $args);
break;
case 'oauth2_auth_token':
oauth2AuthToken($stub, $args);
break;
case 'per_rpc_creds':
perRpcCreds($stub, $args);
break;
default:
echo "Unsupported test case $test_case\n";
exit(1);
}

@ -1,26 +0,0 @@
<?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: net/proto2/bridge/proto/message_set.proto
// Date: 2014-12-03 22:02:20
namespace proto2\bridge {
class MessageSet extends \DrSlump\Protobuf\Message {
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'proto2.bridge.MessageSet');
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
}
}

@ -31,56 +31,64 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
class CallTest extends PHPUnit_Framework_TestCase{
static $server;
static $port;
class CallTest extends PHPUnit_Framework_TestCase
{
public static $server;
public static $port;
public static function setUpBeforeClass() {
self::$server = new Grpc\Server([]);
self::$port = self::$server->addHttp2Port('0.0.0.0:0');
}
public static function setUpBeforeClass()
{
self::$server = new Grpc\Server([]);
self::$port = self::$server->addHttp2Port('0.0.0.0:0');
}
public function setUp() {
$this->channel = new Grpc\Channel('localhost:' . self::$port, []);
$this->call = new Grpc\Call($this->channel,
'/foo',
Grpc\Timeval::infFuture());
}
public function setUp()
{
$this->channel = new Grpc\Channel('localhost:'.self::$port, []);
$this->call = new Grpc\Call($this->channel,
'/foo',
Grpc\Timeval::infFuture());
}
public function testAddEmptyMetadata() {
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => []
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddEmptyMetadata()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => [],
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddSingleMetadata() {
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']]
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddSingleMetadata()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']],
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddMultiValueMetadata() {
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']]
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddMultiValueMetadata()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']],
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddSingleAndMultiValueMetadata() {
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'],
'key2' => ['value2', 'value3']]
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testAddSingleAndMultiValueMetadata()
{
$batch = [
Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'],
'key2' => ['value2', 'value3'], ],
];
$result = $this->call->startBatch($batch);
$this->assertTrue($result->send_metadata);
}
public function testGetPeer() {
$this->assertTrue(is_string($this->call->getPeer()));
}
public function testGetPeer()
{
$this->assertTrue(is_string($this->call->getPeer()));
}
}

@ -31,217 +31,228 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
class EndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() {
$this->server = new Grpc\Server([]);
$this->port = $this->server->addHttp2Port('0.0.0.0:0');
$this->channel = new Grpc\Channel('localhost:' . $this->port, []);
$this->server->start();
}
public function tearDown() {
unset($this->channel);
unset($this->server);
}
public function testSimpleRequestBody() {
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
Grpc\OP_RECV_CLOSE_ON_SERVER => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertFalse($event->cancelled);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true
]);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testMessageWriteFlags() {
$deadline = Grpc\Timeval::infFuture();
$req_text = 'message_write_flags_test';
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
'flags' => Grpc\WRITE_NO_COMPRESS],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
]);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true
]);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse() {
$deadline = Grpc\Timeval::infFuture();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->send_message);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertTrue($event->send_message);
$this->assertFalse($event->cancelled);
$this->assertSame($req_text, $event->message);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$this->assertSame($reply_text, $event->message);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testGetTarget() {
$this->assertTrue(is_string($this->channel->getTarget()));
}
public function testGetConnectivityState() {
$this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE);
}
public function testWatchConnectivityStateFailed() {
$idle_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(500000); // should timeout
$deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState(
class EndToEndTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->server = new Grpc\Server([]);
$this->port = $this->server->addHttp2Port('0.0.0.0:0');
$this->channel = new Grpc\Channel('localhost:'.$this->port, []);
$this->server->start();
}
public function tearDown()
{
unset($this->channel);
unset($this->server);
}
public function testSimpleRequestBody()
{
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertFalse($event->cancelled);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testMessageWriteFlags()
{
$deadline = Grpc\Timeval::infFuture();
$req_text = 'message_write_flags_test';
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
'flags' => Grpc\WRITE_NO_COMPRESS, ],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
]);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse()
{
$deadline = Grpc\Timeval::infFuture();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->send_message);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertTrue($event->send_message);
$this->assertFalse($event->cancelled);
$this->assertSame($req_text, $event->message);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$this->assertSame($reply_text, $event->message);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testGetTarget()
{
$this->assertTrue(is_string($this->channel->getTarget()));
}
public function testGetConnectivityState()
{
$this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE);
}
public function testWatchConnectivityStateFailed()
{
$idle_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(500000); // should timeout
$deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState(
$idle_state, $deadline));
}
}
public function testWatchConnectivityStateSuccess() {
$idle_state = $this->channel->getConnectivityState(true);
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
public function testWatchConnectivityStateSuccess()
{
$idle_state = $this->channel->getConnectivityState(true);
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(3000000); // should finish well before
$deadline = $now->add($delta);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(3000000); // should finish well before
$deadline = $now->add($delta);
$this->assertTrue($this->channel->watchConnectivityState(
$this->assertTrue($this->channel->watchConnectivityState(
$idle_state, $deadline));
$new_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state != $new_state);
}
$new_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state != $new_state);
}
public function testWatchConnectivityStateDoNothing() {
$idle_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
public function testWatchConnectivityStateDoNothing()
{
$idle_state = $this->channel->getConnectivityState();
$this->assertTrue($idle_state == Grpc\CHANNEL_IDLE);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(100000);
$deadline = $now->add($delta);
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(100000);
$deadline = $now->add($delta);
$this->assertFalse($this->channel->watchConnectivityState(
$this->assertFalse($this->channel->watchConnectivityState(
$idle_state, $deadline));
$new_state = $this->channel->getConnectivityState();
$this->assertTrue($new_state == Grpc\CHANNEL_IDLE);
}
$new_state = $this->channel->getConnectivityState();
$this->assertTrue($new_state == Grpc\CHANNEL_IDLE);
}
}

@ -31,186 +31,193 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() {
$credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
$server_credentials = Grpc\ServerCredentials::createSsl(
null,
file_get_contents(dirname(__FILE__) . '/../data/server1.key'),
file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
$this->server = new Grpc\Server();
$this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
class SecureEndToEndTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__).'/../data/ca.pem'));
$server_credentials = Grpc\ServerCredentials::createSsl(
null,
file_get_contents(dirname(__FILE__).'/../data/server1.key'),
file_get_contents(dirname(__FILE__).'/../data/server1.pem'));
$this->server = new Grpc\Server();
$this->port = $this->server->addSecureHttp2Port('0.0.0.0:0',
$server_credentials);
$this->server->start();
$this->host_override = 'foo.test.google.fr';
$this->channel = new Grpc\Channel(
'localhost:' . $this->port,
[
$this->server->start();
$this->host_override = 'foo.test.google.fr';
$this->channel = new Grpc\Channel(
'localhost:'.$this->port,
[
'grpc.ssl_target_name_override' => $this->host_override,
'grpc.default_authority' => $this->host_override,
'credentials' => $credentials
]);
}
public function tearDown() {
unset($this->channel);
unset($this->server);
}
public function testSimpleRequestBody() {
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
Grpc\OP_RECV_CLOSE_ON_SERVER => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertFalse($event->cancelled);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true
]);
$this->assertSame([], $event->metadata);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testMessageWriteFlags() {
$deadline = Grpc\Timeval::infFuture();
$req_text = 'message_write_flags_test';
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
'flags' => Grpc\WRITE_NO_COMPRESS],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
]);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true
]);
$this->assertSame([], $event->metadata);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse() {
$deadline = Grpc\Timeval::infFuture();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_SEND_MESSAGE => ['message' => $req_text]
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->send_message);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text
],
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertTrue($event->send_message);
$this->assertFalse($event->cancelled);
$this->assertSame($req_text, $event->message);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$this->assertSame($reply_text, $event->message);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
'credentials' => $credentials,
]
);
}
public function tearDown()
{
unset($this->channel);
unset($this->server);
}
public function testSimpleRequestBody()
{
$deadline = Grpc\Timeval::infFuture();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertFalse($event->cancelled);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testMessageWriteFlags()
{
$deadline = Grpc\Timeval::infFuture();
$req_text = 'message_write_flags_test';
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $req_text,
'flags' => Grpc\WRITE_NO_COMPRESS, ],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
]);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse()
{
$deadline = Grpc\Timeval::infFuture();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline,
$this->host_override);
$event = $call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_CLOSE_FROM_CLIENT => true,
Grpc\OP_SEND_MESSAGE => ['message' => $req_text],
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_close);
$this->assertTrue($event->send_message);
$event = $this->server->requestCall();
$this->assertSame('dummy_method', $event->method);
$server_call = $event->call;
$event = $server_call->startBatch([
Grpc\OP_SEND_INITIAL_METADATA => [],
Grpc\OP_SEND_MESSAGE => ['message' => $reply_text],
Grpc\OP_SEND_STATUS_FROM_SERVER => [
'metadata' => [],
'code' => Grpc\STATUS_OK,
'details' => $status_text,
],
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_CLOSE_ON_SERVER => true,
]);
$this->assertTrue($event->send_metadata);
$this->assertTrue($event->send_status);
$this->assertTrue($event->send_message);
$this->assertFalse($event->cancelled);
$this->assertSame($req_text, $event->message);
$event = $call->startBatch([
Grpc\OP_RECV_INITIAL_METADATA => true,
Grpc\OP_RECV_MESSAGE => true,
Grpc\OP_RECV_STATUS_ON_CLIENT => true,
]);
$this->assertSame([], $event->metadata);
$this->assertSame($reply_text, $event->message);
$status = $event->status;
$this->assertSame([], $status->metadata);
$this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertSame($status_text, $status->details);
unset($call);
unset($server_call);
}
}

@ -31,56 +31,64 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
class TimevalTest extends PHPUnit_Framework_TestCase{
public function testCompareSame() {
$zero = Grpc\Timeval::zero();
$this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
}
class TimevalTest extends PHPUnit_Framework_TestCase
{
public function testCompareSame()
{
$zero = Grpc\Timeval::zero();
$this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
}
public function testPastIsLessThanZero() {
$zero = Grpc\Timeval::zero();
$past = Grpc\Timeval::infPast();
$this->assertLessThan(0, Grpc\Timeval::compare($past, $zero));
$this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past));
}
public function testPastIsLessThanZero()
{
$zero = Grpc\Timeval::zero();
$past = Grpc\Timeval::infPast();
$this->assertLessThan(0, Grpc\Timeval::compare($past, $zero));
$this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past));
}
public function testFutureIsGreaterThanZero() {
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::infFuture();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $future));
$this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero));
}
public function testFutureIsGreaterThanZero()
{
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::infFuture();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $future));
$this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero));
}
/**
* @depends testFutureIsGreaterThanZero
*/
public function testNowIsBetweenZeroAndFuture() {
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::infFuture();
$now = Grpc\Timeval::now();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $now));
$this->assertLessThan(0, Grpc\Timeval::compare($now, $future));
}
/**
* @depends testFutureIsGreaterThanZero
*/
public function testNowIsBetweenZeroAndFuture()
{
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::infFuture();
$now = Grpc\Timeval::now();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $now));
$this->assertLessThan(0, Grpc\Timeval::compare($now, $future));
}
public function testNowAndAdd() {
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->add($delta);
$this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now));
}
public function testNowAndAdd()
{
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->add($delta);
$this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now));
}
public function testNowAndSubtract() {
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->subtract($delta);
$this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now));
}
public function testNowAndSubtract()
{
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->subtract($delta);
$this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now));
}
public function testAddAndSubtract() {
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->add($delta);
$back_to_now = $deadline->subtract($delta);
$this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now));
}
public function testAddAndSubtract()
{
$now = Grpc\Timeval::now();
$delta = new Grpc\Timeval(1000);
$deadline = $now->add($delta);
$back_to_now = $deadline->subtract($delta);
$this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now));
}
}

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

Loading…
Cancel
Save