Skip to content

Commit

Permalink
Router: Greatly improved error messages from _replicate and _active_t…
Browse files Browse the repository at this point in the history
…asks

If a one-shot replication fails, you now get full info in the response
body, instead of just a "500 Internal Server Error" status.

For #1292
  • Loading branch information
snej committed Jun 15, 2016
1 parent a941a48 commit 2926bf4
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Source/CBLStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ typedef enum {

kCBLStatusServerError = 500,
kCBLStatusNotImplemented = 501,
kCBLStatusServiceUnavailable = 503,
kCBLStatusTimedOut = 504, // "Gateway Timeout"

// Non-HTTP errors:
kCBLStatusBadEncoding = 490,
Expand Down
37 changes: 34 additions & 3 deletions Source/CBLStatus.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// and limitations under the License.

#import "CBLStatus.h"
#import "MYErrorUtils.h"


NSString* const CBLHTTPErrorDomain = @"CBLHTTP";
Expand Down Expand Up @@ -99,12 +100,42 @@ BOOL CBLStatusToOutNSError(CBLStatus status, NSError** outError) {
}


// Mapping NSURLErrorDomain codes to CBLStatus:
static const struct { NSInteger code; CBLStatus status; } kURLErrorToStatusMap[] = {
{NSURLErrorBadURL, kCBLStatusBadRequest},
{NSURLErrorUserCancelledAuthentication, kCBLStatusUnauthorized},
{NSURLErrorUserAuthenticationRequired, kCBLStatusUnauthorized},
{NSURLErrorCannotFindHost, kCBLStatusNotFound},
{NSURLErrorRedirectToNonExistentLocation, kCBLStatusNotFound},
{NSURLErrorUnsupportedURL, kCBLStatusNotImplemented},
{NSURLErrorAppTransportSecurityRequiresSecureConnection, kCBLStatusForbidden},

{NSURLErrorResourceUnavailable, kCBLStatusServiceUnavailable},
{NSURLErrorCannotConnectToHost, kCBLStatusServiceUnavailable},
{NSURLErrorNetworkConnectionLost, kCBLStatusServiceUnavailable},
{NSURLErrorDNSLookupFailed, kCBLStatusServiceUnavailable},
{NSURLErrorNotConnectedToInternet, kCBLStatusServiceUnavailable},
{NSURLErrorNetworkConnectionLost, kCBLStatusServiceUnavailable},

{NSURLErrorTimedOut, kCBLStatusTimedOut},
{NSURLErrorUnknown, kCBLStatusServerError},
{NSURLErrorCancelled, kCBLStatusCanceled},
};


CBLStatus CBLStatusFromNSError(NSError* error, CBLStatus defaultStatus) {
NSInteger code = error.code;
if (!error)
if (!error) {
return kCBLStatusOK;
else if ($equal(error.domain, CBLHTTPErrorDomain))
} else if ($equal(error.domain, CBLHTTPErrorDomain)) {
return (CBLStatus)code;
else
} else if ($equal(error.domain, NSURLErrorDomain)) {
for (unsigned i=0; i < sizeof(kURLErrorToStatusMap)/sizeof(kURLErrorToStatusMap[0]); ++i) {
if (kURLErrorToStatusMap[i].code == code)
return kURLErrorToStatusMap[i].status;
}
return kCBLStatusUpstreamError;
} else {
return defaultStatus;
}
}
34 changes: 27 additions & 7 deletions Source/CBL_Router+Handlers.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#import "CBLJSON.h"

#import "CollectionUtils.h"
#import "MYErrorUtils.h"
#import "Test.h"


Expand Down Expand Up @@ -435,14 +436,36 @@ - (CBLStatus) do_POST_replicate {
// subroutine of -do_POST_replicate
- (void) replicationStopped: (NSNotification*)n {
id<CBL_Replicator> repl = n.object;
_response.status = CBLStatusFromNSError(repl.error, kCBLStatusServerError);
NSError* error = repl.error;
NSDictionary* body;
if (error) {
_response.status = kCBLStatusServerError;
body = [self infoForReplicationError: error];
} else {
body = @{@"ok": @YES, @"session_id": repl.sessionID};
}
[self.response setBodyObject: body];
[self sendResponseHeaders];
[self.response setBodyObject: $dict({@"ok", (repl.error ?nil :$true)},
{@"session_id", repl.sessionID})];
[self sendResponseBodyAndFinish: YES];
}


- (NSDictionary*) infoForReplicationError: (NSError*)error {
if (!error)
return nil;
NSString* errorMsg;
CBLStatus status = CBLStatusFromNSError(error, 0);
if (status != 0)
CBLStatusToHTTPStatus(status, &errorMsg);
if (!errorMsg)
errorMsg = $sprintf(@"%@ %ld", error.domain, (long)error.code);
return $dict({@"error", errorMsg},
{@"reason", error.localizedDescription},
{@"status", (status ? @(status) : nil)},
{@"url", error.my_failingURL.absoluteString});
}


/* CouchDB 1.2's _replicate response looks like this:
{
"history": [
Expand Down Expand Up @@ -534,10 +557,7 @@ - (NSDictionary*) activeTaskInfo: (id<CBL_Replicator>)repl {
} else {
status = kStatusName[repl.status];
}
NSArray* error = nil;
NSError* errorObj = repl.error;
if (errorObj)
error = @[@(errorObj.code), errorObj.localizedDescription];
NSDictionary* error = [self infoForReplicationError: repl.error];

NSArray* activeRequests = nil;
if ([repl respondsToSelector: @selector(activeTasksInfo)])
Expand Down
2 changes: 1 addition & 1 deletion vendor/MYUtilities
Submodule MYUtilities updated 2 files
+2 −0 MYErrorUtils.h
+12 −6 MYErrorUtils.m

0 comments on commit 2926bf4

Please sign in to comment.