diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5b59614 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +--- +Language: Cpp +BasedOnStyle: Google +Standard: Cpp11 +ColumnLimit: 0 +AlignTrailingComments: false +NamespaceIndentation: All +DerivePointerAlignment: false +AlwaysBreakBeforeMultilineStrings: false +AccessModifierOffset: -2 +ObjCSpaceBeforeProtocolList: true +SortIncludes: false +... diff --git a/GCDWebDAVServer/GCDWebDAVServer.m b/GCDWebDAVServer/GCDWebDAVServer.m index 33025d4..110c8b5 100644 --- a/GCDWebDAVServer/GCDWebDAVServer.m +++ b/GCDWebDAVServer/GCDWebDAVServer.m @@ -99,27 +99,27 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* itemName = [absolutePath lastPathComponent]; if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading item name \"%@\" is not allowed", itemName]; } - + // Because HEAD requests are mapped to GET ones, we need to handle directories but it's OK to return nothing per http://webdav.org/specs/rfc4918.html#rfc.section.9.4 if (isDirectory) { return [GCDWebServerResponse response]; } - + if ([self.delegate respondsToSelector:@selector(davServer:didDownloadFileAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate davServer:self didDownloadFileAtPath:absolutePath]; }); } - + if ([request hasByteRange]) { return [GCDWebServerFileResponse responseWithFile:absolutePath byteRange:request.byteRange]; } - + return [GCDWebServerFileResponse responseWithFile:absolutePath]; } @@ -127,7 +127,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if ([request hasByteRange]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Range uploads not supported"]; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; if (![self _checkSandboxedPath:absolutePath]) { @@ -137,27 +137,27 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath]; } - + BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]; if (existing && isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"PUT not allowed on existing collection \"%@\"", relativePath]; } - + NSString* fileName = [absolutePath lastPathComponent]; if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file name \"%@\" is not allowed", fileName]; } - + if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:request.temporaryPath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file to \"%@\" is not permitted", relativePath]; } - + [[NSFileManager defaultManager] removeItemAtPath:absolutePath error:NULL]; NSError* error = nil; if (![[NSFileManager defaultManager] moveItemAtPath:request.temporaryPath toPath:absolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath]; } - + if ([self.delegate respondsToSelector:@selector(davServer:didUploadFileAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate davServer:self didUploadFileAtPath:absolutePath]; @@ -171,28 +171,28 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if (depthHeader && ![depthHeader isEqualToString:@"infinity"]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader]; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; BOOL isDirectory = NO; if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* itemName = [absolutePath lastPathComponent]; if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName]; } - + if (![self shouldDeleteItemAtPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath]; } - + if ([self.delegate respondsToSelector:@selector(davServer:didDeleteItemAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate davServer:self didDeleteItemAtPath:absolutePath]; @@ -205,7 +205,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if ([request hasBody] && (request.contentLength > 0)) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnsupportedMediaType message:@"Unexpected request body for MKCOL method"]; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; if (![self _checkSandboxedPath:absolutePath]) { @@ -215,16 +215,16 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if (![[NSFileManager defaultManager] fileExistsAtPath:[absolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Missing intermediate collection(s) for \"%@\"", relativePath]; } - + NSString* directoryName = [absolutePath lastPathComponent]; if (!_allowHidden && [directoryName hasPrefix:@"."]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName]; } - + if (![self shouldCreateDirectoryAtPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath]; @@ -233,12 +233,12 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { NSString* creationDateHeader = [request.headers objectForKey:@"X-GCDWebServer-CreationDate"]; if (creationDateHeader) { NSDate* date = GCDWebServerParseISO8601(creationDateHeader); - if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate: date} ofItemAtPath:absolutePath error:&error]) { + if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileCreationDate : date } ofItemAtPath:absolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed setting creation date for directory \"%@\"", relativePath]; } } #endif - + if ([self.delegate respondsToSelector:@selector(davServer:didCreateDirectoryAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate davServer:self didCreateDirectoryAtPath:absolutePath]; @@ -254,13 +254,13 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader]; } } - + NSString* srcRelativePath = request.path; NSString* srcAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:srcRelativePath]; if (![self _checkSandboxedPath:srcAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath]; } - + NSString* dstRelativePath = [request.headers objectForKey:@"Destination"]; NSRange range = [dstRelativePath rangeOfString:[request.headers objectForKey:@"Host"]]; if ((dstRelativePath == nil) || (range.location == NSNotFound)) { @@ -274,23 +274,23 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { if (![self _checkSandboxedPath:dstAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", srcRelativePath]; } - + BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:[dstAbsolutePath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Conflict message:@"Invalid destination \"%@\"", dstRelativePath]; } - + NSString* itemName = [dstAbsolutePath lastPathComponent]; if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"%@ to item name \"%@\" is not allowed", isMove ? @"Moving" : @"Copying", itemName]; } - + NSString* overwriteHeader = [request.headers objectForKey:@"Overwrite"]; BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:dstAbsolutePath]; if (existing && ((isMove && ![overwriteHeader isEqualToString:@"T"]) || (!isMove && [overwriteHeader isEqualToString:@"F"]))) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_PreconditionFailed message:@"Destination \"%@\" already exists", dstRelativePath]; } - + if (isMove) { if (![self shouldMoveItemFromPath:srcAbsolutePath toPath:dstAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath]; @@ -300,7 +300,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Copying \"%@\" to \"%@\" is not permitted", srcRelativePath, dstRelativePath]; } } - + NSError* error = nil; if (isMove) { [[NSFileManager defaultManager] removeItemAtPath:dstAbsolutePath error:NULL]; @@ -312,7 +312,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden underlyingError:error message:@"Failed copying \"%@\" to \"%@\"", srcRelativePath, dstRelativePath]; } } - + if (isMove) { if ([self.delegate respondsToSelector:@selector(davServer:didMoveItemFromPath:toPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -326,7 +326,7 @@ static inline BOOL _IsMacFinder(GCDWebServerRequest* request) { }); } } - + return [GCDWebServerResponse responseWithStatusCode:(existing ? kGCDWebServerHTTPStatusCode_NoContent : kGCDWebServerHTTPStatusCode_Created)]; } @@ -355,7 +355,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name [xmlString appendFormat:@"%@", escapedPath]; [xmlString appendString:@""]; [xmlString appendString:@""]; - + if (properties & kDAVProperty_ResourceType) { if (isDirectory) { [xmlString appendString:@""]; @@ -363,19 +363,19 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name [xmlString appendString:@""]; } } - + if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) { [xmlString appendFormat:@"%@", GCDWebServerFormatISO8601([attributes fileCreationDate])]; } - + if ((properties & kDAVProperty_LastModified) && isFile && [attributes objectForKey:NSFileModificationDate]) { // Last modification date is not useful for directories as it changes implicitely and 'Last-Modified' header is not provided for directories anyway [xmlString appendFormat:@"%@", GCDWebServerFormatRFC822([attributes fileModificationDate])]; } - + if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) { [xmlString appendFormat:@"%llu", [attributes fileSize]]; } - + [xmlString appendString:@""]; [xmlString appendString:@"HTTP/1.1 200 OK"]; [xmlString appendString:@""]; @@ -397,7 +397,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name } else { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Unsupported 'Depth' header: %@", depthHeader]; // TODO: Return 403 / propfind-finite-depth for "infinity" depth } - + DAVProperties properties = 0; if (request.data.length) { BOOL success = YES; @@ -438,19 +438,19 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name } else { properties = kDAVAllProperties; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; BOOL isDirectory = NO; if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* itemName = [absolutePath lastPathComponent]; if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Retrieving properties for item name \"%@\" is not allowed", itemName]; } - + NSArray* items = nil; if (isDirectory) { NSError* error = nil; @@ -459,7 +459,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath]; } } - + NSMutableString* xmlString = [NSMutableString stringWithString:@""]; [xmlString appendString:@"\n"]; if (![relativePath hasPrefix:@"/"]) { @@ -477,7 +477,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name } } [xmlString appendString:@""]; - + GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding] contentType:@"application/xml; charset=\"utf-8\""]; response.statusCode = kGCDWebServerHTTPStatusCode_MultiStatus; @@ -488,14 +488,14 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name if (!_IsMacFinder(request)) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"LOCK method only allowed for Mac Finder"]; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; BOOL isDirectory = NO; if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* depthHeader = [request.headers objectForKey:@"Depth"]; NSString* timeoutHeader = [request.headers objectForKey:@"Timeout"]; NSString* scope = nil; @@ -533,16 +533,16 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string]; } - + if (![scope isEqualToString:@"exclusive"] || ![type isEqualToString:@"write"] || ![depthHeader isEqualToString:@"0"]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depthHeader, relativePath]; } - + NSString* itemName = [absolutePath lastPathComponent]; if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking item name \"%@\" is not allowed", itemName]; } - + #ifdef __GCDWEBSERVER_ENABLE_TESTING__ NSString* lockTokenHeader = [request.headers objectForKey:@"X-GCDWebServer-LockToken"]; if (lockTokenHeader) { @@ -556,7 +556,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name CFRelease(string); CFRelease(uuid); } - + NSMutableString* xmlString = [NSMutableString stringWithString:@""]; [xmlString appendString:@"\n"]; [xmlString appendString:@"\n\n"]; @@ -574,7 +574,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name [xmlString appendFormat:@"%@\n", lockroot]; [xmlString appendString:@"\n\n"]; [xmlString appendString:@""]; - + [self logVerbose:@"WebDAV pretending to lock \"%@\"", relativePath]; GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding] contentType:@"application/xml; charset=\"utf-8\""]; @@ -585,24 +585,24 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name if (!_IsMacFinder(request)) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"UNLOCK method only allowed for Mac Finder"]; } - + NSString* relativePath = request.path; NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath]; BOOL isDirectory = NO; if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* tokenHeader = [request.headers objectForKey:@"Lock-Token"]; if (!tokenHeader.length) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Missing 'Lock-Token' header"]; } - + NSString* itemName = [absolutePath lastPathComponent]; if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Unlocking item name \"%@\" is not allowed", itemName]; } - + [self logVerbose:@"WebDAV pretending to unlock \"%@\"", relativePath]; return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent]; } @@ -611,7 +611,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name @implementation GCDWebDAVServer -@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden; +@synthesize uploadDirectory = _uploadDirectory, allowedFileExtensions = _allowedExtensions, allowHiddenItems = _allowHidden; @dynamic delegate; @@ -619,57 +619,76 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name if ((self = [super init])) { _uploadDirectory = [[path stringByStandardizingPath] copy]; GCDWebDAVServer* __unsafe_unretained server = self; - + // 9.1 PROPFIND method - [self addDefaultHandlerForMethod:@"PROPFIND" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performPROPFIND:(GCDWebServerDataRequest*)request]; - }]; - + [self addDefaultHandlerForMethod:@"PROPFIND" + requestClass:[GCDWebServerDataRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performPROPFIND:(GCDWebServerDataRequest*)request]; + }]; + // 9.3 MKCOL Method - [self addDefaultHandlerForMethod:@"MKCOL" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performMKCOL:(GCDWebServerDataRequest*)request]; - }]; - + [self addDefaultHandlerForMethod:@"MKCOL" + requestClass:[GCDWebServerDataRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performMKCOL:(GCDWebServerDataRequest*)request]; + }]; + // 9.4 GET & HEAD methods - [self addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performGET:request]; - }]; - + [self addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performGET:request]; + }]; + // 9.6 DELETE method - [self addDefaultHandlerForMethod:@"DELETE" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performDELETE:request]; - }]; - + [self addDefaultHandlerForMethod:@"DELETE" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performDELETE:request]; + }]; + // 9.7 PUT method - [self addDefaultHandlerForMethod:@"PUT" requestClass:[GCDWebServerFileRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performPUT:(GCDWebServerFileRequest*)request]; - }]; - + [self addDefaultHandlerForMethod:@"PUT" + requestClass:[GCDWebServerFileRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performPUT:(GCDWebServerFileRequest*)request]; + }]; + // 9.8 COPY method - [self addDefaultHandlerForMethod:@"COPY" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performCOPY:request isMove:NO]; - }]; - + [self addDefaultHandlerForMethod:@"COPY" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performCOPY:request isMove:NO]; + }]; + // 9.9 MOVE method - [self addDefaultHandlerForMethod:@"MOVE" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performCOPY:request isMove:YES]; - }]; - + [self addDefaultHandlerForMethod:@"MOVE" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performCOPY:request isMove:YES]; + }]; + // 9.10 LOCK method - [self addDefaultHandlerForMethod:@"LOCK" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performLOCK:(GCDWebServerDataRequest*)request]; - }]; - + [self addDefaultHandlerForMethod:@"LOCK" + requestClass:[GCDWebServerDataRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performLOCK:(GCDWebServerDataRequest*)request]; + }]; + // 9.11 UNLOCK method - [self addDefaultHandlerForMethod:@"UNLOCK" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performUNLOCK:request]; - }]; - + [self addDefaultHandlerForMethod:@"UNLOCK" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performUNLOCK:request]; + }]; + // 10.1 OPTIONS method / DAV Header - [self addDefaultHandlerForMethod:@"OPTIONS" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server performOPTIONS:request]; - }]; - + [self addDefaultHandlerForMethod:@"OPTIONS" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server performOPTIONS:request]; + }]; } return self; } diff --git a/GCDWebServer/Core/GCDWebServer.h b/GCDWebServer/Core/GCDWebServer.h index 78fb4c0..ca0f5a6 100644 --- a/GCDWebServer/Core/GCDWebServer.h +++ b/GCDWebServer/Core/GCDWebServer.h @@ -575,22 +575,22 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess; /** * Logs a message to the logging facility at the VERBOSE level. */ -- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); /** * Logs a message to the logging facility at the INFO level. */ -- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); /** * Logs a message to the logging facility at the WARNING level. */ -- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); /** * Logs a message to the logging facility at the ERROR level. */ -- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); @end diff --git a/GCDWebServer/Core/GCDWebServer.m b/GCDWebServer/Core/GCDWebServer.m index c773f94..7e115d3 100644 --- a/GCDWebServer/Core/GCDWebServer.m +++ b/GCDWebServer/Core/GCDWebServer.m @@ -141,7 +141,7 @@ static void _ExecuteMainThreadRunLoopSources() { @implementation GCDWebServerHandler -@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock; +@synthesize matchBlock = _matchBlock, asyncProcessBlock = _asyncProcessBlock; - (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { if ((self = [super init])) { @@ -162,7 +162,7 @@ static void _ExecuteMainThreadRunLoopSources() { NSInteger _activeConnections; // Accessed through _syncQueue only BOOL _connected; // Accessed on main thread only CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only - + NSDictionary* _options; NSString* _serverName; NSString* _authenticationRealm; @@ -195,9 +195,9 @@ static void _ExecuteMainThreadRunLoopSources() { @implementation GCDWebServer -@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm, - authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts, - shouldAutomaticallyMapHEADToGET=_mapHEADToGET, dispatchQueuePriority=_dispatchQueuePriority; +@synthesize delegate = _delegate, handlers = _handlers, port = _port, serverName = _serverName, authenticationRealm = _authenticationRealm, + authenticationBasicAccounts = _authenticationBasicAccounts, authenticationDigestAccounts = _authenticationDigestAccounts, + shouldAutomaticallyMapHEADToGET = _mapHEADToGET, dispatchQueuePriority = _dispatchQueuePriority; + (void)initialize { GCDWebServerInitializeFunctions(); @@ -220,7 +220,7 @@ static void _ExecuteMainThreadRunLoopSources() { GWS_DCHECK(_activeConnections == 0); GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle - + #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(_sourceGroup); dispatch_release(_syncQueue); @@ -235,10 +235,10 @@ static void _ExecuteMainThreadRunLoopSources() { if (_backgroundTask == UIBackgroundTaskInvalid) { GWS_LOG_DEBUG(@"Did start background task"); _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ - + GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]); [self _endBackgroundTask]; - + }]; } else { GWS_DNOT_REACHED(); @@ -253,13 +253,13 @@ static void _ExecuteMainThreadRunLoopSources() { GWS_DCHECK(_connected == NO); _connected = YES; GWS_LOG_DEBUG(@"Did connect"); - + #if TARGET_OS_IPHONE if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) { [self _startBackgroundTask]; } #endif - + if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) { [_delegate webServerDidConnect:self]; } @@ -267,7 +267,7 @@ static void _ExecuteMainThreadRunLoopSources() { - (void)willStartConnection:(GCDWebServerConnection*)connection { dispatch_sync(_syncQueue, ^{ - + GWS_DCHECK(_activeConnections >= 0); if (_activeConnections == 0) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -282,7 +282,7 @@ static void _ExecuteMainThreadRunLoopSources() { }); } _activeConnections += 1; - + }); } @@ -309,11 +309,11 @@ static void _ExecuteMainThreadRunLoopSources() { GWS_DCHECK(_connected == YES); _connected = NO; GWS_LOG_DEBUG(@"Did disconnect"); - + #if TARGET_OS_IPHONE [self _endBackgroundTask]; #endif - + if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) { [_delegate webServerDidDisconnect:self]; } @@ -356,9 +356,10 @@ static void _ExecuteMainThreadRunLoopSources() { } - (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock { - [self addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - completionBlock(processBlock(request)); - }]; + [self addHandlerWithMatchBlock:matchBlock + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(processBlock(request)); + }]; } - (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { @@ -464,7 +465,7 @@ static inline NSString* _EncodeBase64(NSString* string) { if (listeningSocket > 0) { int yes = 1; setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - + if (bind(listeningSocket, address, length) == 0) { if (listen(listeningSocket, (int)maxPendingConnections) == 0) { GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket); @@ -483,7 +484,7 @@ static inline NSString* _EncodeBase64(NSString* string) { GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); close(listeningSocket); } - + } else { if (error) { *error = GCDWebServerMakePosixError(errno); @@ -497,7 +498,7 @@ static inline NSString* _EncodeBase64(NSString* string) { dispatch_group_enter(_sourceGroup); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0)); dispatch_source_set_cancel_handler(source, ^{ - + @autoreleasepool { int result = close(listeningSocket); if (result != 0) { @@ -507,17 +508,17 @@ static inline NSString* _EncodeBase64(NSString* string) { } } dispatch_group_leave(_sourceGroup); - + }); dispatch_source_set_event_handler(source, ^{ - + @autoreleasepool { struct sockaddr_storage remoteSockAddr; socklen_t remoteAddrLen = sizeof(remoteSockAddr); int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen); if (socket > 0) { NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen]; - + struct sockaddr_storage localSockAddr; socklen_t localAddrLen = sizeof(localSockAddr); NSData* localAddress = nil; @@ -527,28 +528,28 @@ static inline NSString* _EncodeBase64(NSString* string) { } else { GWS_DNOT_REACHED(); } - + int noSigPipe = 1; setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE - + GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened [connection self]; // Prevent compiler from complaining about unused variable / useless statement } else { GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); } } - + }); return source; } - (BOOL)_start:(NSError**)error { GWS_DCHECK(_source4 == NULL); - + NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue]; BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue]; NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue]; - + struct sockaddr_in addr4; bzero(&addr4, sizeof(addr4)); addr4.sin_len = sizeof(addr4); @@ -568,7 +569,7 @@ static inline NSString* _EncodeBase64(NSString* string) { GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno); } } - + struct sockaddr_in6 addr6; bzero(&addr6, sizeof(addr6)); addr6.sin6_len = sizeof(addr6); @@ -580,7 +581,7 @@ static inline NSString* _EncodeBase64(NSString* string) { close(listeningSocket4); return NO; } - + _serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy]; NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil); if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) { @@ -602,24 +603,24 @@ static inline NSString* _EncodeBase64(NSString* string) { _mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue]; _disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue]; _dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue]; - + _source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO]; _source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES]; _port = port; _bindToLocalhost = bindToLocalhost; - + NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil); NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp"); if (bonjourName) { _registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port); if (_registrationService) { CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; - + CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context); CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes); CFStreamError streamError = {0}; CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError); - + _resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService); if (_resolutionService) { CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context); @@ -631,7 +632,7 @@ static inline NSString* _EncodeBase64(NSString* string) { GWS_LOG_ERROR(@"Failed creating CFNetService for registration"); } } - + if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) { DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self); if (status == kDNSServiceErr_NoError) { @@ -654,7 +655,7 @@ static inline NSString* _EncodeBase64(NSString* string) { GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status); } } - + dispatch_resume(_source4); dispatch_resume(_source6); GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL); @@ -663,13 +664,13 @@ static inline NSString* _EncodeBase64(NSString* string) { [_delegate webServerDidStart:self]; }); } - + return YES; } - (void)_stop { GWS_DCHECK(_source4 != NULL); - + if (_dnsService) { _dnsAddress = nil; _dnsPort = 0; @@ -685,7 +686,7 @@ static inline NSString* _EncodeBase64(NSString* string) { DNSServiceRefDeallocate(_dnsService); _dnsService = NULL; } - + if (_registrationService) { if (_resolutionService) { CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes); @@ -700,7 +701,7 @@ static inline NSString* _EncodeBase64(NSString* string) { CFRelease(_registrationService); _registrationService = NULL; } - + dispatch_source_cancel(_source6); dispatch_source_cancel(_source4); dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed @@ -714,12 +715,12 @@ static inline NSString* _EncodeBase64(NSString* string) { _source4 = NULL; _port = 0; _bindToLocalhost = NO; - + _serverName = nil; _authenticationRealm = nil; _authenticationBasicAccounts = nil; _authenticationDigestAccounts = nil; - + dispatch_async(dispatch_get_main_queue(), ^{ if (_disconnectTimer) { CFRunLoopTimerInvalidate(_disconnectTimer); @@ -728,7 +729,7 @@ static inline NSString* _EncodeBase64(NSString* string) { [self _didDisconnect]; } }); - + GWS_LOG_INFO(@"%@ stopped", [self class]); if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -896,32 +897,38 @@ static inline NSString* _EncodeBase64(NSString* string) { @implementation GCDWebServer (Handlers) - (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { - [self addDefaultHandlerForMethod:method requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - completionBlock(block(request)); - }]; + [self addDefaultHandlerForMethod:method + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; } - (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { - [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { - + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + if (![requestMethod isEqualToString:method]) { return nil; } return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; - - } asyncProcessBlock:block]; + + } + asyncProcessBlock:block]; } - (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { - [self addHandlerForMethod:method path:path requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - completionBlock(block(request)); - }]; + [self addHandlerForMethod:method + path:path + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; } - (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { - [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { - + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + if (![requestMethod isEqualToString:method]) { return nil; } @@ -929,24 +936,28 @@ static inline NSString* _EncodeBase64(NSString* string) { return nil; } return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; - - } asyncProcessBlock:block]; + + } + asyncProcessBlock:block]; } else { GWS_DNOT_REACHED(); } } - (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { - [self addHandlerForMethod:method pathRegex:regex requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - completionBlock(block(request)); - }]; + [self addHandlerForMethod:method + pathRegex:regex + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; } - (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL]; if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { - [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { - + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + if (![requestMethod isEqualToString:method]) { return nil; } @@ -972,8 +983,9 @@ static inline NSString* _EncodeBase64(NSString* string) { GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; [request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures]; return request; - - } asyncProcessBlock:block]; + + } + asyncProcessBlock:block]; } else { GWS_DNOT_REACHED(); } @@ -984,29 +996,35 @@ static inline NSString* _EncodeBase64(NSString* string) { @implementation GCDWebServer (GETHandlers) - (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge { - [self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType]; - response.cacheControlMaxAge = cacheAge; - return response; - - }]; + [self addHandlerForMethod:@"GET" + path:path + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType]; + response.cacheControlMaxAge = cacheAge; + return response; + + }]; } - (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests { - [self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - GCDWebServerResponse* response = nil; - if (allowRangeRequests) { - response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment]; - [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; - } else { - response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment]; - } - response.cacheControlMaxAge = cacheAge; - return response; - - }]; + [self addHandlerForMethod:@"GET" + path:path + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = nil; + if (allowRangeRequests) { + response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment]; + [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; + } else { + response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment]; + } + response.cacheControlMaxAge = cacheAge; + return response; + + }]; } - (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path { @@ -1042,8 +1060,8 @@ static inline NSString* _EncodeBase64(NSString* string) { - (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests { if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) { GCDWebServer* __unsafe_unretained server = self; - [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { - + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + if (![requestMethod isEqualToString:@"GET"]) { return nil; } @@ -1051,39 +1069,40 @@ static inline NSString* _EncodeBase64(NSString* string) { return nil; } return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; - - } processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - GCDWebServerResponse* response = nil; - NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]]; - NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType]; - if (fileType) { - if ([fileType isEqualToString:NSFileTypeDirectory]) { - if (indexFilename) { - NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename]; - NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType]; - if ([indexType isEqualToString:NSFileTypeRegular]) { - return [GCDWebServerFileResponse responseWithFile:indexPath]; + + } + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = nil; + NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]]; + NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType]; + if (fileType) { + if ([fileType isEqualToString:NSFileTypeDirectory]) { + if (indexFilename) { + NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename]; + NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType]; + if ([indexType isEqualToString:NSFileTypeRegular]) { + return [GCDWebServerFileResponse responseWithFile:indexPath]; + } + } + response = [server _responseWithContentsOfDirectory:filePath]; + } else if ([fileType isEqualToString:NSFileTypeRegular]) { + if (allowRangeRequests) { + response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange]; + [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; + } else { + response = [GCDWebServerFileResponse responseWithFile:filePath]; + } } } - response = [server _responseWithContentsOfDirectory:filePath]; - } else if ([fileType isEqualToString:NSFileTypeRegular]) { - if (allowRangeRequests) { - response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange]; - [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; + if (response) { + response.cacheControlMaxAge = cacheAge; } else { - response = [GCDWebServerFileResponse responseWithFile:filePath]; + response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; } - } - } - if (response) { - response.cacheControlMaxAge = cacheAge; - } else { - response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; - } - return response; - - }]; + return response; + + }]; } else { GWS_DNOT_REACHED(); } @@ -1204,11 +1223,11 @@ static void _LogResult(NSString* format, ...) { - (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path { GWS_DCHECK([NSThread isMainThread]); - NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs + NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs NSInteger result = -1; if ([self startWithOptions:options error:NULL]) { _ExecuteMainThreadRunLoopSources(); - + result = 0; NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL]; for (NSString* requestFile in files) { @@ -1230,19 +1249,19 @@ static void _LogResult(NSString* format, ...) { if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) { NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]]; if (responseData) { - CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO); + CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO); if (expectedResponse) { CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port); if (actualResponse) { success = YES; - + CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse); CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse); if (actualStatusCode != expectedStatusCode) { _LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode); success = NO; } - + NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse)); NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse)); for (NSString* expectedHeader in expectedHeaders) { @@ -1262,7 +1281,7 @@ static void _LogResult(NSString* format, ...) { success = NO; } } - + NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length"))); NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse)); NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length"))); @@ -1281,14 +1300,14 @@ static void _LogResult(NSString* format, ...) { if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) { NSTask* task = [[NSTask alloc] init]; [task setLaunchPath:@"/usr/bin/opendiff"]; - [task setArguments:@[expectedPath, actualPath]]; + [task setArguments:@[ expectedPath, actualPath ]]; [task launch]; } } #endif #endif } - + CFRelease(actualResponse); } CFRelease(expectedResponse); @@ -1311,9 +1330,9 @@ static void _LogResult(NSString* format, ...) { } _ExecuteMainThreadRunLoopSources(); } - + [self stop]; - + _ExecuteMainThreadRunLoopSources(); } return result; diff --git a/GCDWebServer/Core/GCDWebServerConnection.m b/GCDWebServer/Core/GCDWebServerConnection.m index 1d72f7d..1601e87 100644 --- a/GCDWebServer/Core/GCDWebServerConnection.m +++ b/GCDWebServer/Core/GCDWebServerConnection.m @@ -66,14 +66,14 @@ static int32_t _connectionCounter = 0; NSUInteger _bytesRead; NSUInteger _bytesWritten; BOOL _virtualHEAD; - + CFHTTPMessageRef _requestMessage; GCDWebServerRequest* _request; GCDWebServerHandler* _handler; CFHTTPMessageRef _responseMessage; GCDWebServerResponse* _response; NSInteger _statusCode; - + BOOL _opened; #ifdef __GCDWEBSERVER_ENABLE_TESTING__ NSUInteger _connectionIndex; @@ -89,7 +89,7 @@ static int32_t _connectionCounter = 0; - (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block { dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) { - + @autoreleasepool { if (error == 0) { size_t size = dispatch_data_get_size(buffer); @@ -114,68 +114,72 @@ static int32_t _connectionCounter = 0; block(NO); } } - + }); } - (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block { GWS_DCHECK(_requestMessage); - [self _readData:headersData withLength:NSUIntegerMax completionBlock:^(BOOL success) { - - if (success) { - NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)]; - if (range.location == NSNotFound) { - [self _readHeaders:headersData withCompletionBlock:block]; - } else { - NSUInteger length = range.location + range.length; - if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) { - if (CFHTTPMessageIsHeaderComplete(_requestMessage)) { - block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); + [self _readData:headersData + withLength:NSUIntegerMax + completionBlock:^(BOOL success) { + + if (success) { + NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)]; + if (range.location == NSNotFound) { + [self _readHeaders:headersData withCompletionBlock:block]; } else { - GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); - block(nil); + NSUInteger length = range.location + range.length; + if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) { + if (CFHTTPMessageIsHeaderComplete(_requestMessage)) { + block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); + } else { + GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); + block(nil); + } + } else { + GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket); + block(nil); + } } } else { - GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket); block(nil); } - } - } else { - block(nil); - } - - }]; + + }]; } - (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block { GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]); NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity]; - [self _readData:bodyData withLength:length completionBlock:^(BOOL success) { - - if (success) { - if (bodyData.length <= length) { - NSError* error = nil; - if ([_request performWriteData:bodyData error:&error]) { - NSUInteger remainingLength = length - bodyData.length; - if (remainingLength) { - [self _readBodyWithRemainingLength:remainingLength completionBlock:block]; + [self _readData:bodyData + withLength:length + completionBlock:^(BOOL success) { + + if (success) { + if (bodyData.length <= length) { + NSError* error = nil; + if ([_request performWriteData:bodyData error:&error]) { + NSUInteger remainingLength = length - bodyData.length; + if (remainingLength) { + [self _readBodyWithRemainingLength:remainingLength completionBlock:block]; + } else { + block(YES); + } + } else { + GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); + block(NO); + } } else { - block(YES); + GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket); + block(NO); + GWS_DNOT_REACHED(); } } else { - GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); block(NO); } - } else { - GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket); - block(NO); - GWS_DNOT_REACHED(); - } - } else { - block(NO); - } - - }]; + + }]; } static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { @@ -189,7 +193,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)_readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block { GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]); - + while (1) { NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)]; if (range.location == NSNotFound) { @@ -230,16 +234,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { return; } } - - [self _readData:chunkData withLength:NSUIntegerMax completionBlock:^(BOOL success) { - - if (success) { - [self _readNextBodyChunk:chunkData completionBlock:block]; - } else { - block(NO); - } - - }]; + + [self _readData:chunkData + withLength:NSUIntegerMax + completionBlock:^(BOOL success) { + + if (success) { + [self _readNextBodyChunk:chunkData completionBlock:block]; + } else { + block(NO); + } + + }]; } @end @@ -251,7 +257,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { [data self]; // Keeps ARC from releasing data too early }); dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) { - + @autoreleasepool { if (error == 0) { GWS_DCHECK(remainingData == NULL); @@ -262,7 +268,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { block(NO); } } - + }); #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(buffer); @@ -279,7 +285,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { GWS_DCHECK([_response hasBody]); [_response performReadDataWithCompletion:^(NSData* data, NSError* error) { - + if (data) { if (data.length) { if (_response.usesChunkedTransferEncoding) { @@ -302,22 +308,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { *ptr = '\n'; data = chunk; } - [self _writeData:data withCompletionBlock:^(BOOL success) { - - if (success) { - [self _writeBodyWithCompletionBlock:block]; - } else { - block(NO); - } - - }]; + [self _writeData:data + withCompletionBlock:^(BOOL success) { + + if (success) { + [self _writeBodyWithCompletionBlock:block]; + } else { + block(NO); + } + + }]; } else { if (_response.usesChunkedTransferEncoding) { - [self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) { - - block(success); - - }]; + [self _writeData:_lastChunkData + withCompletionBlock:^(BOOL success) { + + block(success); + + }]; } else { block(YES); } @@ -326,7 +334,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error); block(NO); } - + }]; } @@ -334,7 +342,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { @implementation GCDWebServerConnection -@synthesize server=_server, localAddressData=_localAddress, remoteAddressData=_remoteAddress, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten; +@synthesize server = _server, localAddressData = _localAddress, remoteAddressData = _remoteAddress, totalBytesRead = _bytesRead, totalBytesWritten = _bytesWritten; + (void)initialize { if (_CRLFData == nil) { @@ -376,14 +384,15 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)_startProcessingRequest { GWS_DCHECK(_responseMessage == NULL); - + GCDWebServerResponse* preflightResponse = [self preflightRequest:_request]; if (preflightResponse) { [self _finishProcessingRequest:preflightResponse]; } else { - [self processRequest:_request completion:^(GCDWebServerResponse* processResponse) { - [self _finishProcessingRequest:processResponse]; - }]; + [self processRequest:_request + completion:^(GCDWebServerResponse* processResponse) { + [self _finishProcessingRequest:processResponse]; + }]; } } @@ -391,7 +400,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)_finishProcessingRequest:(GCDWebServerResponse*)response { GWS_DCHECK(_responseMessage == NULL); BOOL hasBody = NO; - + if (response) { response = [self overrideResponse:response forRequest:_request]; } @@ -407,7 +416,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { _response = response; } } - + if (_response) { [self _initializeResponseHeadersWithStatusCode:_response.statusCode]; if (_response.lastModifiedDate) { @@ -436,24 +445,23 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); }]; [self _writeHeadersWithCompletionBlock:^(BOOL success) { - + if (success) { if (hasBody) { [self _writeBodyWithCompletionBlock:^(BOOL successInner) { - + [_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent - + }]; } } else if (hasBody) { [_response performClose]; } - + }]; } else { [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; } - } - (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData { @@ -463,7 +471,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; return; } - + if (initialData.length) { if (![_request performWriteData:initialData error:&error]) { GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); @@ -475,19 +483,20 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { } length -= initialData.length; } - + if (length) { - [self _readBodyWithRemainingLength:length completionBlock:^(BOOL success) { - - NSError* localError = nil; - if ([_request performClose:&localError]) { - [self _startProcessingRequest]; - } else { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; + [self _readBodyWithRemainingLength:length + completionBlock:^(BOOL success) { + + NSError* localError = nil; + if ([_request performClose:&localError]) { + [self _startProcessingRequest]; + } else { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + + }]; } else { if ([_request performClose:&error]) { [self _startProcessingRequest]; @@ -505,100 +514,103 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; return; } - + NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData]; - [self _readNextBodyChunk:chunkData completionBlock:^(BOOL success) { - - NSError* localError = nil; - if ([_request performClose:&localError]) { - [self _startProcessingRequest]; - } else { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; + [self _readNextBodyChunk:chunkData + completionBlock:^(BOOL success) { + + NSError* localError = nil; + if ([_request performClose:&localError]) { + [self _startProcessingRequest]; + } else { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + + }]; } - (void)_readRequestHeaders { _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true); NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity]; - [self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) { - - if (extraData) { - NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase - if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) { - requestMethod = @"GET"; - _virtualHEAD = YES; - } - NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones - NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage)); - if (requestURL) { - requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; - GWS_DCHECK(requestURL); - } - NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash - NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped; - NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{}; - if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) { - for (_handler in _server.handlers) { - _request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery); - if (_request) { - break; + [self _readHeaders:headersData + withCompletionBlock:^(NSData* extraData) { + + if (extraData) { + NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase + if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) { + requestMethod = @"GET"; + _virtualHEAD = YES; } - } - if (_request) { - _request.localAddressData = self.localAddressData; - _request.remoteAddressData = self.remoteAddressData; - if ([_request hasBody]) { - [_request prepareForWriting]; - if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) { - NSString* expectHeader = [requestHeaders objectForKey:@"Expect"]; - if (expectHeader) { - if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing - [self _writeData:_continueData withCompletionBlock:^(BOOL success) { - - if (success) { - if (_request.usesChunkedTransferEncoding) { - [self _readChunkedBodyWithInitialData:extraData]; - } else { - [self _readBodyWithLength:_request.contentLength initialData:extraData]; - } + NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones + NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage)); + if (requestURL) { + requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; + GWS_DCHECK(requestURL); + } + NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash + NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped; + NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{}; + if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) { + for (_handler in _server.handlers) { + _request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery); + if (_request) { + break; + } + } + if (_request) { + _request.localAddressData = self.localAddressData; + _request.remoteAddressData = self.remoteAddressData; + if ([_request hasBody]) { + [_request prepareForWriting]; + if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) { + NSString* expectHeader = [requestHeaders objectForKey:@"Expect"]; + if (expectHeader) { + if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing + [self _writeData:_continueData + withCompletionBlock:^(BOOL success) { + + if (success) { + if (_request.usesChunkedTransferEncoding) { + [self _readChunkedBodyWithInitialData:extraData]; + } else { + [self _readBodyWithLength:_request.contentLength initialData:extraData]; + } + } + + }]; + } else { + GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; } - - }]; + } else { + if (_request.usesChunkedTransferEncoding) { + [self _readChunkedBodyWithInitialData:extraData]; + } else { + [self _readBodyWithLength:_request.contentLength initialData:extraData]; + } + } } else { - GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; + GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest]; } } else { - if (_request.usesChunkedTransferEncoding) { - [self _readChunkedBodyWithInitialData:extraData]; - } else { - [self _readBodyWithLength:_request.contentLength initialData:extraData]; - } + [self _startProcessingRequest]; } } else { - GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest]; + _request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; + GWS_DCHECK(_request); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed]; } } else { - [self _startProcessingRequest]; + [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + GWS_DNOT_REACHED(); } } else { - _request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; - GWS_DCHECK(_request); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed]; + [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; } - } else { - [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - GWS_DNOT_REACHED(); - } - } else { - [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; + + }]; } - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket { @@ -608,15 +620,15 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { _remoteAddress = remoteAddress; _socket = socket; GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket); - + [_server willStartConnection:self]; - + if (![self open]) { close(_socket); return nil; } _opened = YES; - + [self _readRequestHeaders]; } return self; @@ -637,17 +649,17 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { } else { GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket); } - + if (_opened) { [self close]; } - + [_server didEndConnection:self]; - + if (_requestMessage) { CFRelease(_requestMessage); } - + if (_responseMessage) { CFRelease(_responseMessage); } @@ -661,24 +673,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { #ifdef __GCDWEBSERVER_ENABLE_TESTING__ if (_server.recordingEnabled) { _connectionIndex = OSAtomicIncrement32(&_connectionCounter); - + _requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; _requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); GWS_DCHECK(_requestFD > 0); - + _responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; _responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); GWS_DCHECK(_responseFD > 0); } #endif - + return YES; } - (void)didReadBytes:(const void*)bytes length:(NSUInteger)length { GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket); _bytesRead += length; - + #ifdef __GCDWEBSERVER_ENABLE_TESTING__ if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) { GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno); @@ -691,7 +703,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length { GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket); _bytesWritten += length; - + #ifdef __GCDWEBSERVER_ENABLE_TESTING__ if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) { GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno); @@ -819,7 +831,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET } unlink([_requestPath fileSystemRepresentation]); } - + if (_responsePath) { BOOL success = NO; NSError* error = nil; @@ -835,7 +847,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET unlink([_responsePath fileSystemRepresentation]); } #endif - + if (_request) { GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten); } else { diff --git a/GCDWebServer/Core/GCDWebServerFunctions.m b/GCDWebServer/Core/GCDWebServerFunctions.m index 25e41ca..bcfc102 100644 --- a/GCDWebServer/Core/GCDWebServerFunctions.m +++ b/GCDWebServer/Core/GCDWebServerFunctions.m @@ -163,8 +163,8 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) { static NSDictionary* _overrides = nil; if (_overrides == nil) { _overrides = [[NSDictionary alloc] initWithObjectsAndKeys: - @"text/css", @"css", - nil]; + @"text/css", @"css", + nil]; } NSString* mimeType = nil; extension = [extension lowercaseString]; @@ -205,13 +205,13 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { break; } [scanner setScanLocation:([scanner scanLocation] + 1)]; - + NSString* value = nil; [scanner scanUpToString:@"&" intoString:&value]; if (value == nil) { value = @""; } - + key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "]; NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil; value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; @@ -222,7 +222,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); GWS_DNOT_REACHED(); } - + if ([scanner isAtEnd]) { break; } @@ -267,9 +267,9 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { struct ifaddrs* list; if (getifaddrs(&list) >= 0) { for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { -#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV - // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator - // Assumption holds for Apple TV running tvOS +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV + // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator + // Assumption holds for Apple TV running tvOS if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) #else if (strcmp(ifap->ifa_name, primaryInterface)) diff --git a/GCDWebServer/Core/GCDWebServerPrivate.h b/GCDWebServer/Core/GCDWebServerPrivate.h index 8150d58..a7e5016 100644 --- a/GCDWebServer/Core/GCDWebServerPrivate.h +++ b/GCDWebServer/Core/GCDWebServerPrivate.h @@ -123,14 +123,29 @@ extern GCDWebServerLoggingLevel GCDWebServerLogLevel; extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); #if DEBUG -#define GWS_LOG_DEBUG(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); } while (0) +#define GWS_LOG_DEBUG(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \ + } while (0) #else #define GWS_LOG_DEBUG(...) #endif -#define GWS_LOG_VERBOSE(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); } while (0) -#define GWS_LOG_INFO(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); } while (0) -#define GWS_LOG_WARNING(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); } while (0) -#define GWS_LOG_ERROR(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); } while (0) +#define GWS_LOG_VERBOSE(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_INFO(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_WARNING(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_ERROR(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \ + } while (0) #endif @@ -143,10 +158,10 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for #if DEBUG #define GWS_DCHECK(__CONDITION__) \ - do { \ - if (!(__CONDITION__)) { \ - abort(); \ - } \ + do { \ + if (!(__CONDITION__)) { \ + abort(); \ + } \ } while (0) #define GWS_DNOT_REACHED() abort() @@ -171,7 +186,7 @@ static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { } static inline NSError* GCDWebServerMakePosixError(int code) { - return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:strerror(code)]}]; + return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithUTF8String:strerror(code)]}]; } extern void GCDWebServerInitializeFunctions(); @@ -181,7 +196,7 @@ extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSStr extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); extern BOOL GCDWebServerIsTextContentType(NSString* type); extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); -extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2); +extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); @interface GCDWebServerConnection () diff --git a/GCDWebServer/Core/GCDWebServerRequest.m b/GCDWebServer/Core/GCDWebServerRequest.m index 7542dc0..5365f59 100644 --- a/GCDWebServer/Core/GCDWebServerRequest.m +++ b/GCDWebServer/Core/GCDWebServerRequest.m @@ -159,7 +159,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque BOOL _gzipAccepted; NSData* _localAddress; NSData* _remoteAddress; - + BOOL _opened; NSMutableArray* _decoders; NSMutableDictionary* _attributes; @@ -169,8 +169,8 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque @implementation GCDWebServerRequest : NSObject -@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, ifModifiedSince=_modifiedSince, ifNoneMatch=_noneMatch, - byteRange=_range, acceptsGzipContentEncoding=_gzipAccepted, usesChunkedTransferEncoding=_chunked, localAddressData=_localAddress, remoteAddressData=_remoteAddress; +@synthesize method = _method, URL = _url, headers = _headers, path = _path, query = _query, contentType = _type, contentLength = _length, ifModifiedSince = _modifiedSince, ifNoneMatch = _noneMatch, + byteRange = _range, acceptsGzipContentEncoding = _gzipAccepted, usesChunkedTransferEncoding = _chunked, localAddressData = _localAddress, remoteAddressData = _remoteAddress; - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { if ((self = [super init])) { @@ -179,7 +179,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque _headers = headers; _path = [path copy]; _query = query; - + _type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); _chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; @@ -206,13 +206,13 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque } _length = NSUIntegerMax; } - + NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; if (modifiedHeader) { _modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; } _noneMatch = [_headers objectForKey:@"If-None-Match"]; - + _range = NSMakeRange(NSUIntegerMax, 0); NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); if (rangeHeader) { @@ -242,11 +242,11 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url); } } - + if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) { _gzipAccepted = YES; } - + _decoders = [[NSMutableArray alloc] init]; _attributes = [[NSMutableDictionary alloc] init]; } diff --git a/GCDWebServer/Core/GCDWebServerResponse.m b/GCDWebServer/Core/GCDWebServerResponse.m index a5dc537..b5eb57c 100644 --- a/GCDWebServer/Core/GCDWebServerResponse.m +++ b/GCDWebServer/Core/GCDWebServerResponse.m @@ -168,7 +168,7 @@ NSMutableDictionary* _headers; BOOL _chunked; BOOL _gzipped; - + BOOL _opened; NSMutableArray* _encoders; id __unsafe_unretained _reader; @@ -177,8 +177,8 @@ @implementation GCDWebServerResponse -@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, lastModifiedDate=_lastModified, eTag=_eTag, - gzipContentEncodingEnabled=_gzipped, additionalHeaders=_headers; +@synthesize contentType = _type, contentLength = _length, statusCode = _status, cacheControlMaxAge = _maxAge, lastModifiedDate = _lastModified, eTag = _eTag, + gzipContentEncodingEnabled = _gzipped, additionalHeaders = _headers; + (instancetype)response { return [[[self class] alloc] init]; diff --git a/GCDWebServer/Requests/GCDWebServerDataRequest.m b/GCDWebServer/Requests/GCDWebServerDataRequest.m index 840e985..861ab52 100644 --- a/GCDWebServer/Requests/GCDWebServerDataRequest.m +++ b/GCDWebServer/Requests/GCDWebServerDataRequest.m @@ -34,7 +34,7 @@ @interface GCDWebServerDataRequest () { @private NSMutableData* _data; - + NSString* _text; id _jsonObject; } @@ -42,7 +42,7 @@ @implementation GCDWebServerDataRequest -@synthesize data=_data; +@synthesize data = _data; - (BOOL)open:(NSError**)error { if (self.contentLength != NSUIntegerMax) { @@ -52,7 +52,7 @@ } if (_data == nil) { if (error) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed allocating memory"}]; + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }]; } return NO; } diff --git a/GCDWebServer/Requests/GCDWebServerFileRequest.m b/GCDWebServer/Requests/GCDWebServerFileRequest.m index adf67a5..0309d64 100644 --- a/GCDWebServer/Requests/GCDWebServerFileRequest.m +++ b/GCDWebServer/Requests/GCDWebServerFileRequest.m @@ -40,7 +40,7 @@ @implementation GCDWebServerFileRequest -@synthesize temporaryPath=_temporaryPath; +@synthesize temporaryPath = _temporaryPath; - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { @@ -85,14 +85,14 @@ NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; if (creationDateHeader) { NSDate* date = GCDWebServerParseISO8601(creationDateHeader); - if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate: date} ofItemAtPath:_temporaryPath error:error]) { + if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileCreationDate : date } ofItemAtPath:_temporaryPath error:error]) { return NO; } } NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; if (modifiedDateHeader) { NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); - if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: date} ofItemAtPath:_temporaryPath error:error]) { + if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileModificationDate : date } ofItemAtPath:_temporaryPath error:error]) { return NO; } } diff --git a/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m b/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m index c2fc9bf..c6cb485 100644 --- a/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m +++ b/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m @@ -61,7 +61,7 @@ static NSData* _dashNewlineData = nil; @implementation GCDWebServerMultiPart -@synthesize controlName=_controlName, contentType=_contentType, mimeType=_mimeType; +@synthesize controlName = _controlName, contentType = _contentType, mimeType = _mimeType; - (id)initWithControlName:(NSString*)name contentType:(NSString*)type { if ((self = [super init])) { @@ -83,12 +83,12 @@ static NSData* _dashNewlineData = nil; @implementation GCDWebServerMultiPartArgument -@synthesize data=_data, string=_string; +@synthesize data = _data, string = _string; - (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data { if ((self = [super initWithControlName:name contentType:type])) { _data = data; - + if ([self.contentType hasPrefix:@"text/"]) { NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); _string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)]; @@ -112,7 +112,7 @@ static NSData* _dashNewlineData = nil; @implementation GCDWebServerMultiPartFile -@synthesize fileName=_fileName, temporaryPath=_temporaryPath; +@synthesize fileName = _fileName, temporaryPath = _temporaryPath; - (id)initWithControlName:(NSString*)name contentType:(NSString*)type fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath { if ((self = [super initWithControlName:name contentType:type])) { @@ -140,7 +140,7 @@ static NSData* _dashNewlineData = nil; NSMutableData* _data; NSMutableArray* _arguments; NSMutableArray* _files; - + NSString* _controlName; NSString* _fileName; NSString* _contentType; @@ -194,11 +194,10 @@ static NSData* _dashNewlineData = nil; // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 - (BOOL)_parseData { BOOL success = YES; - + if (_state == kParserState_Headers) { NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)]; if (range.location != NSNotFound) { - _controlName = nil; _fileName = nil; _contentType = nil; @@ -256,12 +255,12 @@ static NSData* _dashNewlineData = nil; GWS_DNOT_REACHED(); success = NO; } - + [_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0]; _state = kParserState_Content; } } - + if ((_state == kParserState_Start) || (_state == kParserState_Content)) { NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)]; if (range.location != NSNotFound) { @@ -269,7 +268,6 @@ static NSData* _dashNewlineData = nil; NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange]; NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange]; if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) { - if (_state == kParserState_Content) { const void* dataBytes = _data.bytes; NSUInteger dataLength = range.location - 2; @@ -301,7 +299,7 @@ static NSData* _dashNewlineData = nil; [_arguments addObject:argument]; } } - + if (subRange1.location != NSNotFound) { [_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0]; _state = kParserState_Headers; @@ -333,7 +331,7 @@ static NSData* _dashNewlineData = nil; } } } - + return success; } @@ -358,7 +356,7 @@ static NSData* _dashNewlineData = nil; @implementation GCDWebServerMultiPartFormRequest -@synthesize arguments=_arguments, files=_files; +@synthesize arguments = _arguments, files = _files; + (NSString*)mimeType { return @"multipart/form-data"; @@ -377,7 +375,7 @@ static NSData* _dashNewlineData = nil; _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files]; if (_parser == nil) { if (error) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}]; + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data" }]; } return NO; } @@ -387,7 +385,7 @@ static NSData* _dashNewlineData = nil; - (BOOL)writeData:(NSData*)data error:(NSError**)error { if (![_parser appendBytes:data.bytes length:data.length]) { if (error) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}]; + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data" }]; } return NO; } @@ -399,7 +397,7 @@ static NSData* _dashNewlineData = nil; _parser = nil; if (!atEnd) { if (error) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}]; + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data" }]; } return NO; } diff --git a/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m b/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m index 2c5fcc5..328fd75 100644 --- a/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m +++ b/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m @@ -39,7 +39,7 @@ @implementation GCDWebServerURLEncodedFormRequest -@synthesize arguments=_arguments; +@synthesize arguments = _arguments; + (NSString*)mimeType { return @"application/x-www-form-urlencoded"; @@ -49,12 +49,12 @@ if (![super close:error]) { return NO; } - + NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; _arguments = GCDWebServerParseURLEncodedForm(string); GWS_DCHECK(_arguments); - + return YES; } diff --git a/GCDWebServer/Responses/GCDWebServerDataResponse.m b/GCDWebServer/Responses/GCDWebServerDataResponse.m index 12cd12b..3bffb25 100644 --- a/GCDWebServer/Responses/GCDWebServerDataResponse.m +++ b/GCDWebServer/Responses/GCDWebServerDataResponse.m @@ -49,10 +49,10 @@ GWS_DNOT_REACHED(); return nil; } - + if ((self = [super init])) { _data = data; - + self.contentType = type; self.contentLength = data.length; } diff --git a/GCDWebServer/Responses/GCDWebServerErrorResponse.h b/GCDWebServer/Responses/GCDWebServerErrorResponse.h index dad0114..f80c7ff 100644 --- a/GCDWebServer/Responses/GCDWebServerErrorResponse.h +++ b/GCDWebServer/Responses/GCDWebServerErrorResponse.h @@ -37,45 +37,45 @@ /** * Creates a client error response with the corresponding HTTP status code. */ -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); /** * Creates a server error response with the corresponding HTTP status code. */ -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); /** * Creates a client error response with the corresponding HTTP status code * and an underlying NSError. */ -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); /** * Creates a server error response with the corresponding HTTP status code * and an underlying NSError. */ -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); /** * Initializes a client error response with the corresponding HTTP status code. */ -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); /** * Initializes a server error response with the corresponding HTTP status code. */ -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); /** * Initializes a client error response with the corresponding HTTP status code * and an underlying NSError. */ -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); /** * Initializes a server error response with the corresponding HTTP status code * and an underlying NSError. */ -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); @end diff --git a/GCDWebServer/Responses/GCDWebServerFileResponse.m b/GCDWebServer/Responses/GCDWebServerFileResponse.m index a2b7c3c..3e717a3 100644 --- a/GCDWebServer/Responses/GCDWebServerFileResponse.m +++ b/GCDWebServer/Responses/GCDWebServerFileResponse.m @@ -91,7 +91,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { } #endif NSUInteger fileSize = (NSUInteger)info.st_size; - + BOOL hasByteRange = GCDWebServerIsValidByteRange(range); if (hasByteRange) { if (range.location != NSUIntegerMax) { @@ -108,7 +108,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { range.location = 0; range.length = fileSize; } - + if ((self = [super init])) { _path = [path copy]; _offset = range.location; @@ -118,7 +118,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); } - + if (attachment) { NSString* fileName = [path lastPathComponent]; NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; @@ -130,7 +130,7 @@ static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { GWS_DNOT_REACHED(); } } - + self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension]); self.contentLength = _size; self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); diff --git a/GCDWebServer/Responses/GCDWebServerStreamedResponse.m b/GCDWebServer/Responses/GCDWebServerStreamedResponse.m index 4669617..2ed01c1 100644 --- a/GCDWebServer/Responses/GCDWebServerStreamedResponse.m +++ b/GCDWebServer/Responses/GCDWebServerStreamedResponse.m @@ -48,19 +48,20 @@ } - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { - return [self initWithContentType:type asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { - - NSError* error = nil; - NSData* data = block(&error); - completionBlock(data, error); - - }]; + return [self initWithContentType:type + asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + NSError* error = nil; + NSData* data = block(&error); + completionBlock(data, error); + + }]; } - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { if ((self = [super init])) { _block = [block copy]; - + self.contentType = type; } return self; diff --git a/GCDWebUploader/GCDWebUploader.m b/GCDWebUploader/GCDWebUploader.m index 9bb4453..e6dc497 100644 --- a/GCDWebUploader/GCDWebUploader.m +++ b/GCDWebUploader/GCDWebUploader.m @@ -73,7 +73,7 @@ return YES; } -- (NSString*) _uniquePathForPath:(NSString*)path { +- (NSString*)_uniquePathForPath:(NSString*)path { if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { NSString* directory = [path stringByDeletingLastPathComponent]; NSString* file = [path lastPathComponent]; @@ -101,18 +101,18 @@ if (!isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is not a directory", relativePath]; } - + NSString* directoryName = [absolutePath lastPathComponent]; if (!_allowHidden && [directoryName hasPrefix:@"."]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Listing directory name \"%@\" is not allowed", directoryName]; } - + NSError* error = nil; NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error]; if (contents == nil) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath]; } - + NSMutableArray* array = [NSMutableArray array]; for (NSString* item in [contents sortedArrayUsingSelector:@selector(localizedStandardCompare:)]) { if (_allowHidden || ![item hasPrefix:@"."]) { @@ -120,15 +120,15 @@ NSString* type = [attributes objectForKey:NSFileType]; if ([type isEqualToString:NSFileTypeRegular] && [self _checkFileExtension:item]) { [array addObject:@{ - @"path": [relativePath stringByAppendingPathComponent:item], - @"name": item, - @"size": [attributes objectForKey:NSFileSize] - }]; + @"path" : [relativePath stringByAppendingPathComponent:item], + @"name" : item, + @"size" : [attributes objectForKey:NSFileSize] + }]; } else if ([type isEqualToString:NSFileTypeDirectory]) { [array addObject:@{ - @"path": [[relativePath stringByAppendingPathComponent:item] stringByAppendingString:@"/"], - @"name": item - }]; + @"path" : [[relativePath stringByAppendingPathComponent:item] stringByAppendingString:@"/"], + @"name" : item + }]; } } } @@ -145,13 +145,13 @@ if (isDirectory) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is a directory", relativePath]; } - + NSString* fileName = [absolutePath lastPathComponent]; if (([fileName hasPrefix:@"."] && !_allowHidden) || ![self _checkFileExtension:fileName]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", fileName]; } - - if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath: )]) { + + if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate webUploader:self didDownloadFileAtPath:absolutePath]; }); @@ -162,7 +162,7 @@ - (GCDWebServerResponse*)uploadFile:(GCDWebServerMultiPartFormRequest*)request { NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch]; NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup) - + GCDWebServerMultiPartFile* file = [request firstFileForControlName:@"files[]"]; if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName]; @@ -172,16 +172,16 @@ if (![self _checkSandboxedPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] moveItemAtPath:file.temporaryPath toPath:absolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath]; } - + if ([self.delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate webUploader:self didUploadFileAtPath:absolutePath]; @@ -197,27 +197,27 @@ if (![self _checkSandboxedPath:oldAbsolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath]; } - + NSString* newRelativePath = [request.arguments objectForKey:@"newPath"]; NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:newRelativePath]]; if (![self _checkSandboxedPath:newAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", newRelativePath]; } - + NSString* itemName = [newAbsolutePath lastPathComponent]; if ((!_allowHidden && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName]; } - + if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", oldRelativePath, newRelativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] moveItemAtPath:oldAbsolutePath toPath:newAbsolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath]; } - + if ([self.delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]; @@ -233,21 +233,21 @@ if (![self _checkSandboxedPath:absolutePath] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* itemName = [absolutePath lastPathComponent]; if (([itemName hasPrefix:@"."] && !_allowHidden) || (!isDirectory && ![self _checkFileExtension:itemName])) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName]; } - + if (![self shouldDeleteItemAtPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath]; } - + if ([self.delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate webUploader:self didDeleteItemAtPath:absolutePath]; @@ -262,21 +262,21 @@ if (![self _checkSandboxedPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath]; } - + NSString* directoryName = [absolutePath lastPathComponent]; if (!_allowHidden && [directoryName hasPrefix:@"."]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName]; } - + if (![self shouldCreateDirectoryAtPath:absolutePath]) { return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath]; } - + NSError* error = nil; if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) { return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath]; } - + if ([self.delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath]; @@ -289,8 +289,8 @@ @implementation GCDWebUploader -@synthesize uploadDirectory=_uploadDirectory, allowedFileExtensions=_allowedExtensions, allowHiddenItems=_allowHidden, - title=_title, header=_header, prologue=_prologue, epilogue=_epilogue, footer=_footer; +@synthesize uploadDirectory = _uploadDirectory, allowedFileExtensions = _allowedExtensions, allowHiddenItems = _allowHidden, + title = _title, header = _header, prologue = _prologue, epilogue = _epilogue, footer = _footer; @dynamic delegate; @@ -302,96 +302,116 @@ } _uploadDirectory = [[path stringByStandardizingPath] copy]; GCDWebUploader* __unsafe_unretained server = self; - + // Resource files [self addGETHandlerForBasePath:@"/" directoryPath:[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO]; - + // Web page - [self addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - + [self addHandlerForMethod:@"GET" + path:@"/" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + #if TARGET_OS_IPHONE - NSString* device = [[UIDevice currentDevice] name]; + NSString* device = [[UIDevice currentDevice] name]; #else - NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL)); + NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL)); #endif - NSString* title = server.title; - if (title == nil) { - title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; - if (title == nil) { - title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; - } + NSString* title = server.title; + if (title == nil) { + title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + if (title == nil) { + title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; + } #if !TARGET_OS_IPHONE - if (title == nil) { - title = [[NSProcessInfo processInfo] processName]; - } + if (title == nil) { + title = [[NSProcessInfo processInfo] processName]; + } #endif - } - NSString* header = server.header; - if (header == nil) { - header = title; - } - NSString* prologue = server.prologue; - if (prologue == nil) { - prologue = [siteBundle localizedStringForKey:@"PROLOGUE" value:@"" table:nil]; - } - NSString* epilogue = server.epilogue; - if (epilogue == nil) { - epilogue = [siteBundle localizedStringForKey:@"EPILOGUE" value:@"" table:nil]; - } - NSString* footer = server.footer; - if (footer == nil) { - NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; - NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + } + NSString* header = server.header; + if (header == nil) { + header = title; + } + NSString* prologue = server.prologue; + if (prologue == nil) { + prologue = [siteBundle localizedStringForKey:@"PROLOGUE" value:@"" table:nil]; + } + NSString* epilogue = server.epilogue; + if (epilogue == nil) { + epilogue = [siteBundle localizedStringForKey:@"EPILOGUE" value:@"" table:nil]; + } + NSString* footer = server.footer; + if (footer == nil) { + NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; #if !TARGET_OS_IPHONE - if (!name && !version) { - name = @"OS X"; - version = [[NSProcessInfo processInfo] operatingSystemVersionString]; - } + if (!name && !version) { + name = @"OS X"; + version = [[NSProcessInfo processInfo] operatingSystemVersionString]; + } #endif - footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version]; - } - return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"] - variables:@{ - @"device": device, - @"title": title, - @"header": header, - @"prologue": prologue, - @"epilogue": epilogue, - @"footer": footer - }]; - - }]; - + footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version]; + } + return [GCDWebServerDataResponse responseWithHTMLTemplate:[siteBundle pathForResource:@"index" ofType:@"html"] + variables:@{ + @"device" : device, + @"title" : title, + @"header" : header, + @"prologue" : prologue, + @"epilogue" : epilogue, + @"footer" : footer + }]; + + }]; + // File listing - [self addHandlerForMethod:@"GET" path:@"/list" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server listDirectory:request]; - }]; - + [self addHandlerForMethod:@"GET" + path:@"/list" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server listDirectory:request]; + }]; + // File download - [self addHandlerForMethod:@"GET" path:@"/download" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server downloadFile:request]; - }]; - + [self addHandlerForMethod:@"GET" + path:@"/download" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server downloadFile:request]; + }]; + // File upload - [self addHandlerForMethod:@"POST" path:@"/upload" requestClass:[GCDWebServerMultiPartFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server uploadFile:(GCDWebServerMultiPartFormRequest*)request]; - }]; - + [self addHandlerForMethod:@"POST" + path:@"/upload" + requestClass:[GCDWebServerMultiPartFormRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server uploadFile:(GCDWebServerMultiPartFormRequest*)request]; + }]; + // File and folder moving - [self addHandlerForMethod:@"POST" path:@"/move" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server moveItem:(GCDWebServerURLEncodedFormRequest*)request]; - }]; - + [self addHandlerForMethod:@"POST" + path:@"/move" + requestClass:[GCDWebServerURLEncodedFormRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server moveItem:(GCDWebServerURLEncodedFormRequest*)request]; + }]; + // File and folder deletion - [self addHandlerForMethod:@"POST" path:@"/delete" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server deleteItem:(GCDWebServerURLEncodedFormRequest*)request]; - }]; - + [self addHandlerForMethod:@"POST" + path:@"/delete" + requestClass:[GCDWebServerURLEncodedFormRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server deleteItem:(GCDWebServerURLEncodedFormRequest*)request]; + }]; + // Directory creation - [self addHandlerForMethod:@"POST" path:@"/create" requestClass:[GCDWebServerURLEncodedFormRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - return [server createDirectory:(GCDWebServerURLEncodedFormRequest*)request]; - }]; - + [self addHandlerForMethod:@"POST" + path:@"/create" + requestClass:[GCDWebServerURLEncodedFormRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + return [server createDirectory:(GCDWebServerURLEncodedFormRequest*)request]; + }]; } return self; } diff --git a/Mac/main.m b/Mac/main.m index 33b25e5..5d43831 100644 --- a/Mac/main.m +++ b/Mac/main.m @@ -147,7 +147,7 @@ int main(int argc, const char* argv[]) { NSString* authenticationPassword = nil; BOOL bindToLocalhost = NO; BOOL requestNATPortMapping = NO; - + if (argc == 1) { fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0])); } else { @@ -201,10 +201,9 @@ int main(int argc, const char* argv[]) { } } } - + GCDWebServer* webServer = nil; switch (mode) { - // Simply serve contents of home directory case kMode_WebServer: { fprintf(stdout, "Running in Web Server mode from \"%s\"", [rootDirectory UTF8String]); @@ -212,21 +211,21 @@ int main(int argc, const char* argv[]) { [webServer addGETHandlerForBasePath:@"/" directoryPath:rootDirectory indexFilename:nil cacheAge:0 allowRangeRequests:YES]; break; } - + // Renders a HTML page case kMode_HTMLPage: { fprintf(stdout, "Running in HTML Page mode"); webServer = [[GCDWebServer alloc] init]; [webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - return [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + return [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; + + }]; break; } - + // Implements an HTML form case kMode_HTMLForm: { fprintf(stdout, "Running in HTML Form mode"); @@ -234,9 +233,9 @@ int main(int argc, const char* argv[]) { [webServer addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - NSString* html = @" \ + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + NSString* html = @" \ \
\ Value: \ @@ -244,22 +243,22 @@ int main(int argc, const char* argv[]) {
\ \ "; - return [GCDWebServerDataResponse responseWithHTML:html]; - - }]; + return [GCDWebServerDataResponse responseWithHTML:html]; + + }]; [webServer addHandlerForMethod:@"POST" path:@"/" requestClass:[GCDWebServerURLEncodedFormRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"]; - NSString* html = [NSString stringWithFormat:@"

%@

", value]; - return [GCDWebServerDataResponse responseWithHTML:html]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"]; + NSString* html = [NSString stringWithFormat:@"

%@

", value]; + return [GCDWebServerDataResponse responseWithHTML:html]; + + }]; break; } - + // Implements HTML file upload case kMode_HTMLFileUpload: { fprintf(stdout, "Running in HTML File Upload mode"); @@ -274,48 +273,48 @@ int main(int argc, const char* argv[]) { [webServer addHandlerForMethod:@"GET" path:@"/" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - NSString* html = [NSString stringWithFormat:@"%@", formHTML]; - return [GCDWebServerDataResponse responseWithHTML:html]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + NSString* html = [NSString stringWithFormat:@"%@", formHTML]; + return [GCDWebServerDataResponse responseWithHTML:html]; + + }]; [webServer addHandlerForMethod:@"POST" path:@"/" requestClass:[GCDWebServerMultiPartFormRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - NSMutableString* string = [NSMutableString string]; - for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) { - [string appendFormat:@"%@ = %@
", argument.controlName, argument.string]; - } - for (GCDWebServerMultiPartFile* file in [(GCDWebServerMultiPartFormRequest*)request files]) { - NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:file.temporaryPath error:NULL]; - [string appendFormat:@"%@ = "%@" (%@ | %llu %@)
", file.controlName, file.fileName, file.mimeType, - attributes.fileSize >= 1000 ? attributes.fileSize / 1000 : attributes.fileSize, - attributes.fileSize >= 1000 ? @"KB" : @"Bytes"]; - }; - NSString* html = [NSString stringWithFormat:@"

%@


%@", string, formHTML]; - return [GCDWebServerDataResponse responseWithHTML:html]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + NSMutableString* string = [NSMutableString string]; + for (GCDWebServerMultiPartArgument* argument in [(GCDWebServerMultiPartFormRequest*)request arguments]) { + [string appendFormat:@"%@ = %@
", argument.controlName, argument.string]; + } + for (GCDWebServerMultiPartFile* file in [(GCDWebServerMultiPartFormRequest*)request files]) { + NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:file.temporaryPath error:NULL]; + [string appendFormat:@"%@ = "%@" (%@ | %llu %@)
", file.controlName, file.fileName, file.mimeType, + attributes.fileSize >= 1000 ? attributes.fileSize / 1000 : attributes.fileSize, + attributes.fileSize >= 1000 ? @"KB" : @"Bytes"]; + }; + NSString* html = [NSString stringWithFormat:@"

%@


%@", string, formHTML]; + return [GCDWebServerDataResponse responseWithHTML:html]; + + }]; break; } - + // Serve home directory through WebDAV case kMode_WebDAV: { fprintf(stdout, "Running in WebDAV mode from \"%s\"", [rootDirectory UTF8String]); webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:rootDirectory]; break; } - + // Serve home directory through web uploader case kMode_WebUploader: { fprintf(stdout, "Running in Web Uploader mode from \"%s\"", [rootDirectory UTF8String]); webServer = [[GCDWebUploader alloc] initWithUploadDirectory:rootDirectory]; break; } - + // Test streaming responses case kMode_StreamingResponse: { fprintf(stdout, "Running in Streaming Response mode"); @@ -323,42 +322,44 @@ int main(int argc, const char* argv[]) { [webServer addHandlerForMethod:@"GET" path:@"/sync" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - __block int countDown = 10; - return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" streamBlock:^NSData *(NSError** error) { - - usleep(100 * 1000); - if (countDown) { - return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding]; - } else { - return [NSData data]; - } - - }]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + __block int countDown = 10; + return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" + streamBlock:^NSData*(NSError** error) { + + usleep(100 * 1000); + if (countDown) { + return [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding]; + } else { + return [NSData data]; + } + + }]; + + }]; [webServer addHandlerForMethod:@"GET" path:@"/async" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { - - __block int countDown = 10; - return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; - completionBlock(data, nil); - - }); - - }]; - - }]; + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + __block int countDown = 10; + return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" + asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; + completionBlock(data, nil); + + }); + + }]; + + }]; break; } - + // Test async responses case kMode_AsyncResponse: { fprintf(stdout, "Running in Async Response mode"); @@ -367,41 +368,41 @@ int main(int argc, const char* argv[]) { path:@"/async" requestClass:[GCDWebServerRequest class] asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"]; - completionBlock(response); - }); - - }]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"]; + completionBlock(response); + }); + + }]; [webServer addHandlerForMethod:@"GET" path:@"/async2" requestClass:[GCDWebServerRequest class] asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock handlerCompletionBlock) { - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - __block int countDown = 10; - GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) { - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; - readerCompletionBlock(data, nil); - - }); - - }]; - handlerCompletionBlock(response); - - }); - - }]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + __block int countDown = 10; + GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" + asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock readerCompletionBlock) { + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; + readerCompletionBlock(data, nil); + + }); + + }]; + handlerCompletionBlock(response); + + }); + + }]; break; } - } - + if (webServer) { Delegate* delegate = [[Delegate alloc] init]; if (testDirectory) { @@ -409,7 +410,7 @@ int main(int argc, const char* argv[]) { webServer.delegate = delegate; #endif fprintf(stdout, "\n\n", [testDirectory UTF8String]); - result = (int)[webServer runTestsWithOptions:@{GCDWebServerOption_Port: @8080} inDirectory:testDirectory]; + result = (int)[webServer runTestsWithOptions:@{ GCDWebServerOption_Port : @8080 } inDirectory:testDirectory]; } else { webServer.delegate = delegate; if (recording) { @@ -424,7 +425,7 @@ int main(int argc, const char* argv[]) { [options setObject:@"" forKey:GCDWebServerOption_BonjourName]; if (authenticationUser && authenticationPassword) { [options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm]; - [options setObject:@{authenticationUser: authenticationPassword} forKey:GCDWebServerOption_AuthenticationAccounts]; + [options setObject:@{ authenticationUser : authenticationPassword } forKey:GCDWebServerOption_AuthenticationAccounts]; if ([authenticationMethod isEqualToString:@"Basic"]) { [options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod]; } else if ([authenticationMethod isEqualToString:@"Digest"]) { diff --git a/format-source.sh b/format-source.sh new file mode 100755 index 0000000..cd7c504 --- /dev/null +++ b/format-source.sh @@ -0,0 +1,40 @@ +#!/bin/sh -ex + +# brew install clang-format + +CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'` +if [[ "$CLANG_FORMAT_VERSION" != "4.0.0" ]]; then + echo "Unsupported clang-format version" + exit 1 +fi + +pushd "GCDWebServer/Core" +clang-format -style=file -i *.h *.m +popd +pushd "GCDWebServer/Requests" +clang-format -style=file -i *.h *.m +popd +pushd "GCDWebServer/Responses" +clang-format -style=file -i *.h *.m +popd +pushd "GCDWebUploader" +clang-format -style=file -i *.h *.m +popd +pushd "GCDWebDAVServer" +clang-format -style=file -i *.h *.m +popd + +pushd "Frameworks" +clang-format -style=file -i *.h *.m +popd +pushd "Mac" +clang-format -style=file -i *.m +popd +pushd "iOS" +clang-format -style=file -i *.h *.m +popd +pushd "tvOS" +clang-format -style=file -i *.h *.m +popd + +echo "OK" diff --git a/iOS/ViewController.m b/iOS/ViewController.m index 2f7d79d..fbeae6b 100644 --- a/iOS/ViewController.m +++ b/iOS/ViewController.m @@ -39,7 +39,7 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - + NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; _webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; _webServer.delegate = self; @@ -53,7 +53,7 @@ - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - + [_webServer stop]; _webServer = nil; } diff --git a/tvOS/ViewController.m b/tvOS/ViewController.m index 2f7d79d..fbeae6b 100644 --- a/tvOS/ViewController.m +++ b/tvOS/ViewController.m @@ -39,7 +39,7 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - + NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; _webServer = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; _webServer.delegate = self; @@ -53,7 +53,7 @@ - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - + [_webServer stop]; _webServer = nil; }