Skip to content

Commit

Permalink
Fixed missing response for some failing REST requests
Browse files Browse the repository at this point in the history
Nasty bug fixed: TDRouter would react to some errors (like a request to a nonexistent database, or an invalid document ID) without returning an HTTP response, so the caller's request would fall on the floor and eventually time out.

Also improved the TDRouter unit tests to catch this type of problem.
  • Loading branch information
snej committed Feb 18, 2012
1 parent 5a66eaf commit e0472ee
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 23 deletions.
40 changes: 18 additions & 22 deletions Source/TDRouter.m
Expand Up @@ -218,7 +218,7 @@ - (void) sendResponse {
}


- (void) start {
- (TDStatus) route {
// Refer to: http://wiki.apache.org/couchdb/Complete_HTTP_API_Reference

// We're going to map the request into a selector based on the method and path.
Expand All @@ -230,10 +230,8 @@ - (void) start {

// First interpret the components of the request:
_path = [splitPath(_request.URL) mutableCopy];
if (!_path) {
_response.status = 400;
return;
}
if (!_path)
return 400;

NSUInteger pathLen = _path.count;
if (pathLen > 0) {
Expand All @@ -242,10 +240,8 @@ - (void) start {
[message appendString: dbName]; // special root path, like /_all_dbs
} else {
_db = [[_server databaseNamed: dbName] retain];
if (!_db) {
_response.status = 400;
return;
}
if (!_db)
return 400;
[message appendString: @":"];
}
} else {
Expand All @@ -256,24 +252,18 @@ - (void) start {
if (_db && pathLen > 1) {
// Make sure database exists, then interpret doc name:
TDStatus status = [self openDB];
if (status >= 300) {
_response.status = status;
return;
}
if (status >= 300)
return status;
NSString* name = [_path objectAtIndex: 1];
if (![name hasPrefix: @"_"]) {
// Regular document
if (![TDDatabase isValidDocumentID: name]) {
_response.status = 400;
return;
}
if (![TDDatabase isValidDocumentID: name])
return 400;
docID = name;
} else if ([name isEqualToString: @"_design"] || [name isEqualToString: @"_local"]) {
// "_design/____" and "_local/____" are document names
if (pathLen <= 2) {
_response.status = 404;
return;
}
if (pathLen <= 2)
return 404;
docID = [name stringByAppendingPathComponent: [_path objectAtIndex: 2]];
[_path replaceObjectAtIndex: 1 withObject: docID];
[_path removeObjectAtIndex: 2];
Expand Down Expand Up @@ -322,7 +312,13 @@ - (void) start {
@"TDRouter(Handlers) is missing -- app may be linked without -ObjC linker flag.");
sel = @selector(do_UNKNOWN);
}
TDStatus status = (TDStatus) objc_msgSend(self, sel, _db, docID, attachmentName);
return (TDStatus) objc_msgSend(self, sel, _db, docID, attachmentName);
}


- (void) start {
// Call the appropriate handler method:
TDStatus status = [self route];

// Configure response headers:
if (status < 300 && !_response.body && ![_response.headers objectForKey: @"Content-Type"]) {
Expand Down
11 changes: 10 additions & 1 deletion Source/TDRouter_Tests.m
Expand Up @@ -46,8 +46,17 @@
}
TDRouter* router = [[[TDRouter alloc] initWithServer: server request: request] autorelease];
CAssert(router!=nil);
__block TDResponse* response = nil;
__block NSUInteger dataLength = 0;
__block BOOL calledOnFinished = NO;
router.onResponseReady = ^(TDResponse* theResponse) {CAssert(!response); response = theResponse;};
router.onDataAvailable = ^(NSData* data) {dataLength += data.length;};
router.onFinished = ^{CAssert(!calledOnFinished); calledOnFinished = YES;};
[router start];
return router.response;
CAssert(response);
CAssertEq(dataLength, response.body.asJSON.length);
CAssert(calledOnFinished);
return response;
}

static id ParseJSONResponse(TDResponse* response) {
Expand Down

0 comments on commit e0472ee

Please sign in to comment.