Repository: thrift Updated Branches: refs/heads/master 90b630490 -> df3223c85
THRIFT-3859: Add support for Unix Domain Sockets to TSocketServer and TSocketTransport. Client: cocoa TSocketServer and TSocketTransport have been refactored to support sockets created using either a port or a path. Existing behavior for port-based socket transport is unchanged by this commit. This closes #1031 Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/df3223c8 Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/df3223c8 Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/df3223c8 Branch: refs/heads/master Commit: df3223c85db910e55bc1d5237c145ddcde93e664 Parents: 90b6304 Author: Chris Vasselli <cvasse...@box.com> Authored: Tue Jun 21 16:45:39 2016 -0700 Committer: James E. King, III <jk...@apache.org> Committed: Sun Apr 2 23:14:29 2017 -0400 ---------------------------------------------------------------------- lib/cocoa/src/server/TSocketServer.h | 4 + lib/cocoa/src/server/TSocketServer.m | 117 +++++++++++++++++++----- lib/cocoa/src/transport/TSocketTransport.h | 2 + lib/cocoa/src/transport/TSocketTransport.m | 68 +++++++++++++- 4 files changed, 165 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/df3223c8/lib/cocoa/src/server/TSocketServer.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/server/TSocketServer.h b/lib/cocoa/src/server/TSocketServer.h index fe657ea..95b0d3c 100644 --- a/lib/cocoa/src/server/TSocketServer.h +++ b/lib/cocoa/src/server/TSocketServer.h @@ -41,6 +41,10 @@ extern NSString *const TSockerServerTransportKey; protocolFactory:(id <TProtocolFactory>)protocolFactory processorFactory:(id <TProcessorFactory>)processorFactory; +- (instancetype) initWithPath: (NSString *) path + protocolFactory: (id <TProtocolFactory>) protocolFactory + processorFactory: (id <TProcessorFactory>) processorFactory; + @end http://git-wip-us.apache.org/repos/asf/thrift/blob/df3223c8/lib/cocoa/src/server/TSocketServer.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/server/TSocketServer.m b/lib/cocoa/src/server/TSocketServer.m index ccbbba1..09b603c 100644 --- a/lib/cocoa/src/server/TSocketServer.m +++ b/lib/cocoa/src/server/TSocketServer.m @@ -25,7 +25,7 @@ #import <sys/socket.h> #include <netinet/in.h> - +#include <sys/un.h> NSString *const TSocketServerClientConnectionFinished = @"TSocketServerClientConnectionFinished"; @@ -40,13 +40,14 @@ NSString *const TSockerServerTransportKey = @"TSockerServerTransport"; @property(strong, nonatomic) id<TProcessorFactory> processorFactory; @property(strong, nonatomic) NSFileHandle *socketFileHandle; @property(strong, nonatomic) dispatch_queue_t processingQueue; +@property(strong, nonatomic) NSString *domainSocketPath; @end @implementation TSocketServer --(instancetype) initWithPort:(int)port +-(instancetype) initWithSocket:(CFSocketRef)socket protocolFactory:(id <TProtocolFactory>)protocolFactory processorFactory:(id <TProcessorFactory>)processorFactory; { @@ -62,11 +63,50 @@ NSString *const TSockerServerTransportKey = @"TSockerServerTransport"; _processingQueue = dispatch_queue_create("TSocketServer.processing", processingQueueAttr); // create a socket. - int fd = -1; + int fd = CFSocketGetNative(socket); + + // wrap it in a file handle so we can get messages from it + _socketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd + closeOnDealloc:YES]; + + // throw away our socket + CFSocketInvalidate(socket); + CFRelease(socket); + + // register for notifications of accepted incoming connections + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(connectionAccepted:) + name:NSFileHandleConnectionAcceptedNotification + object:_socketFileHandle]; + + // tell socket to listen + [_socketFileHandle acceptConnectionInBackgroundAndNotify]; + + return self; +} + +- (id) initWithPort: (int) port + protocolFactory: (id <TProtocolFactory>) protocolFactory + processorFactory: (id <TProcessorFactory>) processorFactory +{ + CFSocketRef socket = [[self class] createSocketWithPort:port]; + if (socket == NULL) { + return nil; + } + + if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) { + NSLog(@"TSocketServer: Listening on TCP port %d", port); + } + return self; +} + + ++(CFSocketRef) createSocketWithPort:(int)port +{ CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL); if (socket) { CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate); - fd = CFSocketGetNative(socket); + int fd = CFSocketGetNative(socket); int yes = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); @@ -81,40 +121,75 @@ NSString *const TSockerServerTransportKey = @"TSockerServerTransport"; CFSocketInvalidate(socket); CFRelease(socket); NSLog(@"TSocketServer: Could not bind to address"); - return nil; + return NULL; } + + return socket; } else { NSLog(@"TSocketServer: No server socket"); + return NULL; + } +} + +- (id) initWithPath: (NSString *) path + protocolFactory: (id <TProtocolFactory>) protocolFactory + processorFactory: (id <TProcessorFactory>) processorFactory +{ + _domainSocketPath = path; + CFSocketRef socket = [[self class] createSocketWithPath:path]; + if (socket == NULL) { return nil; } - // wrap it in a file handle so we can get messages from it - _socketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd - closeOnDealloc:YES]; + if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) { + NSLog(@"TSocketServer: Listening on path %@", path); + } + return self; +} - // throw away our socket - CFSocketInvalidate(socket); - CFRelease(socket); ++ (CFSocketRef) createSocketWithPath: (NSString *) path +{ + CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_LOCAL, SOCK_STREAM, IPPROTO_IP, 0, NULL, NULL); + if (socket) { + CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate); + int fd = CFSocketGetNative(socket); + int yes = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); - // register for notifications of accepted incoming connections - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(connectionAccepted:) - name:NSFileHandleConnectionAcceptedNotification - object:_socketFileHandle]; + size_t nullTerminatedPathLength = path.length + 1; + struct sockaddr_un addr; + if (nullTerminatedPathLength> sizeof(addr.sun_path)) { + NSLog(@"TSocketServer: Unable to create socket at path %@. Path is too long.", path); + return NULL; + } - // tell socket to listen - [_socketFileHandle acceptConnectionInBackgroundAndNotify]; + addr.sun_family = AF_LOCAL; + memcpy(addr.sun_path, path.UTF8String, nullTerminatedPathLength); + addr.sun_len = SUN_LEN(&addr); - NSLog(@"TSocketServer: Listening on TCP port %d", port); + NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)]; + if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) { + CFSocketInvalidate(socket); + CFRelease(socket); + NSLog(@"TSocketServer: Could not bind to address"); + return NULL; + } - return self; + return socket; + } else { + NSLog(@"TSocketServer: No server socket"); + return NULL; + } } - -(void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + + if (_domainSocketPath != nil) { + unlink(_domainSocketPath.UTF8String); + } } http://git-wip-us.apache.org/repos/asf/thrift/blob/df3223c8/lib/cocoa/src/transport/TSocketTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSocketTransport.h b/lib/cocoa/src/transport/TSocketTransport.h index 4ea03cc..a7b91a2 100644 --- a/lib/cocoa/src/transport/TSocketTransport.h +++ b/lib/cocoa/src/transport/TSocketTransport.h @@ -28,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN -(id) initWithHostname:(NSString *)hostname port:(int)port; +-(id) initWithPath:(NSString *)path; + @end http://git-wip-us.apache.org/repos/asf/thrift/blob/df3223c8/lib/cocoa/src/transport/TSocketTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSocketTransport.m b/lib/cocoa/src/transport/TSocketTransport.m index 272baf6..9c58abb 100644 --- a/lib/cocoa/src/transport/TSocketTransport.m +++ b/lib/cocoa/src/transport/TSocketTransport.m @@ -24,21 +24,21 @@ #import <CFNetwork/CFNetwork.h> #endif +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/un.h> + @interface TSocketTransport () <NSStreamDelegate> @end @implementation TSocketTransport --(id) initWithHostname:(NSString *)hostname - port:(int)port +- (id) initWithReadStream: (CFReadStreamRef) readStream writeStream: (CFWriteStreamRef) writeStream { NSInputStream *inputStream = nil; NSOutputStream *outputStream = nil; - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)hostname, port, &readStream, &writeStream); if (readStream && writeStream) { CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); @@ -70,4 +70,62 @@ return [super initWithInputStream:inputStream outputStream:outputStream]; } +- (id) initWithHostname: (NSString *) hostname + port: (int) port +{ + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)hostname, port, &readStream, &writeStream); + return [self initWithReadStream:readStream writeStream:writeStream]; +} + +- (id) initWithPath: (NSString *) path +{ + CFSocketNativeHandle sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); + int yes = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + { + NSLog(@"TSocketTransport: Unable to set REUSEADDR property of socket."); + return nil; + } + + NSData *serverAddress = [[self class] createAddressWithPath:path]; + + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + CFStreamCreatePairWithSocket(kCFAllocatorDefault, sockfd, &readStream, &writeStream); + if (!readStream || !writeStream) + { + NSLog(@"TSocketTransport: Unable to create read/write stream pair for socket."); + return nil; + } + + if (connect(sockfd, (struct sockaddr *)serverAddress.bytes, (socklen_t) serverAddress.length) < 0) + { + NSLog(@"TSocketTransport: Connect error: %s\n", strerror(errno)); + return nil; + } + + return [self initWithReadStream:readStream writeStream:writeStream]; +} + ++ (NSData *) createAddressWithPath: (NSString *)path +{ + struct sockaddr_un servaddr; + + size_t nullTerminatedPathLength = path.length + 1; + if (nullTerminatedPathLength> sizeof(servaddr.sun_path)) { + NSLog(@"TSocketTransport: Unable to create socket at path %@. Path is too long.", path); + return nil; + } + + bzero(&servaddr,sizeof(servaddr)); + servaddr.sun_family = AF_LOCAL; + memcpy(servaddr.sun_path, path.UTF8String, nullTerminatedPathLength); + servaddr.sun_len = SUN_LEN(&servaddr); + + return [NSData dataWithBytes:&servaddr length:sizeof(servaddr)]; +} + + @end