shlist

share and manage lists between multiple people
Log | Files | Refs

commit 6964f7f7dab7c07c2a2b35e0db01aa777147be66
parent 1f042b19e6449935a4cbfead730c85d1222b3ee2
Author: Kyle Milz <kyle@Kyles-MacBook-Pro.local>
Date:   Sun, 20 Sep 2015 00:30:50 -0600

ios: start moving connection reuse message passing

Diffstat:
Mios-ng/shlist/Network.m | 442+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
1 file changed, 262 insertions(+), 180 deletions(-)

diff --git a/ios-ng/shlist/Network.m b/ios-ng/shlist/Network.m @@ -2,13 +2,27 @@ #import "DataStructures.h" #import "AddressBook.h" -@interface Network () +@interface Network () { + NSData *msg_data; + NSString *msg_string; + uint8_t msg_buffer[1024]; + unsigned int msg_buf_position; + + uint8_t msg_total_bytes_tmp[2]; + unsigned short msg_total_bytes; + unsigned int msg_total_bytes_pos; + + uint8_t msg_type_tmp[2]; + unsigned short msg_type; + unsigned int msg_type_pos; + + int connected; +} @property (strong, retain) NSMutableData *data; @property (strong, retain) AddressBook *address_book; @property NSMutableDictionary *phnum_to_name_map; - @property (strong, nonatomic) NSString *phone_number; @property (strong, nonatomic) NSData *device_id; @@ -19,25 +33,16 @@ - (id) init { if (self = [super init]) { - /* - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; + connected = 0; + msg_buf_position = 0; - CFStringRef host_name = CFSTR("absentmindedproductions.ca"); + msg_total_bytes = 0; + msg_total_bytes_pos = 0; - CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); - inputShlistStream = (__bridge NSInputStream *)readStream; - outputShlistStream = (__bridge NSOutputStream *)writeStream; + msg_type = 0; + msg_type_pos = 0; - [inputShlistStream setDelegate:self]; - [outputShlistStream setDelegate:self]; - - [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - - [inputShlistStream open]; - [outputShlistStream open]; - */ + // [self connect]; // get instance and wait for privacy window to clear _address_book = [AddressBook shared_address_book]; @@ -72,12 +77,36 @@ return self; } +- (void) connect +{ + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + + CFStringRef host_name = CFSTR("absentmindedproductions.ca"); + + CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); + inputShlistStream = (__bridge NSInputStream *)readStream; + outputShlistStream = (__bridge NSOutputStream *)writeStream; + + [inputShlistStream setDelegate:self]; + [outputShlistStream setDelegate:self]; + + [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputShlistStream open]; + [outputShlistStream open]; + + NSLog(@"info: network: finished connecting to absentmindedproductions.ca"); +} + - (bool) prepare { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; // NSString *phone_num_file = [documentsDirectory stringByAppendingPathComponent:@"phone_num"]; + _phone_number = @"4037082094"; NSString *device_id_file = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; // NSError *error = nil; @@ -97,10 +126,11 @@ // actual phone number const char *phone_number = "4037082094"; - _phone_number = @"4037082094"; [msg appendBytes:phone_number length:10]; - [self writeToServer:msg]; + if (connected == 0) + [self connect]; + [outputShlistStream write:[msg bytes] maxLength:[msg length]]; NSLog(@"info: sent registration message"); // we don't have a device id so we can't do anything yet @@ -113,11 +143,11 @@ return true; } -- (void) send_message:(uint16_t)msg_type contents:(NSData *)payload +- (void) send_message:(uint16_t)send_msg_type contents:(NSData *)payload { NSMutableData *msg = [NSMutableData data]; - uint16_t msg_type_network = htons(msg_type); + uint16_t msg_type_network = htons(send_msg_type); [msg appendBytes:&msg_type_network length:2]; int payload_length = 0; @@ -135,168 +165,261 @@ [msg appendData:payload]; } - [self writeToServer:msg]; + if (connected == 0) { + NSLog(@"info: network: not connected in send_message, reconnecting..."); + [self connect]; + } + if ([outputShlistStream write:[msg bytes] maxLength:[msg length]] == -1) + NSLog(@"warn: network: write error occurred"); } - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { - switch (eventCode) { + NSString *stream_name; + if (stream == inputShlistStream) + stream_name = @"input"; + else if (stream == outputShlistStream) + stream_name = @"output"; + + switch (eventCode) { case NSStreamEventNone: break; case NSStreamEventOpenCompleted: - if (stream == inputShlistStream) { - NSLog(@"info: input stream opened"); - } - else if (stream == outputShlistStream) { - NSLog(@"info: output stream opened"); - } + NSLog(@"info: network: %@ stream opened", stream_name); + connected = 1; break; case NSStreamEventHasBytesAvailable: if (stream == inputShlistStream) { - /* if (![inputShlistStream hasBytesAvailable]) { + NSLog(@"warn: network: input stream had no bytes available"); break; } - */ - NSInteger len; - uint16_t msg_metadata[2]; + // advance the message state machine by at least one byte + [self process_response_bytes]; + } + break; + case NSStreamEventHasSpaceAvailable: + NSLog(@"info: network: stream has space available"); + break; + case NSStreamEventErrorOccurred: + NSLog(@"info: network: stream error occurred"); + // I saw this case when trying to connect to a down server + break; + case NSStreamEventEndEncountered: - len = [inputShlistStream read:(uint8_t *)&msg_metadata maxLength:4]; - if (len != 4) { - NSLog(@"warn: read: msg metadata was %li bytes, expected 4", - (long)len); - break; - } + // close both sides of the connection on end + NSLog(@"ShlistServer::NSStreamEventEndEncountered"); + [inputShlistStream close]; + [outputShlistStream close]; - uint16_t msg_type = ntohs(msg_metadata[0]); - uint16_t msg_length = ntohs(msg_metadata[1]); - if (msg_type > 6) { - NSLog(@"warn: read: out of range msg type %i", msg_type); - break; - } + [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + // [inputShlistStream release]; + // [outputShlistStream release]; - if (msg_length > 1024) { - NSLog(@"warn: read: message too large: %i bytes", msg_length); - break; - } + inputShlistStream = nil; // stream is ivar, so reinit it + outputShlistStream = nil; // stream is ivar, so reinit it + connected = 0; - // guaranteed valid message type and size from here forward - NSLog(@"info: read: got msg type %i (%i bytes)", msg_type, msg_length); + break; + default: + break; + } +} - uint8_t *buffer = malloc(msg_length); - if (buffer == nil) { - NSLog(@"warn: read: couldn't allocate receiving buffer size %i", - msg_length); - break; +- (void) process_response_bytes +{ + uint8_t *buffer = malloc(1024); + while ([inputShlistStream hasBytesAvailable]) { + + unsigned int buffer_pos = 0; + NSInteger buffer_length; + + buffer_length = [inputShlistStream read:buffer maxLength:1024]; + if (buffer_length < 0) { + NSLog(@"warn: network: read returned < 0: %i", buffer_length); + // XXX: should this be break instead? + continue; } - len = [inputShlistStream read:buffer maxLength:msg_length]; - if (len != msg_length) { - NSLog(@"warn: read: main message read byte mismatch: %li vs %i", - (long)len, msg_length); - break; + if (buffer_length == 0) { + NSLog(@"warn: network: buffer length was zero!"); + // maybe break here instead? + continue; } - NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; - NSData *data = [[NSData alloc] initWithBytes:buffer length:msg_length]; + NSLog(@"info: network: processing %i bytes", buffer_length); - if (output == nil) { - NSLog(@"warn: read: couldn't allocate output string"); - break; + // start receiving a new message + if (msg_type_pos == 0) { + msg_type_tmp[0] = buffer[buffer_pos]; + msg_type_pos = 1; + buffer_pos++; + + if (buffer_length == 1) + // we've exhausted the buffer + continue; } - // NSLog(@"info: read: message is %@", output); - - if (msg_type == 0) { - // write key to file - NSLog(@"info: read: writing new keyfile to disk"); - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; - // if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { - [data writeToFile:destinationPath atomically:YES]; - // } - - // set this so we're ready to send other message types - _device_id = data; - - // do a bulk list update - [self send_message:3 contents:nil]; + + // if we got here buffer_length > 1 + if (msg_type_pos == 1) { + msg_type_pos = 2; + msg_type_tmp[1] = buffer[buffer_pos]; + + // we read a single byte from the buffer + buffer_pos++; + + // both bytes are available for reading + msg_type = ntohs(*(uint16_t *)msg_type_tmp); + if (msg_type > 7) { + NSLog(@"warn: network: out of range msg type %i", buffer[0]); + + // bad message type, reset message buffer + msg_type_pos = 0; + msg_buf_position = 0; + msg_total_bytes_pos = 0; + continue; + } + NSLog(@"info: network: got message type %i", msg_type); + + if (buffer_pos == buffer_length) + // we've run out of bytes to process + continue; } - if (msg_type == 1) { - NSLog(@"info: got new list response, not doing anything with it"); + // if we got here buffer_pos < buffer_length + if (msg_total_bytes_pos == 0) { + msg_total_bytes_pos = 1; + msg_total_bytes_tmp[0] = buffer[buffer_pos]; + buffer_pos++; + + if (buffer_pos == buffer_length) + // no more bytes to process + continue; } - if (msg_type == 3) { - [self handle_bulk_list_update:output]; + if (msg_total_bytes_pos == 1) { + msg_total_bytes_pos = 2; + msg_total_bytes_tmp[1] = buffer[buffer_pos]; + buffer_pos++; + + msg_total_bytes = ntohs(*(uint16_t *)msg_total_bytes_tmp); + if (msg_total_bytes > 1024 || msg_total_bytes == 0) { + NSLog(@"warn: network: out of range message length: 0 < %i < 1024", + msg_total_bytes); + + // bad message type, reset message buffer + msg_type_pos = 0; + msg_buf_position = 0; + msg_total_bytes_pos = 0; + continue; + } + NSLog(@"info: network: message length is %i bytes", msg_total_bytes); + + if (buffer_pos == buffer_length) + // no more bytes to process + continue; } - if (msg_type == 4) { - NSLog(@"info: join list response '%@'", output); + unsigned int remaining_bytes = buffer_length - buffer_pos; + + if (msg_buf_position + remaining_bytes >= msg_total_bytes) { + NSLog(@"info: network: buffer length has enough space to read complete message"); - SharedList *shlist = [[SharedList alloc] init]; - shlist.id = data; + unsigned int bytes_for_complete_message = msg_total_bytes - msg_buf_position; + memcpy(msg_buffer + msg_buf_position, &buffer[buffer_pos], bytes_for_complete_message); + msg_buf_position += bytes_for_complete_message; - // XXX: these need to be sent from the server - shlist.items_ready = 0; - shlist.items_total = 99; - // shlist.list_name = <network>; - // shlist.members = <network>; + msg_data = [[NSData alloc] initWithBytes:msg_buffer length:msg_total_bytes]; + msg_string = [[NSString alloc] initWithBytes:msg_buffer length:msg_total_bytes encoding:NSASCIIStringEncoding]; - [shlist_tvc finished_join_list_request:shlist]; + [self handle_message]; + + // reset parsing fields, leave buffer position fields alone though + msg_buf_position = 0; + msg_type_pos = 0; + msg_total_bytes_pos = 0; + + // lop off anything else that's remaining in the read buffer + continue; } - if (msg_type == 5) { - NSLog(@"info: leave list response '%@'", output); + // copy any remaining data into msg buffer + memcpy(msg_buffer + msg_buf_position, &buffer[buffer_pos], remaining_bytes); + msg_buf_position += remaining_bytes; + } - NSArray *fields = [output componentsSeparatedByString:@"\0"]; + // free temporary buffer, not msg_buffer + free(buffer); +} - if ([fields count] != 2) { - NSLog(@"warn: leave list response had wrong number (%i) of fields", - [fields count]); - break; - } +- (void) handle_message +{ + // assert msg_type_pos == 2 and msg_total_bytes_pos == 2 and msg_buf_position == msg_total_bytes - SharedList *shlist = [[SharedList alloc] init]; - shlist.id = [[fields objectAtIndex:0] dataUsingEncoding:NSUTF8StringEncoding]; + if (msg_type == 0) { + // write key to file + NSLog(@"info: read: writing new keyfile to disk"); - // XXX: these need to be sent from the server - // shlist.list_name = <network>; - // shlist.members = <network>; + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; - [shlist_tvc finished_leave_list_request:shlist]; - } + NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; + // if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { + [msg_data writeToFile:destinationPath atomically:YES]; + // } + + // set this so we're ready to send other message types + _device_id = msg_data; + + // do a bulk list update + [self send_message:3 contents:nil]; } - break; - case NSStreamEventHasSpaceAvailable: - [self _writeData]; - break; - case NSStreamEventErrorOccurred: - NSLog(@"ShlistServer::NSStreamEventErrorOccurred"); - // I saw this case when trying to connect to a down server - break; - case NSStreamEventEndEncountered: - { - NSLog(@"ShlistServer::NSStreamEventEndEncountered"); - [inputShlistStream close]; - [outputShlistStream close]; - [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - // [inputShlistStream release]; - // [outputShlistStream release]; + if (msg_type == 1) { + NSLog(@"info: got new list response, not doing anything with it"); + } - inputShlistStream = nil; // stream is ivar, so reinit it - outputShlistStream = nil; // stream is ivar, so reinit it + if (msg_type == 3) { + [self handle_bulk_list_update:msg_string]; + } - break; + if (msg_type == 4) { + NSLog(@"info: join list response '%@'", msg_string); + + SharedList *shlist = [[SharedList alloc] init]; + shlist.id = msg_data; + + // XXX: these need to be sent from the server + shlist.items_ready = 0; + shlist.items_total = 99; + // shlist.list_name = <network>; + // shlist.members = <network>; + + [shlist_tvc finished_join_list_request:shlist]; } - default: - break; + + if (msg_type == 5) { + NSLog(@"info: leave list response '%@'", msg_string); + + NSArray *fields = [msg_string componentsSeparatedByString:@"\0"]; + + if ([fields count] != 2) { + NSLog(@"warn: leave list response had wrong number (%i) of fields", + [fields count]); + return; + } + + SharedList *shlist = [[SharedList alloc] init]; + shlist.id = [[fields objectAtIndex:0] dataUsingEncoding:NSUTF8StringEncoding]; + + // XXX: these need to be sent from the server + // shlist.list_name = <network>; + // shlist.members = <network>; + + [shlist_tvc finished_leave_list_request:shlist]; } } @@ -415,45 +538,4 @@ outputShlistStream = nil; // stream is ivar, so reinit it } -- (void) _readData -{ -} - -- (void) _writeData -{ - NSLog(@"_writeData"); -} - -- (void) writeToServer:(NSData *)data -{ - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - - CFStringRef host_name = CFSTR("absentmindedproductions.ca"); - - CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); - inputShlistStream = (__bridge NSInputStream *)readStream; - outputShlistStream = (__bridge NSOutputStream *)writeStream; - - [inputShlistStream setDelegate:self]; - [outputShlistStream setDelegate:self]; - - [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - - [inputShlistStream open]; - [outputShlistStream open]; - - // const char bytes[] = "\x00\x00\xff\0x00"; - //string literals have implicit trailing '\0' - // size_t length = (sizeof bytes) - 1; - - // NSData *data = [NSData dataWithBytes:bytes length:length]; - NSLog(@"writeToServer()"); - [outputShlistStream write:[data bytes] maxLength:[data length]]; -} - -// - (void) readFromServer: - - @end