HTTPFileResponse.m 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #import "HTTPFileResponse.h"
  2. #import "HTTPConnection.h"
  3. #import "HTTPLogging.h"
  4. #import <unistd.h>
  5. #import <fcntl.h>
  6. #if ! __has_feature(objc_arc)
  7. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  8. #endif
  9. // Log levels : off, error, warn, info, verbose
  10. // Other flags: trace
  11. static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
  12. #define NULL_FD -1
  13. @implementation HTTPFileResponse
  14. - (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent
  15. {
  16. if((self = [super init]))
  17. {
  18. HTTPLogTrace();
  19. connection = parent; // Parents retain children, children do NOT retain parents
  20. fileFD = NULL_FD;
  21. filePath = [[fpath copy] stringByResolvingSymlinksInPath];
  22. if (filePath == nil)
  23. {
  24. HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE);
  25. return nil;
  26. }
  27. NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
  28. if (fileAttributes == nil)
  29. {
  30. HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath);
  31. return nil;
  32. }
  33. fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
  34. fileOffset = 0;
  35. aborted = NO;
  36. // We don't bother opening the file here.
  37. // If this is a HEAD request we only need to know the fileLength.
  38. }
  39. return self;
  40. }
  41. - (void)abort
  42. {
  43. HTTPLogTrace();
  44. [connection responseDidAbort:self];
  45. aborted = YES;
  46. }
  47. - (BOOL)openFile
  48. {
  49. HTTPLogTrace();
  50. fileFD = open([filePath UTF8String], O_RDONLY);
  51. if (fileFD == NULL_FD)
  52. {
  53. HTTPLogError(@"%@[%p]: Unable to open file. filePath: %@", THIS_FILE, self, filePath);
  54. [self abort];
  55. return NO;
  56. }
  57. HTTPLogVerbose(@"%@[%p]: Open fd[%i] -> %@", THIS_FILE, self, fileFD, filePath);
  58. return YES;
  59. }
  60. - (BOOL)openFileIfNeeded
  61. {
  62. if (aborted)
  63. {
  64. // The file operation has been aborted.
  65. // This could be because we failed to open the file,
  66. // or the reading process failed.
  67. return NO;
  68. }
  69. if (fileFD != NULL_FD)
  70. {
  71. // File has already been opened.
  72. return YES;
  73. }
  74. return [self openFile];
  75. }
  76. - (UInt64)contentLength
  77. {
  78. HTTPLogTrace();
  79. return fileLength;
  80. }
  81. - (UInt64)offset
  82. {
  83. HTTPLogTrace();
  84. return fileOffset;
  85. }
  86. - (void)setOffset:(UInt64)offset
  87. {
  88. HTTPLogTrace2(@"%@[%p]: setOffset:%llu", THIS_FILE, self, offset);
  89. if (![self openFileIfNeeded])
  90. {
  91. // File opening failed,
  92. // or response has been aborted due to another error.
  93. return;
  94. }
  95. fileOffset = offset;
  96. off_t result = lseek(fileFD, (off_t)offset, SEEK_SET);
  97. if (result == -1)
  98. {
  99. HTTPLogError(@"%@[%p]: lseek failed - errno(%i) filePath(%@)", THIS_FILE, self, errno, filePath);
  100. [self abort];
  101. }
  102. }
  103. - (NSData *)readDataOfLength:(NSUInteger)length
  104. {
  105. HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length);
  106. if (![self openFileIfNeeded])
  107. {
  108. // File opening failed,
  109. // or response has been aborted due to another error.
  110. return nil;
  111. }
  112. // Determine how much data we should read.
  113. //
  114. // It is OK if we ask to read more bytes than exist in the file.
  115. // It is NOT OK to over-allocate the buffer.
  116. UInt64 bytesLeftInFile = fileLength - fileOffset;
  117. NSUInteger bytesToRead = (NSUInteger)MIN(length, bytesLeftInFile);
  118. // Make sure buffer is big enough for read request.
  119. // Do not over-allocate.
  120. if (buffer == NULL || bufferSize < bytesToRead)
  121. {
  122. bufferSize = bytesToRead;
  123. buffer = reallocf(buffer, (size_t)bufferSize);
  124. if (buffer == NULL)
  125. {
  126. HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self);
  127. [self abort];
  128. return nil;
  129. }
  130. }
  131. // Perform the read
  132. HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, (unsigned long)bytesToRead);
  133. ssize_t result = read(fileFD, buffer, bytesToRead);
  134. // Check the results
  135. if (result < 0)
  136. {
  137. HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath);
  138. [self abort];
  139. return nil;
  140. }
  141. else if (result == 0)
  142. {
  143. HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath);
  144. [self abort];
  145. return nil;
  146. }
  147. else // (result > 0)
  148. {
  149. HTTPLogVerbose(@"%@[%p]: Read %ld bytes from file", THIS_FILE, self, (long)result);
  150. fileOffset += result;
  151. return [NSData dataWithBytes:buffer length:result];
  152. }
  153. }
  154. - (BOOL)isDone
  155. {
  156. BOOL result = (fileOffset == fileLength);
  157. HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
  158. return result;
  159. }
  160. - (NSString *)filePath
  161. {
  162. return filePath;
  163. }
  164. - (void)dealloc
  165. {
  166. HTTPLogTrace();
  167. if (fileFD != NULL_FD)
  168. {
  169. HTTPLogVerbose(@"%@[%p]: Close fd[%i]", THIS_FILE, self, fileFD);
  170. close(fileFD);
  171. }
  172. if (buffer)
  173. free(buffer);
  174. }
  175. @end