1*cc02d7e2SAndroid Build Coastguard Worker/* 2*cc02d7e2SAndroid Build Coastguard Worker * 3*cc02d7e2SAndroid Build Coastguard Worker * Copyright 2015 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker * 5*cc02d7e2SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker * You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker * 9*cc02d7e2SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker * 11*cc02d7e2SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker * limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker * 17*cc02d7e2SAndroid Build Coastguard Worker */ 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard Worker#import "ProtoRPC.h" 20*cc02d7e2SAndroid Build Coastguard Worker 21*cc02d7e2SAndroid Build Coastguard Worker#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 22*cc02d7e2SAndroid Build Coastguard Worker#import <Protobuf/GPBProtocolBuffers.h> 23*cc02d7e2SAndroid Build Coastguard Worker#else 24*cc02d7e2SAndroid Build Coastguard Worker#import <GPBProtocolBuffers.h> 25*cc02d7e2SAndroid Build Coastguard Worker#endif 26*cc02d7e2SAndroid Build Coastguard Worker#import <GRPCClient/GRPCCall.h> 27*cc02d7e2SAndroid Build Coastguard Worker#import <RxLibrary/GRXWriteable.h> 28*cc02d7e2SAndroid Build Coastguard Worker#import <RxLibrary/GRXWriter+Transformations.h> 29*cc02d7e2SAndroid Build Coastguard Worker 30*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCUnaryResponseHandler { 31*cc02d7e2SAndroid Build Coastguard Worker void (^_responseHandler)(id, NSError *); 32*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_t _responseDispatchQueue; 33*cc02d7e2SAndroid Build Coastguard Worker 34*cc02d7e2SAndroid Build Coastguard Worker GPBMessage *_message; 35*cc02d7e2SAndroid Build Coastguard Worker} 36*cc02d7e2SAndroid Build Coastguard Worker 37*cc02d7e2SAndroid Build Coastguard Worker- (nullable instancetype)initWithResponseHandler:(void (^)(id, NSError *))handler 38*cc02d7e2SAndroid Build Coastguard Worker responseDispatchQueue:(dispatch_queue_t)dispatchQueue { 39*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 40*cc02d7e2SAndroid Build Coastguard Worker _responseHandler = handler; 41*cc02d7e2SAndroid Build Coastguard Worker if (dispatchQueue == nil) { 42*cc02d7e2SAndroid Build Coastguard Worker _responseDispatchQueue = dispatch_get_main_queue(); 43*cc02d7e2SAndroid Build Coastguard Worker } else { 44*cc02d7e2SAndroid Build Coastguard Worker _responseDispatchQueue = dispatchQueue; 45*cc02d7e2SAndroid Build Coastguard Worker } 46*cc02d7e2SAndroid Build Coastguard Worker } 47*cc02d7e2SAndroid Build Coastguard Worker return self; 48*cc02d7e2SAndroid Build Coastguard Worker} 49*cc02d7e2SAndroid Build Coastguard Worker 50*cc02d7e2SAndroid Build Coastguard Worker// Implements GRPCProtoResponseHandler 51*cc02d7e2SAndroid Build Coastguard Worker- (dispatch_queue_t)dispatchQueue { 52*cc02d7e2SAndroid Build Coastguard Worker return _responseDispatchQueue; 53*cc02d7e2SAndroid Build Coastguard Worker} 54*cc02d7e2SAndroid Build Coastguard Worker 55*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata { 56*cc02d7e2SAndroid Build Coastguard Worker _responseHeaders = [initialMetadata copy]; 57*cc02d7e2SAndroid Build Coastguard Worker} 58*cc02d7e2SAndroid Build Coastguard Worker 59*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveProtoMessage:(GPBMessage *)message { 60*cc02d7e2SAndroid Build Coastguard Worker _message = message; 61*cc02d7e2SAndroid Build Coastguard Worker} 62*cc02d7e2SAndroid Build Coastguard Worker 63*cc02d7e2SAndroid Build Coastguard Worker- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { 64*cc02d7e2SAndroid Build Coastguard Worker _responseTrailers = [trailingMetadata copy]; 65*cc02d7e2SAndroid Build Coastguard Worker GPBMessage *message = _message; 66*cc02d7e2SAndroid Build Coastguard Worker _message = nil; 67*cc02d7e2SAndroid Build Coastguard Worker _responseHandler(message, error); 68*cc02d7e2SAndroid Build Coastguard Worker} 69*cc02d7e2SAndroid Build Coastguard Worker 70*cc02d7e2SAndroid Build Coastguard Worker// Intentional no-op since flow control is N/A in a unary call 71*cc02d7e2SAndroid Build Coastguard Worker- (void)didWriteMessage { 72*cc02d7e2SAndroid Build Coastguard Worker} 73*cc02d7e2SAndroid Build Coastguard Worker 74*cc02d7e2SAndroid Build Coastguard Worker@end 75*cc02d7e2SAndroid Build Coastguard Worker 76*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCUnaryProtoCall { 77*cc02d7e2SAndroid Build Coastguard Worker GRPCStreamingProtoCall *_call; 78*cc02d7e2SAndroid Build Coastguard Worker GPBMessage *_message; 79*cc02d7e2SAndroid Build Coastguard Worker} 80*cc02d7e2SAndroid Build Coastguard Worker 81*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions 82*cc02d7e2SAndroid Build Coastguard Worker message:(GPBMessage *)message 83*cc02d7e2SAndroid Build Coastguard Worker responseHandler:(id<GRPCProtoResponseHandler>)handler 84*cc02d7e2SAndroid Build Coastguard Worker callOptions:(GRPCCallOptions *)callOptions 85*cc02d7e2SAndroid Build Coastguard Worker responseClass:(Class)responseClass { 86*cc02d7e2SAndroid Build Coastguard Worker NSAssert(message != nil, @"message cannot be empty."); 87*cc02d7e2SAndroid Build Coastguard Worker NSAssert(responseClass != nil, @"responseClass cannot be empty."); 88*cc02d7e2SAndroid Build Coastguard Worker if (message == nil || responseClass == nil) { 89*cc02d7e2SAndroid Build Coastguard Worker return nil; 90*cc02d7e2SAndroid Build Coastguard Worker } 91*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 92*cc02d7e2SAndroid Build Coastguard Worker _call = [[GRPCStreamingProtoCall alloc] initWithRequestOptions:requestOptions 93*cc02d7e2SAndroid Build Coastguard Worker responseHandler:handler 94*cc02d7e2SAndroid Build Coastguard Worker callOptions:callOptions 95*cc02d7e2SAndroid Build Coastguard Worker responseClass:responseClass]; 96*cc02d7e2SAndroid Build Coastguard Worker _message = [message copy]; 97*cc02d7e2SAndroid Build Coastguard Worker } 98*cc02d7e2SAndroid Build Coastguard Worker return self; 99*cc02d7e2SAndroid Build Coastguard Worker} 100*cc02d7e2SAndroid Build Coastguard Worker 101*cc02d7e2SAndroid Build Coastguard Worker- (void)start { 102*cc02d7e2SAndroid Build Coastguard Worker [_call start]; 103*cc02d7e2SAndroid Build Coastguard Worker [_call receiveNextMessage]; 104*cc02d7e2SAndroid Build Coastguard Worker [_call writeMessage:_message]; 105*cc02d7e2SAndroid Build Coastguard Worker [_call finish]; 106*cc02d7e2SAndroid Build Coastguard Worker} 107*cc02d7e2SAndroid Build Coastguard Worker 108*cc02d7e2SAndroid Build Coastguard Worker- (void)cancel { 109*cc02d7e2SAndroid Build Coastguard Worker [_call cancel]; 110*cc02d7e2SAndroid Build Coastguard Worker} 111*cc02d7e2SAndroid Build Coastguard Worker 112*cc02d7e2SAndroid Build Coastguard Worker@end 113*cc02d7e2SAndroid Build Coastguard Worker 114*cc02d7e2SAndroid Build Coastguard Worker@interface GRPCStreamingProtoCall () <GRPCResponseHandler> 115*cc02d7e2SAndroid Build Coastguard Worker 116*cc02d7e2SAndroid Build Coastguard Worker@end 117*cc02d7e2SAndroid Build Coastguard Worker 118*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCStreamingProtoCall { 119*cc02d7e2SAndroid Build Coastguard Worker GRPCRequestOptions *_requestOptions; 120*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> _handler; 121*cc02d7e2SAndroid Build Coastguard Worker GRPCCallOptions *_callOptions; 122*cc02d7e2SAndroid Build Coastguard Worker Class _responseClass; 123*cc02d7e2SAndroid Build Coastguard Worker 124*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *_call; 125*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_t _dispatchQueue; 126*cc02d7e2SAndroid Build Coastguard Worker} 127*cc02d7e2SAndroid Build Coastguard Worker 128*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions 129*cc02d7e2SAndroid Build Coastguard Worker responseHandler:(id<GRPCProtoResponseHandler>)handler 130*cc02d7e2SAndroid Build Coastguard Worker callOptions:(GRPCCallOptions *)callOptions 131*cc02d7e2SAndroid Build Coastguard Worker responseClass:(Class)responseClass { 132*cc02d7e2SAndroid Build Coastguard Worker NSAssert(requestOptions.host.length != 0 && requestOptions.path.length != 0, 133*cc02d7e2SAndroid Build Coastguard Worker @"Invalid callOptions."); 134*cc02d7e2SAndroid Build Coastguard Worker NSAssert(handler != nil, @"handler cannot be empty."); 135*cc02d7e2SAndroid Build Coastguard Worker if (requestOptions.host.length == 0 || requestOptions.path.length == 0) { 136*cc02d7e2SAndroid Build Coastguard Worker return nil; 137*cc02d7e2SAndroid Build Coastguard Worker } 138*cc02d7e2SAndroid Build Coastguard Worker if (handler == nil) { 139*cc02d7e2SAndroid Build Coastguard Worker return nil; 140*cc02d7e2SAndroid Build Coastguard Worker } 141*cc02d7e2SAndroid Build Coastguard Worker 142*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 143*cc02d7e2SAndroid Build Coastguard Worker _requestOptions = [requestOptions copy]; 144*cc02d7e2SAndroid Build Coastguard Worker _handler = handler; 145*cc02d7e2SAndroid Build Coastguard Worker _callOptions = [callOptions copy]; 146*cc02d7e2SAndroid Build Coastguard Worker _responseClass = responseClass; 147*cc02d7e2SAndroid Build Coastguard Worker 148*cc02d7e2SAndroid Build Coastguard Worker // Set queue QoS only when iOS version is 8.0 or above and Xcode version is 9.0 or above 149*cc02d7e2SAndroid Build Coastguard Worker#if __IPHONE_OS_VERSION_MAX_ALLOWED < 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED < 101300 150*cc02d7e2SAndroid Build Coastguard Worker if (@available(iOS 8.0, macOS 10.10, *)) { 151*cc02d7e2SAndroid Build Coastguard Worker _dispatchQueue = dispatch_queue_create( 152*cc02d7e2SAndroid Build Coastguard Worker NULL, 153*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); 154*cc02d7e2SAndroid Build Coastguard Worker } else { 155*cc02d7e2SAndroid Build Coastguard Worker#else 156*cc02d7e2SAndroid Build Coastguard Worker { 157*cc02d7e2SAndroid Build Coastguard Worker#endif 158*cc02d7e2SAndroid Build Coastguard Worker _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 159*cc02d7e2SAndroid Build Coastguard Worker } 160*cc02d7e2SAndroid Build Coastguard Worker dispatch_set_target_queue(_dispatchQueue, handler.dispatchQueue); 161*cc02d7e2SAndroid Build Coastguard Worker 162*cc02d7e2SAndroid Build Coastguard Worker _call = [[GRPCCall2 alloc] initWithRequestOptions:_requestOptions 163*cc02d7e2SAndroid Build Coastguard Worker responseHandler:self 164*cc02d7e2SAndroid Build Coastguard Worker callOptions:_callOptions]; 165*cc02d7e2SAndroid Build Coastguard Worker } 166*cc02d7e2SAndroid Build Coastguard Worker return self; 167*cc02d7e2SAndroid Build Coastguard Worker} 168*cc02d7e2SAndroid Build Coastguard Worker 169*cc02d7e2SAndroid Build Coastguard Worker- (void)start { 170*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *copiedCall; 171*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 172*cc02d7e2SAndroid Build Coastguard Worker copiedCall = _call; 173*cc02d7e2SAndroid Build Coastguard Worker } 174*cc02d7e2SAndroid Build Coastguard Worker [copiedCall start]; 175*cc02d7e2SAndroid Build Coastguard Worker} 176*cc02d7e2SAndroid Build Coastguard Worker 177*cc02d7e2SAndroid Build Coastguard Worker- (void)cancel { 178*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *copiedCall; 179*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 180*cc02d7e2SAndroid Build Coastguard Worker copiedCall = _call; 181*cc02d7e2SAndroid Build Coastguard Worker _call = nil; 182*cc02d7e2SAndroid Build Coastguard Worker if ([_handler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { 183*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 184*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 185*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 186*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 187*cc02d7e2SAndroid Build Coastguard Worker self->_handler = nil; 188*cc02d7e2SAndroid Build Coastguard Worker } 189*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler didCloseWithTrailingMetadata:nil 190*cc02d7e2SAndroid Build Coastguard Worker error:[NSError errorWithDomain:kGRPCErrorDomain 191*cc02d7e2SAndroid Build Coastguard Worker code:GRPCErrorCodeCancelled 192*cc02d7e2SAndroid Build Coastguard Worker userInfo:@{ 193*cc02d7e2SAndroid Build Coastguard Worker NSLocalizedDescriptionKey : 194*cc02d7e2SAndroid Build Coastguard Worker @"Canceled by app" 195*cc02d7e2SAndroid Build Coastguard Worker }]]; 196*cc02d7e2SAndroid Build Coastguard Worker }); 197*cc02d7e2SAndroid Build Coastguard Worker } else { 198*cc02d7e2SAndroid Build Coastguard Worker _handler = nil; 199*cc02d7e2SAndroid Build Coastguard Worker } 200*cc02d7e2SAndroid Build Coastguard Worker } 201*cc02d7e2SAndroid Build Coastguard Worker [copiedCall cancel]; 202*cc02d7e2SAndroid Build Coastguard Worker} 203*cc02d7e2SAndroid Build Coastguard Worker 204*cc02d7e2SAndroid Build Coastguard Worker- (void)writeMessage:(GPBMessage *)message { 205*cc02d7e2SAndroid Build Coastguard Worker NSAssert([message isKindOfClass:[GPBMessage class]], @"Parameter message must be a GPBMessage"); 206*cc02d7e2SAndroid Build Coastguard Worker if (![message isKindOfClass:[GPBMessage class]]) { 207*cc02d7e2SAndroid Build Coastguard Worker NSLog(@"Failed to send a message that is non-proto."); 208*cc02d7e2SAndroid Build Coastguard Worker return; 209*cc02d7e2SAndroid Build Coastguard Worker } 210*cc02d7e2SAndroid Build Coastguard Worker 211*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *copiedCall; 212*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 213*cc02d7e2SAndroid Build Coastguard Worker copiedCall = _call; 214*cc02d7e2SAndroid Build Coastguard Worker } 215*cc02d7e2SAndroid Build Coastguard Worker [copiedCall writeData:[message data]]; 216*cc02d7e2SAndroid Build Coastguard Worker} 217*cc02d7e2SAndroid Build Coastguard Worker 218*cc02d7e2SAndroid Build Coastguard Worker- (void)finish { 219*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *copiedCall; 220*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 221*cc02d7e2SAndroid Build Coastguard Worker copiedCall = _call; 222*cc02d7e2SAndroid Build Coastguard Worker _call = nil; 223*cc02d7e2SAndroid Build Coastguard Worker } 224*cc02d7e2SAndroid Build Coastguard Worker [copiedCall finish]; 225*cc02d7e2SAndroid Build Coastguard Worker} 226*cc02d7e2SAndroid Build Coastguard Worker 227*cc02d7e2SAndroid Build Coastguard Worker- (void)receiveNextMessage { 228*cc02d7e2SAndroid Build Coastguard Worker [self receiveNextMessages:1]; 229*cc02d7e2SAndroid Build Coastguard Worker} 230*cc02d7e2SAndroid Build Coastguard Worker- (void)receiveNextMessages:(NSUInteger)numberOfMessages { 231*cc02d7e2SAndroid Build Coastguard Worker GRPCCall2 *copiedCall; 232*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 233*cc02d7e2SAndroid Build Coastguard Worker copiedCall = _call; 234*cc02d7e2SAndroid Build Coastguard Worker } 235*cc02d7e2SAndroid Build Coastguard Worker [copiedCall receiveNextMessages:numberOfMessages]; 236*cc02d7e2SAndroid Build Coastguard Worker} 237*cc02d7e2SAndroid Build Coastguard Worker 238*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata { 239*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 240*cc02d7e2SAndroid Build Coastguard Worker if (initialMetadata != nil && 241*cc02d7e2SAndroid Build Coastguard Worker [_handler respondsToSelector:@selector(didReceiveInitialMetadata:)]) { 242*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 243*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 244*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 245*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 246*cc02d7e2SAndroid Build Coastguard Worker } 247*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler didReceiveInitialMetadata:initialMetadata]; 248*cc02d7e2SAndroid Build Coastguard Worker }); 249*cc02d7e2SAndroid Build Coastguard Worker } 250*cc02d7e2SAndroid Build Coastguard Worker } 251*cc02d7e2SAndroid Build Coastguard Worker} 252*cc02d7e2SAndroid Build Coastguard Worker 253*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveData:(id)data { 254*cc02d7e2SAndroid Build Coastguard Worker if (data == nil) return; 255*cc02d7e2SAndroid Build Coastguard Worker 256*cc02d7e2SAndroid Build Coastguard Worker NSError *error = nil; 257*cc02d7e2SAndroid Build Coastguard Worker GPBMessage *parsed = [_responseClass parseFromData:data error:&error]; 258*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 259*cc02d7e2SAndroid Build Coastguard Worker if (parsed && [_handler respondsToSelector:@selector(didReceiveProtoMessage:)]) { 260*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 261*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 262*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 263*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 264*cc02d7e2SAndroid Build Coastguard Worker } 265*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler didReceiveProtoMessage:parsed]; 266*cc02d7e2SAndroid Build Coastguard Worker }); 267*cc02d7e2SAndroid Build Coastguard Worker } else if (!parsed && [_handler respondsToSelector:@selector(didCloseWithTrailingMetadata: 268*cc02d7e2SAndroid Build Coastguard Worker error:)]) { 269*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 270*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 271*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 272*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 273*cc02d7e2SAndroid Build Coastguard Worker self->_handler = nil; 274*cc02d7e2SAndroid Build Coastguard Worker } 275*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler 276*cc02d7e2SAndroid Build Coastguard Worker didCloseWithTrailingMetadata:nil 277*cc02d7e2SAndroid Build Coastguard Worker error:ErrorForBadProto(data, self->_responseClass, error)]; 278*cc02d7e2SAndroid Build Coastguard Worker }); 279*cc02d7e2SAndroid Build Coastguard Worker [_call cancel]; 280*cc02d7e2SAndroid Build Coastguard Worker _call = nil; 281*cc02d7e2SAndroid Build Coastguard Worker } 282*cc02d7e2SAndroid Build Coastguard Worker } 283*cc02d7e2SAndroid Build Coastguard Worker} 284*cc02d7e2SAndroid Build Coastguard Worker 285*cc02d7e2SAndroid Build Coastguard Worker- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { 286*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 287*cc02d7e2SAndroid Build Coastguard Worker if ([_handler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { 288*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 289*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 290*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 291*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 292*cc02d7e2SAndroid Build Coastguard Worker self->_handler = nil; 293*cc02d7e2SAndroid Build Coastguard Worker } 294*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler didCloseWithTrailingMetadata:trailingMetadata error:error]; 295*cc02d7e2SAndroid Build Coastguard Worker }); 296*cc02d7e2SAndroid Build Coastguard Worker } 297*cc02d7e2SAndroid Build Coastguard Worker _call = nil; 298*cc02d7e2SAndroid Build Coastguard Worker } 299*cc02d7e2SAndroid Build Coastguard Worker} 300*cc02d7e2SAndroid Build Coastguard Worker 301*cc02d7e2SAndroid Build Coastguard Worker- (void)didWriteData { 302*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 303*cc02d7e2SAndroid Build Coastguard Worker if ([_handler respondsToSelector:@selector(didWriteMessage)]) { 304*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(_dispatchQueue, ^{ 305*cc02d7e2SAndroid Build Coastguard Worker id<GRPCProtoResponseHandler> copiedHandler = nil; 306*cc02d7e2SAndroid Build Coastguard Worker @synchronized(self) { 307*cc02d7e2SAndroid Build Coastguard Worker copiedHandler = self->_handler; 308*cc02d7e2SAndroid Build Coastguard Worker } 309*cc02d7e2SAndroid Build Coastguard Worker [copiedHandler didWriteMessage]; 310*cc02d7e2SAndroid Build Coastguard Worker }); 311*cc02d7e2SAndroid Build Coastguard Worker } 312*cc02d7e2SAndroid Build Coastguard Worker } 313*cc02d7e2SAndroid Build Coastguard Worker} 314*cc02d7e2SAndroid Build Coastguard Worker 315*cc02d7e2SAndroid Build Coastguard Worker- (dispatch_queue_t)dispatchQueue { 316*cc02d7e2SAndroid Build Coastguard Worker return _dispatchQueue; 317*cc02d7e2SAndroid Build Coastguard Worker} 318*cc02d7e2SAndroid Build Coastguard Worker 319*cc02d7e2SAndroid Build Coastguard Worker@end 320*cc02d7e2SAndroid Build Coastguard Worker 321*cc02d7e2SAndroid Build Coastguard Worker/** 322*cc02d7e2SAndroid Build Coastguard Worker * Generate an NSError object that represents a failure in parsing a proto class. 323*cc02d7e2SAndroid Build Coastguard Worker */ 324*cc02d7e2SAndroid Build Coastguard WorkerNSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) { 325*cc02d7e2SAndroid Build Coastguard Worker NSDictionary *info = @{ 326*cc02d7e2SAndroid Build Coastguard Worker NSLocalizedDescriptionKey : @"Unable to parse response from the server", 327*cc02d7e2SAndroid Build Coastguard Worker NSLocalizedRecoverySuggestionErrorKey : 328*cc02d7e2SAndroid Build Coastguard Worker @"If this RPC is idempotent, retry " 329*cc02d7e2SAndroid Build Coastguard Worker @"with exponential backoff. Otherwise, query the server status before " 330*cc02d7e2SAndroid Build Coastguard Worker @"retrying.", 331*cc02d7e2SAndroid Build Coastguard Worker NSUnderlyingErrorKey : parsingError, 332*cc02d7e2SAndroid Build Coastguard Worker @"Expected class" : expectedClass, 333*cc02d7e2SAndroid Build Coastguard Worker @"Received value" : proto, 334*cc02d7e2SAndroid Build Coastguard Worker }; 335*cc02d7e2SAndroid Build Coastguard Worker // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public. 336*cc02d7e2SAndroid Build Coastguard Worker return [NSError errorWithDomain:@"io.grpc" code:13 userInfo:info]; 337*cc02d7e2SAndroid Build Coastguard Worker} 338