-//
-// MultiDeviceNetworking.m
-// Security
-//
-
-#import "MultiDeviceNetworking.h"
-#import "MultiDeviceNetworkingProtocol.h"
-
-@interface MDNCounters ()
-@property (assign) unsigned long kvsSyncAndWait;
-@property (assign) unsigned long kvsFlush;
-@property (assign) unsigned long kvsSend;
-@property (assign) unsigned long kvsRecv;
-@property (assign) unsigned long kvsRecvAll;
-@property (strong) NSMutableDictionary<NSString *,NSNumber *> *kvsKeys;
-
-- (void)addCountToKey:(NSString *)key;
-@end
-
-
-@interface MDNConnection : NSObject <MultiDeviceNetworkingProtocol>
-@property (weak) MultiDeviceNetworking *network;
-@property NSXPCConnection *inConnection;
-@property NSXPCConnection *outConnection;
-@property MDNCounters *counters;
-@end
-
-@interface MultiDeviceNetworking () <NSXPCListenerDelegate>
-@property NSXPCListener *networkListener;
-@property NSMutableDictionary *kvs;
-@property NSMutableArray<MDNConnection *> *connections;
-@property dispatch_queue_t serialQueue;
-@property NSMutableDictionary<NSString *, XCTestExpectation *> *expectations;
-@end
-
-@implementation MDNCounters
-
-- (instancetype)init {
- if ((self = [super init]) == NULL) {
- return nil;
- }
- self.kvsKeys = [NSMutableDictionary dictionary];
- return self;
-}
-
-- (NSDictionary *)summary{
- NSDictionary *kvsKeys = @{};
- @synchronized(self.kvsKeys) {
- kvsKeys = [self.kvsKeys copy];
- }
- return @{
- @"kvsSyncAndWait" : @(self.kvsSyncAndWait),
- @"kvsFlush" : @(self.kvsFlush),
- @"kvsSend" : @(self.kvsSend),
- @"kvsRecv" : @(self.kvsRecv),
- @"kvsRecvAll" : @(self.kvsRecvAll),
- @"kvsKeys" : kvsKeys,
- };
-}
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"<MDNCounters: %@>", [self summary]];
-}
-- (void)addCountToKey:(NSString *)key
-{
- @synchronized(self.kvsKeys) {
- NSNumber *number = self.kvsKeys[key];
- self.kvsKeys[key] = @([number longValue] + 1);
- }
-}
-
-@end
-
-@implementation MultiDeviceNetworking
-
-- (instancetype)init
-{
- self = [super init];
- if (self) {
- self.networkListener = [NSXPCListener anonymousListener];
- self.networkListener.delegate = self;
- [self.networkListener resume];
- self.kvs = [[NSMutableDictionary alloc] init];
- self.connections = [NSMutableArray array];
- self.serialQueue = dispatch_queue_create("MultiDeviceNetworking.flushQueue", NULL);
- self.expectations = [NSMutableDictionary dictionary];
- }
- return self;
-}
-
-- (NSXPCListenerEndpoint *)endpoint
-{
- return [self.networkListener endpoint];
-}
-
-- (void)dumpKVSState
-{
- @synchronized(self.kvs) {
- puts("KVS STATE");
- [self.kvs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull __unused stop) {
- puts([[NSString stringWithFormat:@"%@ - %@", key, obj] UTF8String]);
- }];
- }
-}
-
-- (void)dumpCounters
-{
- @synchronized(self.connections) {
- puts("Network counters:");
- for (MDNConnection *conn in self.connections) {
- puts([[NSString stringWithFormat:@"%@", conn.counters] UTF8String]);
- }
- }
-}
-
-
-- (void)disconnectAll
-{
- @synchronized(self.connections) {
- for (MDNConnection *conn in self.connections) {
- [conn.inConnection invalidate];
- [conn.outConnection invalidate];
- }
- self.connections = [NSMutableArray array];
- }
-}
-
-- (void)setTestExpectation:(XCTestExpectation *)expectation forKey:(NSString *)key
-{
- self.expectations[key] = expectation;
-}
-
-- (void)clearTestExpectations
-{
- self.expectations = [NSMutableDictionary dictionary];
-}
-
-- (void)fulfill:(NSString *)key
-{
- [self.expectations[key] fulfill];
-}
-
-
-
-//MARK: - setup listener
-
-- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
-{
- newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingProtocol)];
-
- MDNConnection *conn = [[MDNConnection alloc] init];
- conn.network = self;
- conn.inConnection = newConnection;
- newConnection.exportedObject = conn;
-
- [self.connections addObject:conn];
- [newConnection resume];
-
- return YES;
-}
-
-@end
-
-
-//MARK: - KVS fun
-
-@implementation MDNConnection
-
-- (instancetype)init
-{
- if ((self = [super init]) == nil)
- return nil;
- _counters = [[MDNCounters alloc] init];
- return self;
-}
-
-- (void)MDNRegisterCallback:(NSXPCListenerEndpoint *)callback complete:(MDNComplete)complete
-{
- self.outConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:callback];
- self.outConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingCallbackProtocol)];
-
- __typeof(self) weakSelf = self;
- self.outConnection.invalidationHandler = ^{
- __typeof(self) strongSelf = weakSelf;
- strongSelf.outConnection = nil;
- };
-
-
- [self.outConnection resume];
- complete(NULL, NULL);
-}
-
-- (void)MDNCloudPut:(NSDictionary *)values complete:(MDNComplete)complete {
- MultiDeviceNetworking *network = self.network;
- @synchronized(network.kvs) {
- [network.kvs setValuesForKeysWithDictionary:values];
- }
- /* interact with test expections so that tests can check that something happned in KVS */
- [network fulfill:@"Network"];
- for (NSString *key in values.allKeys) {
- NSString *dataSummary = @"";
- id value = values[key];
- if ([value isKindOfClass:[NSString class]]) {
- dataSummary = [NSString stringWithFormat:@" = string[%ld]", [(NSString *)value length]];
- } else if ([value isKindOfClass:[NSData class]]) {
- NSUInteger length = [(NSData *)value length];
- NSData *subdata = [(NSData *)value subdataWithRange:NSMakeRange(0, MIN(length, 4))];
- dataSummary = [NSString stringWithFormat:@" = data[%lu][%@]", (unsigned long)length, subdata];
- } else {
- dataSummary = [NSString stringWithFormat:@" = other(%@)", [value description]];
- }
- NSLog(@"KVS key update: %@%@", key, dataSummary);
- [network fulfill:key];
- [self.counters addCountToKey:key];
- }
-
-
- self.counters.kvsSend++;
- for (MDNConnection *conn in network.connections) {
- if (conn == self || conn.outConnection == NULL) {
- continue;
- }
- conn.counters.kvsRecv++;
- [[conn.outConnection remoteObjectProxy] MDNCItemsChanged:values complete:^(NSDictionary *returnedValues, NSError *error) {
- ;
- }];
- }
- complete(@{}, NULL);
-}
-
-- (void)MDNCloudsynchronizeAndWait:(NSDictionary *)values complete:(MDNComplete)complete {
- MultiDeviceNetworking *network = self.network;
- NSDictionary *kvsCopy = NULL;
- @synchronized(network.kvs) {
- kvsCopy = [network.kvs copy];
- }
- self.counters.kvsSyncAndWait++;
- [[self.outConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
- NSLog(@"foo: %@", error);
- //abort();
- }] MDNCItemsChanged:kvsCopy complete:^(NSDictionary *returnedValues, NSError *error) {
- }];
- dispatch_async(network.serialQueue, ^{
- complete(@{}, NULL);
- });
-}
-
-- (void)MDNCloudGet:(NSArray *)keys complete:(MDNComplete)complete{
- MultiDeviceNetworking *network = self.network;
- NSLog(@"asking for: %@", keys);
- self.counters.kvsRecv++;
- NSMutableDictionary *reply = [NSMutableDictionary dictionary];
- @synchronized(network.kvs) {
- for (id key in keys) {
- reply[key] = network.kvs[key];
- }
- }
- complete(reply, NULL);
-}
-
-- (void)MDNCloudGetAll:(MDNComplete)complete
-{
- MultiDeviceNetworking *network = self.network;
- NSDictionary *kvsCopy = NULL;
- self.counters.kvsRecvAll++;
- @synchronized(network.kvs) {
- kvsCopy = [network.kvs copy];
- }
- complete(kvsCopy, NULL);
-}
-
-- (void)MDNCloudRemoveKeys:(NSArray<NSString *> *)keys complete:(MDNComplete)complete
-{
- MultiDeviceNetworking *network = self.network;
- @synchronized(network.kvs) {
- if (keys) {
- for (NSString *key in keys) {
- network.kvs[key] = NULL;
- }
- } else {
- network.kvs = [NSMutableDictionary dictionary];
- }
- }
- complete(NULL, NULL);
-}
-
-- (void)MDNCloudFlush:(MDNComplete)complete
-{
- self.counters.kvsFlush++;
- dispatch_async(self.network.serialQueue, ^{
- complete(@{}, NULL);
- });
-}
-
-@end