Home | History | Annotate | Download | only in private
      1 /*
      2  *
      3  * Copyright 2015 gRPC authors.
      4  *
      5  * 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
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * 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.
     16  *
     17  */
     18 
     19 #import "GRPCRequestHeaders.h"
     20 
     21 #import <Foundation/Foundation.h>
     22 
     23 #import "NSDictionary+GRPC.h"
     24 
     25 // Used by the setter.
     26 static void CheckIsNonNilASCII(NSString *name, NSString *value) {
     27   if (!value) {
     28     [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name];
     29   }
     30   if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) {
     31     [NSException raise:NSInvalidArgumentException
     32                 format:@"%@ %@ contains non-ASCII characters", name, value];
     33   }
     34 }
     35 
     36 // Precondition: key isn't nil.
     37 static void CheckKeyValuePairIsValid(NSString *key, id value) {
     38   if ([key hasSuffix:@"-bin"]) {
     39     if (![value isKindOfClass:NSData.class]) {
     40       [NSException raise:NSInvalidArgumentException
     41                   format:
     42                       @"Expected NSData value for header %@ ending in \"-bin\", "
     43                       @"instead got %@",
     44                       key, value];
     45     }
     46   } else {
     47     if (![value isKindOfClass:NSString.class]) {
     48       [NSException raise:NSInvalidArgumentException
     49                   format:
     50                       @"Expected NSString value for header %@ not ending in \"-bin\", "
     51                       @"instead got %@",
     52                       key, value];
     53     }
     54     CheckIsNonNilASCII(@"Text header value", (NSString *)value);
     55   }
     56 }
     57 
     58 @implementation GRPCRequestHeaders {
     59   __weak GRPCCall *_call;
     60   // The NSMutableDictionary superclass doesn't hold any storage (so that people can implement their
     61   // own in subclasses). As that's not the reason we're subclassing, we just delegate storage to the
     62   // default NSMutableDictionary subclass returned by the cluster (e.g. __NSDictionaryM on iOS 9).
     63   NSMutableDictionary *_delegate;
     64 }
     65 
     66 - (instancetype)init {
     67   return [self initWithCall:nil];
     68 }
     69 
     70 - (instancetype)initWithCapacity:(NSUInteger)numItems {
     71   return [self init];
     72 }
     73 
     74 - (instancetype)initWithCoder:(NSCoder *)aDecoder {
     75   return [self init];
     76 }
     77 
     78 - (instancetype)initWithCall:(GRPCCall *)call {
     79   return [self initWithCall:call storage:[NSMutableDictionary dictionary]];
     80 }
     81 
     82 // Designated initializer
     83 - (instancetype)initWithCall:(GRPCCall *)call storage:(NSMutableDictionary *)storage {
     84   // TODO(jcanizales): Throw if call or storage are nil.
     85   if ((self = [super init])) {
     86     _call = call;
     87     _delegate = storage;
     88   }
     89   return self;
     90 }
     91 
     92 - (instancetype)initWithObjects:(const id _Nonnull __unsafe_unretained *)objects
     93                         forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys
     94                           count:(NSUInteger)cnt {
     95   return [self init];
     96 }
     97 
     98 - (void)checkCallIsNotStarted {
     99   if (_call.state != GRXWriterStateNotStarted) {
    100     [NSException raise:@"Invalid modification"
    101                 format:@"Cannot modify request headers after call is started"];
    102   }
    103 }
    104 
    105 - (id)objectForKey:(NSString *)key {
    106   return _delegate[key.lowercaseString];
    107 }
    108 
    109 - (void)setObject:(id)obj forKey:(NSString *)key {
    110   CheckIsNonNilASCII(@"Header name", key);
    111   key = key.lowercaseString;
    112   CheckKeyValuePairIsValid(key, obj);
    113   _delegate[key] = obj;
    114 }
    115 
    116 - (void)removeObjectForKey:(NSString *)key {
    117   [self checkCallIsNotStarted];
    118   [_delegate removeObjectForKey:key.lowercaseString];
    119 }
    120 
    121 - (NSUInteger)count {
    122   return _delegate.count;
    123 }
    124 
    125 - (NSEnumerator *_Nonnull)keyEnumerator {
    126   return [_delegate keyEnumerator];
    127 }
    128 
    129 @end
    130