Merging more server support and exception fixes for Cocoa
Summary: Submitted by Andrew McGeachie.
Reviewed By: mcslee
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665281 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cocoa/TBinaryProtocol.h b/lib/cocoa/TBinaryProtocol.h
index ac8bc57..2c56740 100644
--- a/lib/cocoa/TBinaryProtocol.h
+++ b/lib/cocoa/TBinaryProtocol.h
@@ -1,5 +1,7 @@
#import "TProtocol.h"
#import "TTransport.h"
+#import "TProtocolFactory.h"
+
@interface TBinaryProtocol : NSObject <TProtocol> {
id <TTransport> mTransport;
@@ -14,3 +16,13 @@
strictWrite: (BOOL) strictWrite;
@end;
+
+
+@interface TBinaryProtocolFactory : NSObject <TProtocolFactory> {
+}
+
++ (TBinaryProtocolFactory *) sharedFactory;
+
+- (TBinaryProtocol *) newProtocolOnTransport: (id <TTransport>) transport;
+
+@end
\ No newline at end of file
diff --git a/lib/cocoa/TBinaryProtocol.m b/lib/cocoa/TBinaryProtocol.m
index ba7808c..19db55f 100644
--- a/lib/cocoa/TBinaryProtocol.m
+++ b/lib/cocoa/TBinaryProtocol.m
@@ -5,6 +5,26 @@
int32_t VERSION_MASK = 0xffff0000;
+static TBinaryProtocolFactory * gSharedFactory = nil;
+
+@implementation TBinaryProtocolFactory
+
++ (TBinaryProtocolFactory *) sharedFactory {
+ if (gSharedFactory == nil) {
+ gSharedFactory = [[TBinaryProtocolFactory alloc] init];
+ }
+
+ return gSharedFactory;
+}
+
+- (TBinaryProtocol *) newProtocolOnTransport: (id <TTransport>) transport {
+ return [[[TBinaryProtocol alloc] initWithTransport: transport] autorelease];
+}
+
+@end
+
+
+
@implementation TBinaryProtocol
- (id) initWithTransport: (id <TTransport>) transport
@@ -54,7 +74,8 @@
if (size < 0) {
int version = size & VERSION_MASK;
if (version != VERSION_1) {
- @throw [TProtocolException exceptionWithName: @"Bad version in readMessageBegin"];
+ @throw [TProtocolException exceptionWithName: @"TProtocolException"
+ reason: @"Bad version in readMessageBegin"];
}
if (type != NULL) {
*type = version & 0x00FF;
@@ -69,7 +90,8 @@
}
} else {
if (mStrictRead) {
- @throw [TProtocolException exceptionWithName: @"Missing version in readMessageBegin, old client?"];
+ @throw [TProtocolException exceptionWithName: @"TProtocolException"
+ reason: @"Missing version in readMessageBegin, old client?"];
}
NSString * messageName = [self readStringBody: size];
if (name != NULL) {
@@ -194,8 +216,8 @@
uint8_t * buff = malloc(size);
if (buff == NULL) {
@throw [TProtocolException
- exceptionWithName: @"Out of memory"
- reason: [NSString stringWithFormat: @"Unable to allocate %d bytes trying to read binary data.",
+ exceptionWithName: @"TProtocolException"
+ reason: [NSString stringWithFormat: @"Out of memory. Unable to allocate %d bytes trying to read binary data.",
size]];
}
[mTransport readAll: buff offset: 0 length: size];
@@ -348,7 +370,8 @@
[self writeI32: length];
[mTransport write: (uint8_t *) utf8Bytes offset: 0 length: length];
} else {
- // instead of crashing when we get null, let's write out a zero length string
+ // instead of crashing when we get null, let's write out a zero
+ // length string
[self writeI32: 0];
}
}
diff --git a/lib/cocoa/TException.m b/lib/cocoa/TException.m
index 38d00ca..33ed7b5 100644
--- a/lib/cocoa/TException.m
+++ b/lib/cocoa/TException.m
@@ -30,4 +30,16 @@
}
+- (NSString *) description
+{
+ NSMutableString * result = [NSMutableString stringWithString: [self name]];
+ [result appendFormat: @": %@", [self reason]];
+ if ([self userInfo] != nil) {
+ [result appendFormat: @"\n userInfo = %@", [self userInfo]];
+ }
+
+ return result;
+}
+
+
@end
diff --git a/lib/cocoa/THTTPClient.m b/lib/cocoa/THTTPClient.m
index 096770d..45c0b80 100644
--- a/lib/cocoa/THTTPClient.m
+++ b/lib/cocoa/THTTPClient.m
@@ -39,6 +39,7 @@
[mURL release];
[mRequest release];
[mRequestData release];
+ [mResponseData release];
[super dealloc];
}
@@ -75,19 +76,20 @@
[mRequestData setLength: 0];
if (responseData == nil) {
- @throw [TTransportException exceptionWithName: @"Could not make HTTP request"
- reason: @"unknown"
+ @throw [TTransportException exceptionWithName: @"TTransportException"
+ reason: @"Could not make HTTP request"
error: error];
}
if (![response isKindOfClass: [NSHTTPURLResponse class]]) {
- @throw [TTransportException exceptionWithName: @"Unexpected NSURLResponse type"];
+ @throw [TTransportException exceptionWithName: @"TTransportException"
+ reason: @"Unexpected NSURLResponse type"];
}
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response;
if ([httpResponse statusCode] != 200) {
- @throw [TTransportException exceptionWithName:
- [NSString stringWithFormat: @"Bad response from HTTP server: %d",
- [httpResponse statusCode]]];
+ @throw [TTransportException exceptionWithName: @"TTransportException"
+ reason: [NSString stringWithFormat: @"Bad response from HTTP server: %d",
+ [httpResponse statusCode]]];
}
// phew!
diff --git a/lib/cocoa/TNSFileHandleTransport.h b/lib/cocoa/TNSFileHandleTransport.h
new file mode 100644
index 0000000..fc03ce3
--- /dev/null
+++ b/lib/cocoa/TNSFileHandleTransport.h
@@ -0,0 +1,16 @@
+
+#import <Cocoa/Cocoa.h>
+#import "TTransport.h"
+
+@interface TNSFileHandleTransport : NSObject <TTransport> {
+ NSFileHandle * mInputFileHandle;
+ NSFileHandle * mOutputFileHandle;
+}
+
+- (id) initWithFileHandle: (NSFileHandle *) fileHandle;
+
+- (id) initWithInputFileHandle: (NSFileHandle *) inputFileHandle
+ outputFileHandle: (NSFileHandle *) outputFileHandle;
+
+
+@end
diff --git a/lib/cocoa/TNSFileHandleTransport.m b/lib/cocoa/TNSFileHandleTransport.m
new file mode 100644
index 0000000..7ad1ba7
--- /dev/null
+++ b/lib/cocoa/TNSFileHandleTransport.m
@@ -0,0 +1,68 @@
+
+#import "TNSFileHandleTransport.h"
+
+
+@implementation TNSFileHandleTransport
+
+- (id) initWithFileHandle: (NSFileHandle *) fileHandle
+{
+ return [self initWithInputFileHandle: fileHandle
+ outputFileHandle: fileHandle];
+}
+
+
+- (id) initWithInputFileHandle: (NSFileHandle *) inputFileHandle
+ outputFileHandle: (NSFileHandle *) outputFileHandle
+{
+ self = [super init];
+
+ mInputFileHandle = [inputFileHandle retain];
+ mOutputFileHandle = [outputFileHandle retain];
+
+ return self;
+}
+
+
+- (void) dealloc {
+ [mInputFileHandle release];
+ [mOutputFileHandle release];
+ [super dealloc];
+}
+
+
+- (int) readAll: (uint8_t *) buf offset: (int) off length: (int) len
+{
+ int got = 0;
+ while (got < len) {
+ NSData * d = [mInputFileHandle readDataOfLength: len-got];
+ if ([d length] == 0) {
+ @throw [NSException exceptionWithName: @"TTransportException"
+ reason: @"Cannot read. No more data."
+ userInfo: nil];
+ }
+ [d getBytes: buf+got];
+ got += [d length];
+ }
+ return got;
+}
+
+
+- (void) write: (uint8_t *) data offset: (unsigned int) offset length: (unsigned int) length
+{
+ NSData * dataObject = [[NSData alloc] initWithBytesNoCopy: data+offset
+ length: length
+ freeWhenDone: NO];
+
+ [mOutputFileHandle writeData: dataObject];
+
+
+ [dataObject release];
+}
+
+
+- (void) flush
+{
+ [mOutputFileHandle synchronizeFile]; // ?
+}
+
+@end
diff --git a/lib/cocoa/TProcessor.h b/lib/cocoa/TProcessor.h
new file mode 100644
index 0000000..01b2a61
--- /dev/null
+++ b/lib/cocoa/TProcessor.h
@@ -0,0 +1,9 @@
+#import <Cocoa/Cocoa.h>
+
+
+@protocol TProcessor <NSObject>
+
+- (BOOL) processOnInputProtocol: (id <TProtocol>) inProtocol
+ outputProtocol: (id <TProtocol>) outProtocol;
+
+@end
diff --git a/lib/cocoa/TProtocolFactory.h b/lib/cocoa/TProtocolFactory.h
new file mode 100644
index 0000000..06d62c8
--- /dev/null
+++ b/lib/cocoa/TProtocolFactory.h
@@ -0,0 +1,10 @@
+#import <Cocoa/Cocoa.h>
+#import "TProtocol.h"
+#import "TTransport.h"
+
+
+@protocol TProtocolFactory <NSObject>
+
+- (id <TProtocol>) newProtocolOnTransport: (id <TTransport>) transport;
+
+@end
diff --git a/lib/cocoa/TSocketClient.h b/lib/cocoa/TSocketClient.h
new file mode 100644
index 0000000..8905ed4
--- /dev/null
+++ b/lib/cocoa/TSocketClient.h
@@ -0,0 +1,13 @@
+#import <Cocoa/Cocoa.h>
+#import "TNSStreamTransport.h"
+
+@interface TSocketClient : TNSStreamTransport {
+}
+
+- (id) initWithHostname: (NSString *) hostname
+ port: (int) port;
+
+@end
+
+
+
diff --git a/lib/cocoa/TSocketClient.m b/lib/cocoa/TSocketClient.m
new file mode 100644
index 0000000..f3acc3b
--- /dev/null
+++ b/lib/cocoa/TSocketClient.m
@@ -0,0 +1,24 @@
+#import <Cocoa/Cocoa.h>
+#import "TSocketClient.h"
+
+@implementation TSocketClient
+
+- (id) initWithHostname: (NSString *) hostname
+ port: (int) port
+{
+ NSInputStream * input = nil;
+ NSOutputStream * output = nil;
+
+ [NSStream getStreamsToHost: [NSHost hostWithName: hostname]
+ port: port
+ inputStream: &input
+ outputStream: &output];
+
+ return [super initWithInputStream: input outputStream: output];
+}
+
+
+@end
+
+
+
diff --git a/lib/cocoa/TSocketServer.h b/lib/cocoa/TSocketServer.h
new file mode 100644
index 0000000..4b8e8d0
--- /dev/null
+++ b/lib/cocoa/TSocketServer.h
@@ -0,0 +1,21 @@
+#import <Cocoa/Cocoa.h>
+#import "TProtocolFactory.h"
+#import "TProcessor.h"
+
+
+@interface TSocketServer : NSObject {
+ NSSocketPort * mServerSocket;
+ NSFileHandle * mSocketFileHandle;
+ id <TProtocolFactory> mInputProtocolFactory;
+ id <TProtocolFactory> mOutputProtocolFactory;
+ id <TProcessor> mProcessor;
+}
+
+- (id) initWithPort: (int) port
+ protocolFactory: (id <TProtocolFactory>) protocolFactory
+ processor: (id <TProcessor>) processor;
+
+@end
+
+
+
diff --git a/lib/cocoa/TSocketServer.m b/lib/cocoa/TSocketServer.m
new file mode 100644
index 0000000..a65d017
--- /dev/null
+++ b/lib/cocoa/TSocketServer.m
@@ -0,0 +1,80 @@
+#import <Cocoa/Cocoa.h>
+#import "TSocketServer.h"
+#import "TNSFileHandleTransport.h"
+#import "TProtocol.h"
+
+
+@implementation TSocketServer
+
+- (id) initWithPort: (int) port
+ protocolFactory: (id <TProtocolFactory>) protocolFactory
+ processor: (id <TProcessor>) processor;
+{
+ self = [super init];
+
+ mInputProtocolFactory = [protocolFactory retain];
+ mOutputProtocolFactory = [protocolFactory retain];
+ mProcessor = [processor retain];
+
+ // create a socket
+ mServerSocket = [[NSSocketPort alloc] initWithTCPPort: 8081];
+ // wrap it in a file handle so we can get messages from it
+ mSocketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor: [mServerSocket socket]
+ closeOnDealloc: YES];
+
+ // register for notifications of accepted incoming connections
+ [[NSNotificationCenter defaultCenter] addObserver: self
+ selector: @selector(connectionAccepted:)
+ name: NSFileHandleConnectionAcceptedNotification
+ object: mSocketFileHandle];
+
+ // tell socket to listen
+ [mSocketFileHandle acceptConnectionInBackgroundAndNotify];
+
+ return self;
+}
+
+
+- (void) dealloc {
+ [mInputProtocolFactory release];
+ [mOutputProtocolFactory release];
+ [mProcessor release];
+ [mSocketFileHandle release];
+ [mServerSocket release];
+ [super dealloc];
+}
+
+
+- (void) connentionAccepted: (NSNotification *) aNotification
+{
+ NSFileHandle * socket = [[aNotification userInfo] objectForKey: NSFileHandleNotificationFileHandleItem];
+
+ // now that we have a client connected, spin off a thread to handle activity
+ [NSThread detachNewThreadSelector: @selector(handleClientConnection:)
+ toTarget: self
+ withObject: socket];
+
+ [[aNotification object] acceptConnectionInBackgroundAndNotify];
+}
+
+
+- (void) handleClientConnection: (NSFileHandle *) clientSocket
+{
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+
+ TNSFileHandleTransport * transport = [[TNSFileHandleTransport alloc] initWithFileHandle: clientSocket];
+
+ id <TProtocol> inProtocol = [mInputProtocolFactory newProtocolOnTransport: transport];
+ id <TProtocol> outProtocol = [mOutputProtocolFactory newProtocolOnTransport: transport];
+
+ while ([mProcessor processOnInputProtocol: inProtocol outputProtocol: outProtocol]);
+
+ [pool release];
+}
+
+
+
+@end
+
+
+