#import "DAVConnection.h" #import "HTTPMessage.h" #import "HTTPFileResponse.h" #import "HTTPAsyncFileResponse.h" #import "PUTResponse.h" #import "DELETEResponse.h" #import "DAVResponse.h" #import "HTTPLogging.h" #define HTTP_BODY_MAX_MEMORY_SIZE (1024 * 1024) #define HTTP_ASYNC_FILE_RESPONSE_THRESHOLD (16 * 1024 * 1024) static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; @implementation DAVConnection - (void) dealloc { [requestContentStream close]; } - (BOOL) supportsMethod:(NSString*)method atPath:(NSString*)path { // HTTPFileResponse & HTTPAsyncFileResponse if ([method isEqualToString:@"GET"]) return YES; if ([method isEqualToString:@"HEAD"]) return YES; // PUTResponse if ([method isEqualToString:@"PUT"]) return YES; // DELETEResponse if ([method isEqualToString:@"DELETE"]) return YES; // DAVResponse if ([method isEqualToString:@"OPTIONS"]) return YES; if ([method isEqualToString:@"PROPFIND"]) return YES; if ([method isEqualToString:@"MKCOL"]) return YES; if ([method isEqualToString:@"MOVE"]) return YES; if ([method isEqualToString:@"COPY"]) return YES; if ([method isEqualToString:@"LOCK"]) return YES; if ([method isEqualToString:@"UNLOCK"]) return YES; return NO; } - (BOOL) expectsRequestBodyFromMethod:(NSString*)method atPath:(NSString*)path { // PUTResponse if ([method isEqualToString:@"PUT"]) { return YES; } // DAVResponse if ([method isEqual:@"PROPFIND"] || [method isEqual:@"MKCOL"]) { return [request headerField:@"Content-Length"] ? YES : NO; } if ([method isEqual:@"LOCK"]) { return YES; } return NO; } - (void) prepareForBodyWithSize:(UInt64)contentLength { NSAssert(requestContentStream == nil, @"requestContentStream should be nil"); NSAssert(requestContentBody == nil, @"requestContentBody should be nil"); if (contentLength > HTTP_BODY_MAX_MEMORY_SIZE) { requestContentBody = [[NSTemporaryDirectory() stringByAppendingString:[[NSProcessInfo processInfo] globallyUniqueString]] copy]; requestContentStream = [[NSOutputStream alloc] initToFileAtPath:requestContentBody append:NO]; [requestContentStream open]; } else { requestContentBody = [[NSMutableData alloc] initWithCapacity:(NSUInteger)contentLength]; requestContentStream = nil; } } - (void) processBodyData:(NSData*)postDataChunk { NSAssert(requestContentBody != nil, @"requestContentBody should not be nil"); if (requestContentStream) { [requestContentStream write:[postDataChunk bytes] maxLength:[postDataChunk length]]; } else { [(NSMutableData*)requestContentBody appendData:postDataChunk]; } } - (void) finishBody { NSAssert(requestContentBody != nil, @"requestContentBody should not be nil"); if (requestContentStream) { [requestContentStream close]; requestContentStream = nil; } } - (void)finishResponse { NSAssert(requestContentStream == nil, @"requestContentStream should be nil"); requestContentBody = nil; [super finishResponse]; } - (NSObject*) httpResponseForMethod:(NSString*)method URI:(NSString*)path { if ([method isEqualToString:@"HEAD"] || [method isEqualToString:@"GET"]) { NSString* filePath = [self filePathForURI:path allowDirectory:NO]; if (filePath) { NSDictionary* fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL]; if (fileAttributes) { if ([[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue] > HTTP_ASYNC_FILE_RESPONSE_THRESHOLD) { return [[HTTPAsyncFileResponse alloc] initWithFilePath:filePath forConnection:self]; } else { return [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self]; } } } } if ([method isEqualToString:@"PUT"]) { NSString* filePath = [self filePathForURI:path allowDirectory:YES]; if (filePath) { if ([requestContentBody isKindOfClass:[NSString class]]) { return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyFile:requestContentBody]; } else if ([requestContentBody isKindOfClass:[NSData class]]) { return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyData:requestContentBody]; } else { HTTPLogError(@"Internal error"); } } } if ([method isEqualToString:@"DELETE"]) { NSString* filePath = [self filePathForURI:path allowDirectory:YES]; if (filePath) { return [[DELETEResponse alloc] initWithFilePath:filePath]; } } if ([method isEqualToString:@"OPTIONS"] || [method isEqualToString:@"PROPFIND"] || [method isEqualToString:@"MKCOL"] || [method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"] || [method isEqualToString:@"LOCK"] || [method isEqualToString:@"UNLOCK"]) { NSString* filePath = [self filePathForURI:path allowDirectory:YES]; if (filePath) { NSString* rootPath = [config documentRoot]; NSString* resourcePath = [filePath substringFromIndex:([rootPath length] + 1)]; if (requestContentBody) { if ([requestContentBody isKindOfClass:[NSString class]]) { requestContentBody = [NSData dataWithContentsOfFile:requestContentBody]; } else if (![requestContentBody isKindOfClass:[NSData class]]) { HTTPLogError(@"Internal error"); return nil; } } return [[DAVResponse alloc] initWithMethod:method headers:[request allHeaderFields] bodyData:requestContentBody resourcePath:resourcePath rootPath:rootPath]; } } return nil; } @end