Changeset 3144

Show
Ignore:
Timestamp:
02/21/06 02:06:24 (3 years ago)
Author:
timothy
Message:

Message throttling to prevent being excess flooded off the server. Allows 5 commands per seconds.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/cocoa-networking/Chat Core/MVChatConnection.h

    r3128 r3144  
    220220- (void) sendRawMessage:(id) raw immediately:(BOOL) now; 
    221221- (void) sendRawMessageWithFormat:(NSString *) format, ...; 
     222- (void) sendRawMessageImmediatelyWithFormat:(NSString *) format, ...; 
    222223- (void) sendRawMessageWithComponents:(id) firstComponent, ...; 
     224- (void) sendRawMessageImmediatelyWithComponents:(id) firstComponent, ...; 
    223225 
    224226#pragma mark - 
  • branches/cocoa-networking/Chat Core/MVChatConnection.m

    r3139 r3144  
    531531} 
    532532 
     533- (void) sendRawMessageImmediatelyWithFormat:(NSString *) format, ... { 
     534        NSParameterAssert( format != nil ); 
     535 
     536        va_list ap; 
     537        va_start( ap, format ); 
     538 
     539        NSString *command = [[NSString allocWithZone:nil] initWithFormat:format arguments:ap]; 
     540 
     541        va_end( ap ); 
     542 
     543        [self sendRawMessage:command immediately:YES]; 
     544        [command release]; 
     545} 
     546 
    533547- (void) sendRawMessageWithComponents:(id) firstComponent, ... { 
    534548        NSParameterAssert( firstComponent != nil ); 
     
    555569 
    556570        [self sendRawMessage:data immediately:NO]; 
     571        [data release]; 
     572} 
     573 
     574- (void) sendRawMessageImmediatelyWithComponents:(id) firstComponent, ... { 
     575        NSParameterAssert( firstComponent != nil ); 
     576 
     577        NSMutableData *data = [[NSMutableData allocWithZone:nil] initWithCapacity:512]; 
     578        id object = firstComponent; 
     579 
     580        va_list ap; 
     581        va_start( ap, firstComponent ); 
     582 
     583        do { 
     584                if( [object isKindOfClass:[NSData class]] ) { 
     585                        [data appendData:object]; 
     586                } else if( [firstComponent isKindOfClass:[NSString class]] ) { 
     587                        NSData *stringData = [object dataUsingEncoding:[self encoding] allowLossyConversion:YES]; 
     588                        [data appendData:stringData]; 
     589                } else { 
     590                        NSData *stringData = [[object description] dataUsingEncoding:[self encoding] allowLossyConversion:YES]; 
     591                        [data appendData:stringData]; 
     592                } 
     593        } while( object = va_arg( ap, void * ) ); 
     594 
     595        va_end( ap ); 
     596 
     597        [self sendRawMessage:data immediately:YES]; 
    557598        [data release]; 
    558599} 
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.h

    r3142 r3144  
    1212        NSTimer *_periodicCleanUpTimer; 
    1313        NSThread *_connectionThread; 
     14        NSDate *_queueWait; 
     15        NSDate *_lastCommand; 
     16        NSMutableArray *_sendQueue; 
     17        NSTimer *_sendQueueTimer; 
    1418        NSMutableDictionary *_knownUsers; 
    1519        NSMutableSet *_fileTransfers; 
     
    4751 
    4852- (void) _periodicCleanUp; 
     53- (void) _startQueueTimer; 
     54- (void) _stopSendQueueTimer; 
    4955@end 
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.m

    r3142 r3144  
    1414#import "NSStringAdditions.h" 
    1515#import "NSDataAdditions.h" 
     16 
     17#define JVMaximumCommandsSentAtOnce 5 
    1618 
    1719static const NSStringEncoding supportedEncodings[] = { 
     
    197199        [_realName release]; 
    198200        [_threadWaitLock release]; 
     201        [_periodicCleanUpTimer release]; 
     202        [_sendQueue release]; 
     203        [_sendQueueTimer release]; 
     204        [_queueWait release]; 
     205        [_lastCommand release]; 
    199206 
    200207        _chatConnection = nil; 
     
    209216        _realName = nil; 
    210217        _threadWaitLock = nil; 
     218        _periodicCleanUpTimer = nil; 
     219        _sendQueue = nil; 
     220        _sendQueueTimer = nil; 
     221        _queueWait = nil; 
     222        _lastCommand = nil; 
    211223 
    212224        [super dealloc]; 
     
    240252        [old release]; 
    241253 
     254        old = _queueWait; 
     255        _queueWait = [[NSDate dateWithTimeIntervalSinceNow:120.] retain]; 
     256        [old release]; 
     257 
    242258        [self _willConnect]; // call early so other code has a chance to change our info 
    243259 
     
    254270- (void) disconnectWithReason:(NSAttributedString *) reason { 
    255271        [self cancelPendingReconnectAttempts]; 
     272        if( _sendQueueTimer ) [self performSelector:@selector( _stopSendQueueTimer ) withObject:nil inThread:_connectionThread]; 
    256273 
    257274        if( _status == MVChatConnectionConnectedStatus ) { 
    258275                if( [[reason string] length] ) { 
    259276                        NSData *msg = [[self class] _flattenedIRCDataForMessage:reason withEncoding:[self encoding] andChatFormat:[self outgoingChatFormat]]; 
    260                         [self sendRawMessageWithComponents:@"QUIT :", msg, nil]; 
    261                 } else [self sendRawMessage:@"QUIT"]; 
     277                        [self sendRawMessageImmediatelyWithComponents:@"QUIT :", msg, nil]; 
     278                } else [self sendRawMessage:@"QUIT" immediately:YES]; 
    262279        } 
    263280 
     
    300317 
    301318        if( [self isConnected] ) 
    302                 [self sendRawMessageWithFormat:@"NICK %@", nickname]; 
     319                [self sendRawMessageImmediatelyWithFormat:@"NICK %@", nickname]; 
    303320} 
    304321 
     
    315332- (void) setNicknamePassword:(NSString *) password { 
    316333        if( ! [[self localUser] isIdentified] && password && [self isConnected] ) 
    317                 [self sendRawMessageWithFormat:@"NickServ IDENTIFY %@", password]; 
     334                [self sendRawMessageImmediatelyWithFormat:@"NickServ IDENTIFY %@", password]; 
    318335        [super setNicknamePassword:password]; 
    319336} 
     
    377394        NSParameterAssert( [raw isKindOfClass:[NSData class]] || [raw isKindOfClass:[NSString class]] ); 
    378395 
    379         NSMutableData *data = nil; 
    380         NSString *string = nil; 
    381  
    382         if( [raw isKindOfClass:[NSMutableData class]] ) { 
    383                 data = [raw retain]; 
    384                 string = [[NSString allocWithZone:nil] initWithData:data encoding:[self encoding]]; 
    385         } else if( [raw isKindOfClass:[NSData class]] ) { 
    386                 data = [raw mutableCopyWithZone:nil]; 
    387                 string = [[NSString allocWithZone:nil] initWithData:data encoding:[self encoding]]; 
    388         } else if( [raw isKindOfClass:[NSString class]] ) { 
    389                 data = [[raw dataUsingEncoding:[self encoding] allowLossyConversion:YES] mutableCopyWithZone:nil]; 
    390                 string = [raw retain]; 
    391         } 
    392  
    393         // IRC messages are always lines of characters terminated with a CR-LF 
    394         // (Carriage Return - Line Feed) pair, and these messages SHALL NOT 
    395         // exceed 512 characters in length, counting all characters including 
    396         // the trailing CR-LF. Thus, there are 510 characters maximum allowed 
    397         // for the command and its parameters. 
    398  
    399         if( [data length] > 510 ) [data setLength:510]; 
    400         [data appendBytes:"\x0D\x0A" length:2]; 
    401  
    402         [self performSelector:@selector( _writeDataToServer: ) withObject:data inThread:_connectionThread]; 
    403  
    404         [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVChatConnectionGotRawMessageNotification object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:string, @"message", [NSNumber numberWithBool:YES], @"outbound", nil]]; 
    405  
    406         [string release]; 
    407         [data release]; 
     396        if( ! now ) { 
     397                @synchronized( _sendQueue ) { 
     398                        now = ! [_sendQueue count]; 
     399                } 
     400 
     401                if( now ) now = ( ! _queueWait || [_queueWait timeIntervalSinceNow] <= 0. ); 
     402                if( now ) now = ( ! _lastCommand || [_lastCommand timeIntervalSinceNow] <= -0.2 ); 
     403        } 
     404 
     405        if( now ) { 
     406                [self performSelector:@selector( _writeDataToServer: ) withObject:raw inThread:_connectionThread]; 
     407 
     408                id old = _lastCommand; 
     409                _lastCommand = [[NSDate allocWithZone:nil] init]; 
     410                [old release]; 
     411        } else { 
     412                if( ! _sendQueue ) _sendQueue = [[NSMutableArray allocWithZone:nil] initWithCapacity:20]; 
     413 
     414                @synchronized( _sendQueue ) { 
     415                        [_sendQueue addObject:raw]; 
     416                } 
     417 
     418                if( ! _sendQueueTimer ) 
     419                        [self performSelector:@selector( _startQueueTimer ) withObject:nil inThread:_connectionThread]; 
     420        } 
    408421} 
    409422 
     
    507520        if( ! _cachedDate || ABS( [_cachedDate timeIntervalSinceNow] ) > 300. ) { 
    508521                [self sendRawMessage:@"LIST"]; 
    509                 [_cachedDate release]; 
     522 
     523                id old = _cachedDate; 
    510524                _cachedDate = [[NSDate allocWithZone:nil] init]; 
     525                [old release]; 
    511526        } 
    512527} 
     
    514529- (void) stopFetchingChatRoomList { 
    515530        if( _cachedDate && ABS( [_cachedDate timeIntervalSinceNow] ) < 600. ) 
    516                 [self sendRawMessage:@"LIST STOP"]; 
     531                [self sendRawMessage:@"LIST STOP" immediately:YES]; 
    517532} 
    518533 
     
    529544 
    530545                NSData *msg = [[self class] _flattenedIRCDataForMessage:message withEncoding:[self encoding] andChatFormat:[self outgoingChatFormat]]; 
    531                 [self sendRawMessageWithComponents:@"AWAY :", msg, nil]; 
     546                [self sendRawMessageImmediatelyWithComponents:@"AWAY :", msg, nil]; 
    532547        } else { 
    533548                [[self localUser] _setStatus:MVChatUserAvailableStatus]; 
    534                 [self sendRawMessage:@"AWAY"]; 
     549                [self sendRawMessage:@"AWAY" immediately:YES]; 
    535550        } 
    536551} 
     
    566581 
    567582        BOOL active = YES; 
    568         while( active && ( _status == MVChatConnectionConnectedStatus || _status == MVChatConnectionConnectingStatus ) ) 
     583        while( active && ( _status == MVChatConnectionConnectedStatus || _status == MVChatConnectionConnectingStatus || [_chatConnection isConnected] ) ) 
    569584                active = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
     585 
     586        // make sure the connection has sent all the delegate calls it has scheduled 
     587        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.]]; 
    570588 
    571589        if( [NSThread currentThread] == _connectionThread ) 
     
    579597 
    580598- (void) _didDisconnect { 
    581         [_knownUsers removeAllObjects]; 
    582  
    583599        if( _status == MVChatConnectionServerDisconnectedStatus ) { 
    584600                if( ABS( [_lastConnectAttempt timeIntervalSinceNow] ) > 300. ) 
     
    636652 
    637653- (void) socketDidDisconnect:(AsyncSocket *) sock { 
    638         id old = _localUser; 
    639         _localUser = nil; 
     654        if( sock != _chatConnection ) return; 
     655 
     656        id old = _chatConnection; 
     657        _chatConnection = nil; 
     658        [old setDelegate:nil]; 
    640659        [old release]; 
    641660 
    642         [self _periodicCleanUp]; 
     661        NSLog(@"socketDidDisconnect" ); 
     662 
     663        [self _stopSendQueueTimer]; 
     664 
     665        @synchronized( _sendQueue ) { 
     666                [_sendQueue removeAllObjects]; 
     667        } 
     668 
     669        old = _lastCommand; 
     670        _lastCommand = nil; 
     671        [old release]; 
     672 
     673        old = _queueWait; 
     674        _queueWait = nil; 
     675        [old release]; 
     676 
     677        @synchronized( _knownUsers ) { 
     678                [_knownUsers removeAllObjects]; 
     679        } 
     680 
    643681        [_periodicCleanUpTimer invalidate]; 
    644682        [_periodicCleanUpTimer release]; 
     
    649687 
    650688- (void) socket:(AsyncSocket *) sock didConnectToHost:(NSString *) host port:(UInt16) port { 
    651         if( [[self password] length] ) [self sendRawMessageWithFormat:@"PASS %@", [self password]]; 
    652         [self sendRawMessageWithFormat:@"NICK %@", [self nickname]]; 
    653         [self sendRawMessageWithFormat:@"USER %@ 0 * :%@", [self username], [self realName]]; 
     689        if( [[self password] length] ) [self sendRawMessageImmediatelyWithFormat:@"PASS %@", [self password]]; 
     690        [self sendRawMessageImmediatelyWithFormat:@"NICK %@", [self nickname]]; 
     691        [self sendRawMessageImmediatelyWithFormat:@"USER %@ 0 * :%@", [self username], [self realName]]; 
    654692 
    655693        id old = _localUser; 
     
    801839#pragma mark - 
    802840 
    803 - (void) _writeDataToServer:(NSData *) data { 
     841- (void) _writeDataToServer:(id) raw { 
     842        NSMutableData *data = nil; 
     843        NSString *string = nil; 
     844 
     845        if( [raw isKindOfClass:[NSMutableData class]] ) { 
     846                data = [raw retain]; 
     847                string = [[NSString allocWithZone:nil] initWithData:data encoding:[self encoding]]; 
     848        } else if( [raw isKindOfClass:[NSData class]] ) { 
     849                data = [raw mutableCopyWithZone:nil]; 
     850                string = [[NSString allocWithZone:nil] initWithData:data encoding:[self encoding]]; 
     851        } else if( [raw isKindOfClass:[NSString class]] ) { 
     852                data = [[raw dataUsingEncoding:[self encoding] allowLossyConversion:YES] mutableCopyWithZone:nil]; 
     853                string = [raw retain]; 
     854        } 
     855 
     856        // IRC messages are always lines of characters terminated with a CR-LF 
     857        // (Carriage Return - Line Feed) pair, and these messages SHALL NOT 
     858        // exceed 512 characters in length, counting all characters including 
     859        // the trailing CR-LF. Thus, there are 510 characters maximum allowed 
     860        // for the command and its parameters. 
     861 
     862        if( [data length] > 510 ) [data setLength:510]; 
     863        [data appendBytes:"\x0D\x0A" length:2]; 
     864 
    804865        [_chatConnection writeData:data withTimeout:-1. tag:0]; 
     866 
     867        [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVChatConnectionGotRawMessageNotification object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:string, @"message", [NSNumber numberWithBool:YES], @"outbound", nil]]; 
     868 
     869        [string release]; 
     870        [data release]; 
    805871} 
    806872 
     
    914980} 
    915981 
     982- (void) _startQueueTimer { 
     983        if( _sendQueueTimer ) return; 
     984        _sendQueueTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector( _sendQueue ) userInfo:nil repeats:YES] retain]; 
     985} 
     986 
     987- (void) _stopSendQueueTimer { 
     988        id old = _sendQueueTimer; 
     989        _sendQueueTimer = nil; 
     990        [old invalidate]; 
     991        [old release]; 
     992} 
     993 
     994- (void) _sendQueue { 
     995        @synchronized( _sendQueue ) { 
     996                if( ! [_sendQueue count] ) { 
     997                        [self _stopSendQueueTimer]; 
     998                        return; 
     999                } 
     1000        } 
     1001 
     1002        if( _queueWait && [_queueWait timeIntervalSinceNow] > 0. ) return; 
     1003 
     1004        NSData *data = nil; 
     1005        @synchronized( _sendQueue ) { 
     1006                data = [[_sendQueue objectAtIndex:0] retain]; 
     1007                [_sendQueue removeObjectAtIndex:0]; 
     1008        } 
     1009 
     1010        [self _writeDataToServer:data]; 
     1011        [data release]; 
     1012 
     1013        id old = _lastCommand; 
     1014        _lastCommand = [[NSDate allocWithZone:nil] init]; 
     1015        [old release]; 
     1016} 
     1017 
    9161018#pragma mark - 
    9171019 
     
    9361038 
    9371039- (void) _handle001WithParameters:(NSArray *) parameters fromSender:(MVChatUser *) sender { 
     1040        id old = _queueWait; 
     1041        _queueWait = [[NSDate dateWithTimeIntervalSinceNow:0.5] retain]; 
     1042        [old release]; 
     1043 
    9381044        [self performSelectorOnMainThread:@selector( _didConnect ) withObject:nil waitUntilDone:NO];     
    9391045        // Identify if we have a user password 
    9401046        if( [[self nicknamePassword] length] ) 
    941                 [self sendRawMessageWithFormat:@"NickServ IDENTIFY %@", [self nicknamePassword]]; 
     1047                [self sendRawMessageImmediatelyWithFormat:@"NickServ IDENTIFY %@", [self nicknamePassword]]; 
    9421048        if( [parameters count] >= 1 ) { 
    9431049                NSString *nickname = [parameters objectAtIndex:0]; 
     
    10361142                                                if( ! [self nicknamePassword] ) { 
    10371143                                                        [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVChatConnectionNeedNicknamePasswordNotification object:self userInfo:nil]; 
    1038                                                 } else [self sendRawMessageWithFormat:@"NickServ IDENTIFY %@", [self nicknamePassword]]; 
     1144                                                } else [self sendRawMessageImmediatelyWithFormat:@"NickServ IDENTIFY %@", [self nicknamePassword]]; 
    10391145                                        } else if( [msg rangeOfString:@"Password accepted"].location != NSNotFound ) { 
    10401146                                                [[self localUser] _setIdentified:YES]; 
     
    12491355                        [room _clearBannedUsers]; 
    12501356 
    1251                         [self sendRawMessageWithFormat:@"WHO %@", name]; 
    1252                         [self sendRawMessageWithFormat:@"MODE %@ b", name]; 
     1357                        [self sendRawMessageImmediatelyWithFormat:@"WHO %@", name]; 
     1358                        [self sendRawMessageImmediatelyWithFormat:@"MODE %@ b", name]; 
    12531359                } else { 
    12541360                        if( [sender status] != MVChatUserAwayStatus ) [sender _setStatus:MVChatUserAvailableStatus]; 
     
    14591565- (void) _handlePingWithParameters:(NSArray *) parameters fromSender:(MVChatUser *) sender { 
    14601566        if( [parameters count] >= 1 ) { 
    1461                 if( [parameters count] == 1 ) [self sendRawMessageWithComponents:@"PONG :", [parameters objectAtIndex:0], nil]; 
    1462                 else [self sendRawMessageWithComponents:@"PONG ", [parameters objectAtIndex:1], @" :", [parameters objectAtIndex:0], nil]; 
     1567                if( [parameters count] == 1 ) [self sendRawMessageImmediatelyWithComponents:@"PONG :", [parameters objectAtIndex:0], nil]; 
     1568                else [self sendRawMessageImmediatelyWithComponents:@"PONG ", [parameters objectAtIndex:1], @" :", [parameters objectAtIndex:0], nil]; 
    14631569        } 
    14641570} 
  • branches/cocoa-networking/Chat Core/MVIRCChatRoom.m

    r3137 r3144  
    3535- (void) partWithReason:(NSAttributedString *) reason { 
    3636        if( ! [self isJoined] ) return; 
    37         if( ! [reason length] ) [[self connection] sendRawMessage:[NSString stringWithFormat:@"PART %@", [self name]] immediately:YES]; 
    38         else [[self connection] sendRawMessage:[NSString stringWithFormat:@"PART %@ :%@", [self name], [reason string]] immediately:YES]; 
     37        if( ! [reason length] ) [[self connection] sendRawMessageImmediatelyWithFormat:@"PART %@", [self name]]; 
     38        else [[self connection] sendRawMessageImmediatelyWithFormat:@"PART %@ :%@", [self name], [reason string]]; 
    3939        [self _setDateParted:[NSDate date]]; 
    4040} 
     
    221221                NSData *msg = [MVIRCChatConnection _flattenedIRCDataForMessage:reason withEncoding:[self encoding] andChatFormat:[[self connection] outgoingChatFormat]]; 
    222222                NSString *prefix = [[NSString allocWithZone:nil] initWithFormat:@"KICK %@ %@ :", [self name], [user nickname]]; 
    223                 [[self connection] sendRawMessageWithComponents:prefix, msg, nil]; 
     223                [[self connection] sendRawMessageImmediatelyWithComponents:prefix, msg, nil]; 
    224224                [prefix release]; 
    225         } else [[self connection] sendRawMessageWithFormat:@"KICK %@ %@", [self name], [user nickname]]; 
     225        } else [[self connection] sendRawMessageImmediatelyWithFormat:@"KICK %@ %@", [self name], [user nickname]]; 
    226226} 
    227227 
    228228- (void) addBanForUser:(MVChatUser *) user { 
    229229        [super addBanForUser:user]; 
    230         [[self connection] sendRawMessageWithFormat:@"MODE %@ +b %@!%@@%@", [self name], ( [user nickname] ? [user nickname] : @"*" ), ( [user username] ? [user username] : @"*" ), ( [user address] ? [user address] : @"*" )]; 
     230        [[self connection] sendRawMessageImmediatelyWithFormat:@"MODE %@ +b %@!%@@%@", [self name], ( [user nickname] ? [user nickname] : @"*" ), ( [user username] ? [user username] : @"*" ), ( [user address] ? [user address] : @"*" )]; 
    231231} 
    232232 
    233233- (void) removeBanForUser:(MVChatUser *) user { 
    234234        [super removeBanForUser:user]; 
    235         [[self connection] sendRawMessageWithFormat:@"MODE %@ -b %@!%@@%@", [self name], ( [user nickname] ? [user nickname] : @"*" ), ( [user username] ? [user username] : @"*" ), ( [user address] ? [user address] : @"*" )]; 
     235        [[self connection] sendRawMessageImmediatelyWithFormat:@"MODE %@ -b %@!%@@%@", [self name], ( [user nickname] ? [user nickname] : @"*" ), ( [user username] ? [user username] : @"*" ), ( [user address] ? [user address] : @"*" )]; 
    236236} 
    237237@end