From 84fa531e4d5a3ca295ffdf93b3b1d676b3dec26f Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 28 Aug 2015 10:55:55 -0700 Subject: [PATCH 01/13] Added class for setting request headers on a call --- src/objective-c/GRPCClient/GRPCCall+OAuth2.m | 1 + src/objective-c/GRPCClient/GRPCCall.h | 4 +- src/objective-c/GRPCClient/GRPCCall.m | 18 ++- .../GRPCClient/private/GRPCRequestHeaders.h | 51 +++++++ .../GRPCClient/private/GRPCRequestHeaders.m | 128 ++++++++++++++++++ 5 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 src/objective-c/GRPCClient/private/GRPCRequestHeaders.h create mode 100644 src/objective-c/GRPCClient/private/GRPCRequestHeaders.m diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m index 83b0de18e32..cb2cee17d77 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m @@ -32,6 +32,7 @@ */ #import "GRPCCall+OAuth2.h" +#import "private/GRPCRequestHeaders.h" static NSString * const kAuthorizationHeader = @"authorization"; static NSString * const kBearerPrefix = @"Bearer "; diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 4eda499b1a8..975ff8feffe 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -48,6 +48,8 @@ #import #import +@class GRPCRequestHeaders; + // Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by // the server. extern id const kGRPCHeadersKey; @@ -70,7 +72,7 @@ extern id const kGRPCTrailersKey; // // For convenience, the property is initialized to an empty NSMutableDictionary, and the setter // accepts (and copies) both mutable and immutable dictionaries. -- (NSMutableDictionary *)requestHeaders; // nonatomic +- (GRPCRequestHeaders *)requestHeaders; // nonatomic - (void)setRequestHeaders:(NSDictionary *)requestHeaders; // nonatomic, copy // This dictionary is populated with the HTTP headers received from the server. This happens before diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index ff5d1c5aaff..afa01e2f0fe 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -41,6 +41,7 @@ #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" #import "private/NSError+GRPC.h" +#import "private/GRPCRequestHeaders.h" NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey"; NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; @@ -93,7 +94,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; // the response arrives. GRPCCall *_retainSelf; - NSMutableDictionary *_requestHeaders; + GRPCRequestHeaders *_requestHeaders; } @synthesize state = _state; @@ -124,19 +125,23 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; _requestWriter = requestWriter; - _requestHeaders = [NSMutableDictionary dictionary]; + _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; } return self; } #pragma mark Metadata -- (NSMutableDictionary *)requestHeaders { +- (GRPCRequestHeaders *)requestHeaders { return _requestHeaders; } - (void)setRequestHeaders:(NSDictionary *)requestHeaders { - _requestHeaders = [NSMutableDictionary dictionaryWithDictionary:requestHeaders]; + GRPCRequestHeaders *newHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; + for (id key in requestHeaders) { + newHeaders[key] = requestHeaders[key]; + } + _requestHeaders = newHeaders; } #pragma mark Finish @@ -230,10 +235,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Send headers -- (void)sendHeaders:(NSDictionary *)headers { +- (void)sendHeaders:(GRPCRequestHeaders *)headers { // TODO(jcanizales): Add error handlers for async failures [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] - initWithMetadata:headers ?: @{} handler:nil]]]; + initWithMetadata:[headers asDictionary] ?: @{} + handler:nil]]]; } #pragma mark GRXWriteable implementation diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h new file mode 100644 index 00000000000..320545190fa --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -0,0 +1,51 @@ +/* + * + * 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 +#include + +@class GRPCCall; + +@interface GRPCRequestHeaders : NSObject + +- (instancetype)initWithCall:(GRPCCall *)call; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)aKey; + +- (NSDictionary *)asDictionary; + +@end \ No newline at end of file diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m new file mode 100644 index 00000000000..f479ed7501c --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -0,0 +1,128 @@ +/* + * + * 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 +#import "GRPCRequestHeaders.h" +#import "GRPCCall.h" + +static NSString* normalizeKey(NSString* key) { + if ([key canBeConvertedToEncoding:NSASCIIStringEncoding]) { + return [key lowercaseString]; + } else { + return nil; + } +} + +static bool isKeyValuePairValid(NSString *key, id value) { + if ([key hasSuffix:@"-bin"]) { + if (![value isKindOfClass:[NSData class]]) { + return false; + } + } else { + if (![value isKindOfClass:[NSString class]]) { + return false; + } + } + return true; +} + +@implementation GRPCRequestHeaders { + __weak GRPCCall *_call; + NSMutableDictionary *_proxy; +} + +- (instancetype) initWithCall:(GRPCCall *)call { + self = [super init]; + if (self) { + _call = call; + _proxy = [NSMutableDictionary dictionary]; + } + return self; +} + +- (id) objectForKeyedSubscript:(NSString *)key { + NSString *normalizedKey = normalizeKey(key); + if (normalizedKey) { + return _proxy[normalizedKey]; + } else { + return [NSNull null]; + } +} + +- (void) setObject:(id)obj forKeyedSubscript:(NSString *)key { + if (_call.state == GRXWriterStateNotStarted) { + NSString *normalizedKey = normalizeKey(key); + if (normalizedKey) { + if (isKeyValuePairValid(key, obj)) { + _proxy[normalizedKey] = obj; + } else { + [NSException raise:@"Invalid key/value pair" + format:@"Key %@ could not be added with value %@", key, obj]; + } + } else { + [NSException raise:@"Invalid key" format:@"Key %@ contains illegal characters", key]; + } + } else { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request metadata after call is started"]; + } +} + +- (void) removeObjectForKey:(NSString *)aKey { + if (_call.state == GRXWriterStateNotStarted) { + NSString *normalizedKey = normalizeKey(aKey); + if (normalizedKey) { + [_proxy removeObjectForKey:normalizedKey]; + } else { + [NSException raise:@"Invalid key" format:@"Key %@ contains illegal characters", aKey]; + } + } else { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request metadata after call is started"]; + } +} + +- (void) removeAllObjects { + if (_call.state == GRXWriterStateNotStarted) { + [_proxy removeAllObjects]; + } else { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request metadata after call is started"]; + } +} + +- (NSDictionary *)asDictionary { + return [NSDictionary dictionaryWithDictionary:_proxy]; +} + +@end \ No newline at end of file From 86e5a050f7db82f49e92bc79be3a0f8623353be3 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Tue, 1 Sep 2015 10:52:23 -0700 Subject: [PATCH 02/13] Make GRPCRequestHeaders public and import from GRPCCall --- src/objective-c/GRPCClient/GRPCCall+OAuth2.m | 1 - src/objective-c/GRPCClient/GRPCCall.h | 2 +- src/objective-c/GRPCClient/GRPCCall.m | 1 - src/objective-c/GRPCClient/{private => }/GRPCRequestHeaders.h | 0 src/objective-c/GRPCClient/{private => }/GRPCRequestHeaders.m | 0 5 files changed, 1 insertion(+), 3 deletions(-) rename src/objective-c/GRPCClient/{private => }/GRPCRequestHeaders.h (100%) rename src/objective-c/GRPCClient/{private => }/GRPCRequestHeaders.m (100%) diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m index cb2cee17d77..83b0de18e32 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m @@ -32,7 +32,6 @@ */ #import "GRPCCall+OAuth2.h" -#import "private/GRPCRequestHeaders.h" static NSString * const kAuthorizationHeader = @"authorization"; static NSString * const kBearerPrefix = @"Bearer "; diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 975ff8feffe..1487001da5c 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -48,7 +48,7 @@ #import #import -@class GRPCRequestHeaders; +#import "GRPCRequestHeaders.h" // Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by // the server. diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index afa01e2f0fe..e6d845c9c26 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -41,7 +41,6 @@ #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" #import "private/NSError+GRPC.h" -#import "private/GRPCRequestHeaders.h" NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey"; NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/GRPCRequestHeaders.h similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCRequestHeaders.h rename to src/objective-c/GRPCClient/GRPCRequestHeaders.h diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/GRPCRequestHeaders.m similarity index 100% rename from src/objective-c/GRPCClient/private/GRPCRequestHeaders.m rename to src/objective-c/GRPCClient/GRPCRequestHeaders.m From 5c339d15b322952c71b7f3ecef94f6d2eb7dd2b4 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 17:41:43 -0700 Subject: [PATCH 03/13] Remove -[asDictionary] --- src/objective-c/GRPCClient/GRPCCall.m | 5 ++--- src/objective-c/GRPCClient/GRPCRequestHeaders.h | 5 +++-- src/objective-c/GRPCClient/GRPCRequestHeaders.m | 10 ++++++++-- src/objective-c/GRPCClient/private/GRPCWrappedCall.h | 4 +++- src/objective-c/GRPCClient/private/GRPCWrappedCall.m | 4 +++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index e6d845c9c26..d5f775c6e63 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -236,9 +236,8 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; - (void)sendHeaders:(GRPCRequestHeaders *)headers { // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] - initWithMetadata:[headers asDictionary] ?: @{} - handler:nil]]]; + [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers + handler:nil]]]; } #pragma mark GRXWriteable implementation diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/GRPCRequestHeaders.h index 320545190fa..5a93f82cd8b 100644 --- a/src/objective-c/GRPCClient/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/GRPCRequestHeaders.h @@ -38,6 +38,9 @@ @interface GRPCRequestHeaders : NSObject +@property(nonatomic, readonly) NSUInteger count; +@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; + - (instancetype)initWithCall:(GRPCCall *)call; - (id)objectForKeyedSubscript:(NSString *)key; @@ -46,6 +49,4 @@ - (void)removeAllObjects; - (void)removeObjectForKey:(NSString *)aKey; -- (NSDictionary *)asDictionary; - @end \ No newline at end of file diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/GRPCRequestHeaders.m index f479ed7501c..041bc4e7a86 100644 --- a/src/objective-c/GRPCClient/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/GRPCRequestHeaders.m @@ -34,6 +34,7 @@ #import #import "GRPCRequestHeaders.h" #import "GRPCCall.h" +#import "NSDictionary+GRPC.h" static NSString* normalizeKey(NSString* key) { if ([key canBeConvertedToEncoding:NSASCIIStringEncoding]) { @@ -121,8 +122,13 @@ static bool isKeyValuePairValid(NSString *key, id value) { } } -- (NSDictionary *)asDictionary { - return [NSDictionary dictionaryWithDictionary:_proxy]; +// TODO(jcanizales): Just forward all invocations? + +- (NSUInteger)count { + return _proxy.count; } +- (grpc_metadata *)grpc_metadataArray { + return _proxy.grpc_metadataArray; +} @end \ No newline at end of file diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index da11cbb761b..ba34c0a6e79 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -36,6 +36,8 @@ #import "GRPCChannel.h" +@class GRPCRequestHeaders; + @interface GRPCOperation : NSObject @property(nonatomic, readonly) grpc_op op; // Guaranteed to be called when the operation has finished. @@ -44,7 +46,7 @@ @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(NSDictionary *)metadata +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index fe3d51da53a..3cb5613bd07 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -38,6 +38,8 @@ #include #include +#import "GRPCRequestHeaders.h" + #import "GRPCCompletionQueue.h" #import "GRPCHost.h" #import "NSDictionary+GRPC.h" @@ -65,7 +67,7 @@ return [self initWithMetadata:nil handler:nil]; } -- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count; From 030e5d0c9c2b9abd17dc061a3a682114a4d2cc38 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 17:52:30 -0700 Subject: [PATCH 04/13] nit: Style guide --- .../GRPCClient/GRPCRequestHeaders.m | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/GRPCRequestHeaders.m index 041bc4e7a86..23140cb78a6 100644 --- a/src/objective-c/GRPCClient/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/GRPCRequestHeaders.m @@ -31,12 +31,14 @@ * */ -#import #import "GRPCRequestHeaders.h" + +#import + #import "GRPCCall.h" #import "NSDictionary+GRPC.h" -static NSString* normalizeKey(NSString* key) { +static NSString* NormalizeKey(NSString* key) { if ([key canBeConvertedToEncoding:NSASCIIStringEncoding]) { return [key lowercaseString]; } else { @@ -44,7 +46,7 @@ static NSString* normalizeKey(NSString* key) { } } -static bool isKeyValuePairValid(NSString *key, id value) { +static bool IsKeyValuePairValid(NSString *key, id value) { if ([key hasSuffix:@"-bin"]) { if (![value isKindOfClass:[NSData class]]) { return false; @@ -62,17 +64,16 @@ static bool isKeyValuePairValid(NSString *key, id value) { NSMutableDictionary *_proxy; } -- (instancetype) initWithCall:(GRPCCall *)call { - self = [super init]; - if (self) { +- (instancetype)initWithCall:(GRPCCall *)call { + if ((self = [super init])) { _call = call; _proxy = [NSMutableDictionary dictionary]; } return self; } -- (id) objectForKeyedSubscript:(NSString *)key { - NSString *normalizedKey = normalizeKey(key); +- (id)objectForKeyedSubscript:(NSString *)key { + NSString *normalizedKey = NormalizeKey(key); if (normalizedKey) { return _proxy[normalizedKey]; } else { @@ -80,11 +81,11 @@ static bool isKeyValuePairValid(NSString *key, id value) { } } -- (void) setObject:(id)obj forKeyedSubscript:(NSString *)key { +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { if (_call.state == GRXWriterStateNotStarted) { - NSString *normalizedKey = normalizeKey(key); + NSString *normalizedKey = NormalizeKey(key); if (normalizedKey) { - if (isKeyValuePairValid(key, obj)) { + if (IsKeyValuePairValid(key, obj)) { _proxy[normalizedKey] = obj; } else { [NSException raise:@"Invalid key/value pair" @@ -99,9 +100,9 @@ static bool isKeyValuePairValid(NSString *key, id value) { } } -- (void) removeObjectForKey:(NSString *)aKey { +- (void)removeObjectForKey:(NSString *)aKey { if (_call.state == GRXWriterStateNotStarted) { - NSString *normalizedKey = normalizeKey(aKey); + NSString *normalizedKey = NormalizeKey(aKey); if (normalizedKey) { [_proxy removeObjectForKey:normalizedKey]; } else { @@ -113,7 +114,7 @@ static bool isKeyValuePairValid(NSString *key, id value) { } } -- (void) removeAllObjects { +- (void)removeAllObjects { if (_call.state == GRXWriterStateNotStarted) { [_proxy removeAllObjects]; } else { From 05640a58dfef890165b49045cd158cc4defe7a86 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 19:35:13 -0700 Subject: [PATCH 05/13] Simpler code, better exceptions. --- .../GRPCClient/GRPCRequestHeaders.m | 85 ++++++++----------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/GRPCRequestHeaders.m index 23140cb78a6..d4bbbdf84a3 100644 --- a/src/objective-c/GRPCClient/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/GRPCRequestHeaders.m @@ -38,25 +38,33 @@ #import "GRPCCall.h" #import "NSDictionary+GRPC.h" +// Used by the setters. static NSString* NormalizeKey(NSString* key) { - if ([key canBeConvertedToEncoding:NSASCIIStringEncoding]) { - return [key lowercaseString]; - } else { - return nil; + if (!key) { + [NSException raise:NSInvalidArgumentException format:@"Key cannot be nil"]; + } + if (![key canBeConvertedToEncoding:NSASCIIStringEncoding]) { + [NSException raise:NSInvalidArgumentException + format:@"Key %@ contains non-ASCII characters", key]; } + return key.lowercaseString; } -static bool IsKeyValuePairValid(NSString *key, id value) { +// Precondition: key isn't nil. +static void CheckKeyValuePairIsValid(NSString *key, id value) { if ([key hasSuffix:@"-bin"]) { - if (![value isKindOfClass:[NSData class]]) { - return false; + if (![value isKindOfClass:NSData.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSData value for header %@ ending in \"-bin\", " + @"instead got %@", key, value]; } } else { - if (![value isKindOfClass:[NSString class]]) { - return false; + if (![value isKindOfClass:NSString.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSString value for header %@ not ending in \"-bin\", " + @"instead got %@", key, value]; } } - return true; } @implementation GRPCRequestHeaders { @@ -72,55 +80,32 @@ static bool IsKeyValuePairValid(NSString *key, id value) { return self; } -- (id)objectForKeyedSubscript:(NSString *)key { - NSString *normalizedKey = NormalizeKey(key); - if (normalizedKey) { - return _proxy[normalizedKey]; - } else { - return [NSNull null]; +- (void)checkCallIsNotStarted { + if (_call.state != GRXWriterStateNotStarted) { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request headers after call is started"]; } } +- (id)objectForKeyedSubscript:(NSString *)key { + return _proxy[key.lowercaseString]; +} + - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { - if (_call.state == GRXWriterStateNotStarted) { - NSString *normalizedKey = NormalizeKey(key); - if (normalizedKey) { - if (IsKeyValuePairValid(key, obj)) { - _proxy[normalizedKey] = obj; - } else { - [NSException raise:@"Invalid key/value pair" - format:@"Key %@ could not be added with value %@", key, obj]; - } - } else { - [NSException raise:@"Invalid key" format:@"Key %@ contains illegal characters", key]; - } - } else { - [NSException raise:@"Invalid modification" - format:@"Cannot modify request metadata after call is started"]; - } + [self checkCallIsNotStarted]; + key = NormalizeKey(key); + CheckKeyValuePairIsValid(key, obj); + _proxy[key] = obj; } -- (void)removeObjectForKey:(NSString *)aKey { - if (_call.state == GRXWriterStateNotStarted) { - NSString *normalizedKey = NormalizeKey(aKey); - if (normalizedKey) { - [_proxy removeObjectForKey:normalizedKey]; - } else { - [NSException raise:@"Invalid key" format:@"Key %@ contains illegal characters", aKey]; - } - } else { - [NSException raise:@"Invalid modification" - format:@"Cannot modify request metadata after call is started"]; - } +- (void)removeObjectForKey:(NSString *)key { + [self checkCallIsNotStarted]; + [_proxy removeObjectForKey:NormalizeKey(key)]; } - (void)removeAllObjects { - if (_call.state == GRXWriterStateNotStarted) { - [_proxy removeAllObjects]; - } else { - [NSException raise:@"Invalid modification" - format:@"Cannot modify request metadata after call is started"]; - } + [self checkCallIsNotStarted]; + [_proxy removeAllObjects]; } // TODO(jcanizales): Just forward all invocations? From 2f10127f85ebb4f81d64e195bee7731a40d00fa2 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 21:55:37 -0700 Subject: [PATCH 06/13] Publish GRPCRequestHeaders as a protocol So we can make the property a dictionary later, by just extending NSMutableDictionary to conform to the protocol. --- src/objective-c/GRPCClient/GRPCCall.h | 17 +++++++++++++++-- src/objective-c/GRPCClient/GRPCCall.m | 5 +++-- .../{ => private}/GRPCRequestHeaders.h | 4 ++-- .../{ => private}/GRPCRequestHeaders.m | 0 .../GRPCClient/private/GRPCWrappedCall.h | 5 ++--- .../GRPCClient/private/GRPCWrappedCall.m | 4 +--- 6 files changed, 23 insertions(+), 12 deletions(-) rename src/objective-c/GRPCClient/{ => private}/GRPCRequestHeaders.h (95%) rename src/objective-c/GRPCClient/{ => private}/GRPCRequestHeaders.m (100%) diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 1487001da5c..fc274a095f8 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -48,13 +48,26 @@ #import #import -#import "GRPCRequestHeaders.h" +#include // 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; +@protocol GRPCRequestHeaders + +@property(nonatomic, readonly) NSUInteger count; +@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; + +@end + // Represents a single gRPC remote call. @interface GRPCCall : GRXWriter @@ -72,7 +85,7 @@ extern id const kGRPCTrailersKey; // // For convenience, the property is initialized to an empty NSMutableDictionary, and the setter // accepts (and copies) both mutable and immutable dictionaries. -- (GRPCRequestHeaders *)requestHeaders; // nonatomic +- (id)requestHeaders; // nonatomic - (void)setRequestHeaders:(NSDictionary *)requestHeaders; // nonatomic, copy // This dictionary is populated with the HTTP headers received from the server. This happens before diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index d5f775c6e63..1be753e688a 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -37,6 +37,7 @@ #include #import +#import "private/GRPCRequestHeaders.h" #import "private/GRPCWrappedCall.h" #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" @@ -131,7 +132,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Metadata -- (GRPCRequestHeaders *)requestHeaders { +- (id)requestHeaders { return _requestHeaders; } @@ -234,7 +235,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Send headers -- (void)sendHeaders:(GRPCRequestHeaders *)headers { +- (void)sendHeaders:(id)headers { // TODO(jcanizales): Add error handlers for async failures [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers handler:nil]]]; diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h similarity index 95% rename from src/objective-c/GRPCClient/GRPCRequestHeaders.h rename to src/objective-c/GRPCClient/private/GRPCRequestHeaders.h index 5a93f82cd8b..066f71a8f6a 100644 --- a/src/objective-c/GRPCClient/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -34,9 +34,9 @@ #import #include -@class GRPCCall; +#import "GRPCCall.h" -@interface GRPCRequestHeaders : NSObject +@interface GRPCRequestHeaders : NSObject @property(nonatomic, readonly) NSUInteger count; @property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; diff --git a/src/objective-c/GRPCClient/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m similarity index 100% rename from src/objective-c/GRPCClient/GRPCRequestHeaders.m rename to src/objective-c/GRPCClient/private/GRPCRequestHeaders.m diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index ba34c0a6e79..9147ba08167 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -35,8 +35,7 @@ #include #import "GRPCChannel.h" - -@class GRPCRequestHeaders; +#import "GRPCCall.h" @interface GRPCOperation : NSObject @property(nonatomic, readonly) grpc_op op; @@ -46,7 +45,7 @@ @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata +- (instancetype)initWithMetadata:(id)metadata handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index 3cb5613bd07..1f8c647fa6f 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -38,8 +38,6 @@ #include #include -#import "GRPCRequestHeaders.h" - #import "GRPCCompletionQueue.h" #import "GRPCHost.h" #import "NSDictionary+GRPC.h" @@ -67,7 +65,7 @@ return [self initWithMetadata:nil handler:nil]; } -- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(id)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count; From 5af286884e793c72a6842de5b2a79ac5e0eaa500 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 22:22:18 -0700 Subject: [PATCH 07/13] Make the grpc_metadataArray property private. --- src/objective-c/GRPCClient/GRPCCall.h | 1 - src/objective-c/GRPCClient/private/GRPCWrappedCall.h | 4 ++-- src/objective-c/GRPCClient/private/GRPCWrappedCall.m | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index fc274a095f8..f4d1838da79 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -58,7 +58,6 @@ extern id const kGRPCTrailersKey; @protocol GRPCRequestHeaders @property(nonatomic, readonly) NSUInteger count; -@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; - (id)objectForKeyedSubscript:(NSString *)key; - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index 9147ba08167..4ca2766147e 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -35,7 +35,7 @@ #include #import "GRPCChannel.h" -#import "GRPCCall.h" +#import "GRPCRequestHeaders.h" @interface GRPCOperation : NSObject @property(nonatomic, readonly) grpc_op op; @@ -45,7 +45,7 @@ @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(id)metadata +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index 1f8c647fa6f..cea7c479e0f 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -65,7 +65,7 @@ return [self initWithMetadata:nil handler:nil]; } -- (instancetype)initWithMetadata:(id)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count; From 12da424c9ae7fe35b926bfb5761ec1a300ccbf0f Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 22:22:34 -0700 Subject: [PATCH 08/13] nit: Documentation and formatting. --- src/objective-c/GRPCClient/GRPCCall.h | 7 +++++++ src/objective-c/GRPCClient/private/GRPCRequestHeaders.h | 4 ++-- src/objective-c/GRPCClient/private/GRPCRequestHeaders.m | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index f4d1838da79..c9b6e6d6e23 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -55,6 +55,13 @@ extern id const kGRPCHeadersKey; extern id const kGRPCTrailersKey; +// 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 @property(nonatomic, readonly) NSUInteger count; diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h index 066f71a8f6a..1391b5725f1 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -47,6 +47,6 @@ - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; - (void)removeAllObjects; -- (void)removeObjectForKey:(NSString *)aKey; +- (void)removeObjectForKey:(NSString *)key; -@end \ No newline at end of file +@end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index d4bbbdf84a3..d0d8de89a13 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -117,4 +117,4 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { - (grpc_metadata *)grpc_metadataArray { return _proxy.grpc_metadataArray; } -@end \ No newline at end of file +@end From acf5e107f3678334c08cc5effdbc73c3ae3f9042 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 22:56:01 -0700 Subject: [PATCH 09/13] Let remove nil or non-ASCII keys (noop instead of throw). --- .../GRPCClient/private/GRPCRequestHeaders.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index d0d8de89a13..50b04a6a872 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -38,8 +38,8 @@ #import "GRPCCall.h" #import "NSDictionary+GRPC.h" -// Used by the setters. -static NSString* NormalizeKey(NSString* key) { +// Used by the setter. +static void CheckKeyIsValid(NSString* key) { if (!key) { [NSException raise:NSInvalidArgumentException format:@"Key cannot be nil"]; } @@ -47,7 +47,6 @@ static NSString* NormalizeKey(NSString* key) { [NSException raise:NSInvalidArgumentException format:@"Key %@ contains non-ASCII characters", key]; } - return key.lowercaseString; } // Precondition: key isn't nil. @@ -93,14 +92,15 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { [self checkCallIsNotStarted]; - key = NormalizeKey(key); + CheckKeyIsValid(key); + key = key.lowercaseString; CheckKeyValuePairIsValid(key, obj); _proxy[key] = obj; } - (void)removeObjectForKey:(NSString *)key { [self checkCallIsNotStarted]; - [_proxy removeObjectForKey:NormalizeKey(key)]; + [_proxy removeObjectForKey:key.lowercaseString]; } - (void)removeAllObjects { From 5f6001eb2330d8f1ba35674ffd6dfa0f7f0ecbe3 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 22:56:43 -0700 Subject: [PATCH 10/13] nit: _proxy -> _delegate --- .../GRPCClient/private/GRPCRequestHeaders.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index 50b04a6a872..7633107f559 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -68,13 +68,13 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { @implementation GRPCRequestHeaders { __weak GRPCCall *_call; - NSMutableDictionary *_proxy; + NSMutableDictionary *_delegate; } - (instancetype)initWithCall:(GRPCCall *)call { if ((self = [super init])) { _call = call; - _proxy = [NSMutableDictionary dictionary]; + _delegate = [NSMutableDictionary dictionary]; } return self; } @@ -87,7 +87,7 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { } - (id)objectForKeyedSubscript:(NSString *)key { - return _proxy[key.lowercaseString]; + return _delegate[key.lowercaseString]; } - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { @@ -95,26 +95,26 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { CheckKeyIsValid(key); key = key.lowercaseString; CheckKeyValuePairIsValid(key, obj); - _proxy[key] = obj; + _delegate[key] = obj; } - (void)removeObjectForKey:(NSString *)key { [self checkCallIsNotStarted]; - [_proxy removeObjectForKey:key.lowercaseString]; + [_delegate removeObjectForKey:key.lowercaseString]; } - (void)removeAllObjects { [self checkCallIsNotStarted]; - [_proxy removeAllObjects]; + [_delegate removeAllObjects]; } // TODO(jcanizales): Just forward all invocations? - (NSUInteger)count { - return _proxy.count; + return _delegate.count; } - (grpc_metadata *)grpc_metadataArray { - return _proxy.grpc_metadataArray; + return _delegate.grpc_metadataArray; } @end From 2529735d3d2c9124484b4c242e29e2fe8e0f938c Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 23:00:23 -0700 Subject: [PATCH 11/13] nit: remove obsolete comment --- src/objective-c/GRPCClient/private/GRPCRequestHeaders.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index 7633107f559..310f1655e49 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -108,8 +108,6 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { [_delegate removeAllObjects]; } -// TODO(jcanizales): Just forward all invocations? - - (NSUInteger)count { return _delegate.count; } From bbba491b7cbf75104d1efcc72653a8498a935f17 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 23:05:53 -0700 Subject: [PATCH 12/13] Reject non-ASCII text header values too. --- .../GRPCClient/private/GRPCRequestHeaders.m | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index 310f1655e49..dfec2a7e7e5 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -39,13 +39,13 @@ #import "NSDictionary+GRPC.h" // Used by the setter. -static void CheckKeyIsValid(NSString* key) { - if (!key) { - [NSException raise:NSInvalidArgumentException format:@"Key cannot be nil"]; +static void CheckIsNonNilASCII(NSString *name, NSString* value) { + if (!value) { + [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name]; } - if (![key canBeConvertedToEncoding:NSASCIIStringEncoding]) { + if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) { [NSException raise:NSInvalidArgumentException - format:@"Key %@ contains non-ASCII characters", key]; + format:@"%@ %@ contains non-ASCII characters", name, value]; } } @@ -63,6 +63,7 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { format:@"Expected NSString value for header %@ not ending in \"-bin\", " @"instead got %@", key, value]; } + CheckIsNonNilASCII(@"Text header value", (NSString *)value); } } @@ -92,7 +93,7 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { [self checkCallIsNotStarted]; - CheckKeyIsValid(key); + CheckIsNonNilASCII(@"Header name", key); key = key.lowercaseString; CheckKeyValuePairIsValid(key, obj); _delegate[key] = obj; From 6697e7f9eaffd0d5882632d235f5a7203fa20cf1 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 2 Sep 2015 23:47:56 -0700 Subject: [PATCH 13/13] =?UTF-8?q?Don=E2=80=99t=20append=20-bin=20automatic?= =?UTF-8?q?ally=20to=20binary=20request=20headers.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GRPCClient/private/NSDictionary+GRPC.m | 44 +++++++------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m index 99c890e4ee7..a7f6d34ed58 100644 --- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m +++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m @@ -40,8 +40,8 @@ @interface NSData (GRPCMetadata) + (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata; -// Fill a metadata object with the binary value in this NSData and the given key. -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key; +// Fill a metadata object with the binary value in this NSData. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; @end @implementation NSData (GRPCMetadata) @@ -50,9 +50,7 @@ return [self dataWithBytes:metadata->value length:metadata->value_length]; } -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key { - // TODO(jcanizales): Encode Unicode chars as ASCII. - metadata->key = [key stringByAppendingString:@"-bin"].UTF8String; +- (void)grpc_initMetadata:(grpc_metadata *)metadata { metadata->value = self.bytes; metadata->value_length = self.length; } @@ -63,8 +61,8 @@ @interface NSString (GRPCMetadata) + (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata; -// Fill a metadata object with the textual value in this NSString and the given key. -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key; +// Fill a metadata object with the textual value in this NSString. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; @end @implementation NSString (GRPCMetadata) @@ -74,22 +72,8 @@ encoding:NSASCIIStringEncoding]; } -- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key { - if ([key hasSuffix:@"-bin"]) { - // Disallow this, as at best it will confuse the server. If the app really needs to send a - // textual header with a name ending in "-bin", it can be done by removing the suffix and - // encoding the NSString as a NSData object. - // - // Why raise an exception: In the most common case, the developer knows this won't happen in - // their code, so the exception isn't triggered. In the rare cases when the developer can't - // tell, it's easy enough to add a sanitizing filter before the header is set. There, the - // developer can choose whether to drop such a header, or trim its name. Doing either ourselves, - // silently, would be very unintuitive for the user. - [NSException raise:NSInvalidArgumentException - format:@"Metadata keys ending in '-bin' are reserved for NSData values."]; - } - // TODO(jcanizales): Encode Unicode chars as ASCII. - metadata->key = key.UTF8String; +// Precondition: This object contains only ASCII characters. +- (void)grpc_initMetadata:(grpc_metadata *)metadata { metadata->value = self.UTF8String; metadata->value_length = self.length; } @@ -124,19 +108,21 @@ return metadata; } +// Preconditions: All keys are ASCII strings. Keys ending in -bin have NSData values; the others +// have NSString values. - (grpc_metadata *)grpc_metadataArray { grpc_metadata *metadata = gpr_malloc([self count] * sizeof(grpc_metadata)); - int i = 0; - for (id key in self) { + grpc_metadata *current = metadata; + for (NSString* key in self) { id value = self[key]; - grpc_metadata *current = &metadata[i]; - if ([value respondsToSelector:@selector(grpc_initMetadata:withKey:)]) { - [value grpc_initMetadata:current withKey:key]; + current->key = key.UTF8String; + if ([value respondsToSelector:@selector(grpc_initMetadata:)]) { + [value grpc_initMetadata:current]; } else { [NSException raise:NSInvalidArgumentException format:@"Metadata values must be NSString or NSData."]; } - i += 1; + ++current; } return metadata; }