HTTPDynamicFileResponse.m 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #import "HTTPDynamicFileResponse.h"
  2. #import "HTTPConnection.h"
  3. #import "HTTPLogging.h"
  4. #if ! __has_feature(objc_arc)
  5. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  6. #endif
  7. // Log levels : off, error, warn, info, verbose
  8. // Other flags: trace
  9. static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
  10. #define NULL_FD -1
  11. @implementation HTTPDynamicFileResponse
  12. - (id)initWithFilePath:(NSString *)fpath
  13. forConnection:(HTTPConnection *)parent
  14. separator:(NSString *)separatorStr
  15. replacementDictionary:(NSDictionary *)dict
  16. {
  17. if ((self = [super initWithFilePath:fpath forConnection:parent]))
  18. {
  19. HTTPLogTrace();
  20. separator = [separatorStr dataUsingEncoding:NSUTF8StringEncoding];
  21. replacementDict = dict;
  22. }
  23. return self;
  24. }
  25. - (BOOL)isChunked
  26. {
  27. HTTPLogTrace();
  28. return YES;
  29. }
  30. - (UInt64)contentLength
  31. {
  32. // This method shouldn't be called since we're using a chunked response.
  33. // We override it just to be safe.
  34. HTTPLogTrace();
  35. return 0;
  36. }
  37. - (void)setOffset:(UInt64)offset
  38. {
  39. // This method shouldn't be called since we're using a chunked response.
  40. // We override it just to be safe.
  41. HTTPLogTrace();
  42. }
  43. - (BOOL)isDone
  44. {
  45. BOOL result = (readOffset == fileLength) && (readBufferOffset == 0);
  46. HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
  47. return result;
  48. }
  49. - (void)processReadBuffer
  50. {
  51. HTTPLogTrace();
  52. // At this point, the readBuffer has readBufferOffset bytes available.
  53. // This method is in charge of updating the readBufferOffset.
  54. NSUInteger bufLen = readBufferOffset;
  55. NSUInteger sepLen = [separator length];
  56. // We're going to start looking for the separator at the beginning of the buffer,
  57. // and stop when we get to the point where the separator would no longer fit in the buffer.
  58. NSUInteger offset = 0;
  59. NSUInteger stopOffset = (bufLen > sepLen) ? bufLen - sepLen + 1 : 0;
  60. // In order to do the replacement, we need to find the starting and ending separator.
  61. // For example:
  62. //
  63. // %%USER_NAME%%
  64. //
  65. // Where "%%" is the separator.
  66. BOOL found1 = NO;
  67. BOOL found2 = NO;
  68. NSUInteger s1 = 0;
  69. NSUInteger s2 = 0;
  70. const void *sep = [separator bytes];
  71. while (offset < stopOffset)
  72. {
  73. const void *subBuffer = readBuffer + offset;
  74. if (memcmp(subBuffer, sep, sepLen) == 0)
  75. {
  76. if (!found1)
  77. {
  78. // Found the first separator
  79. found1 = YES;
  80. s1 = offset;
  81. offset += sepLen;
  82. HTTPLogVerbose(@"%@[%p]: Found s1 at %lu", THIS_FILE, self, (unsigned long)s1);
  83. }
  84. else
  85. {
  86. // Found the second separator
  87. found2 = YES;
  88. s2 = offset;
  89. offset += sepLen;
  90. HTTPLogVerbose(@"%@[%p]: Found s2 at %lu", THIS_FILE, self, (unsigned long)s2);
  91. }
  92. if (found1 && found2)
  93. {
  94. // We found our separators.
  95. // Now extract the string between the two separators.
  96. NSRange fullRange = NSMakeRange(s1, (s2 - s1 + sepLen));
  97. NSRange strRange = NSMakeRange(s1 + sepLen, (s2 - s1 - sepLen));
  98. // Wish we could use the simple subdataWithRange method.
  99. // But that method copies the bytes...
  100. // So for performance reasons, we need to use the methods that don't copy the bytes.
  101. void *strBuf = readBuffer + strRange.location;
  102. NSUInteger strLen = strRange.length;
  103. NSString *key = [[NSString alloc] initWithBytes:strBuf length:strLen encoding:NSUTF8StringEncoding];
  104. if (key)
  105. {
  106. // Is there a given replacement for this key?
  107. id value = [replacementDict objectForKey:key];
  108. if (value)
  109. {
  110. // Found the replacement value.
  111. // Now perform the replacement in the buffer.
  112. HTTPLogVerbose(@"%@[%p]: key(%@) -> value(%@)", THIS_FILE, self, key, value);
  113. NSData *v = [[value description] dataUsingEncoding:NSUTF8StringEncoding];
  114. NSUInteger vLength = [v length];
  115. if (fullRange.length == vLength)
  116. {
  117. // Replacement is exactly the same size as what it is replacing
  118. // memcpy(void *restrict dst, const void *restrict src, size_t n);
  119. memcpy(readBuffer + fullRange.location, [v bytes], vLength);
  120. }
  121. else // (fullRange.length != vLength)
  122. {
  123. NSInteger diff = (NSInteger)vLength - (NSInteger)fullRange.length;
  124. if (diff > 0)
  125. {
  126. // Replacement is bigger than what it is replacing.
  127. // Make sure there is room in the buffer for the replacement.
  128. if (diff > (readBufferSize - bufLen))
  129. {
  130. NSUInteger inc = MAX(diff, 256);
  131. readBufferSize += inc;
  132. readBuffer = reallocf(readBuffer, readBufferSize);
  133. }
  134. }
  135. // Move the data that comes after the replacement.
  136. //
  137. // If replacement is smaller than what it is replacing,
  138. // then we are shifting the data toward the beginning of the buffer.
  139. //
  140. // If replacement is bigger than what it is replacing,
  141. // then we are shifting the data toward the end of the buffer.
  142. //
  143. // memmove(void *dst, const void *src, size_t n);
  144. //
  145. // The memmove() function copies n bytes from src to dst.
  146. // The two areas may overlap; the copy is always done in a non-destructive manner.
  147. void *src = readBuffer + fullRange.location + fullRange.length;
  148. void *dst = readBuffer + fullRange.location + vLength;
  149. NSUInteger remaining = bufLen - (fullRange.location + fullRange.length);
  150. memmove(dst, src, remaining);
  151. // Now copy the replacement into its location.
  152. //
  153. // memcpy(void *restrict dst, const void *restrict src, size_t n)
  154. //
  155. // The memcpy() function copies n bytes from src to dst.
  156. // If the two areas overlap, behavior is undefined.
  157. memcpy(readBuffer + fullRange.location, [v bytes], vLength);
  158. // And don't forget to update our indices.
  159. bufLen += diff;
  160. offset += diff;
  161. stopOffset += diff;
  162. }
  163. }
  164. }
  165. found1 = found2 = NO;
  166. }
  167. }
  168. else
  169. {
  170. offset++;
  171. }
  172. }
  173. // We've gone through our buffer now, and performed all the replacements that we could.
  174. // It's now time to update the amount of available data we have.
  175. if (readOffset == fileLength)
  176. {
  177. // We've read in the entire file.
  178. // So there can be no more replacements.
  179. data = [[NSData alloc] initWithBytes:readBuffer length:bufLen];
  180. readBufferOffset = 0;
  181. }
  182. else
  183. {
  184. // There are a couple different situations that we need to take into account here.
  185. //
  186. // Imagine the following file:
  187. // My name is %%USER_NAME%%
  188. //
  189. // Situation 1:
  190. // The first chunk of data we read was "My name is %%".
  191. // So we found the first separator, but not the second.
  192. // In this case we can only return the data that precedes the first separator.
  193. //
  194. // Situation 2:
  195. // The first chunk of data we read was "My name is %".
  196. // So we didn't find any separators, but part of a separator may be included in our buffer.
  197. NSUInteger available;
  198. if (found1)
  199. {
  200. // Situation 1
  201. available = s1;
  202. }
  203. else
  204. {
  205. // Situation 2
  206. available = stopOffset;
  207. }
  208. // Copy available data
  209. data = [[NSData alloc] initWithBytes:readBuffer length:available];
  210. // Remove the copied data from the buffer.
  211. // We do this by shifting the remaining data toward the beginning of the buffer.
  212. NSUInteger remaining = bufLen - available;
  213. memmove(readBuffer, readBuffer + available, remaining);
  214. readBufferOffset = remaining;
  215. }
  216. [connection responseHasAvailableData:self];
  217. }
  218. - (void)dealloc
  219. {
  220. HTTPLogTrace();
  221. }
  222. @end