Permalink
Browse files

Added optional HTTP server

There's a new target that builds a "TouchDBListener" framework.
This framework implements a basic HTTP server on top of TouchDB.
The Mac demo app uses it and starts up the server on port 8888 at launch.

Easter egg: If you add a parameter "pretty=true" to the URLs given to this server, it'll pretty-print the JSON response. I also made it always do this in debug builds.
  • Loading branch information...
1 parent ce5ddd8 commit ec35a7ab83f712d349f03f4f069d74a4c2074719 @snej snej committed Dec 29, 2011
View
@@ -1,3 +1,6 @@
[submodule "vendor/fmdb"]
path = vendor/fmdb
url = git://github.com/couchbaselabs/fmdb.git
+[submodule "vendor/CocoaHTTPServer"]
+ path = vendor/CocoaHTTPServer
+ url = git://github.com/robbiehanson/CocoaHTTPServer.git
@@ -17,6 +17,7 @@
#import "DemoQuery.h"
#import "Test.h"
#import "TouchDB.h"
+#import <TouchDBListener/TDListener.h>
#import <CouchCocoa/CouchCocoa.h>
#import <CouchCocoa/CouchTouchDBServer.h>
@@ -37,8 +38,8 @@ @implementation DemoAppController
- (void) applicationDidFinishLaunching: (NSNotification*)n {
- gRESTLogLevel = kRESTLogRequestURLs;
- gCouchLogLevel = 1;
+ //gRESTLogLevel = kRESTLogRequestURLs;
+ //gCouchLogLevel = 1;
NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];
NSString* dbName = [bundleInfo objectForKey: @"DemoDatabase"];
@@ -89,6 +90,9 @@ - (void) applicationDidFinishLaunching: (NSNotification*)n {
selector: @selector(replicationProgressChanged:)
name: TDReplicatorProgressChangedNotification
object: nil];
+
+ // Start a listener socket:
+ [[[TDListener alloc] initWithTDServer: server.touchServer port: 8888] start];
}
@@ -0,0 +1,19 @@
+//
+// TDHTTPConnection.h
+// TouchDB
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "HTTPConnection.h"
+@class TDRouter;
+
+
+@interface TDHTTPConnection : HTTPConnection
+{
+ @private
+ TDRouter* _router;
+}
+
+@end
@@ -0,0 +1,69 @@
+//
+// TDHTTPConnection.m
+// TouchDB
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "TDHTTPConnection.h"
+#import "TDHTTPServer.h"
+#import "TDHTTPResponse.h"
+#import "TDListener.h"
+#import "TDServer.h"
+#import "TDRouter.h"
+#import "HTTPMessage.h"
+#import "HTTPDataResponse.h"
+
+
+@implementation TDHTTPConnection
+
+
+- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
+{
+ return YES;
+}
+
+
+- (NSObject<HTTPResponse>*)httpResponseForMethod:(NSString *)method URI:(NSString *)path {
+ NSLog(@"TDListener: %@ %@", method, path); //TEMP
+ NSURL* url = [NSURL URLWithString: [@"touchdb://" stringByAppendingString: path]];
+ NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL: url];
+ urlRequest.HTTPMethod = method;
+ urlRequest.HTTPBody = request.body;
+ NSDictionary* headers = request.allHeaderFields;
+ for (NSString* header in headers) {
+ [urlRequest setValue: [headers objectForKey: header] forHTTPHeaderField: header];
+ }
+
+ TDRouter* router = [[TDRouter alloc] initWithServer: ((TDHTTPServer*)config.server).tdServer
+ request: urlRequest];
+ __block bool finished = false;
+ __block TDResponse* routerResponse = nil;
+ NSMutableData* data = [NSMutableData data];
+ router.onResponseReady = ^(TDResponse* r) {
+ routerResponse = r;
+ };
+ router.onDataAvailable = ^(NSData* content) {
+ [data appendData: content];
+ };
+ router.onFinished = ^{
+ finished = true;
+ };
+
+ [((TDHTTPServer*)config.server).listener onServerThread: ^{[router start];}];
+ NSAssert(finished, @"Router didn't finish");
+
+ BOOL pretty = [router boolQuery: @"pretty"];
+#if DEBUG
+ pretty = YES;
+#endif
+ TDHTTPResponse* response = [[[TDHTTPResponse alloc] initWithTDResponse: routerResponse
+ pretty: pretty] autorelease];
+
+ [router release];
+ return response;
+}
+
+
+@end
View
@@ -0,0 +1,22 @@
+//
+// TDHTTPResponse.h
+// TouchDB
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "HTTPDataResponse.h"
+@class TDResponse;
+
+@interface TDHTTPResponse : HTTPDataResponse
+{
+ TDResponse* _response;
+}
+
+- (id) initWithTDResponse: (TDResponse*)response pretty: (BOOL)pretty;
+
+- (NSInteger)status;
+- (NSDictionary *)httpHeaders;
+
+@end
View
@@ -0,0 +1,39 @@
+//
+// TDHTTPResponse.m
+// TouchDB
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "TDHTTPResponse.h"
+#import "TDRouter.h"
+#import "TDBody.h"
+
+
+@implementation TDHTTPResponse
+
+
+- (id) initWithTDResponse: (TDResponse*)response pretty: (BOOL)pretty {
+ self = [super initWithData: (pretty ? response.body.asPrettyJSON : response.body.asJSON)];
+ if (self) {
+ _response = [response retain];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_response release];
+ [super dealloc];
+}
+
+- (NSInteger)status {
+ return _response.status;
+}
+
+- (NSDictionary *)httpHeaders {
+ return _response.headers;
+}
+
+
+@end
View
@@ -0,0 +1,24 @@
+//
+// TDHTTPServer.h
+// TouchDB
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "HTTPServer.h"
+@class TDListener, TDServer;
+
+
+@interface TDHTTPServer : HTTPServer {
+@private
+ TDListener* _listener;
+ TDServer* _tdServer;
+}
+
+@property (retain) TDListener* listener;
+@property (retain) TDServer* tdServer;
+
+@end
+
+
View
@@ -0,0 +1,29 @@
+//
+// TDListener.h
+// TouchDBListener
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+@class TDHTTPServer, TDServer;
+
+
+/** A simple HTTP server that provides remote access to the TouchDB REST API. */
+@interface TDListener : NSObject
+{
+ TDHTTPServer* _httpServer;
+ TDServer* _tdServer;
+ dispatch_queue_t _queue;
+}
+
+- (id) initWithTDServer: (TDServer*)server port: (UInt16)port;
+
+- (BOOL) start;
+- (void) stop;
+
+/** Runs the block *synchronously* on the single server thread. */
+- (void) onServerThread: (void(^)())block;
+
+@end
View
@@ -0,0 +1,68 @@
+//
+// TDListener.m
+// TouchDBListener
+//
+// Created by Jens Alfke on 12/29/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "TDListener.h"
+#import "TDHTTPServer.h"
+#import "TDHTTPConnection.h"
+#import "TDServer.h"
+
+#import "HTTPServer.h"
+
+
+@implementation TDListener
+
+
+- (id) initWithTDServer: (TDServer*)server port: (UInt16)port {
+ self = [super init];
+ if (self) {
+ _tdServer = [server retain];
+ _httpServer = [[TDHTTPServer alloc] init];
+ _httpServer.listener = self;
+ _httpServer.tdServer = _tdServer;
+ _httpServer.port = port;
+ _httpServer.connectionClass = [TDHTTPConnection class];
+ _queue = dispatch_queue_create("TDListener", DISPATCH_QUEUE_SERIAL);
+ }
+ return self;
+}
+
+
+- (void)dealloc
+{
+ [self stop];
+ [_tdServer release];
+ [_httpServer release];
+ dispatch_release(_queue);
+ [super dealloc];
+}
+
+
+- (void) onServerThread: (void(^)())block {
+ dispatch_sync(_queue, block);
+}
+
+
+- (BOOL) start {
+ NSError* error;
+ return [_httpServer start: &error];
+}
+
+- (void) stop {
+ [_httpServer stop];
+}
+
+
+@end
+
+
+
+@implementation TDHTTPServer
+
+@synthesize listener=_listener, tdServer=_tdServer;
+
+@end
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.couchbase.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2011 Couchbase, Inc. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
@@ -0,0 +1,7 @@
+# TouchDBListener.exp
+# TouchDB
+#
+# Created by Jens Alfke on 12/29/11.
+# Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+
+.objc_class_name_TDListener
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
View
@@ -27,6 +27,7 @@
@property (readonly) BOOL isValidJSON;
@property (readonly) NSData* asJSON;
+@property (readonly) NSData* asPrettyJSON;
@property (readonly) NSString* asJSONString;
@property (readonly) id asObject;
@property (readonly) BOOL error;
View
@@ -76,6 +76,21 @@ - (NSData*) asJSON {
return _json;
}
+- (NSData*) asPrettyJSON {
+ id props = self.properties;
+ if (props) {
+ NSData* json = [NSJSONSerialization dataWithJSONObject: props
+ options: NSJSONWritingPrettyPrinted
+ error: nil];
+ if (json) {
+ NSMutableData* mjson = [[json mutableCopy] autorelease];
+ [mjson appendBytes: "\n" length: 1];
+ return mjson;
+ }
+ }
+ return self.asJSON;
+}
+
- (NSString*) asJSONString {
return self.asJSON.my_UTF8ToString;
}
View
@@ -37,6 +37,9 @@ typedef void (^OnFinishedBlock)();
- (id) initWithServer: (TDServer*)server request: (NSURLRequest*)request;
+- (NSString*) query: (NSString*)param;
+- (BOOL) boolQuery: (NSString*)param;
+
@property (copy) OnResponseReadyBlock onResponseReady;
@property (copy) OnDataAvailableBlock onDataAvailable;
@property (copy) OnFinishedBlock onFinished;
Oops, something went wrong.

0 comments on commit ec35a7a

Please sign in to comment.