| #import "HTTPFileResponse.h" | 
 | #import "HTTPConnection.h" | 
 | #import "HTTPLogging.h" | 
 |  | 
 | #import <unistd.h> | 
 | #import <fcntl.h> | 
 |  | 
 | #if ! __has_feature(objc_arc) | 
 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). | 
 | #endif | 
 |  | 
 | // Log levels : off, error, warn, info, verbose | 
 | // Other flags: trace | 
 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE; | 
 |  | 
 | #define NULL_FD  -1 | 
 |  | 
 |  | 
 | @implementation HTTPFileResponse | 
 |  | 
 | - (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent | 
 | { | 
 |     if((self = [super init])) | 
 |     { | 
 |         HTTPLogTrace(); | 
 |          | 
 |         connection = parent; // Parents retain children, children do NOT retain parents | 
 |          | 
 |         fileFD = NULL_FD; | 
 |         filePath = [[fpath copy] stringByResolvingSymlinksInPath]; | 
 |         if (filePath == nil) | 
 |         { | 
 |             HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE); | 
 |              | 
 |             return nil; | 
 |         } | 
 |          | 
 |         NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; | 
 |         if (fileAttributes == nil) | 
 |         { | 
 |             HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath); | 
 |              | 
 |             return nil; | 
 |         } | 
 |          | 
 |         fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue]; | 
 |         fileOffset = 0; | 
 |          | 
 |         aborted = NO; | 
 |          | 
 |         // We don't bother opening the file here. | 
 |         // If this is a HEAD request we only need to know the fileLength. | 
 |     } | 
 |     return self; | 
 | } | 
 |  | 
 | - (void)abort | 
 | { | 
 |     HTTPLogTrace(); | 
 |      | 
 |     [connection responseDidAbort:self]; | 
 |     aborted = YES; | 
 | } | 
 |  | 
 | - (BOOL)openFile | 
 | { | 
 |     HTTPLogTrace(); | 
 |      | 
 |     fileFD = open([filePath UTF8String], O_RDONLY); | 
 |     if (fileFD == NULL_FD) | 
 |     { | 
 |         HTTPLogError(@"%@[%p]: Unable to open file. filePath: %@", THIS_FILE, self, filePath); | 
 |          | 
 |         [self abort]; | 
 |         return NO; | 
 |     } | 
 |      | 
 |     HTTPLogVerbose(@"%@[%p]: Open fd[%i] -> %@", THIS_FILE, self, fileFD, filePath); | 
 |      | 
 |     return YES; | 
 | } | 
 |  | 
 | - (BOOL)openFileIfNeeded | 
 | { | 
 |     if (aborted) | 
 |     { | 
 |         // The file operation has been aborted. | 
 |         // This could be because we failed to open the file, | 
 |         // or the reading process failed. | 
 |         return NO; | 
 |     } | 
 |      | 
 |     if (fileFD != NULL_FD) | 
 |     { | 
 |         // File has already been opened. | 
 |         return YES; | 
 |     } | 
 |      | 
 |     return [self openFile]; | 
 | } | 
 |  | 
 | - (UInt64)contentLength | 
 | { | 
 |     HTTPLogTrace(); | 
 |      | 
 |     return fileLength; | 
 | } | 
 |  | 
 | - (UInt64)offset | 
 | { | 
 |     HTTPLogTrace(); | 
 |      | 
 |     return fileOffset; | 
 | } | 
 |  | 
 | - (void)setOffset:(UInt64)offset | 
 | { | 
 |     HTTPLogTrace2(@"%@[%p]: setOffset:%llu", THIS_FILE, self, offset); | 
 |      | 
 |     if (![self openFileIfNeeded]) | 
 |     { | 
 |         // File opening failed, | 
 |         // or response has been aborted due to another error. | 
 |         return; | 
 |     } | 
 |      | 
 |     fileOffset = offset; | 
 |      | 
 |     off_t result = lseek(fileFD, (off_t)offset, SEEK_SET); | 
 |     if (result == -1) | 
 |     { | 
 |         HTTPLogError(@"%@[%p]: lseek failed - errno(%i) filePath(%@)", THIS_FILE, self, errno, filePath); | 
 |          | 
 |         [self abort]; | 
 |     } | 
 | } | 
 |  | 
 | - (NSData *)readDataOfLength:(NSUInteger)length | 
 | { | 
 |     HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length); | 
 |      | 
 |     if (![self openFileIfNeeded]) | 
 |     { | 
 |         // File opening failed, | 
 |         // or response has been aborted due to another error. | 
 |         return nil; | 
 |     } | 
 |      | 
 |     // Determine how much data we should read. | 
 |     //  | 
 |     // It is OK if we ask to read more bytes than exist in the file. | 
 |     // It is NOT OK to over-allocate the buffer. | 
 |      | 
 |     UInt64 bytesLeftInFile = fileLength - fileOffset; | 
 |      | 
 |     NSUInteger bytesToRead = (NSUInteger)MIN(length, bytesLeftInFile); | 
 |      | 
 |     // Make sure buffer is big enough for read request. | 
 |     // Do not over-allocate. | 
 |      | 
 |     if (buffer == NULL || bufferSize < bytesToRead) | 
 |     { | 
 |         bufferSize = bytesToRead; | 
 |         buffer = reallocf(buffer, (size_t)bufferSize); | 
 |          | 
 |         if (buffer == NULL) | 
 |         { | 
 |             HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self); | 
 |              | 
 |             [self abort]; | 
 |             return nil; | 
 |         } | 
 |     } | 
 |      | 
 |     // Perform the read | 
 |      | 
 |     HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, (unsigned long)bytesToRead); | 
 |      | 
 |     ssize_t result = read(fileFD, buffer, bytesToRead); | 
 |      | 
 |     // Check the results | 
 |      | 
 |     if (result < 0) | 
 |     { | 
 |         HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath); | 
 |          | 
 |         [self abort]; | 
 |         return nil; | 
 |     } | 
 |     else if (result == 0) | 
 |     { | 
 |         HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath); | 
 |          | 
 |         [self abort]; | 
 |         return nil; | 
 |     } | 
 |     else // (result > 0) | 
 |     { | 
 |         HTTPLogVerbose(@"%@[%p]: Read %ld bytes from file", THIS_FILE, self, (long)result); | 
 |          | 
 |         fileOffset += result; | 
 |          | 
 |         return [NSData dataWithBytes:buffer length:result]; | 
 |     } | 
 | } | 
 |  | 
 | - (BOOL)isDone | 
 | { | 
 |     BOOL result = (fileOffset == fileLength); | 
 |      | 
 |     HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO")); | 
 |      | 
 |     return result; | 
 | } | 
 |  | 
 | - (NSString *)filePath | 
 | { | 
 |     return filePath; | 
 | } | 
 |  | 
 | - (void)dealloc | 
 | { | 
 |     HTTPLogTrace(); | 
 |      | 
 |     if (fileFD != NULL_FD) | 
 |     { | 
 |         HTTPLogVerbose(@"%@[%p]: Close fd[%i]", THIS_FILE, self, fileFD); | 
 |          | 
 |         close(fileFD); | 
 |     } | 
 |      | 
 |     if (buffer) | 
 |         free(buffer); | 
 |      | 
 | } | 
 |  | 
 | @end |