From b9692ca809e4318ae87995cae771e40c04f7c27f Mon Sep 17 00:00:00 2001
From: Jorge Canizales <jcanizales@google.com>
Date: Tue, 2 Jun 2015 12:01:16 -0700
Subject: [PATCH] Fix NSDictionary <-> metadata codec

---
 .../GRPCClient/private/NSDictionary+GRPC.m    | 73 ++++++++++++++++---
 1 file changed, 63 insertions(+), 10 deletions(-)

diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
index c350f32f2a..7705aa46a1 100644
--- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
+++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
@@ -35,21 +35,77 @@
 
 #include <grpc/support/alloc.h>
 
+#pragma mark Category for binary metadata elements
+
+@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;
+@end
+
+@implementation NSData (GRPCMetadata)
++ (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata {
+  // TODO(jcanizales): Should we use a non-copy constructor?
+  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;
+  metadata->value = self.bytes;
+  metadata->value_length = self.length;
+}
+@end
+
+#pragma mark Category for textual metadata elements
+
+@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;
+@end
+
+@implementation NSString (GRPCMetadata)
++ (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata {
+  return [[self alloc] initWithBytes:metadata->value
+                              length:metadata->value_length
+                            encoding:NSASCIIStringEncoding];
+}
+
+- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key {
+  // TODO(jcanizales): Encode Unicode chars as ASCII.
+  metadata->key = key.UTF8String;
+  metadata->value = self.UTF8String;
+  metadata->value_length = self.length;
+}
+@end
+
+#pragma mark Category for metadata arrays
+
 @implementation NSDictionary (GRPC)
 + (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 stringWithUTF8String:entry->key];
+    // 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?
       continue;
     }
+    id value;
+    if ([name hasSuffix:@"-bin"]) {
+      name = [name substringToIndex:name.length - 4];
+      value = [NSData grpc_dataFromMetadataValue:entry];
+    } else {
+      value = [NSString grpc_stringFromMetadataValue:entry];
+    }
     if (!metadata[name]) {
       metadata[name] = [NSMutableArray array];
     }
-    // TODO(jcanizales): Should we use a non-copy constructor?
-    [metadata[name] addObject:[NSData dataWithBytes:entry->value
-                                             length:entry->value_length]];
+    [metadata[name] addObject:value];
   }
   return metadata;
 }
@@ -60,11 +116,8 @@
   for (id key in self) {
     id value = self[key];
     grpc_metadata *current = &metadata[i];
-    current->key = [key UTF8String];
-    if ([value isKindOfClass:[NSData class]]) {
-      current->value = [value bytes];
-    } else if ([value isKindOfClass:[NSString class]]) {
-      current->value = [value UTF8String];
+    if ([value respondsToSelector:@selector(grpc_initMetadata:withKey:)]) {
+      [value grpc_initMetadata:current withKey:key];
     } else {
       [NSException raise:NSInvalidArgumentException
                   format:@"Metadata values must be NSString or NSData."];
-- 
GitLab