Permalink
Browse files

Merge remote-tracking branch 'origin/master'

Conflicts:
	Source/ChangeTracker/TDChangeTracker.m
	Source/ChangeTracker/TDSocketChangeTracker.m
	Source/TDBody.m
	Source/TDCollateJSON.m
	Source/TDDatabase+Attachments.h
	Source/TDDatabase+Insertion.m
	Source/TDDatabase+LocalDocs.m
	Source/TDDatabase.m
	Source/TDJSON.h
	Source/TDJSON.m
	Source/TDMisc.m
	Source/TDMultipartDownloader.m
	Source/TDPuller.m
	Source/TDPusher.m
	Source/TDRemoteRequest.m
	Source/TDReplicatorManager.h
	Source/TDReplicator_Tests.m
	Source/TDRouter+Handlers.m
	Source/TDRouter.h
	Source/TDRouter.m
	Source/TDRouter_Tests.m
	Source/TDServer.m
	Source/TDURLProtocol.m
	Source/TDView.m
	TouchDB.xcodeproj/project.pbxproj
  • Loading branch information...
1 parent 2a463d3 commit 487f6f34108e0987e932982f72bcb465efd8b93a @snej snej committed May 7, 2012
Showing with 4,107 additions and 1,624 deletions.
  1. +7 −4 Demo-Mac/DemoAppController.m
  2. +8 −1 Demo-Mac/TouchServ.m
  3. +6 −0 GNUmakefile
  4. +4 −0 Listener/TDHTTPResponse.h
  5. +94 −70 Listener/TDHTTPResponse.m
  6. +3 −4 Listener/TDListener.h
  7. +3 −7 Listener/TDListener.m
  8. +4 −4 README.md
  9. +3 −0 Source/ChangeTracker/TDChangeTracker.h
  10. +22 −15 Source/ChangeTracker/TDChangeTracker.m
  11. +5 −4 Source/ChangeTracker/TDConnectionChangeTracker.m
  12. +4 −0 Source/ChangeTracker/TDSocketChangeTracker.h
  13. +186 −65 Source/ChangeTracker/TDSocketChangeTracker.m
  14. +36 −0 Source/TDAttachment.h
  15. +58 −0 Source/TDAttachment.m
  16. +3 −2 Source/TDBlobStore.h
  17. +12 −1 Source/TDBlobStore.m
  18. +2 −2 Source/TDBody.m
  19. +44 −0 Source/TDCanonicalJSON.h
  20. +288 −0 Source/TDCanonicalJSON.m
  21. +4 −5 Source/TDCollateJSON.m
  22. +16 −5 Source/TDDatabase+Attachments.h
  23. +231 −133 Source/TDDatabase+Attachments.m
  24. +1 −1 Source/TDDatabase+Insertion.h
  25. +144 −67 Source/TDDatabase+Insertion.m
  26. +14 −14 Source/TDDatabase+LocalDocs.m
  27. +4 −5 Source/TDDatabase+Replication.m
  28. +7 −6 Source/TDDatabase.h
  29. +45 −161 Source/TDDatabase.m
  30. +38 −0 Source/TDDatabaseManager.h
  31. +215 −0 Source/TDDatabaseManager.m
  32. +158 −66 Source/TDDatabase_Tests.m
  33. +14 −0 Source/TDGNUstep.h
  34. +7 −1 Source/TDGNUstep.m
  35. +28 −14 Source/TDInternal.h
  36. +19 −5 Source/TDJSON.h
  37. +70 −5 Source/TDJSON.m
  38. +10 −1 Source/TDMisc.h
  39. +67 −16 Source/TDMisc.m
  40. +44 −0 Source/TDMultipartDocumentReader.h
  41. +282 −0 Source/TDMultipartDocumentReader.m
  42. +4 −12 Source/TDMultipartDownloader.h
  43. +28 −157 Source/TDMultipartDownloader.m
  44. +1 −0 Source/TDMultipartUploader.h
  45. +4 −1 Source/TDMultipartUploader.m
  46. +4 −0 Source/TDPuller.h
  47. +195 −65 Source/TDPuller.m
  48. +33 −28 Source/TDPusher.m
  49. +7 −0 Source/TDReachability.m
  50. +6 −0 Source/TDRemoteRequest.h
  51. +28 −5 Source/TDRemoteRequest.m
  52. +28 −0 Source/TDReplicator.h
  53. +91 −31 Source/TDReplicator.m
  54. +4 −3 Source/TDReplicatorManager.h
  55. +45 −28 Source/TDReplicatorManager.m
  56. +36 −19 Source/TDReplicator_Tests.m
  57. +9 −0 Source/TDRevision.h
  58. +125 −2 Source/TDRevision.m
  59. +167 −153 Source/TDRouter+Handlers.m
  60. +20 −7 Source/TDRouter.h
  61. +156 −46 Source/TDRouter.m
  62. +203 −102 Source/TDRouter_Tests.m
  63. +10 −14 Source/TDServer.h
  64. +69 −122 Source/TDServer.m
  65. +49 −0 Source/TDStatus.h
  66. +69 −0 Source/TDStatus.m
  67. +18 −0 Source/TDURLProtocol.h
  68. +168 −41 Source/TDURLProtocol.m
  69. +102 −48 Source/TDView.m
  70. +135 −20 Source/TDView_Tests.m
  71. +76 −34 TouchDB.xcodeproj/project.pbxproj
  72. +1 −0 TouchDB.xcodeproj/xcshareddata/xcschemes/Mac Demo.xcscheme
  73. +4 −0 TouchDB.xcodeproj/xcshareddata/xcschemes/iOS Demo.xcscheme
  74. +1 −1 vendor/MYUtilities
  75. +1 −1 vendor/fmdb
@@ -102,11 +102,14 @@ - (void) applicationDidFinishLaunching: (NSNotification*)n {
#ifdef FOR_TESTING_PURPOSES
// Start a listener socket:
- sListener = [[TDListener alloc] initWithTDServer: server.touchServer port: 8888];
- [sListener start];
+ [server tellTDServer: ^(TDServer* tdServer) {
+ // Register support for handling certain JS functions used in the CouchDB unit tests:
+ [TDView setCompiler: self];
+
+ sListener = [[TDListener alloc] initWithTDServer: tdServer port: 8888];
+ [sListener start];
+ }];
- // Register support for handling certain JS functions used in the CouchDB unit tests:
- [TDView setCompiler: self];
#endif
}
View
@@ -60,9 +60,16 @@ int main (int argc, const char * argv[])
// Start a listener socket:
TDListener* listener = [[TDListener alloc] initWithTDServer: server port: kPortNumber];
+
+ if (argc >= 2 && strcmp(argv[1], "--readonly") == 0)
+ listener.readOnly = YES;
+
[listener start];
- Log(@"TouchServ %@ is listening on port %d ... relax!", [TDRouter versionString], kPortNumber);
+ Log(@"TouchServ %@ is listening%@ on port %d ... relax!",
+ [TDRouter versionString],
+ (listener.readOnly ? @" in read-only mode" : @""),
+ kPortNumber);
[[NSRunLoop currentRunLoop] run];
View
@@ -13,9 +13,11 @@ TouchDB_OBJC_FILES = \
Source/TDDatabase+Attachments.m \
Source/TDDatabase+Insertion.m \
Source/TDDatabase+LocalDocs.m \
+ Source/TDAttachment.m \
Source/TDBody.m \
Source/TDRevision.m \
Source/TDView.m \
+ Source/TDDatabaseManager.m \
Source/TDServer.m \
Source/TDBlobStore.m \
\
@@ -29,6 +31,7 @@ TouchDB_OBJC_FILES = \
Source/TDPusher.m \
Source/TDReplicatorManager.m \
Source/TDRemoteRequest.m \
+ Source/TDMultipartDocumentReader.m \
Source/TDMultipartDownloader.m \
Source/TDMultipartReader.m \
Source/TDMultipartUploader.m \
@@ -37,12 +40,14 @@ TouchDB_OBJC_FILES = \
Source/TDReachability_Stubs.m \
\
Source/TDBatcher.m \
+ Source/TDCanonicalJSON.m \
Source/TDCollateJSON.m \
Source/TDGNUstep.m \
Source/TDBase64.m \
Source/TDJSON.m \
Source/TDMisc.m \
Source/TDSequenceMap.m \
+ Source/TDStatus.m \
\
Source/TDBlobStore_Tests.m \
Source/TDDatabase_Tests.m \
@@ -86,6 +91,7 @@ TouchDB_HEADER_FILES = \
TDRevision.h \
TDRouter.h \
TDServer.h \
+ TDStatus.h \
TDURLProtocol.h \
TDView.h \
TDC.h
@@ -16,12 +16,16 @@
TDHTTPConnection* _connection;
TDResponse* _response;
BOOL _finished;
+ BOOL _askedIfChunked;
BOOL _chunked;
+ BOOL _delayedHeaders;
NSMutableData* _data; // Data received, waiting to be read by the connection
UInt64 _dataOffset; // Offset in response of 1st byte of _data
UInt64 _offset; // Offset in response for next readData
}
- (id) initWithRouter: (TDRouter*)router forConnection:(TDHTTPConnection*)connection;
+@property UInt64 offset;
+
@end
View
@@ -24,7 +24,7 @@
@interface TDHTTPResponse ()
- (void) onResponseReady: (TDResponse*)response;
-- (void) onDataAvailable: (NSData*)data;
+- (void) onDataAvailable: (NSData*)data finished: (BOOL)finished;
- (void) onFinished;
@end
@@ -41,17 +41,26 @@ - (id) initWithRouter: (TDRouter*)router forConnection:(TDHTTPConnection*)connec
router.onResponseReady = ^(TDResponse* r) {
[self onResponseReady: r];
};
- router.onDataAvailable = ^(NSData* data) {
- [self onDataAvailable: data];
+ router.onDataAvailable = ^(NSData* data, BOOL finished) {
+ [self onDataAvailable: data finished: finished];
};
router.onFinished = ^{
[self onFinished];
};
+
+ if (connection.listener.readOnly) {
+ router.onAccessCheck = ^TDStatus(TDDatabase* db, NSString* docID, SEL action) {
+ NSString* method = router.request.HTTPMethod;
+ if (![method isEqualToString: @"GET"] && ![method isEqualToString: @"HEAD"])
+ return kTDStatusForbidden;
+ return kTDStatusOK;
+ };
+ }
// Run the router, synchronously:
LogTo(TDListenerVerbose, @"%@: Starting...", self);
- [_connection.listener onServerThread: ^{[router start];}];
- _chunked = !_finished;
+ [router start];
+ LogTo(TDListenerVerbose, @"%@: Returning from -init", self);
}
return self;
}
@@ -75,109 +84,123 @@ - (NSString*) description {
* implement this method in your custom response class and return YES.
**/
- (BOOL) isChunked {
- return _chunked;
+ @synchronized(self) {
+ if (!_askedIfChunked) {
+ _chunked = !_finished;
+ }
+ LogTo(TDListenerVerbose, @"%@ answers isChunked=%d", self, _chunked);
+ return _chunked;
+ }
}
-- (BOOL) delayResponeHeaders {
- return _chunked && !_response;
+- (BOOL) delayResponeHeaders { // [sic]
+ @synchronized(self) {
+ LogTo(TDListenerVerbose, @"%@ answers delayResponeHeaders=%d", self, !_response);
+ if (!_response)
+ _delayedHeaders = YES;
+ return !_response;
+ }
}
- (void) onResponseReady: (TDResponse*)response {
- _response = [response retain];
- LogTo(TDListener, @" %@ --> %i", self, _response.status);
- if (_chunked)
- [_connection responseHasAvailableData: self];
+ @synchronized(self) {
+ _response = [response retain];
+ LogTo(TDListener, @" %@ --> %i", self, _response.status);
+ if (_delayedHeaders)
+ [_connection responseHasAvailableData: self];
+ }
}
- (NSInteger) status {
+ LogTo(TDListenerVerbose, @"%@ answers status=%d", self, _response.status);
return _response.status;
}
- (NSDictionary *) httpHeaders {
+ LogTo(TDListenerVerbose, @"%@ answers httpHeaders={%d headers}", self, _response.headers.count);
return _response.headers;
}
-- (void) onDataAvailable: (NSData*)data {
- LogTo(TDListenerVerbose, @"%@ adding %u bytes", self, (unsigned)data.length);
- if (_data)
- [_data appendData: data];
- else
- _data = [data mutableCopy];
- if (_chunked)
- [_connection responseHasAvailableData: self];
+- (void) onDataAvailable: (NSData*)data finished: (BOOL)finished {
+ @synchronized(self) {
+ LogTo(TDListenerVerbose, @"%@ adding %u bytes", self, (unsigned)data.length);
+ if (_data)
+ [_data appendData: data];
+ else
+ _data = [data mutableCopy];
+ if (finished)
+ [self onFinished];
+ else if (_chunked)
+ [_connection responseHasAvailableData: self];
+ }
}
-- (UInt64) offset {return _offset;}
-- (void) setOffset: (UInt64)offset {_offset = offset;}
+@synthesize offset=_offset;
+
- (UInt64) contentLength {
- if (!_finished)
- return 0;
- return _dataOffset + _data.length;
+ @synchronized(self) {
+ if (!_finished)
+ return 0;
+ return _dataOffset + _data.length;
+ }
}
- (NSData*) readDataOfLength: (NSUInteger)length {
- NSAssert(_offset >= _dataOffset, @"Invalid offset %llu, min is %llu", _offset, _dataOffset);
- NSRange range;
- range.location = (NSUInteger)(_offset - _dataOffset);
- if (range.location >= _data.length)
- return nil;
- NSUInteger bytesAvailable = _data.length - range.location;
- range.length = MIN(length, bytesAvailable);
- NSData* result = [_data subdataWithRange: range];
- _offset += range.length;
- if (range.length == bytesAvailable) {
- // Client has read all of the available data, so we can discard it
- _dataOffset += _data.length;
- [_data autorelease];
- _data = nil;
+ @synchronized(self) {
+ NSAssert(_offset >= _dataOffset, @"Invalid offset %llu, min is %llu", _offset, _dataOffset);
+ NSRange range;
+ range.location = (NSUInteger)(_offset - _dataOffset);
+ if (range.location >= _data.length) {
+ LogTo(TDListenerVerbose, @"%@ sending nil bytes", self);
+ return nil;
+ }
+ NSUInteger bytesAvailable = _data.length - range.location;
+ range.length = MIN(length, bytesAvailable);
+ NSData* result = [_data subdataWithRange: range];
+ _offset += range.length;
+ if (range.length == bytesAvailable) {
+ // Client has read all of the available data, so we can discard it
+ _dataOffset += _data.length;
+ [_data autorelease];
+ _data = nil;
+ }
+ LogTo(TDListenerVerbose, @"%@ sending %u bytes", self, result.length);
+ return result;
}
- LogTo(TDListenerVerbose, @"%@ sending %u bytes", self, result.length);
- return result;
}
- (BOOL) isDone {
+ LogTo(TDListenerVerbose, @"%@ answers isDone=%d", self, _finished);
return _finished;
}
- (void) onFinished {
- if (_finished)
- return;
- _finished = true;
-
- LogTo(TDListenerVerbose, @"%@ Finished!", self);
-
- // Break cycles:
- _router.onResponseReady = nil;
- _router.onDataAvailable = nil;
- _router.onFinished = nil;
-
- if (!_chunked) {
- // Response finished immediately, before the connection asked for any data, so we're free
- // to massage the response:
- int status = _response.status;
- if (status >= 300 && _data.length == 0) {
- // Put a generic error message in the body:
- NSString* errorMsg;
- switch (status) {
- case 404: errorMsg = @"not_found"; break;
- // TODO: There are more of these to add; see error_info() in couch_httpd.erl
- default:
- errorMsg = [NSHTTPURLResponse localizedStringForStatusCode: status];
- }
- NSString* responseStr = [NSString stringWithFormat: @"{\"status\": %i, \"error\":\"%@\"}\n",
- status, errorMsg];
- [self onDataAvailable: [responseStr dataUsingEncoding: NSUTF8StringEncoding]];
- [_response.headers setObject: @"text/plain; encoding=UTF-8" forKey: @"Content-Type"];
- } else {
+ @synchronized(self) {
+ if (_finished)
+ return;
+ _finished = true;
+ _askedIfChunked = true;
+
+ LogTo(TDListenerVerbose, @"%@ Finished!", self);
+
+ // Break cycles:
+ _router.onResponseReady = nil;
+ _router.onDataAvailable = nil;
+ _router.onFinished = nil;
+
+ if (!_chunked || _offset == 0) {
+ // Response finished immediately, before the connection asked for any data, so we're free
+ // to massage the response:
+ LogTo(TDListenerVerbose, @"%@ prettifying response body", self);
#if DEBUG
BOOL pretty = YES;
#else
@@ -188,6 +211,7 @@ - (void) onFinished {
_data = [_response.body.asPrettyJSON mutableCopy];
}
}
+ [_connection responseHasAvailableData: self];
}
}
View
@@ -15,15 +15,14 @@
{
TDHTTPServer* _httpServer;
TDServer* _tdServer;
- dispatch_queue_t _queue;
+ BOOL _readOnly;
}
- (id) initWithTDServer: (TDServer*)server port: (UInt16)port;
+@property BOOL readOnly;
+
- (BOOL) start;
- (void) stop;
-/** Runs the block *synchronously* on the single server thread. */
-- (void) onServerThread: (void(^)())block;
-
@end
View
@@ -24,6 +24,9 @@
@implementation TDListener
+@synthesize readOnly=_readOnly;
+
+
- (id) initWithTDServer: (TDServer*)server port: (UInt16)port {
self = [super init];
if (self) {
@@ -33,7 +36,6 @@ - (id) initWithTDServer: (TDServer*)server port: (UInt16)port {
_httpServer.tdServer = _tdServer;
_httpServer.port = port;
_httpServer.connectionClass = [TDHTTPConnection class];
- _queue = dispatch_queue_create("TDListener", DISPATCH_QUEUE_SERIAL);
}
return self;
}
@@ -44,16 +46,10 @@ - (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];
Oops, something went wrong.

0 comments on commit 487f6f3

Please sign in to comment.