123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- #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
|