blob: 5dca971b08a470ee622d8a5698d219f452641f3a [file] [log] [blame]
Jorge Canizales2779ccb2015-04-20 23:42:46 -07001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Jorge Canizales2779ccb2015-04-20 23:42:46 -07004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Jorge Canizales2779ccb2015-04-20 23:42:46 -07008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Jorge Canizales2779ccb2015-04-20 23:42:46 -070010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Jorge Canizales2779ccb2015-04-20 23:42:46 -070016 *
17 */
18
19#import "ProtoRPC.h"
20
Jorge Canizales800a71c2016-07-13 14:44:33 -070021#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
Muxi Yanc92d90a2018-04-11 18:10:02 -070022#import <Protobuf/GPBProtocolBuffers.h>
Jorge Canizales800a71c2016-07-13 14:44:33 -070023#else
Muxi Yanc92d90a2018-04-11 18:10:02 -070024#import <GPBProtocolBuffers.h>
Jorge Canizales800a71c2016-07-13 14:44:33 -070025#endif
Jorge Canizales3936ed72015-06-21 14:43:56 -070026#import <RxLibrary/GRXWriteable.h>
Jorge Canizales3936ed72015-06-21 14:43:56 -070027#import <RxLibrary/GRXWriter+Transformations.h>
Jorge Canizales2779ccb2015-04-20 23:42:46 -070028
Jorge Canizalesafb7a0e2015-09-03 02:39:36 -070029static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) {
30 NSDictionary *info = @{
Muxi Yanc92d90a2018-04-11 18:10:02 -070031 NSLocalizedDescriptionKey : @"Unable to parse response from the server",
32 NSLocalizedRecoverySuggestionErrorKey :
33 @"If this RPC is idempotent, retry "
34 @"with exponential backoff. Otherwise, query the server status before "
35 @"retrying.",
36 NSUnderlyingErrorKey : parsingError,
37 @"Expected class" : expectedClass,
38 @"Received value" : proto,
39 };
Jorge Canizalesafb7a0e2015-09-03 02:39:36 -070040 // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public.
Muxi Yanc92d90a2018-04-11 18:10:02 -070041 return [NSError errorWithDomain:@"io.grpc" code:13 userInfo:info];
Jorge Canizalesafb7a0e2015-09-03 02:39:36 -070042}
43
Muxi Yan45ca2022018-02-05 11:48:00 -080044#pragma clang diagnostic push
45#pragma clang diagnostic ignored "-Wdeprecated-implementations"
Jorge Canizales2779ccb2015-04-20 23:42:46 -070046@implementation ProtoRPC {
Muxi Yan45ca2022018-02-05 11:48:00 -080047#pragma clang diagnostic pop
Jorge Canizales2779ccb2015-04-20 23:42:46 -070048 id<GRXWriteable> _responseWriteable;
49}
50
Jorge Canizalesbe808e32015-07-04 14:37:58 -070051#pragma clang diagnostic push
52#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
Jorge Canizales2779ccb2015-04-20 23:42:46 -070053- (instancetype)initWithHost:(NSString *)host
Jorge Canizalesbe808e32015-07-04 14:37:58 -070054 path:(NSString *)path
Jorge Canizales56047122015-07-17 12:18:08 -070055 requestsWriter:(GRXWriter *)requestsWriter {
Jorge Canizalesbe808e32015-07-04 14:37:58 -070056 [NSException raise:NSInvalidArgumentException
57 format:@"Please use ProtoRPC's designated initializer instead."];
58 return nil;
Jorge Canizales2779ccb2015-04-20 23:42:46 -070059}
Jorge Canizalesbe808e32015-07-04 14:37:58 -070060#pragma clang diagnostic pop
Jorge Canizales2779ccb2015-04-20 23:42:46 -070061
62// Designated initializer
63- (instancetype)initWithHost:(NSString *)host
Makarand Dharmapurikar6f950102016-06-07 16:52:19 -070064 method:(GRPCProtoMethod *)method
Jorge Canizales56047122015-07-17 12:18:08 -070065 requestsWriter:(GRXWriter *)requestsWriter
Jorge Canizales2779ccb2015-04-20 23:42:46 -070066 responseClass:(Class)responseClass
67 responsesWriteable:(id<GRXWriteable>)responsesWriteable {
68 // Because we can't tell the type system to constrain the class, we need to check at runtime:
Marc Wandschneiderf03d2662015-05-27 12:48:24 -070069 if (![responseClass respondsToSelector:@selector(parseFromData:error:)]) {
Jorge Canizales2779ccb2015-04-20 23:42:46 -070070 [NSException raise:NSInvalidArgumentException
71 format:@"A protobuf class to parse the responses must be provided."];
72 }
Jorge Canizales6482d5d2015-04-21 01:30:00 -070073 // A writer that serializes the proto messages to send.
Jorge Canizales29f55d52015-07-17 12:19:13 -070074 GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) {
Jorge Canizalese7209c52015-09-03 01:53:06 -070075 if (![proto isKindOfClass:GPBMessage.class]) {
76 [NSException raise:NSInvalidArgumentException
77 format:@"Request must be a proto message: %@", proto];
78 }
Jorge Canizales29f55d52015-07-17 12:19:13 -070079 return [proto data];
80 }];
Jorge Canizales5260f532015-07-04 14:47:41 -070081 if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) {
Jorge Canizalese7209c52015-09-03 01:53:06 -070082 __weak ProtoRPC *weakSelf = self;
83
Jorge Canizales6482d5d2015-04-21 01:30:00 -070084 // A writeable that parses the proto messages received.
Jorge Canizales2779ccb2015-04-20 23:42:46 -070085 _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
Jorge Canizalese7209c52015-09-03 01:53:06 -070086 // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread.
87 NSError *error = nil;
88 id parsed = [responseClass parseFromData:value error:&error];
89 if (parsed) {
90 [responsesWriteable writeValue:parsed];
91 } else {
Jorge Canizalesafb7a0e2015-09-03 02:39:36 -070092 [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)];
Jorge Canizalese7209c52015-09-03 01:53:06 -070093 }
Muxi Yanc92d90a2018-04-11 18:10:02 -070094 }
95 completionHandler:^(NSError *errorOrNil) {
96 [responsesWriteable writesFinishedWithError:errorOrNil];
97 }];
Jorge Canizales2779ccb2015-04-20 23:42:46 -070098 }
99 return self;
100}
101
102- (void)start {
103 [self startWithWriteable:_responseWriteable];
104}
105
106- (void)startWithWriteable:(id<GRXWriteable>)writeable {
107 [super startWithWriteable:writeable];
Jorge Canizales6482d5d2015-04-21 01:30:00 -0700108 // Break retain cycles.
Jorge Canizales2779ccb2015-04-20 23:42:46 -0700109 _responseWriteable = nil;
110}
111@end
Makarand Dharmapurikar6f950102016-06-07 16:52:19 -0700112
Makarand Dharmapurikar4f11ab12016-06-08 10:40:00 -0700113@implementation GRPCProtoCall
Makarand Dharmapurikar6f950102016-06-07 16:52:19 -0700114
115@end