http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/server/TSocketServer.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/server/TSocketServer.m b/lib/cocoa/src/server/TSocketServer.m index 07bc829..ccbbba1 100644 --- a/lib/cocoa/src/server/TSocketServer.m +++ b/lib/cocoa/src/server/TSocketServer.m @@ -21,37 +21,51 @@ #import "TSocketServer.h" #import "TNSFileHandleTransport.h" #import "TProtocol.h" -#import "TTransportException.h" -#import "TObjective-C.h" +#import "TTransportError.h" + #import <sys/socket.h> #include <netinet/in.h> -NSString * const kTSocketServer_ClientConnectionFinishedForProcessorNotification = @"TSocketServer_ClientConnectionFinishedForProcessorNotification"; -NSString * const kTSocketServer_ProcessorKey = @"TSocketServer_Processor"; -NSString * const kTSockerServer_TransportKey = @"TSockerServer_Transport"; +NSString *const TSocketServerClientConnectionFinished = @"TSocketServerClientConnectionFinished"; +NSString *const TSocketServerProcessorKey = @"TSocketServerProcessor"; +NSString *const TSockerServerTransportKey = @"TSockerServerTransport"; + + +@interface TSocketServer () + +@property(strong, nonatomic) id<TProtocolFactory> inputProtocolFactory; +@property(strong, nonatomic) id<TProtocolFactory> outputProtocolFactory; +@property(strong, nonatomic) id<TProcessorFactory> processorFactory; +@property(strong, nonatomic) NSFileHandle *socketFileHandle; +@property(strong, nonatomic) dispatch_queue_t processingQueue; + +@end @implementation TSocketServer -- (id) initWithPort: (int) port - protocolFactory: (id <TProtocolFactory>) protocolFactory - processorFactory: (id <TProcessorFactory>) processorFactory +-(instancetype) initWithPort:(int)port + protocolFactory:(id <TProtocolFactory>)protocolFactory + processorFactory:(id <TProcessorFactory>)processorFactory; { self = [super init]; - mInputProtocolFactory = [protocolFactory retain_stub]; - mOutputProtocolFactory = [protocolFactory retain_stub]; - mProcessorFactory = [processorFactory retain_stub]; + _inputProtocolFactory = protocolFactory; + _outputProtocolFactory = protocolFactory; + _processorFactory = processorFactory; + + dispatch_queue_attr_t processingQueueAttr = + dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0); + + _processingQueue = dispatch_queue_create("TSocketServer.processing", processingQueueAttr); // create a socket. int fd = -1; CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL); if (socket) { - CFOptionFlags flagsToClear = kCFSocketCloseOnInvalidate; - CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~flagsToClear); - + CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate); fd = CFSocketGetNative(socket); int yes = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); @@ -63,135 +77,88 @@ NSString * const kTSockerServer_TransportKey = @"TSockerServer_Transport"; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)]; - if (CFSocketSetAddress(socket, (bridge_stub CFDataRef)address) != kCFSocketSuccess) { + if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) { CFSocketInvalidate(socket); CFRelease(socket); - NSLog(@"*** Could not bind to address"); + NSLog(@"TSocketServer: Could not bind to address"); return nil; } - } else { - NSLog(@"*** No server socket"); + } + else { + NSLog(@"TSocketServer: No server socket"); return nil; } - + // wrap it in a file handle so we can get messages from it - mSocketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor: fd - closeOnDealloc: YES]; - + _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: mSocketFileHandle]; - + + // register for notifications of accepted incoming connections + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(connectionAccepted:) + name:NSFileHandleConnectionAcceptedNotification + object:_socketFileHandle]; + // tell socket to listen - [mSocketFileHandle acceptConnectionInBackgroundAndNotify]; - - NSLog(@"Listening on TCP port %d", port); - + [_socketFileHandle acceptConnectionInBackgroundAndNotify]; + + NSLog(@"TSocketServer: Listening on TCP port %d", port); + return self; } -- (void) dealloc { +-(void) dealloc +{ [[NSNotificationCenter defaultCenter] removeObserver:self]; - [mInputProtocolFactory release_stub]; - [mOutputProtocolFactory release_stub]; - [mProcessorFactory release_stub]; - [mSocketFileHandle release_stub]; - [super dealloc_stub]; } -- (void) connectionAccepted: (NSNotification *) aNotification +-(void) connectionAccepted:(NSNotification *)notification { - NSFileHandle * socket = [[aNotification userInfo] objectForKey: NSFileHandleNotificationFileHandleItem]; + NSFileHandle *socket = [notification.userInfo objectForKey:NSFileHandleNotificationFileHandleItem]; + + // Now that we have a client connected, handle request on queue + dispatch_async(_processingQueue, ^{ - // now that we have a client connected, spin off a thread to handle activity - [NSThread detachNewThreadSelector: @selector(handleClientConnection:) - toTarget: self - withObject: socket]; + [self handleClientConnection:socket]; - [[aNotification object] acceptConnectionInBackgroundAndNotify]; + }); + + // Continue accepting connections + [_socketFileHandle acceptConnectionInBackgroundAndNotify]; } -- (void) handleClientConnection: (NSFileHandle *) clientSocket +-(void) handleClientConnection:(NSFileHandle *)clientSocket { -#if __has_feature(objc_arc) - @autoreleasepool { - TNSFileHandleTransport * transport = [[TNSFileHandleTransport alloc] initWithFileHandle: clientSocket]; - id<TProcessor> processor = [mProcessorFactory processorForTransport: transport]; - - id <TProtocol> inProtocol = [mInputProtocolFactory newProtocolOnTransport: transport]; - id <TProtocol> outProtocol = [mOutputProtocolFactory newProtocolOnTransport: transport]; - - @try { - BOOL result = NO; - do { - @autoreleasepool { - result = [processor processOnInputProtocol: inProtocol outputProtocol: outProtocol]; - } - } while (result); - } - @catch (TTransportException * te) { - (void)te; - //NSLog(@"Caught transport exception, abandoning client connection: %@", te); - } - - NSNotification * n = [NSNotification notificationWithName: kTSocketServer_ClientConnectionFinishedForProcessorNotification - object: self - userInfo: [NSDictionary dictionaryWithObjectsAndKeys: - processor, - kTSocketServer_ProcessorKey, - transport, - kTSockerServer_TransportKey, - nil]]; - [[NSNotificationCenter defaultCenter] performSelectorOnMainThread: @selector(postNotification:) withObject: n waitUntilDone: YES]; - - } -#else - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - TNSFileHandleTransport * transport = [[TNSFileHandleTransport alloc] initWithFileHandle: clientSocket]; - id<TProcessor> processor = [mProcessorFactory processorForTransport: transport]; - - id <TProtocol> inProtocol = [[mInputProtocolFactory newProtocolOnTransport: transport] autorelease]; - id <TProtocol> outProtocol = [[mOutputProtocolFactory newProtocolOnTransport: transport] autorelease]; - - @try { - BOOL result = NO; - do { - NSAutoreleasePool * myPool = [[NSAutoreleasePool alloc] init]; - result = [processor processOnInputProtocol: inProtocol outputProtocol: outProtocol]; - [myPool release]; - } while (result); - } - @catch (TTransportException * te) { - //NSLog(@"Caught transport exception, abandoning client connection: %@", te); - } + @autoreleasepool { - NSNotification * n = [NSNotification notificationWithName: kTSocketServer_ClientConnectionFinishedForProcessorNotification - object: self - userInfo: [NSDictionary dictionaryWithObjectsAndKeys: - processor, - kTSocketServer_ProcessorKey, - transport, - kTSockerServer_TransportKey, - nil]]; - [[NSNotificationCenter defaultCenter] performSelectorOnMainThread: @selector(postNotification:) withObject: n waitUntilDone: YES]; - - [pool release]; -#endif -} + TNSFileHandleTransport *transport = [[TNSFileHandleTransport alloc] initWithFileHandle:clientSocket]; + id<TProcessor> processor = [_processorFactory processorForTransport:transport]; + id <TProtocol> inProtocol = [_inputProtocolFactory newProtocolOnTransport:transport]; + id <TProtocol> outProtocol = [_outputProtocolFactory newProtocolOnTransport:transport]; + NSError *error; + if (![processor processOnInputProtocol:inProtocol outputProtocol:outProtocol error:&error]) { + // Handle error + NSLog(@"Error processing request: %@", error); + } -@end + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:TSocketServerClientConnectionFinished + object:self + userInfo:@{TSocketServerProcessorKey: processor, + TSockerServerTransportKey: transport}]; + }); + } +} +@end
http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TAsyncTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TAsyncTransport.h b/lib/cocoa/src/transport/TAsyncTransport.h index f75b701..bab4fbd 100644 --- a/lib/cocoa/src/transport/TAsyncTransport.h +++ b/lib/cocoa/src/transport/TAsyncTransport.h @@ -18,12 +18,29 @@ */ #import "TTransport.h" -#import "TException.h" -typedef void(^TAsyncFailureBlock)(TException *); +NS_ASSUME_NONNULL_BEGIN + + +@protocol TAsyncTransport; + + +@protocol TAsyncTransportFactory <NSObject> + +-(id<TAsyncTransport>) newTransport; + +@end + + +typedef void (^TAsyncCompletionBlock)(); +typedef void (^TAsyncFailureBlock)(NSError * __nonnull); + @protocol TAsyncTransport <TTransport> -- (void) flush:(dispatch_block_t)flushed failure:(TAsyncFailureBlock)failure; +-(void) flushWithCompletion:(TAsyncCompletionBlock)completed failure:(TAsyncFailureBlock)failure; @end + + +NS_ASSUME_NONNULL_END \ No newline at end of file http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TFramedTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TFramedTransport.h b/lib/cocoa/src/transport/TFramedTransport.h index fc38877..ea68ac4 100644 --- a/lib/cocoa/src/transport/TFramedTransport.h +++ b/lib/cocoa/src/transport/TFramedTransport.h @@ -20,10 +20,14 @@ #import <Foundation/Foundation.h> #import "TTransport.h" -@interface TFramedTransport : NSObject <TTransport> { - id <TTransport> mTransport; -} +NS_ASSUME_NONNULL_BEGIN -- (id) initWithTransport: (id <TTransport>) transport; + +@interface TFramedTransport : NSObject <TTransport> + +-(id) initWithTransport:(id <TTransport>)transport; @end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TFramedTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TFramedTransport.m b/lib/cocoa/src/transport/TFramedTransport.m index 2148806..4db65c4 100644 --- a/lib/cocoa/src/transport/TFramedTransport.m +++ b/lib/cocoa/src/transport/TFramedTransport.m @@ -18,126 +18,163 @@ */ #import "TFramedTransport.h" -#import "TTransportException.h" -#import "TObjective-C.h" +#import "TTransportError.h" #define HEADER_SIZE 4 #define INIT_FRAME_SIZE 1024 -@implementation TFramedTransport { - NSMutableData* writeBuffer; - NSMutableData* readBuffer; - NSUInteger readOffset; - uint8_t dummy_header[HEADER_SIZE]; -} -- (id) initWithTransport:(id <TTransport>)transport -{ - mTransport = [transport retain_stub]; - readBuffer = nil; - readOffset = 0; - writeBuffer = [[NSMutableData alloc] initWithCapacity:INIT_FRAME_SIZE]; - [writeBuffer appendBytes:dummy_header length:HEADER_SIZE]; - return self; -} +@interface TFramedTransport () + +@property(strong, nonatomic) id<TTransport> transport; +@property(strong, nonatomic) NSMutableData *writeBuffer; +@property(strong, nonatomic) NSMutableData *readBuffer; +@property(assign, nonatomic) NSUInteger readOffset; + +@end + + +@implementation TFramedTransport -- (void) dealloc +-(id) initWithTransport:(id <TTransport>)aTransport { - [mTransport release_stub]; - [writeBuffer release_stub]; - if (readBuffer != nil) - [readBuffer release_stub]; - [super dealloc_stub]; + if ((self = [self init])) { + _transport = aTransport; + _readBuffer = nil; + _readOffset = 0; + _writeBuffer = [NSMutableData dataWithLength:HEADER_SIZE]; + } + return self; } -- (void)flush +-(BOOL) flush:(NSError **)error { - size_t headerAndDataLength = [writeBuffer length]; - if (headerAndDataLength < HEADER_SIZE) { - @throw [TTransportException exceptionWithReason:@"Framed transport buffer has no header"]; + int len = (int)[_writeBuffer length]; + int data_len = len - HEADER_SIZE; + if (data_len < 0) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{}]; } + return NO; + } - size_t dataLength = headerAndDataLength - HEADER_SIZE; - uint8_t i32rd[HEADER_SIZE]; - i32rd[0] = (uint8_t)(0xff & (dataLength >> 24)); - i32rd[1] = (uint8_t)(0xff & (dataLength >> 16)); - i32rd[2] = (uint8_t)(0xff & (dataLength >> 8)); - i32rd[3] = (uint8_t)(0xff & (dataLength)); - - // should we make a copy of the writeBuffer instead? Better for threaded operations! - [writeBuffer replaceBytesInRange:NSMakeRange(0, HEADER_SIZE) withBytes:i32rd length:HEADER_SIZE]; - [mTransport write:[writeBuffer mutableBytes] offset:0 length:headerAndDataLength]; - [mTransport flush]; - - // reuse old memory buffer - [writeBuffer setLength:0]; - [writeBuffer appendBytes:dummy_header length:HEADER_SIZE]; + UInt8 i32rd[HEADER_SIZE]; + i32rd[0] = (UInt8)(0xff & (data_len >> 24)); + i32rd[1] = (UInt8)(0xff & (data_len >> 16)); + i32rd[2] = (UInt8)(0xff & (data_len >> 8)); + i32rd[3] = (UInt8)(0xff & (data_len)); + + // should we make a copy of the writeBuffer instead? Better for threaded + // operations! + [_writeBuffer replaceBytesInRange:NSMakeRange(0, HEADER_SIZE) + withBytes:i32rd length:HEADER_SIZE]; + + if (![_transport write:_writeBuffer.mutableBytes offset:0 length:len error:error]) { + return NO; + } + + if (![_transport flush:error]) { + return NO; + } + + _writeBuffer.length = HEADER_SIZE; + + return YES; } -- (void) write: (const uint8_t *) data offset: (size_t) offset length: (size_t) length +-(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error { - [writeBuffer appendBytes:data+offset length:length]; + [_writeBuffer appendBytes:data+offset length:length]; + + return YES; } -- (size_t) readAll: (uint8_t *) buf offset: (size_t) offset length: (size_t) length +-(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error { - if (readBuffer == nil) { - [self readFrame]; + UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; + if (got != length) { + + // Report underflow only if readAvail didn't report error already + if (error && !*error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:nil]; } - - if (readBuffer != nil) { - size_t bufferLength = [readBuffer length]; - if (bufferLength - readOffset >= length) { - [readBuffer getBytes:buf range:NSMakeRange(readOffset,length)]; // copy data - readOffset += length; - } else { - // void the previous readBuffer data and request a new frame - [self readFrame]; - [readBuffer getBytes:buf range:NSMakeRange(0,length)]; // copy data - readOffset = length; - } - } - return length; + + return NO; + } + + return YES; } -- (void)readFrame +-(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)length error:(NSError *__autoreleasing *)error { - uint8_t i32rd[HEADER_SIZE]; - [mTransport readAll: i32rd offset: 0 length: HEADER_SIZE]; - int32_t headerValue = - ((i32rd[0] & 0xff) << 24) | - ((i32rd[1] & 0xff) << 16) | - ((i32rd[2] & 0xff) << 8) | - ((i32rd[3] & 0xff)); - if (headerValue < 0) { - NSString *reason = [NSString stringWithFormat: - @"Frame header reports negative frame size: %"PRId32, - headerValue]; - @throw [TTransportException exceptionWithReason:reason]; + UInt32 got = 0; + while (got < length) { + + NSUInteger avail = _readBuffer.length - _readOffset; + if (avail == 0) { + if (![self readFrame:error]) { + return 0; + } + avail = _readBuffer.length; } - /* Cast should be safe: - * Have verified headerValue non-negative and of lesser or equal bitwidth to size_t. */ - size_t frameSize = (size_t)headerValue; - [self ensureReadBufferHasLength:frameSize]; + NSRange range; + range.location = _readOffset; + range.length = MIN(length - got, avail); + + [_readBuffer getBytes:outBuffer+outBufferOffset+got range:range]; + _readOffset += range.length; + got += range.length; + } - [mTransport readAll:[readBuffer mutableBytes] offset:0 length:frameSize]; + return got; } -- (void)ensureReadBufferHasLength:(size_t)length +-(BOOL) readFrame:(NSError **)error { - if (readBuffer == nil) { - readBuffer = [[NSMutableData alloc] initWithLength:length]; - } else { - size_t currentLength = [readBuffer length]; - BOOL isTooLong = (currentLength >= length); - if (isTooLong) { - [readBuffer setLength:length]; - } else { - size_t lengthToAdd = length - currentLength; - [readBuffer increaseLengthBy:lengthToAdd]; - } + UInt8 i32rd[HEADER_SIZE]; + if (![_transport readAll:i32rd offset:0 length:HEADER_SIZE error:error]) { + return NO; + } + + SInt32 size = + ((i32rd[0] & 0xff) << 24) | + ((i32rd[1] & 0xff) << 16) | + ((i32rd[2] & 0xff) << 8) | + ((i32rd[3] & 0xff)); + + if (_readBuffer == nil) { + + _readBuffer = [NSMutableData dataWithLength:size]; + + } + else { + + SInt32 len = (SInt32)_readBuffer.length; + if (len >= size) { + + _readBuffer.length = size; + + } + else { + + // increase length of data buffer + [_readBuffer increaseLengthBy:size-len]; + } + + } + + // copy into internal memory buffer + if (![_transport readAll:_readBuffer.mutableBytes offset:0 length:size error:error]) { + return NO; + } + + return YES; } @end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPClient.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPClient.h b/lib/cocoa/src/transport/THTTPClient.h deleted file mode 100644 index 78935fb..0000000 --- a/lib/cocoa/src/transport/THTTPClient.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import <Foundation/Foundation.h> -#import "TTransport.h" - -@interface THTTPClient : NSObject <TTransport> { - NSURL * mURL; - NSMutableURLRequest * mRequest; - NSMutableData * mRequestData; - NSData * mResponseData; - size_t mResponseDataOffset; - NSString * mUserAgent; - int mTimeout; -} - -- (id) initWithURL: (NSURL *) aURL; - -- (id) initWithURL: (NSURL *) aURL - userAgent: (NSString *) userAgent - timeout: (int) timeout; - -- (void) setURL: (NSURL *) aURL; - -@end - http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPClient.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPClient.m b/lib/cocoa/src/transport/THTTPClient.m deleted file mode 100644 index 169927c..0000000 --- a/lib/cocoa/src/transport/THTTPClient.m +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "THTTPClient.h" -#import "TTransportException.h" -#import "TObjective-C.h" - -@implementation THTTPClient - - -- (void) setupRequest -{ - if (mRequest != nil) { - [mRequest release_stub]; - } - - // set up our request object that we'll use for each request - mRequest = [[NSMutableURLRequest alloc] initWithURL: mURL]; - [mRequest setHTTPMethod: @"POST"]; - [mRequest setValue: @"application/x-thrift" forHTTPHeaderField: @"Content-Type"]; - [mRequest setValue: @"application/x-thrift" forHTTPHeaderField: @"Accept"]; - - NSString * userAgent = mUserAgent; - if (!userAgent) { - userAgent = @"Cocoa/THTTPClient"; - } - [mRequest setValue: userAgent forHTTPHeaderField: @"User-Agent"]; - - [mRequest setCachePolicy: NSURLRequestReloadIgnoringCacheData]; - if (mTimeout) { - [mRequest setTimeoutInterval: mTimeout]; - } -} - - -- (id) initWithURL: (NSURL *) aURL -{ - return [self initWithURL: aURL - userAgent: nil - timeout: 0]; -} - - -- (id) initWithURL: (NSURL *) aURL - userAgent: (NSString *) userAgent - timeout: (int) timeout -{ - self = [super init]; - if (!self) { - return nil; - } - - mTimeout = timeout; - if (userAgent) { - mUserAgent = [userAgent retain_stub]; - } - mURL = [aURL retain_stub]; - - [self setupRequest]; - - // create our request data buffer - mRequestData = [[NSMutableData alloc] initWithCapacity: 1024]; - - return self; -} - - -- (void) setURL: (NSURL *) aURL -{ - [aURL retain_stub]; - [mURL release_stub]; - mURL = aURL; - - [self setupRequest]; -} - - -- (void) dealloc -{ - [mURL release_stub]; - [mUserAgent release_stub]; - [mRequest release_stub]; - [mRequestData release_stub]; - [mResponseData release_stub]; - [super dealloc_stub]; -} - - -- (size_t) readAll: (uint8_t *) buf offset: (size_t) offset length: (size_t) length -{ - NSRange r; - r.location = mResponseDataOffset; - r.length = length; - - [mResponseData getBytes: buf+offset range: r]; - mResponseDataOffset += length; - - return length; -} - - -- (void) write: (const uint8_t *) data offset: (size_t) offset length: (size_t) length -{ - [mRequestData appendBytes: data+offset length: length]; -} - - -- (void) flush -{ - [mRequest setHTTPBody: mRequestData]; // not sure if it copies the data - - // make the HTTP request - NSURLResponse * response; - NSError * error; - NSData * responseData = - [NSURLConnection sendSynchronousRequest: mRequest returningResponse: &response error: &error]; - - [mRequestData setLength: 0]; - - if (responseData == nil) { - @throw [TTransportException exceptionWithName: @"TTransportException" - reason: @"Could not make HTTP request" - error: error]; - } - if (![response isKindOfClass: [NSHTTPURLResponse class]]) { - @throw [TTransportException exceptionWithName: @"TTransportException" - reason: [NSString stringWithFormat: @"Unexpected NSURLResponse type: %@", - NSStringFromClass([response class])]]; - } - - NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response; - if ([httpResponse statusCode] != 200) { - @throw [TTransportException exceptionWithName: @"TTransportException" - reason: [NSString stringWithFormat: @"Bad response from HTTP server: %ld", - (long)[httpResponse statusCode]]]; - } - - // phew! - [mResponseData release_stub]; - mResponseData = [responseData retain_stub]; - mResponseDataOffset = 0; -} - - -@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPSessionTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPSessionTransport.h b/lib/cocoa/src/transport/THTTPSessionTransport.h new file mode 100644 index 0000000..003499b --- /dev/null +++ b/lib/cocoa/src/transport/THTTPSessionTransport.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> +#import "TAsyncTransport.h" + +NS_ASSUME_NONNULL_BEGIN + + +typedef NSError *__nullable (^THTTPSessionTransportResponseValidateBlock) (NSHTTPURLResponse *response, NSData *responseData); + + +@interface THTTPSessionTransportFactory : NSObject<TAsyncTransportFactory> + +@property (strong, nonatomic) THTTPSessionTransportResponseValidateBlock responseValidate; + ++(void) setupDefaultsForSessionConfiguration:(NSURLSessionConfiguration *)config + withProtocolName:(NSString *)protocolName; + +-(id) initWithSession:(NSURLSession *)session + URL:(NSURL *)aURL; + +@end + + +@interface THTTPSessionTransport : NSObject <TAsyncTransport> + +@end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPSessionTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPSessionTransport.m b/lib/cocoa/src/transport/THTTPSessionTransport.m new file mode 100644 index 0000000..c10b7fc --- /dev/null +++ b/lib/cocoa/src/transport/THTTPSessionTransport.m @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "THTTPSessionTransport.h" +#import "TTransportError.h" + + +@interface THTTPSessionTransportFactory () + +@property (strong, nonatomic) NSURLSession *session; +@property (strong, nonatomic) NSURL *url; + +@end + + +@interface THTTPSessionTransport () + +@property (strong, nonatomic) THTTPSessionTransportFactory *factory; +@property (strong, nonatomic) NSMutableData *requestData; +@property (strong, nonatomic) NSData *responseData; +@property (assign, nonatomic) NSUInteger responseDataOffset; + +-(instancetype) initWithFactory:(THTTPSessionTransportFactory *)factory; + +@end + + +@implementation THTTPSessionTransportFactory + ++(void) setupDefaultsForSessionConfiguration:(NSURLSessionConfiguration *)config withProtocolName:(NSString *)protocolName +{ + NSString *thriftContentType = @"application/x-thrift"; + if (protocolName.length) { + thriftContentType = [thriftContentType stringByAppendingFormat:@"; p=%@", protocolName]; + } + + config.requestCachePolicy = NSURLRequestReloadIgnoringCacheData; + config.HTTPShouldUsePipelining = YES; + config.HTTPShouldSetCookies = NO; + config.URLCache = nil; + config.HTTPAdditionalHeaders = @{@"Content-Type":thriftContentType, + @"Accept":thriftContentType, + @"User-Agent":@"Thrift/Cocoa (Session)"}; +} + + +-(id) initWithSession:(NSURLSession *)session URL:(NSURL *)url +{ + self = [super init]; + if (self) { + _session = session; + _url = url; + } + + return self; +} + +-(id<TAsyncTransport>) newTransport +{ + return [[THTTPSessionTransport alloc] initWithFactory:self]; +} + +-(NSURLSessionDataTask *) taskWithRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler + error:(NSError *__autoreleasing *)error +{ + NSURLSessionDataTask *newTask = [_session dataTaskWithRequest:request completionHandler:completionHandler]; + if (!newTask) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{NSLocalizedDescriptionKey:@"Failed to create session data task"}]; + } + return nil; + } + + return newTask; +} + +-(NSError *) validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data +{ + if (_responseValidate) { + return _responseValidate(response, data); + } + return nil; +} + +@end + + + +@implementation THTTPSessionTransport + +-(instancetype) initWithFactory:(THTTPSessionTransportFactory *)factory +{ + self = [super init]; + if (self) { + _factory = factory; + } + return self; +} + +-(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error +{ + UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; + if (got != length) { + + // Report underflow only if readAvail didn't report error already + if (error && !*error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:nil]; + } + + return NO; + } + + return YES; +} + +-(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error +{ + NSUInteger avail = _responseData.length - _responseDataOffset; + + NSRange range; + range.location = _responseDataOffset; + range.length = MIN(maxLength, avail); + + [_responseData getBytes:outBuffer+outBufferOffset range:range]; + _responseDataOffset += range.length; + + return (UInt32)range.length; +} + +-(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error +{ + if (!_requestData) { + _requestData = [NSMutableData dataWithCapacity:256]; + } + + [_requestData appendBytes:data+offset length:length]; + + return YES; +} + +-(void) flushWithCompletion:(TAsyncCompletionBlock)completed failure:(TAsyncFailureBlock)failure +{ + NSError *error; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:_factory.url]; + request.HTTPMethod = @"POST"; + request.HTTPBody = _requestData; + + _requestData = nil; + + NSURLSessionDataTask *task = [_factory taskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + + // Check response type + if (!error && ![response isKindOfClass:NSHTTPURLResponse.class]) { + + error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{TTransportErrorHttpErrorKey: @(THttpTransportErrorInvalidResponse)}]; + + } + + // Check status code + NSHTTPURLResponse *httpResponse = (id)response; + if (!error && httpResponse.statusCode != 200) { + + THttpTransportError code; + if (httpResponse.statusCode == 401) { + code = THttpTransportErrorAuthentication; + } + else { + code = THttpTransportErrorInvalidStatus; + } + + error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{TTransportErrorHttpErrorKey: @(code), + @"statusCode":@(httpResponse.statusCode)}]; + } + + // Allow factory to check + if (!error) { + error = [_factory validateResponse:httpResponse data:data]; + } + + _responseDataOffset = 0; + + if (error) { + + _responseData = nil; + + failure(error); + + } + else { + + if (data == nil) { + data = [NSData data]; + } + + _responseData = data; + + completed(self); + } + + } error:&error]; + + if (!task) { + failure(error); + return; + } + + [task resume]; +} + +-(BOOL) flush:(NSError *__autoreleasing *)error +{ + dispatch_semaphore_t completed = dispatch_semaphore_create(0); + + __block BOOL result; + __block NSError *internalError; + + [self flushWithCompletion:^(id < TAsyncTransport > transport) { + + result = YES; + + dispatch_semaphore_signal(completed); + + } failure:^(NSError *error) { + + internalError = error; + + result = NO; + + dispatch_semaphore_signal(completed); + + }]; + + dispatch_semaphore_wait(completed, DISPATCH_TIME_FOREVER); + + if (error) { + *error = internalError; + } + + return result; +} + +@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPTransport.h b/lib/cocoa/src/transport/THTTPTransport.h new file mode 100644 index 0000000..3c35daf --- /dev/null +++ b/lib/cocoa/src/transport/THTTPTransport.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> +#import "TTransport.h" + +NS_ASSUME_NONNULL_BEGIN + + +@interface THTTPTransport : NSObject <TTransport> + +-(id) initWithURL:(NSURL *)aURL; + +-(id) initWithURL:(NSURL *)aURL + userAgent:(nullable NSString *)userAgent + timeout:(int)timeout; + +-(void) setURL:(NSURL *)aURL; + +@end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/THTTPTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/THTTPTransport.m b/lib/cocoa/src/transport/THTTPTransport.m new file mode 100644 index 0000000..e4046c6 --- /dev/null +++ b/lib/cocoa/src/transport/THTTPTransport.m @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "THTTPTransport.h" +#import "TTransportError.h" + + +@interface THTTPTransport () + +@property (strong, nonatomic) NSURL *url; +@property (strong, nonatomic) NSMutableURLRequest *request; +@property (strong, nonatomic) NSMutableData *requestData; +@property (strong, nonatomic) NSData *responseData; +@property (assign, nonatomic) NSUInteger responseDataOffset; +@property (strong, nonatomic) NSString *userAgent; +@property (assign, nonatomic) NSTimeInterval timeout; + +@end + + +@implementation THTTPTransport + +-(void) setupRequest +{ + // set up our request object that we'll use for each request + _request = [[NSMutableURLRequest alloc] initWithURL:_url]; + [_request setHTTPMethod:@"POST"]; + [_request setValue:@"application/x-thrift" forHTTPHeaderField:@"Content-Type"]; + [_request setValue:@"application/x-thrift" forHTTPHeaderField:@"Accept"]; + + NSString *userAgent = _userAgent; + if (!userAgent) { + userAgent = @"Thrift/Cocoa"; + } + [_request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + + [_request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + if (_timeout) { + [_request setTimeoutInterval:_timeout]; + } +} + + +-(id) initWithURL:(NSURL *)aURL +{ + return [self initWithURL:aURL + userAgent:nil + timeout:0]; +} + + +-(id) initWithURL:(NSURL *)aURL + userAgent:(NSString *)aUserAgent + timeout:(int)aTimeout +{ + self = [super init]; + if (!self) { + return nil; + } + + _timeout = aTimeout; + _userAgent = aUserAgent; + _url = aURL; + + [self setupRequest]; + + // create our request data buffer + _requestData = [[NSMutableData alloc] initWithCapacity:1024]; + + return self; +} + +-(void) setURL:(NSURL *)aURL +{ + _url = aURL; + + [self setupRequest]; +} + +-(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error +{ + UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; + if (got != length) { + + // Report underflow only if readAvail didn't report error already + if (error && !*error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:nil]; + } + + return NO; + } + + return YES; +} + +-(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error +{ + NSUInteger avail = _responseData.length - _responseDataOffset; + + NSRange range; + range.location = _responseDataOffset; + range.length = MIN(maxLength, avail); + + [_responseData getBytes:outBuffer+outBufferOffset range:range]; + _responseDataOffset += range.length; + + return (UInt32)range.length; +} + +-(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error +{ + [_requestData appendBytes:data+offset length:length]; + + return YES; +} + +-(BOOL) flush:(NSError *__autoreleasing *)error +{ + [_request setHTTPBody:_requestData]; + + _responseDataOffset = 0; + + // make the HTTP request + NSURLResponse *response; + _responseData = [NSURLConnection sendSynchronousRequest:_request returningResponse:&response error:error]; + if (!_responseData) { + return NO; + } + + [_requestData setLength:0]; + + if (![response isKindOfClass:NSHTTPURLResponse.class]) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{TTransportErrorHttpErrorKey: @(THttpTransportErrorInvalidResponse)}]; + } + return NO; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if ([httpResponse statusCode] != 200) { + if (error) { + + THttpTransportError code; + if (httpResponse.statusCode == 401) { + code = THttpTransportErrorAuthentication; + } + else { + code = THttpTransportErrorInvalidStatus; + } + + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorUnknown + userInfo:@{TTransportErrorHttpErrorKey: @(code), + @"statusCode":@(httpResponse.statusCode)}]; + } + return NO; + } + + return YES; +} + +@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TMemoryBuffer.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TMemoryBuffer.h b/lib/cocoa/src/transport/TMemoryBuffer.h index fa4d371..6249d32 100644 --- a/lib/cocoa/src/transport/TMemoryBuffer.h +++ b/lib/cocoa/src/transport/TMemoryBuffer.h @@ -20,10 +20,18 @@ #import <Foundation/Foundation.h> #import "TTransport.h" -@interface TMemoryBuffer : NSObject <TTransport> { - NSMutableData *mBuffer; - NSUInteger mOffset; -} -- (id)initWithData:(NSData *)data; -- (NSData *)getBuffer; +NS_ASSUME_NONNULL_BEGIN + + +@interface TMemoryBuffer : NSObject <TTransport> + +-(NSData *) buffer; + +-(id) initWithData:(NSData *)data; + +-(id) initWithDataNoCopy:(NSMutableData *)data; + @end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TMemoryBuffer.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TMemoryBuffer.m b/lib/cocoa/src/transport/TMemoryBuffer.m index 4513ab8..ec19cc8 100644 --- a/lib/cocoa/src/transport/TMemoryBuffer.m +++ b/lib/cocoa/src/transport/TMemoryBuffer.m @@ -18,57 +18,104 @@ */ #import "TMemoryBuffer.h" -#import "TTransportException.h" -#import "TObjective-C.h" +#import "TTransportError.h" + #define GARBAGE_BUFFER_SIZE 4096 // 4KiB + +@interface TMemoryBuffer () + +@property(strong, nonatomic) NSMutableData *buffer; +@property(assign, nonatomic) UInt32 bufferOffset; + +@end + + @implementation TMemoryBuffer -- (id)init { - if ((self = [super init])) { - mBuffer = [[NSMutableData alloc] init]; - mOffset = 0; - } - return self; + +-(id) init +{ + if ((self = [super init])) { + _buffer = [NSMutableData new]; + _bufferOffset = 0; + } + return self; } -- (id)initWithData:(NSData *)data { - if ((self = [super init])) { - mBuffer = [data mutableCopy]; - mOffset = 0; - } - return self; +-(id) initWithData:(NSData *)data +{ + if (self = [super init]) { + _buffer = [data mutableCopy]; + _bufferOffset = 0; + } + return self; } -- (size_t) readAll: (uint8_t *) buf offset: (size_t) offset length: (size_t) length +-(id) initWithDataNoCopy:(NSMutableData *)data { - if ([mBuffer length] - mOffset < length) { - @throw [TTransportException exceptionWithReason:@"Not enough bytes remain in buffer"]; - } - [mBuffer getBytes:buf range:NSMakeRange(mOffset, length)]; - mOffset += length; - if (mOffset >= GARBAGE_BUFFER_SIZE) { - [mBuffer replaceBytesInRange:NSMakeRange(0, mOffset) withBytes:NULL length:0]; - mOffset = 0; - } - return length; + if (self = [super init]) { + _buffer = data; + _bufferOffset = 0; + } + return self; } -- (void) write: (const uint8_t *) data offset: (size_t) offset length: (size_t) length +-(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error { - [mBuffer appendBytes:data+offset length:length]; + UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; + if (got != length) { + + // Report underflow only if readAvail didn't report error already + if (error && !*error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:nil]; + } + + return NO; + } + + return YES; } -- (void)flush { - // noop +-(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error +{ + UInt32 avail = (UInt32)_buffer.length - _bufferOffset; + if (avail == 0) { + return 0; + } + + NSRange range; + range.location = _bufferOffset; + range.length = MIN(maxLength, avail); + + [_buffer getBytes:outBuffer + outBufferOffset range:range]; + _bufferOffset += range.length; + + if (_bufferOffset >= GARBAGE_BUFFER_SIZE) { + [_buffer replaceBytesInRange:NSMakeRange(0, _bufferOffset) withBytes:NULL length:0]; + _bufferOffset = 0; + } + + return (UInt32)range.length; +} + +-(BOOL) write:(const UInt8 *)inBuffer offset:(UInt32)inBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error +{ + [_buffer appendBytes:inBuffer + inBufferOffset length:length]; + + return YES; } -- (NSData *)getBuffer { - return [[mBuffer copy] autorelease_stub]; +-(NSData *) buffer +{ + return _buffer; } -- (void)dealloc { - [mBuffer release_stub]; - [super dealloc_stub]; +-(BOOL) flush:(NSError *__autoreleasing *)error +{ + return YES; } + @end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TNSFileHandleTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TNSFileHandleTransport.h b/lib/cocoa/src/transport/TNSFileHandleTransport.h index ba2a209..db6edf3 100644 --- a/lib/cocoa/src/transport/TNSFileHandleTransport.h +++ b/lib/cocoa/src/transport/TNSFileHandleTransport.h @@ -21,15 +21,18 @@ #import <Foundation/Foundation.h> #import "TTransport.h" -@interface TNSFileHandleTransport : NSObject <TTransport> { - NSFileHandle * mInputFileHandle; - NSFileHandle * mOutputFileHandle; -} +NS_ASSUME_NONNULL_BEGIN -- (id) initWithFileHandle: (NSFileHandle *) fileHandle; -- (id) initWithInputFileHandle: (NSFileHandle *) inputFileHandle - outputFileHandle: (NSFileHandle *) outputFileHandle; +@interface TNSFileHandleTransport : NSObject <TTransport> + +-(id) initWithFileHandle:(NSFileHandle *)fileHandle; + +-(id) initWithInputFileHandle:(NSFileHandle *)inputFileHandle + outputFileHandle:(NSFileHandle *)outputFileHandle; @end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TNSFileHandleTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TNSFileHandleTransport.m b/lib/cocoa/src/transport/TNSFileHandleTransport.m index c2b18ca..c907f87 100644 --- a/lib/cocoa/src/transport/TNSFileHandleTransport.m +++ b/lib/cocoa/src/transport/TNSFileHandleTransport.m @@ -19,75 +19,100 @@ #import "TNSFileHandleTransport.h" -#import "TTransportException.h" -#import "TObjective-C.h" +#import "TTransportError.h" + + +@interface TNSFileHandleTransport () + +@property(strong, nonatomic) NSFileHandle *inputFileHandle; +@property(strong, nonatomic) NSFileHandle *outputFileHandle; + +@end @implementation TNSFileHandleTransport -- (id) initWithFileHandle: (NSFileHandle *) fileHandle +-(id) initWithFileHandle:(NSFileHandle *)fileHandle { - return [self initWithInputFileHandle: fileHandle - outputFileHandle: fileHandle]; + return [self initWithInputFileHandle:fileHandle + outputFileHandle:fileHandle]; } -- (id) initWithInputFileHandle: (NSFileHandle *) inputFileHandle - outputFileHandle: (NSFileHandle *) outputFileHandle +-(id) initWithInputFileHandle:(NSFileHandle *)aInputFileHandle + outputFileHandle:(NSFileHandle *)aOutputFileHandle { self = [super init]; - - mInputFileHandle = [inputFileHandle retain_stub]; - mOutputFileHandle = [outputFileHandle retain_stub]; - + if (self) { + _inputFileHandle = aInputFileHandle; + _outputFileHandle = aOutputFileHandle; + } return self; } -- (void) dealloc { - [mInputFileHandle release_stub]; - [mOutputFileHandle release_stub]; - [super dealloc_stub]; +-(BOOL) readAll:(UInt8 *)buf offset:(UInt32)off length:(UInt32)len error:(NSError *__autoreleasing *)error +{ + UInt32 got = 0; + while (got < len) { + + NSData *d = [_inputFileHandle readDataOfLength:len-got]; + if (d.length == 0) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:nil]; + } + return NO; + } + + [d getBytes:buf+got length:d.length]; + got += d.length; + } + return YES; } -- (size_t) readAll: (uint8_t *) buf offset: (size_t) offset length: (size_t) length +-(UInt32) readAvail:(UInt8 *)buf offset:(UInt32)off maxLength:(UInt32)len error:(NSError *__autoreleasing *)error { - size_t totalBytesRead = 0; - while (totalBytesRead < length) { - NSData * data = [mInputFileHandle readDataOfLength: length-totalBytesRead]; - if ([data length] == 0) { - @throw [TTransportException exceptionWithName: @"TTransportException" - reason: @"Cannot read. No more data."]; + UInt32 got = 0; + while (got < len) { + + NSData *d = [_inputFileHandle readDataOfLength:len-got]; + if (d.length == 0) { + break; } - [data getBytes: buf+totalBytesRead]; - totalBytesRead += [data length]; + + [d getBytes:buf+got length:d.length]; + got += d.length; } - return totalBytesRead; + return got; } -- (void) write: (const uint8_t *) data offset: (size_t) offset length: (size_t) length +-(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error { - const void *pos = data + offset; - NSData * dataObject = [[NSData alloc] initWithBytesNoCopy: (void *)pos - length: length - freeWhenDone: NO]; + void *pos = (void *)data + offset; @try { - [mOutputFileHandle writeData: dataObject]; - } @catch (NSException * e) { - @throw [TTransportException exceptionWithName: @"TTransportException" - reason: [NSString stringWithFormat: @"%s: Unable to write data: %@", __PRETTY_FUNCTION__, e]]; + [_outputFileHandle writeData:[NSData dataWithBytesNoCopy:pos length:length freeWhenDone:NO]]; + } + @catch (NSException *e) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorNotOpen + userInfo:@{}]; + } + return NO; } - [dataObject release_stub]; + return YES; } -- (void) flush +-(BOOL) flush:(NSError *__autoreleasing *)error { - + return YES; } @end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TNSStreamTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TNSStreamTransport.h b/lib/cocoa/src/transport/TNSStreamTransport.h index 8011fb9..54c4884 100644 --- a/lib/cocoa/src/transport/TNSStreamTransport.h +++ b/lib/cocoa/src/transport/TNSStreamTransport.h @@ -20,21 +20,24 @@ #import <Foundation/Foundation.h> #import "TTransport.h" -@interface TNSStreamTransport : NSObject <TTransport> { +NS_ASSUME_NONNULL_BEGIN -} -@property (nonatomic, strong) NSInputStream * mInput; -@property (nonatomic, strong) NSOutputStream * mOutput; +@interface TNSStreamTransport : NSObject <TTransport> -- (id) initWithInputStream: (NSInputStream *) input - outputStream: (NSOutputStream *) output; +@property (strong, nonatomic) NSInputStream *input; +@property (strong, nonatomic) NSOutputStream *output; -- (id) initWithInputStream: (NSInputStream *) input; +-(id) initWithInputStream:(nullable NSInputStream *)input + outputStream:(nullable NSOutputStream *)output; -- (id) initWithOutputStream: (NSOutputStream *) output; +-(id) initWithInputStream:(NSInputStream *)input; -@end +-(id) initWithOutputStream:(NSOutputStream *)output; + +-(void) close; +@end +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TNSStreamTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TNSStreamTransport.m b/lib/cocoa/src/transport/TNSStreamTransport.m index 7ac1cdc..c425043 100644 --- a/lib/cocoa/src/transport/TNSStreamTransport.m +++ b/lib/cocoa/src/transport/TNSStreamTransport.m @@ -18,79 +18,136 @@ */ #import "TNSStreamTransport.h" -#import "TTransportException.h" -#import "TObjective-C.h" +#import "TTransportError.h" + + +@interface TNSStreamTransport () +@end @implementation TNSStreamTransport -- (id) initWithInputStream: (NSInputStream *) input - outputStream: (NSOutputStream *) output +-(id) initWithInputStream:(NSInputStream *)input + outputStream:(NSOutputStream *)output { self = [super init]; - self.mInput = [input retain_stub]; - self.mOutput = [output retain_stub]; + if (self) { + _input = input; + _output = output; + } return self; } -- (id) initWithInputStream: (NSInputStream *) input +-(id) initWithInputStream:(NSInputStream *)input { - return [self initWithInputStream: input outputStream: nil]; + return [self initWithInputStream:input outputStream:nil]; } -- (id) initWithOutputStream: (NSOutputStream *) output +-(id) initWithOutputStream:(NSOutputStream *)output { - return [self initWithInputStream: nil outputStream: output]; + return [self initWithInputStream:nil outputStream:output]; } -- (void) dealloc +-(void) dealloc { - [self.mInput release_stub]; - [self.mOutput release_stub]; - [super dealloc_stub]; + [self close]; } +-(BOOL) readAll:(UInt8 *)buf offset:(UInt32)off length:(UInt32)len error:(NSError *__autoreleasing *)error +{ + UInt32 got = 0; + while (got < len) { + + UInt32 read = (UInt32)[_input read:buf+off+got maxLength:len-got]; + if (read <= 0) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorNotOpen + userInfo:@{}]; + } + return NO; + } -- (size_t) readAll: (uint8_t *) buf offset: (size_t) offset length: (size_t) length + got += read; + } + + return YES; +} + + +-(UInt32) readAvail:(UInt8 *)buf offset:(UInt32)off maxLength:(UInt32)len error:(NSError *__autoreleasing *)error { - size_t totalBytesRead = 0; - ssize_t bytesRead = 0; - while (totalBytesRead < length) { - bytesRead = [self.mInput read: buf+offset+totalBytesRead maxLength: length-totalBytesRead]; - - BOOL encounteredErrorOrEOF = (bytesRead <= 0); - if (encounteredErrorOrEOF) { - @throw [TTransportException exceptionWithReason: @"Cannot read. Remote side has closed."]; - } else { - /* bytesRead is guaranteed to be positive and within the range representable by size_t. */ - totalBytesRead += (size_t)bytesRead; + UInt32 got = 0; + while (got < len) { + + UInt32 read = (UInt32)[_input read:buf+off+got maxLength:len-got]; + if (read <= 0) { + break; } + + got += read; } - return totalBytesRead; + + return got; } -- (void) write: (const uint8_t *) data offset: (size_t) offset length: (size_t) length +-(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error { - size_t totalBytesWritten = 0; - ssize_t bytesWritten = 0; - while (totalBytesWritten < length) { - bytesWritten = [self.mOutput write: data+offset+totalBytesWritten maxLength: length-totalBytesWritten]; - if (bytesWritten < 0) { - @throw [TTransportException exceptionWithReason: @"Error writing to transport output stream." - error: [self.mOutput streamError]]; - } else if (bytesWritten == 0) { - @throw [TTransportException exceptionWithReason: @"End of output stream."]; - } else { - /* bytesWritten is guaranteed to be positive and within the range representable by size_t. */ - totalBytesWritten += (size_t)bytesWritten; + int got = 0; + NSInteger total = 0; + while (got < length) { + + total = [_output write:data+offset+got maxLength:length-got]; + if (total == -1) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorNotOpen + userInfo:@{}]; + } + return NO; } + else if (total == 0) { + if (error) { + *error = [NSError errorWithDomain:TTransportErrorDomain + code:TTransportErrorEndOfFile + userInfo:@{}]; + } + return NO; + } + + got += total; } + + return YES; } -- (void) flush +-(BOOL) flush:(NSError *__autoreleasing *)error { - // no flush for you! + return YES; +} + +-(void) close +{ + NSInputStream *input = self.input; + if (input) { + // Close and reset inputstream + CFReadStreamSetProperty((__bridge CFReadStreamRef)(input), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); + [input setDelegate:nil]; + [input close]; + [input removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + input = nil; + } + + NSOutputStream *output = self.output; + if (output) { + // Close and reset outputstream + CFWriteStreamSetProperty((__bridge CFWriteStreamRef)(output), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); + [output setDelegate:nil]; + [output close]; + [output removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + output = nil; + } } @end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketClient.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketClient.h b/lib/cocoa/src/transport/TSSLSocketClient.h deleted file mode 100644 index 44de124..0000000 --- a/lib/cocoa/src/transport/TSSLSocketClient.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import <Foundation/Foundation.h> -#import "TNSStreamTransport.h" - -@interface TSSLSocketClient : TNSStreamTransport -#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) -<NSStreamDelegate> -#endif -{ - NSInputStream *inputStream; - NSOutputStream *outputStream; -@private - NSString *sslHostname; - int sd; -} - -- (id) initWithHostname: (NSString *) hostname - port: (int) port; - -- (BOOL) isOpen; - -@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketClient.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketClient.m b/lib/cocoa/src/transport/TSSLSocketClient.m deleted file mode 100644 index d8c55d6..0000000 --- a/lib/cocoa/src/transport/TSSLSocketClient.m +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#import <Foundation/Foundation.h> -#import <CoreFoundation/CoreFoundation.h> -#import "TSSLSocketClient.h" -#import "TSSLSocketException.h" -#import "TObjective-C.h" -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> - -#if !TARGET_OS_IPHONE -#import <CoreServices/CoreServices.h> -#else -#import <CFNetwork/CFNetwork.h> -#endif - -@implementation TSSLSocketClient - -- (id) initWithHostname: (NSString *) hostname - port: (int) port -{ - sslHostname = hostname; - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - - - /* create a socket structure */ - struct sockaddr_in pin; - struct hostent *hp = NULL; - for(int i = 0; i < 10; i++) { - - - - if ((hp = gethostbyname([hostname UTF8String])) == NULL) { - NSLog(@"failed to resolve hostname %@", hostname); - herror("resolv"); - if(i == 9) { - @throw [TSSLSocketException exceptionWithReason: @"failed to resolve hostname"]; - } - [NSThread sleepForTimeInterval:0.2]; - } else { - break; - } - } - - memset (&pin, 0, sizeof(pin)); - pin.sin_family = AF_INET; - memcpy(&pin.sin_addr, hp->h_addr, sizeof(struct in_addr)); - pin.sin_port = htons (port); - - /* create the socket */ - if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) - { - NSLog(@"failed to create socket for host %@:%d", hostname, port); - @throw [TSSLSocketException exceptionWithReason: @"failed to create socket"]; - } - - /* open a connection */ - if (connect (sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) - { - NSLog(@"failed to create conenct to host %@:%d", hostname, port); - @throw [TSSLSocketException exceptionWithReason: @"failed to connect"]; - } - CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream); - - CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - if (readStream && writeStream) { - CFReadStreamSetProperty(readStream, - kCFStreamPropertySocketSecurityLevel, - kCFStreamSocketSecurityLevelTLSv1); - - NSDictionary *settings = - [NSDictionary dictionaryWithObjectsAndKeys: - (id)kCFBooleanTrue, (id)kCFStreamSSLValidatesCertificateChain, - nil]; - - CFReadStreamSetProperty((CFReadStreamRef)readStream, - kCFStreamPropertySSLSettings, - (CFTypeRef)settings); - CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, - kCFStreamPropertySSLSettings, - (CFTypeRef)settings); - - inputStream = (bridge_stub NSInputStream *)readStream; - [inputStream retain_stub]; - [inputStream setDelegate:self]; - [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - [inputStream open]; - - outputStream = (bridge_stub NSOutputStream *)writeStream; - [outputStream retain_stub]; - [outputStream setDelegate:self]; - [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream open]; - - - CFRelease(readStream); - CFRelease(writeStream); - } - - - - self = [super initWithInputStream: inputStream outputStream: outputStream]; - - return self; -} - -#pragma mark - -#pragma mark NSStreamDelegate -- (void)stream:(NSStream *)aStream - handleEvent:(NSStreamEvent)eventCode { - switch (eventCode) { - case NSStreamEventNone: - break; - case NSStreamEventHasBytesAvailable: - break; - case NSStreamEventOpenCompleted: - break; - case NSStreamEventHasSpaceAvailable: - { - SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef)(sslHostname)); - SecTrustRef trust = NULL; - CFArrayRef streamCertificatesRef = - CFBridgingRetain((__bridge id)((__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]))); - SecTrustCreateWithCertificates(CFBridgingRetain((__bridge id)(streamCertificatesRef)), - policy, - &trust); - - SecTrustResultType trustResultType = kSecTrustResultInvalid; - SecTrustEvaluate(trust, &trustResultType); - - BOOL proceed = NO; - switch (trustResultType) { - case kSecTrustResultProceed: - proceed = YES; - break; - case kSecTrustResultUnspecified: - NSLog(@"Trusted by OS"); - proceed = YES; - break; - case kSecTrustResultRecoverableTrustFailure: - proceed = recoverFromTrustFailure(trust); - break; - case kSecTrustResultDeny: - NSLog(@"Deny"); - break; - case kSecTrustResultFatalTrustFailure: - NSLog(@"FatalTrustFailure"); - break; - case kSecTrustResultOtherError: - NSLog(@"OtherError"); - break; - case kSecTrustResultInvalid: - NSLog(@"Invalid"); - break; - default: - NSLog(@"Default"); - break; - } - - if (trust) { - CFRelease(trust); - } - if (policy) { - CFRelease(policy); - } - if (!proceed) { - NSLog(@"Cannot trust certificate. TrustResultType: %u", trustResultType); - [aStream close]; - @throw [TSSLSocketException exceptionWithReason: @"Cannot trust certificate"]; - } - } - break; - case NSStreamEventErrorOccurred: - { - NSError *theError = [aStream streamError]; - NSLog(@"Error occurred opening stream: %@", theError); -// @throw [TSSLSocketException exceptionWithReason: @"Error occurred opening stream" error: theError]; - break; - } - case NSStreamEventEndEncountered: - break; - } -} - -bool recoverFromTrustFailure(SecTrustRef myTrust) -{ - - SecTrustResultType trustResult; - OSStatus status = SecTrustEvaluate(myTrust, &trustResult); - - CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime; - CFDateRef newDate; - if (trustResult == kSecTrustResultRecoverableTrustFailure) { - trustTime = SecTrustGetVerifyTime(myTrust); - timeIncrement = 31536000; - currentTime = CFAbsoluteTimeGetCurrent(); - newTime = currentTime - timeIncrement; - if (trustTime - newTime){ - newDate = CFDateCreate(NULL, newTime); - SecTrustSetVerifyDate(myTrust, newDate); - status = SecTrustEvaluate(myTrust, &trustResult); - } - } - if (trustResult != kSecTrustResultProceed) { - NSLog(@"Certificate trust failure"); - return false; - } - return true; -} - -- (void)close -{ - if(self.mInput) { - //Close and reset inputstream - CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mInput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - [self.mInput setDelegate:nil]; - [self.mInput close]; - [self.mInput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - self.mInput = nil; - } - - if(self.mOutput) { - //Close and reset outputstream - CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mOutput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - [self.mOutput setDelegate:nil]; - [self.mOutput close]; - [self.mOutput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - self.mOutput = nil; - } -} - -- (BOOL) isOpen -{ - if(sd > 0) - return TRUE; - else - return FALSE; -} - -@end - http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketException.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketException.h b/lib/cocoa/src/transport/TSSLSocketException.h deleted file mode 100644 index c290b05..0000000 --- a/lib/cocoa/src/transport/TSSLSocketException.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TTransportException.h" - -@interface TSSLSocketException : TTransportException - -+ (id) exceptionWithReason: (NSString *) reason - error: (NSError *) error; - -+ (id) exceptionWithReason: (NSString *) reason; - -@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketException.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketException.m b/lib/cocoa/src/transport/TSSLSocketException.m deleted file mode 100644 index 99eadf5..0000000 --- a/lib/cocoa/src/transport/TSSLSocketException.m +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TSSLSocketException.h" - -@implementation TSSLSocketException - -+ (id) exceptionWithReason: (NSString *) reason - error: (NSError *) error -{ - NSDictionary * userInfo = nil; - if (error != nil) { - userInfo = [NSDictionary dictionaryWithObject: error forKey: @"error"]; - } - - return [super exceptionWithName: @"TSSLSocketException" - reason: reason - userInfo: userInfo]; -} - -+ (id) exceptionWithReason: (NSString *) reason -{ - return [self exceptionWithReason: reason error: nil]; -} - -@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketTransport.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketTransport.h b/lib/cocoa/src/transport/TSSLSocketTransport.h new file mode 100644 index 0000000..b606c4a --- /dev/null +++ b/lib/cocoa/src/transport/TSSLSocketTransport.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> +#import "TNSStreamTransport.h" + +NS_ASSUME_NONNULL_BEGIN + + +@interface TSSLSocketTransport : TNSStreamTransport <NSStreamDelegate> + +-(id) initWithHostname:(NSString *)hostname + port:(int)port + error:(NSError **)error; + +-(BOOL) isOpen; + +@end + + +NS_ASSUME_NONNULL_END http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketTransport.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketTransport.m b/lib/cocoa/src/transport/TSSLSocketTransport.m new file mode 100644 index 0000000..ab5eb3d --- /dev/null +++ b/lib/cocoa/src/transport/TSSLSocketTransport.m @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#import <Foundation/Foundation.h> +#import <CoreFoundation/CoreFoundation.h> +#import "TSSLSocketTransport.h" +#import "TSSLSocketTransportError.h" +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#if !TARGET_OS_IPHONE +#import <CoreServices/CoreServices.h> +#else +#import <CFNetwork/CFNetwork.h> +#endif + +@interface TSSLSocketTransport () + +@property(strong, nonatomic) NSString *sslHostname; +@property(assign, nonatomic) int sd; + +@end + + +@implementation TSSLSocketTransport + +-(id) initWithHostname:(NSString *)hostname + port:(int)port + error:(NSError **)error +{ + _sslHostname = hostname; + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + + + /* create a socket structure */ + struct sockaddr_in pin; + struct hostent *hp = NULL; + for (int i = 0; i < 10; i++) { + + + + if ((hp = gethostbyname([hostname UTF8String])) == NULL) { + NSLog(@"failed to resolve hostname %@", hostname); + herror("resolv"); + if (i == 9) { + if (error) { + *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain + code:TSSLSocketTransportErrorHostanameResolution + userInfo:nil]; + } + return nil; + } + [NSThread sleepForTimeInterval:0.2]; + } + else { + break; + } + } + + memset(&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + memcpy(&pin.sin_addr, hp->h_addr, sizeof(struct in_addr)); + pin.sin_port = htons(port); + + /* create the socket */ + if ((_sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + NSLog(@"failed to create socket for host %@:%d", hostname, port); + if (error) { + *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain + code:TSSLSocketTransportErrorSocketCreate + userInfo:nil]; + } + return nil; + } + + /* open a connection */ + if (connect(_sd, (struct sockaddr *)&pin, sizeof(pin)) == -1) { + NSLog(@"failed to create conenct to host %@:%d", hostname, port); + if (error) { + *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain + code:TSSLSocketTransportErrorConnect + userInfo:nil]; + } + return nil; + } + CFStreamCreatePairWithSocket(kCFAllocatorDefault, _sd, &readStream, &writeStream); + + CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); + CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); + + NSInputStream *inputStream; + NSOutputStream *outputStream; + + if (readStream && writeStream) { + + CFReadStreamSetProperty(readStream, + kCFStreamPropertySocketSecurityLevel, + kCFStreamSocketSecurityLevelTLSv1); + + NSDictionary *settings = @{(__bridge NSString *)kCFStreamSSLValidatesCertificateChain: @YES}; + + CFReadStreamSetProperty((CFReadStreamRef)readStream, + kCFStreamPropertySSLSettings, + (CFTypeRef)settings); + + CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, + kCFStreamPropertySSLSettings, + (CFTypeRef)settings); + + inputStream = (__bridge NSInputStream *)readStream; + [inputStream setDelegate:self]; + [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + [inputStream open]; + + outputStream = (__bridge NSOutputStream *)writeStream; + [outputStream setDelegate:self]; + [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream open]; + + CFRelease(readStream); + CFRelease(writeStream); + } + + self = [super initWithInputStream:inputStream outputStream:outputStream]; + + return self; +} + +-(void) dealloc +{ + [self close]; +} + +#pragma mark - +#pragma mark NSStreamDelegate + +-(void) stream:(NSStream *)aStream + handleEvent:(NSStreamEvent)eventCode +{ + switch (eventCode) { + case NSStreamEventNone: + break; + + case NSStreamEventHasBytesAvailable: + break; + + case NSStreamEventOpenCompleted: + break; + + case NSStreamEventHasSpaceAvailable: { + + BOOL proceed = NO; + SecTrustResultType trustResult = kSecTrustResultInvalid; + CFMutableArrayRef newPolicies = NULL; + + do { + + SecTrustRef trust = (__bridge SecTrustRef)[aStream propertyForKey:(NSString *)kCFStreamPropertySSLPeerTrust]; + + // Add new policy to current list of policies + SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef)(_sslHostname)); + if (!policy) { + break; + } + + CFArrayRef policies; + if (SecTrustCopyPolicies(trust, &policies) != errSecSuccess) { + CFRelease(policy); + break; + } + + newPolicies = CFArrayCreateMutableCopy(NULL, 0, policies); + CFArrayAppendValue(newPolicies, policy); + + CFRelease(policies); + CFRelease(policy); + + // Update trust policies + if (SecTrustSetPolicies(trust, newPolicies) != errSecSuccess) { + break; + } + + // Evaluate the trust chain + if (SecTrustEvaluate(trust, &trustResult) != errSecSuccess) { + break; + } + + switch (trustResult) { + case kSecTrustResultProceed: + // NSLog(@"Trusted by USER"); + proceed = YES; + break; + + case kSecTrustResultUnspecified: + // NSLog(@"Trusted by OS"); + proceed = YES; + break; + + case kSecTrustResultRecoverableTrustFailure: + proceed = recoverFromTrustFailure(trust, trustResult); + break; + + case kSecTrustResultDeny: + // NSLog(@"Deny"); + break; + + case kSecTrustResultFatalTrustFailure: + // NSLog(@"FatalTrustFailure"); + break; + + case kSecTrustResultOtherError: + // NSLog(@"OtherError"); + break; + + case kSecTrustResultInvalid: + // NSLog(@"Invalid"); + break; + + default: + // NSLog(@"Default"); + break; + } + + } + while (NO); + + if (!proceed) { + NSLog(@"TSSLSocketTransport: Cannot trust certificate. Result: %u", trustResult); + [aStream close]; + } + + if (newPolicies) { + CFRelease(newPolicies); + } + + } + break; + + case NSStreamEventErrorOccurred: { + NSLog(@"TSSLSocketTransport: Error occurred opening stream: %@", [aStream streamError]); + break; + } + + case NSStreamEventEndEncountered: + break; + } +} + +BOOL recoverFromTrustFailure(SecTrustRef myTrust, SecTrustResultType lastTrustResult) +{ + CFAbsoluteTime trustTime = SecTrustGetVerifyTime(myTrust); + CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent(); + + CFAbsoluteTime timeIncrement = 31536000; + CFAbsoluteTime newTime = currentTime - timeIncrement; + + if (trustTime - newTime) { + + CFDateRef newDate = CFDateCreate(NULL, newTime); + SecTrustSetVerifyDate(myTrust, newDate); + CFRelease(newDate); + + if (SecTrustEvaluate(myTrust, &lastTrustResult) != errSecSuccess) { + return NO; + } + + } + + if (lastTrustResult == kSecTrustResultProceed || lastTrustResult == kSecTrustResultUnspecified) { + return YES; + } + + NSLog(@"TSSLSocketTransport: Unable to recover certificate trust failure"); + return YES; +} + +-(BOOL) isOpen +{ + if (_sd > 0) { + return TRUE; + } + else { + return FALSE; + } +} + +@end http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/lib/cocoa/src/transport/TSSLSocketTransportError.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/transport/TSSLSocketTransportError.h b/lib/cocoa/src/transport/TSSLSocketTransportError.h new file mode 100644 index 0000000..e17f39e --- /dev/null +++ b/lib/cocoa/src/transport/TSSLSocketTransportError.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "TTransportError.h" + + +extern NSString *TSSLSocketTransportErrorDomain; + + +typedef NS_ENUM (int, TSSLSocketTransportError) { + TSSLSocketTransportErrorHostanameResolution = -10000, + TSSLSocketTransportErrorSocketCreate = -10001, + TSSLSocketTransportErrorConnect = -10002, +};
