diff --git a/Source/GSFileHandle.m b/Source/GSFileHandle.m index ddfae81e9..cc3ec34f2 100644 --- a/Source/GSFileHandle.m +++ b/Source/GSFileHandle.m @@ -95,13 +95,156 @@ #endif // Maximum data in single I/O operation -#define NETBUF_SIZE 4096 +#define NETBUF_SIZE (1024 * 16) #define READ_SIZE NETBUF_SIZE*10 static GSFileHandle* fh_stdin = nil; static GSFileHandle* fh_stdout = nil; static GSFileHandle* fh_stderr = nil; +@interface GSTcpTune : NSObject +- (int) recvSize; +- (int) sendSize: (int)bytesToSend; +- (void) tune: (void*)handle; +@end + +@implementation GSTcpTune + +static int tuneLinger = -1; +static int tuneReceive = 0; +static BOOL tuneSendAll = NO; +static int tuneRBuf = 0; +static int tuneSBuf = 0; + ++ (void) defaultsChanged: (NSNotification*)n +{ + NSUserDefaults *defs = (NSUserDefaults*)[n object]; + NSString *str; + + if (nil == defs) + { + defs = [NSUserDefaults standardUserDefaults]; + } + str = [defs stringForKey: @"GSTcpLinger"]; + if (nil == str) + { + tuneLinger = -1; + } + else + { + tuneLinger = [str intValue]; + } + tuneRBuf = (int)[defs integerForKey: @"GSTcpRcvBuf"]; + tuneSBuf = (int)[defs integerForKey: @"GSTcpSndBuf"]; + tuneReceive = (int)[defs integerForKey: @"GSTcpReceive"]; + tuneSendAll = [defs boolForKey: @"GSTcpSendAll"]; +} + ++ (void) initialize +{ + static BOOL beenHere = NO; + + if (NO == beenHere) + { + NSNotificationCenter *nc; + NSUserDefaults *defs; + + beenHere = YES; + nc = [NSNotificationCenter defaultCenter]; + defs = [NSUserDefaults standardUserDefaults]; + [nc addObserver: self + selector: @selector(defaultsChanged:) + name: NSUserDefaultsDidChangeNotification + object: defs]; + [self defaultsChanged: nil]; + } +} + +- (int) recvSize +{ + if (tuneReceive > 0) + { + return tuneReceive; // Return receive buffer size + } + if (tuneRBuf > 0) + { + return tuneRBuf; // Return socket receive buffer size + } + return READ_SIZE; // Return hard-coded default +} + +- (int) sendSize: (int)bytesToSend +{ + if (YES == tuneSendAll) + { + return bytesToSend; // Try to send all in one go + } + if (tuneSBuf > 0 && tuneSBuf <= bytesToSend) + { + return tuneSBuf; // Limit to socket send buffer + } + if (NETBUF_SIZE <= bytesToSend) + { + return NETBUF_SIZE; // Limit to hard coded default + } + return bytesToSend; +} + +- (void) tune: (void*)handle +{ + int desc = (int)(intptr_t)handle; + int value; + + /* + * Enable tcp-level tracking of whether connection is alive. + */ + value = 1; + setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&value, sizeof(value)); + + if (tuneLinger >= 0) + { + struct linger l; + + l.l_onoff = 1; + l.l_linger = tuneLinger; + if (setsockopt(desc, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0) + { + NSLog(@"Failed to set GSTcpLinger %d: %@", + tuneLinger, [NSError _last]); + } + } + + if (tuneRBuf > 0) + { + /* Set the receive buffer for the socket. + */ + if (setsockopt(desc, SOL_SOCKET, SO_RCVBUF, + (char *)&tuneRBuf, sizeof(tuneRBuf)) < 0) + { + NSLog(@"Failed to set GSTcpRcvBuf %d: %@", tuneRBuf, [NSError _last]); + } + else + { + NSDebugMLLog(@"GSTcpTune", @"Set GSTcpRcvBuf %d", tuneRBuf); + } + } + if (tuneSBuf > 0) + { + /* Set the send buffer for the socket. + */ + if (setsockopt(desc, SOL_SOCKET, SO_SNDBUF, + (char *)&tuneSBuf, sizeof(tuneSBuf)) < 0) + { + NSLog(@"Failed to set GSTcpSndBuf %d: %@", tuneSBuf, [NSError _last]); + } + else + { + NSDebugMLLog(@"GSTcpTune", @"Set GSTcpSndBuf %d", tuneSBuf); + } + } +} +@end + // Key to info dictionary for operation mode. static NSString* NotificationKey = @"NSFileHandleNotificationKey"; @@ -112,6 +255,16 @@ static NSString* NotificationKey = @"NSFileHandleNotificationKey"; @implementation GSFileHandle +static GSTcpTune *tune = nil; + ++ (void) initialize +{ + if (nil == tune) + { + tune = [GSTcpTune new]; + } +} + /** * Encapsulates low level read operation to get data from the operating * system. @@ -608,8 +761,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; { static NSString *esocks = nil; static NSString *dsocks = nil; - static int rbuf = 0; - static int sbuf = 0; static BOOL beenHere = NO; int net; struct sockaddr sin; @@ -617,7 +768,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; NSString *lhost = nil; NSString *shost = nil; NSString *sport = nil; - int status; if (beenHere == NO) { @@ -638,8 +788,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; } esocks = [esocks copy]; } - rbuf = (int)[defs integerForKey: @"GSTcpRcvBuf"]; - sbuf = (int)[defs integerForKey: @"GSTcpSndBuf"]; } if (a == nil || [a isEqualToString: @""]) @@ -752,40 +900,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; DESTROY(self); return nil; } - /* - * Enable tcp-level tracking of whether connection is alive. - */ - status = 1; - setsockopt(net, SOL_SOCKET, SO_KEEPALIVE, (char *)&status, sizeof(status)); - if (rbuf > 0) - { - /* Set the receive buffer for the socket. - */ - if (setsockopt(net, SOL_SOCKET, SO_RCVBUF, - (char *)&rbuf, sizeof(rbuf)) < 0) - { - NSLog(@"Failed to set GSTcpRcvBuf %d: %@", rbuf, [NSError _last]); - } - else - { - NSLog(@"Set GSTcpRcvBuf %d", rbuf); - } - } - if (sbuf > 0) - { - /* Set the send buffer for the socket. - */ - if (setsockopt(net, SOL_SOCKET, SO_SNDBUF, - (char *)&sbuf, sizeof(sbuf)) < 0) - { - NSLog(@"Failed to set GSTcpSndBuf %d: %@", sbuf, [NSError _last]); - } - else - { - NSLog(@"Set GSTcpSndBuf %d", sbuf); - } - } + [tune tune: (void*)(intptr_t)net]; if (lhost != nil) { @@ -1254,7 +1370,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; - (NSData*) availableData { - char buf[READ_SIZE]; + int rmax = [tune recvSize]; + char buf[rmax]; NSMutableData* d; int len; @@ -1323,7 +1440,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; - (NSData*) readDataToEndOfFile { - char buf[READ_SIZE]; + int rmax = [tune recvSize]; + char buf[rmax]; NSMutableData* d; int len; @@ -1350,7 +1468,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; { NSMutableData *d; int got; - char buf[READ_SIZE]; + int rmax = [tune recvSize]; + char buf[rmax]; [self checkRead]; if (isNonBlocking == YES) @@ -1358,7 +1477,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; [self setNonBlocking: NO]; } - d = [NSMutableData dataWithCapacity: len < READ_SIZE ? len : READ_SIZE]; + d = [NSMutableData dataWithCapacity: len < sizeof(buf) ? len : sizeof(buf)]; do { int chunk = len > sizeof(buf) ? sizeof(buf) : len; @@ -1397,10 +1516,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; { int toWrite = len - pos; - if (toWrite > NETBUF_SIZE) - { - toWrite = NETBUF_SIZE; - } + toWrite = [tune sendSize: toWrite]; rval = [self write: (char*)ptr+pos length: toWrite]; if (rval < 0) { @@ -1590,7 +1706,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; [self ignoreReadDescriptor]; [self ignoreWriteDescriptor]; - [self setNonBlocking: wasNonBlocking]; + [self setNonBlocking: NO]; #if USE_ZLIB if (gzDescriptor != 0) { @@ -1598,6 +1714,26 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; gzDescriptor = 0; } #endif + if (YES == isSocket) + { + shutdown(descriptor, SHUT_WR); + for(;;) + { + int result; + char buffer[4096]; + + result = read(descriptor, buffer, sizeof(buffer)); + if (result <= 0) + { + if (result < 0) + { + NSLog(@"%@ read fail on socket shutdown: %@", + self, [NSError _last]); + } + break; + } + } + } (void)close(descriptor); descriptor = -1; acceptOK = NO; @@ -1917,17 +2053,11 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; GSFileHandle *h; struct sockaddr sin; unsigned int size = sizeof(sin); - int status; - - /* - * Enable tcp-level tracking of whether connection is alive. - */ - status = 1; - setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&status, - sizeof(status)); + [tune tune: (void*)(intptr_t)desc]; + h = [[[self class] alloc] initWithFileDescriptor: desc - closeOnDealloc: YES]; + closeOnDealloc: YES]; h->isSocket = YES; getpeername(desc, &sin, &size); [h setAddr: &sin]; @@ -1946,7 +2076,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr"; NSMutableData *item; int length; int received = 0; - char buf[READ_SIZE]; + int rmax = [tune recvSize]; + char buf[rmax]; item = [readInfo objectForKey: NSFileHandleNotificationDataItem]; /*