Merge branch 'master' of github.com:grpc/grpc into flags-abi

pull/2054/head
David Garcia Quintas 10 years ago
commit 4d9414064e
  1. 3
      build.json
  2. 147
      doc/connectivity-semantics-and-api.md
  3. 2
      gRPC.podspec
  4. 4
      include/grpc/grpc.h
  5. 5
      include/grpc/support/slice.h
  6. 3
      src/core/support/log_win32.c
  7. 58
      src/objective-c/GRPCClient/GRPCCall.h
  8. 45
      src/objective-c/GRPCClient/GRPCCall.m
  9. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  10. 29
      src/objective-c/GRPCClient/private/GRPCWrappedCall.m
  11. 3
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.h
  12. 13
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
  13. 16
      src/objective-c/GRPCClient/private/NSError+GRPC.h
  14. 11
      src/objective-c/GRPCClient/private/NSError+GRPC.m
  15. 24
      src/objective-c/README.md
  16. 6
      src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec
  17. 6
      src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec
  18. 101
      src/objective-c/tests/GRPCClientTests.m
  19. 2
      test/core/end2end/gen_build_json.py
  20. 18
      tools/jenkins/run_jenkins.sh
  21. 2
      tools/run_tests/run_tests.py
  22. 1
      tools/run_tests/tests.json
  23. 19
      vsprojects/Grpc.mak

@ -1361,6 +1361,9 @@
"grpc",
"gpr_test_util",
"gpr"
],
"platforms": [
"posix"
]
},
{

@ -0,0 +1,147 @@
gRPC Connectivity Semantics and API
===================================
This document describes the connectivity semantics for gRPC channels and the
corresponding impact on RPCs. We then discuss an API.
States of Connectivity
----------------------
gRPC Channels provide the abstraction over which clients can communicate with
servers.The client-side channel object can be constructed using little more
than a DNS name. Channels encapsulate a range of functionality including name
resolution, establishing a TCP connection (with retries and backoff) and TLS
handshakes. Channels can also handle errors on established connections and
reconnect, or in the case of HTTP/2 GO_AWAY, re-resolve the name and reconnect.
To hide the details of all this activity from the user of the gRPC API (i.e.,
application code) while exposing meaningful information about the state of a
channel, we use a state machine with four states, defined below:
CONNECTING: The channel is trying to establish a connection and is waiting to
make progress on one of the steps involved in name resolution, TCP connection
establishment or TLS handshake. This may be used as the initial state for channels upon
creation.
READY: The channel has successfully established a connection all the way
through TLS handshake (or equivalent) and all subsequent attempt to communicate
have succeeded (or are pending without any known failure ).
TRANSIENT_FAILURE: There has been some transient failure (such as a TCP 3-way
handshake timing out or a socket error). Channels in this state will eventually
switch to the CONNECTING state and try to establish a connection again. Since
retries are done with exponential backoff, channels that fail to connect will
start out spending very little time in this state but as the attempts fail
repeatedly, the channel will spend increasingly large amounts of time in this
state. For many non-fatal failures (e.g., TCP connection attempts timing out
because the server is not yet available), the channel may spend increasingly
large amounts of time in this state.
IDLE: This is the state where the channel is not even trying to create a
connection because of a lack of new or pending RPCs. New channels MAY be created
in this state. Any attempt to start an RPC on the channel will push the channel
out of this state to connecting. When there has been no RPC activity on a channel
for a specified IDLE_TIMEOUT, i.e., no new or pending (active) RPCs for this
period, channels that are READY or CONNECTING switch to IDLE. Additionaly,
channels that receive a GOAWAY when there are no active or pending RPCs should
also switch to IDLE to avoid connection overload at servers that are attempting
to shed connections. We will use a default IDLE_TIMEOUT of 300 seconds (5 minutes).
SHUTDOWN: This channel has started shutting down. Any new RPCs should fail
immediately. Pending RPCs may continue running till the application cancels them.
Channels may enter this state either because the application explicitly requested
a shutdown or if a non-recoverable error has happened during attempts to connect
communicate . (As of 6/12/2015, there are no known errors (while connecting or
communicating) that are classified as non-recoverable)
Channels that enter this state never leave this state.
The following table lists the legal transitions from one state to another and
corresponding reasons. Empty cells denote disallowed transitions.
<table style='border: 1px solid black'>
<tr>
<th>From/To</th>
<th>CONNECTING</th>
<th>READY</th>
<th>TRANSIENT_FAILURE</th>
<th>IDLE</th>
<th>SHUTDOWN</th>
</tr>
<tr>
<th>CONNECTING</th>
<td>Incremental progress during connection establishment</td>
<td>All steps needed to establish a connection succeeded</td>
<td>Any failure in any of the steps needed to establish connection</td>
<td>No RPC activity on channel for IDLE_TIMEOUT</td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>READY</th>
<td></td>
<td>Incremental successful communication on established channel.</td>
<td>Any failure encountered while expecting successful communication on
established channel.</td>
<td>No RPC activity on channel for IDLE_TIMEOUT <br>OR<br>upon receiving a GOAWAY while there are no pending RPCs.</td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>TRANSIENT_FAILURE</th>
<td>Wait time required to implement (exponential) backoff is over.</td>
<td></td>
<td></td>
<td></td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>IDLE</th>
<td>Any new RPC activity on the channel</td>
<td></td>
<td></td>
<td></td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>FATAL_FAILURE</th>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
Channel State API
-----------------
All gRPC libraries will expose a channel-level API method to poll the current
state of a channel. In C++, this method is called GetCurrentState and returns
an enum for one of the four legal states.
All libraries should also expose an API that enables the application (user of
the gRPC API) to be notified when the channel state changes. Since state
changes can be rapid and race with any such notification, the notification
should just inform the user that some state change has happened, leaving it to
the user to poll the channel for the current state.
The synchronous version of this API is:
```cpp
bool WaitForStateChange(gpr_timespec deadline, ChannelState source_state);
```
which returns true when the state changes to something other than the
source_state and false if the deadline expires. Asynchronous and futures based
APIs should have a corresponding method that allows the application to be
notified when the state of a channel changes.
Note that a notification is delivered every time there is a transition from any
state to any *other* state. On the other hand the rules for legal state
transition, require a transition from CONNECTING to TRANSIENT_FAILURE and back
to CONNECTING for every recoverable failure, even if the corresponding
exponential backoff requires no wait before retry. The combined effect is that
the application may receive state change notifications that appear spurious.
e.g., an application waiting for state changes on a channel that is CONNECTING
may receive a state change notification but find the channel in the same
CONNECTING state on polling for current state because the channel may have
spent infinitesimally small amount of time in the TRANSIENT_FAILURE state.

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'gRPC'
s.version = '0.5.1'
s.version = '0.6.0'
s.summary = 'gRPC client library for iOS/OSX'
s.homepage = 'http://www.grpc.io'
s.license = 'New BSD'

@ -223,7 +223,7 @@ typedef enum {
GRPC_OP_SEND_INITIAL_METADATA = 0,
/* Send a message: 0 or more of these operations can occur for each call */
GRPC_OP_SEND_MESSAGE,
/* Send a close from the server: one and only one instance MUST be sent from
/* Send a close from the client: one and only one instance MUST be sent from
the client,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
@ -242,7 +242,7 @@ typedef enum {
the status will indicate some failure.
*/
GRPC_OP_RECV_STATUS_ON_CLIENT,
/* Receive status on the server: one and only one must be made on the server
/* Receive close on the server: one and only one must be made on the server
*/
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;

@ -110,8 +110,9 @@ gpr_slice gpr_slice_ref(gpr_slice s);
/* Decrement the ref count of s. If the ref count of s reaches zero, all
slices sharing the ref count are destroyed, and considered no longer
initialized. If s is ultimately derived from a call to gpr_slice_new(start,
len, dest) where dest!=NULL , then (*dest)(start, len) is called. Requires
s initialized. */
len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is
ultimately derived from a call to gpr_slice_new_with_len(start, len, dest)
where dest!=NULL , then (*dest)(start, len). Requires s initialized. */
void gpr_slice_unref(gpr_slice s);
/* Create a slice pointing at some data. Calls malloc to allocate a refcount

@ -42,6 +42,7 @@
#include <grpc/support/log_win32.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc/support/string_util.h>
#include "src/core/support/string.h"
#include "src/core/support/string_win32.h"
@ -106,7 +107,7 @@ char *gpr_format_message(DWORD messageid) {
NULL, messageid,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)(&tmessage), 0, NULL);
if (status == 0) return gpr_strdup("Unable to retreive error string");
if (status == 0) return gpr_strdup("Unable to retrieve error string");
message = gpr_tchar_to_char(tmessage);
LocalFree(tmessage);
return message;

@ -31,43 +31,55 @@
*
*/
// 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 <gRPC/GRXWriter.h>
@class GRPCMethodName;
@class GRPCCall;
// Key used in |NSError|'s |userInfo| dictionary to store the response metadata sent by the server.
extern id const kGRPCStatusMetadataKey;
// 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.
// Represents a single gRPC remote call.
@interface GRPCCall : NSObject<GRXWriter>
// These HTTP2 headers will be passed to the server as part of this call. Each
// HTTP2 header is a name-value pair with string names and either string or binary values.
// 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.requestMetadata = @{
// @"Authorization": @"Bearer ...",
// @"SomeBinaryHeader": someData
// };
// call.requestMetadata = @{@"Authorization": @"Bearer ..."};
//
// call.requestMetadata[@"SomeBinaryHeader"] = someData;
//
// After the call is started, modifying this won't have any effect.
@property(nonatomic, readwrite) NSMutableDictionary *requestMetadata;
//
// For convenience, the property is initialized to an empty NSMutableDictionary, and the setter
// accepts (and copies) both mutable and immutable dictionaries.
- (NSMutableDictionary *)requestMetadata; // nonatomic
- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy
// This isn't populated until the first event is delivered to the handler.
// This dictionary is populated with the HTTP headers received from the server. When the RPC ends,
// the HTTP trailers received are added to the dictionary too. It has the same structure as the
// request metadata dictionary.
//
// The first time this object calls |writeValue| on the writeable passed to |startWithWriteable|,
// the |responseMetadata| dictionary already contains the response headers. When it calls
// |writesFinishedWithError|, the dictionary contains both the response headers and trailers.
@property(atomic, readonly) NSDictionary *responseMetadata;
// The request writer has to write NSData objects into the provided Writeable. The server will

@ -46,9 +46,9 @@
#import "private/NSDictionary+GRPC.h"
#import "private/NSError+GRPC.h"
NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
@interface GRPCCall () <GRXWriteable>
// Makes it readwrite.
@property(atomic, strong) NSDictionary *responseMetadata;
@end
// The following methods of a C gRPC call object aren't reentrant, and thus
@ -82,6 +82,9 @@
// correct ordering.
GRPCDelegateWrapper *_responseWriteable;
id<GRXWriter> _requestWriter;
NSMutableDictionary *_requestMetadata;
NSMutableDictionary *_responseMetadata;
}
@synthesize state = _state;
@ -116,10 +119,27 @@
_callQueue = dispatch_queue_create("org.grpc.call", NULL);
_requestWriter = requestWriter;
_requestMetadata = [NSMutableDictionary dictionary];
_responseMetadata = [NSMutableDictionary dictionary];
}
return self;
}
#pragma mark Metadata
- (NSMutableDictionary *)requestMetadata {
return _requestMetadata;
}
- (void)setRequestMetadata:(NSDictionary *)requestMetadata {
_requestMetadata = [NSMutableDictionary dictionaryWithDictionary:requestMetadata];
}
- (NSDictionary *)responseMetadata {
return _responseMetadata;
}
#pragma mark Finish
- (void)finishWithError:(NSError *)errorOrNil {
@ -277,7 +297,7 @@
// The first one (metadataHandler), when the response headers are received.
// The second one (completionHandler), whenever the RPC finishes for any reason.
- (void)invokeCallWithMetadataHandler:(void(^)(NSDictionary *))metadataHandler
completionHandler:(void(^)(NSError *))completionHandler {
completionHandler:(void(^)(NSError *, NSDictionary *))completionHandler {
// TODO(jcanizales): Add error handlers for async failures
[_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMetadata alloc]
initWithHandler:metadataHandler]]];
@ -287,16 +307,23 @@
- (void)invokeCall {
__weak GRPCCall *weakSelf = self;
[self invokeCallWithMetadataHandler:^(NSDictionary *metadata) {
// Response metadata received.
[self invokeCallWithMetadataHandler:^(NSDictionary *headers) {
// Response headers received.
GRPCCall *strongSelf = weakSelf;
if (strongSelf) {
strongSelf.responseMetadata = metadata;
[strongSelf->_responseMetadata addEntriesFromDictionary:headers];
[strongSelf startNextRead];
}
} completionHandler:^(NSError *error) {
// TODO(jcanizales): Merge HTTP2 trailers into response metadata.
[weakSelf finishWithError:error];
} completionHandler:^(NSError *error, NSDictionary *trailers) {
GRPCCall *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf->_responseMetadata addEntriesFromDictionary:trailers];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
userInfo[kGRPCStatusMetadataKey] = strongSelf->_responseMetadata;
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
[strongSelf finishWithError:error];
}
}];
// Now that the RPC has been initiated, request writes can start.
[_requestWriter startWithWriteable:self];

@ -79,7 +79,7 @@ typedef void(^GRPCCompletionHandler)(NSDictionary *);
@interface GRPCOpRecvStatus : NSObject <GRPCOp>
- (instancetype)initWithHandler:(void(^)(NSError *))handler NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithHandler:(void(^)(NSError *, NSDictionary *))handler NS_DESIGNATED_INITIALIZER;
@end

@ -165,9 +165,7 @@
}
- (void)finish {
NSDictionary *metadata = [NSDictionary
grpc_dictionaryFromMetadata:_recvInitialMetadata.metadata
count:_recvInitialMetadata.count];
NSDictionary *metadata = [NSDictionary grpc_dictionaryFromMetadataArray:_recvInitialMetadata];
if (_handler) {
_handler(metadata);
}
@ -209,41 +207,44 @@
@end
@implementation GRPCOpRecvStatus{
void(^_handler)(NSError *);
void(^_handler)(NSError *, NSDictionary *);
grpc_status_code _statusCode;
char *_details;
size_t _detailsCapacity;
grpc_status _status;
grpc_metadata_array _metadata;
}
- (instancetype) init {
return [self initWithHandler:nil];
}
- (instancetype) initWithHandler:(void (^)(NSError *))handler {
- (instancetype) initWithHandler:(void (^)(NSError *, NSDictionary *))handler {
if (self = [super init]) {
_handler = handler;
grpc_metadata_array_init(&_status.metadata);
grpc_metadata_array_init(&_metadata);
}
return self;
}
- (void)getOp:(grpc_op *)op {
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.status = &_status.status;
op->data.recv_status_on_client.status_details = &_status.details;
op->data.recv_status_on_client.status = &_statusCode;
op->data.recv_status_on_client.status_details = &_details;
op->data.recv_status_on_client.status_details_capacity = &_detailsCapacity;
op->data.recv_status_on_client.trailing_metadata = &_status.metadata;
op->data.recv_status_on_client.trailing_metadata = &_metadata;
}
- (void)finish {
if (_handler) {
NSError *error = [NSError grpc_errorFromStatus:&_status];
_handler(error);
NSError *error = [NSError grpc_errorFromStatusCode:_statusCode details:_details];
NSDictionary *trailers = [NSDictionary grpc_dictionaryFromMetadataArray:_metadata];
_handler(error, trailers);
}
}
- (void)dealloc {
grpc_metadata_array_destroy(&_status.metadata);
gpr_free(_status.details);
grpc_metadata_array_destroy(&_metadata);
gpr_free(_details);
}
@end

@ -35,6 +35,7 @@
#include <grpc/grpc.h>
@interface NSDictionary (GRPC)
+ (instancetype)grpc_dictionaryFromMetadata:(struct grpc_metadata *)entries count:(size_t)count;
+ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array;
+ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count;
- (grpc_metadata *)grpc_metadataArray;
@end

@ -98,14 +98,18 @@
#pragma mark Category for metadata arrays
@implementation NSDictionary (GRPC)
+ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array {
return [self grpc_dictionaryFromMetadata:array.metadata count:array.count];
}
+ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count {
NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count];
for (grpc_metadata *entry = entries; entry < entries + count; entry++) {
// TODO(jcanizales): Verify in a C library test that it's converting header names to lower case
// automatically.
NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding];
if (!name) {
// log?
if (!name || metadata[name]) {
// Log if name is nil?
continue;
}
id value;
@ -115,10 +119,7 @@
} else {
value = [NSString grpc_stringFromMetadataValue:entry];
}
if (!metadata[name]) {
metadata[name] = [NSMutableArray array];
}
[metadata[name] addObject:value];
metadata[name] = value;
}
return metadata;
}

@ -32,6 +32,7 @@
*/
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
// TODO(jcanizales): Make the domain string public.
extern NSString *const kGRPCErrorDomain;
@ -56,17 +57,8 @@ typedef NS_ENUM(NSInteger, GRPCErrorCode) {
GRPCErrorCodeDataLoss = 15
};
// TODO(jcanizales): This is conflating trailing metadata with Status details. Fix it once there's
// a decision on how to codify Status.
#include <grpc/grpc.h>
typedef struct grpc_status {
grpc_status_code status;
char *details;
grpc_metadata_array metadata;
} grpc_status;
@interface NSError (GRPC)
// Returns nil if the status is OK. Otherwise, a NSError whose code is one of
// GRPCErrorCode and whose domain is kGRPCErrorDomain.
+ (instancetype)grpc_errorFromStatus:(struct grpc_status *)status;
// 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

@ -35,17 +35,16 @@
#include <grpc.h>
NSString *const kGRPCErrorDomain = @"org.grpc";
NSString * const kGRPCErrorDomain = @"io.grpc";
@implementation NSError (GRPC)
+ (instancetype)grpc_errorFromStatus:(struct grpc_status *)status {
if (status->status == GRPC_STATUS_OK) {
+ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details {
if (statusCode == GRPC_STATUS_OK) {
return nil;
}
NSString *message =
[NSString stringWithFormat:@"Code=%i Message='%s'", status->status, status->details];
NSString *message = [NSString stringWithCString:details encoding:NSASCIIStringEncoding];
return [NSError errorWithDomain:kGRPCErrorDomain
code:status->status
code:statusCode
userInfo:@{NSLocalizedDescriptionKey: message}];
}
@end

@ -52,11 +52,11 @@ Pod::Spec.new do |s|
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
# You can run this command manually if you later change your protos and need to regenerate.
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto **/*.proto"
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto"
# The --objc_out plugin generates a pair of .pbobjc.h/.pbobjc.m files for each .proto file.
s.subspec "Messages" do |ms|
ms.source_files = "*.pbobjc.{h,m}", "**/*.pbobjc.{h,m}"
ms.source_files = "*.pbobjc.{h,m}"
ms.header_mappings_dir = "."
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
@ -65,7 +65,7 @@ Pod::Spec.new do |s|
# The --objcgrpc_out plugin generates a pair of .pbrpc.h/.pbrpc.m files for each .proto file with
# a service defined.
s.subspec "Services" do |ss|
ss.source_files = "*.pbrpc.{h,m}", "**/*.pbrpc.{h,m}"
ss.source_files = "*.pbrpc.{h,m}"
ss.header_mappings_dir = "."
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.5"
@ -74,9 +74,21 @@ Pod::Spec.new do |s|
end
```
The file should be named `<Podspec file name>.podspec`. Once your library has a Podspec, Cocoapods
can install it into any XCode project. For that, go into your project's directory and create a
Podfile by running:
The file should be named `<Podspec file name>.podspec`.
Note: If your proto files are in a directory hierarchy, you might want to adjust the _globs_ used in
the sample Podspec above. For example, you could use:
```ruby
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto **/*.proto"
...
ms.source_files = "*.pbobjc.{h,m}", "**/*.pbobjc.{h,m}"
...
ss.source_files = "*.pbrpc.{h,m}", "**/*.pbrpc.{h,m}"
```
Once your library has a Podspec, Cocoapods can install it into any XCode project. For that, go into
your project's directory and create a Podfile by running:
```sh
pod init

@ -7,17 +7,17 @@ Pod::Spec.new do |s|
s.osx.deployment_target = "10.8"
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto **/*.proto"
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto"
s.subspec "Messages" do |ms|
ms.source_files = "*.pbobjc.{h,m}", "**/*.pbobjc.{h,m}"
ms.source_files = "*.pbobjc.{h,m}"
ms.header_mappings_dir = "."
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
end
s.subspec "Services" do |ss|
ss.source_files = "*.pbrpc.{h,m}", "**/*.pbrpc.{h,m}"
ss.source_files = "*.pbrpc.{h,m}"
ss.header_mappings_dir = "."
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.5"

@ -7,17 +7,17 @@ Pod::Spec.new do |s|
s.osx.deployment_target = "10.8"
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto **/*.proto"
s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto"
s.subspec "Messages" do |ms|
ms.source_files = "*.pbobjc.{h,m}", "**/*.pbobjc.{h,m}"
ms.source_files = "*.pbobjc.{h,m}"
ms.header_mappings_dir = "."
ms.requires_arc = false
ms.dependency "Protobuf", "~> 3.0.0-alpha-3"
end
s.subspec "Services" do |ss|
ss.source_files = "*.pbrpc.{h,m}", "**/*.pbrpc.{h,m}"
ss.source_files = "*.pbrpc.{h,m}"
ss.header_mappings_dir = "."
ss.requires_arc = true
ss.dependency "gRPC", "~> 0.5"

@ -43,24 +43,38 @@
// 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 = @"grpc-test.sandbox.google.com";
static NSString * const kPackage = @"grpc.testing";
static NSString * const kService = @"TestService";
static GRPCMethodName *kInexistentMethod;
static GRPCMethodName *kEmptyCallMethod;
static GRPCMethodName *kUnaryCallMethod;
@interface GRPCClientTests : XCTestCase
@end
@implementation GRPCClientTests
- (void)testConnectionToRemoteServer {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
- (void)setUp {
// This method isn't implemented by the remote server.
GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:@"grpc.testing"
interface:@"TestService"
method:@"Nonexistent"];
kInexistentMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
interface:kService
method:@"Inexistent"];
kEmptyCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
interface:kService
method:@"EmptyCall"];
kUnaryCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage
interface:kService
method:@"UnaryCall"];
}
id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[NSData data]];
- (void)testConnectionToRemoteServer {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
GRPCCall *call = [[GRPCCall alloc] initWithHost:@"grpc-test.sandbox.google.com"
method:method
requestsWriter:requestsWriter];
GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
method:kInexistentMethod
requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTFail(@"Received unexpected response: %@", value);
@ -80,15 +94,9 @@
__weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
__weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:@"grpc.testing"
interface:@"TestService"
method:@"EmptyCall"];
id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[NSData data]];
GRPCCall *call = [[GRPCCall alloc] initWithHost:@"grpc-test.sandbox.google.com"
method:method
requestsWriter:requestsWriter];
GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
method:kEmptyCallMethod
requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTAssertNotNil(value, @"nil value received as response.");
@ -105,34 +113,27 @@
}
- (void)testSimpleProtoRPC {
__weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."];
__weak XCTestExpectation *expectedResponse =
[self expectationWithDescription:@"Expected response."];
__weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
__weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:@"grpc.testing"
interface:@"TestService"
method:@"UnaryCall"];
RMTSimpleRequest *request = [[RMTSimpleRequest alloc] init];
RMTSimpleRequest *request = [RMTSimpleRequest message];
request.responseSize = 100;
request.fillUsername = YES;
request.fillOauthScope = YES;
id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]];
GRPCCall *call = [[GRPCCall alloc] initWithHost:@"grpc-test.sandbox.google.com"
method:method
GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
method:kUnaryCallMethod
requestsWriter:requestsWriter];
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTAssertNotNil(value, @"nil value received as response.");
[response fulfill];
XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
RMTSimpleResponse *response = [RMTSimpleResponse parseFromData:value error:NULL];
RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
// We expect empty strings, not nil:
XCTAssertNotNil(response.username, @"Response's username is nil.");
XCTAssertNotNil(response.oauthScope, @"Response's OAuth scope is nil.");
[expectedResponse fulfill];
XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
[response fulfill];
} completionHandler:^(NSError *errorOrNil) {
XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
[completion fulfill];
@ -143,4 +144,36 @@
[self waitForExpectationsWithTimeout:2. handler:nil];
}
- (void)testMetadata {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
RMTSimpleRequest *request = [RMTSimpleRequest message];
request.fillUsername = YES;
request.fillOauthScope = YES;
id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]];
GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
method:kUnaryCallMethod
requestsWriter:requestsWriter];
call.requestMetadata[@"Authorization"] = @"Bearer bogusToken";
id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
XCTFail(@"Received unexpected response: %@", value);
} completionHandler:^(NSError *errorOrNil) {
XCTAssertNotNil(errorOrNil, @"Finished without error!");
XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
XCTAssertEqualObjects(call.responseMetadata, errorOrNil.userInfo[kGRPCStatusMetadataKey],
@"Metadata in the NSError object and call object differ.");
NSString *challengeHeader = call.responseMetadata[@"www-authenticate"];
XCTAssertGreaterThan(challengeHeader.length, 0,
@"No challenge in response headers %@", call.responseMetadata);
[expectation fulfill];
}];
[call startWithWriteable:responsesWriteable];
[self waitForExpectationsWithTimeout:2. handler:nil];
}
@end

@ -102,7 +102,7 @@ def main():
'language': 'c',
'secure': 'check' if END2END_FIXTURES[f].secure else 'no',
'src': ['test/core/end2end/fixtures/%s.c' % f],
'platforms': [ 'posix' ] if f.endswith('_posix') else [ 'windows', 'posix' ],
'platforms': [ 'posix' ] if f.endswith('_posix') else END2END_FIXTURES[f].platforms,
}
for f in sorted(END2END_FIXTURES.keys())] + [
{

@ -47,15 +47,29 @@ then
FETCH_PULL_REQUEST_CMD="&& git fetch $GIT_URL refs/pull/$ghprbPullId/merge refs/pull/$ghprbPullId/head"
fi
# Make sure the CID file is gone.
rm -f docker.cid
# Run tests inside docker
docker run grpc/grpc_jenkins_slave bash -c -l "git clone --recursive $GIT_URL /var/local/git/grpc \
docker run --cidfile=docker.cid grpc/grpc_jenkins_slave bash -c -l "git clone --recursive $GIT_URL /var/local/git/grpc \
&& cd /var/local/git/grpc \
$FETCH_PULL_REQUEST_CMD \
&& git checkout -f $GIT_COMMIT \
&& git submodule update \
&& nvm use 0.12 \
&& rvm use ruby-2.1 \
&& tools/run_tests/run_tests.py -t -l $language"
&& tools/run_tests/run_tests.py -t -l $language" || DOCKER_FAILED="true"
DOCKER_CID=`cat docker.cid`
if [ "$DOCKER_FAILED" == "" ]
then
echo "Docker finished successfully, deleting the container $DOCKER_CID"
docker rm $DOCKER_CID
else
echo "Docker exited with failure, keeping container $DOCKER_CID."
echo "You can SSH to the worker and use 'docker start CID' and 'docker exec -i -t CID bash' to debug the problem."
fi
elif [ "$platform" == "windows" ]
then
echo "building $language on Windows"

@ -123,7 +123,7 @@ class CLanguage(object):
if travis and target['flaky']:
continue
if self.platform == 'windows':
binary = 'vsprojects\\test_bin\\%s.exe' % (target['name'])
binary = 'vsprojects/test_bin/%s.exe' % (target['name'])
else:
binary = 'bins/%s/%s' % (config.build_config, target['name'])
out.append(config.job_spec([binary], [binary]))

@ -353,7 +353,6 @@
"language": "c",
"name": "httpcli_test",
"platforms": [
"windows",
"posix"
]
},

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save