From 35ce17832314418dd643655de221a55a9cb06162 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Mon, 7 Apr 2014 23:00:19 -0700 Subject: [PATCH] Added GCDWebServerErrorResponse --- CGDWebServer/GCDWebServer.m | 2 +- CGDWebServer/GCDWebServerErrorResponse.h | 41 ++++++++ CGDWebServer/GCDWebServerErrorResponse.m | 125 +++++++++++++++++++++++ CGDWebServer/GCDWebServerPrivate.h | 1 + CGDWebServer/GCDWebServerResponse.h | 6 +- CGDWebServer/GCDWebServerResponse.m | 18 ---- GCDWebServer.xcodeproj/project.pbxproj | 8 ++ 7 files changed, 177 insertions(+), 24 deletions(-) create mode 100644 CGDWebServer/GCDWebServerErrorResponse.h create mode 100644 CGDWebServer/GCDWebServerErrorResponse.m diff --git a/CGDWebServer/GCDWebServer.m b/CGDWebServer/GCDWebServer.m index 552f6fe..179bbbe 100644 --- a/CGDWebServer/GCDWebServer.m +++ b/CGDWebServer/GCDWebServer.m @@ -652,7 +652,7 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er if (response) { response.cacheControlMaxAge = cacheAge; } else { - response = [GCDWebServerResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound]; + response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; } return response; diff --git a/CGDWebServer/GCDWebServerErrorResponse.h b/CGDWebServer/GCDWebServerErrorResponse.h new file mode 100644 index 0000000..9e51a45 --- /dev/null +++ b/CGDWebServer/GCDWebServerErrorResponse.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2012-2014, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerDataResponse.h" +#import "GCDWebServerHTTPStatusCodes.h" + +// Returns responses with an HTML body containing the error message +@interface GCDWebServerErrorResponse : GCDWebServerDataResponse ++ (GCDWebServerErrorResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); ++ (GCDWebServerErrorResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); ++ (GCDWebServerErrorResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); ++ (GCDWebServerErrorResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); +- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); +- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); +- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); +- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); +@end diff --git a/CGDWebServer/GCDWebServerErrorResponse.m b/CGDWebServer/GCDWebServerErrorResponse.m new file mode 100644 index 0000000..abc368a --- /dev/null +++ b/CGDWebServer/GCDWebServerErrorResponse.m @@ -0,0 +1,125 @@ +/* + Copyright (c) 2012-2014, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerPrivate.h" + +@interface GCDWebServerErrorResponse () +- (id)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments; +@end + +@implementation GCDWebServerErrorResponse + ++ (GCDWebServerErrorResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]); + va_end(arguments); + return response; +} + ++ (GCDWebServerErrorResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]); + va_end(arguments); + return response; +} + ++ (GCDWebServerErrorResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]); + va_end(arguments); + return response; +} + ++ (GCDWebServerErrorResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = ARC_AUTORELEASE([[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]); + va_end(arguments); + return response; +} + +static inline NSString* _EscapeHTMLString(NSString* string) { + return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; +} + +- (id)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { + NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; + NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; + NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; + NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", + title, title, _EscapeHTMLString(message), error]; + if ((self = [self initWithHTML:html])) { + self.statusCode = statusCode; + } + ARC_RELEASE(message); + return self; +} + +- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +@end diff --git a/CGDWebServer/GCDWebServerPrivate.h b/CGDWebServer/GCDWebServerPrivate.h index 1915aa5..08662c2 100644 --- a/CGDWebServer/GCDWebServerPrivate.h +++ b/CGDWebServer/GCDWebServerPrivate.h @@ -60,6 +60,7 @@ #import "GCDWebServerURLEncodedFormRequest.h" #import "GCDWebServerDataResponse.h" +#import "GCDWebServerErrorResponse.h" #import "GCDWebServerFileResponse.h" #import "GCDWebServerStreamingResponse.h" diff --git a/CGDWebServer/GCDWebServerResponse.h b/CGDWebServer/GCDWebServerResponse.h index 18470a0..ead69e5 100644 --- a/CGDWebServer/GCDWebServerResponse.h +++ b/CGDWebServer/GCDWebServerResponse.h @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "GCDWebServerHTTPStatusCodes.h" +#import @protocol GCDWebServerBodyReader - (BOOL)open:(NSError**)error; // Return NO on error ("error" is guaranteed to be non-NULL) @@ -47,11 +47,7 @@ @interface GCDWebServerResponse (Extensions) + (GCDWebServerResponse*)responseWithStatusCode:(NSInteger)statusCode; -+ (GCDWebServerResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)error; -+ (GCDWebServerResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)error; + (GCDWebServerResponse*)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; - (id)initWithStatusCode:(NSInteger)statusCode; -- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)error; -- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)error; - (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; @end diff --git a/CGDWebServer/GCDWebServerResponse.m b/CGDWebServer/GCDWebServerResponse.m index e3e5085..4cc88e6 100644 --- a/CGDWebServer/GCDWebServerResponse.m +++ b/CGDWebServer/GCDWebServerResponse.m @@ -252,14 +252,6 @@ return ARC_AUTORELEASE([[self alloc] initWithStatusCode:statusCode]); } -+ (GCDWebServerResponse*)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)error { - return ARC_AUTORELEASE([[self alloc] initWithClientError:error]); -} - -+ (GCDWebServerResponse*)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)error { - return ARC_AUTORELEASE([[self alloc] initWithServerError:error]); -} - + (GCDWebServerResponse*)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { return ARC_AUTORELEASE([[self alloc] initWithRedirect:location permanent:permanent]); } @@ -271,16 +263,6 @@ return self; } -- (id)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)error { - DCHECK(((NSInteger)error >= 400) && ((NSInteger)error < 500)); - return [self initWithStatusCode:error]; -} - -- (id)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)error { - DCHECK(((NSInteger)error >= 500) && ((NSInteger)error < 600)); - return [self initWithStatusCode:error]; -} - - (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { if ((self = [self init])) { self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; diff --git a/GCDWebServer.xcodeproj/project.pbxproj b/GCDWebServer.xcodeproj/project.pbxproj index 8e9e4a9..79dd7ca 100644 --- a/GCDWebServer.xcodeproj/project.pbxproj +++ b/GCDWebServer.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; }; E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; }; E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; }; + E276647C18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */; }; + E276647D18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */; }; E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; }; E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; }; E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */; }; @@ -113,6 +115,8 @@ E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; + E276647A18F3BC2100A034BA /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerErrorResponse.h; sourceTree = ""; }; + E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerErrorResponse.m; sourceTree = ""; }; E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = ""; }; E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = ""; }; E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = ""; }; @@ -196,6 +200,8 @@ E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */, E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */, E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */, + E276647A18F3BC2100A034BA /* GCDWebServerErrorResponse.h */, + E276647B18F3BC2100A034BA /* GCDWebServerErrorResponse.m */, E2A0E7FB18F1D36C00C580B1 /* GCDWebServerFileRequest.h */, E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */, E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */, @@ -357,6 +363,7 @@ E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */, E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */, E22112891690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */, + E276647C18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */, E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */, E2A0E7F518F1D1E500C580B1 /* GCDWebServerStreamingResponse.m in Sources */, E2A0E80118F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */, @@ -372,6 +379,7 @@ buildActionMask = 2147483647; files = ( E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */, + E276647D18F3BC2100A034BA /* GCDWebServerErrorResponse.m in Sources */, E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */, E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */, E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,