123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- #import "HTTPDynamicFileResponse.h"
- #import "HTTPConnection.h"
- #import "HTTPLogging.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 HTTPDynamicFileResponse
- - (id)initWithFilePath:(NSString *)fpath
- forConnection:(HTTPConnection *)parent
- separator:(NSString *)separatorStr
- replacementDictionary:(NSDictionary *)dict
- {
- if ((self = [super initWithFilePath:fpath forConnection:parent]))
- {
- HTTPLogTrace();
-
- separator = [separatorStr dataUsingEncoding:NSUTF8StringEncoding];
- replacementDict = dict;
- }
- return self;
- }
- - (BOOL)isChunked
- {
- HTTPLogTrace();
-
- return YES;
- }
- - (UInt64)contentLength
- {
- // This method shouldn't be called since we're using a chunked response.
- // We override it just to be safe.
-
- HTTPLogTrace();
-
- return 0;
- }
- - (void)setOffset:(UInt64)offset
- {
- // This method shouldn't be called since we're using a chunked response.
- // We override it just to be safe.
-
- HTTPLogTrace();
- }
- - (BOOL)isDone
- {
- BOOL result = (readOffset == fileLength) && (readBufferOffset == 0);
-
- HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
-
- return result;
- }
- - (void)processReadBuffer
- {
- HTTPLogTrace();
-
- // At this point, the readBuffer has readBufferOffset bytes available.
- // This method is in charge of updating the readBufferOffset.
-
- NSUInteger bufLen = readBufferOffset;
- NSUInteger sepLen = [separator length];
-
- // We're going to start looking for the separator at the beginning of the buffer,
- // and stop when we get to the point where the separator would no longer fit in the buffer.
-
- NSUInteger offset = 0;
- NSUInteger stopOffset = (bufLen > sepLen) ? bufLen - sepLen + 1 : 0;
-
- // In order to do the replacement, we need to find the starting and ending separator.
- // For example:
- //
- // %%USER_NAME%%
- //
- // Where "%%" is the separator.
-
- BOOL found1 = NO;
- BOOL found2 = NO;
-
- NSUInteger s1 = 0;
- NSUInteger s2 = 0;
-
- const void *sep = [separator bytes];
-
- while (offset < stopOffset)
- {
- const void *subBuffer = readBuffer + offset;
-
- if (memcmp(subBuffer, sep, sepLen) == 0)
- {
- if (!found1)
- {
- // Found the first separator
-
- found1 = YES;
- s1 = offset;
- offset += sepLen;
-
- HTTPLogVerbose(@"%@[%p]: Found s1 at %lu", THIS_FILE, self, (unsigned long)s1);
- }
- else
- {
- // Found the second separator
-
- found2 = YES;
- s2 = offset;
- offset += sepLen;
-
- HTTPLogVerbose(@"%@[%p]: Found s2 at %lu", THIS_FILE, self, (unsigned long)s2);
- }
-
- if (found1 && found2)
- {
- // We found our separators.
- // Now extract the string between the two separators.
-
- NSRange fullRange = NSMakeRange(s1, (s2 - s1 + sepLen));
- NSRange strRange = NSMakeRange(s1 + sepLen, (s2 - s1 - sepLen));
-
- // Wish we could use the simple subdataWithRange method.
- // But that method copies the bytes...
- // So for performance reasons, we need to use the methods that don't copy the bytes.
-
- void *strBuf = readBuffer + strRange.location;
- NSUInteger strLen = strRange.length;
-
- NSString *key = [[NSString alloc] initWithBytes:strBuf length:strLen encoding:NSUTF8StringEncoding];
- if (key)
- {
- // Is there a given replacement for this key?
-
- id value = [replacementDict objectForKey:key];
- if (value)
- {
- // Found the replacement value.
- // Now perform the replacement in the buffer.
-
- HTTPLogVerbose(@"%@[%p]: key(%@) -> value(%@)", THIS_FILE, self, key, value);
-
- NSData *v = [[value description] dataUsingEncoding:NSUTF8StringEncoding];
- NSUInteger vLength = [v length];
-
- if (fullRange.length == vLength)
- {
- // Replacement is exactly the same size as what it is replacing
-
- // memcpy(void *restrict dst, const void *restrict src, size_t n);
-
- memcpy(readBuffer + fullRange.location, [v bytes], vLength);
- }
- else // (fullRange.length != vLength)
- {
- NSInteger diff = (NSInteger)vLength - (NSInteger)fullRange.length;
-
- if (diff > 0)
- {
- // Replacement is bigger than what it is replacing.
- // Make sure there is room in the buffer for the replacement.
-
- if (diff > (readBufferSize - bufLen))
- {
- NSUInteger inc = MAX(diff, 256);
-
- readBufferSize += inc;
- readBuffer = reallocf(readBuffer, readBufferSize);
- }
- }
-
- // Move the data that comes after the replacement.
- //
- // If replacement is smaller than what it is replacing,
- // then we are shifting the data toward the beginning of the buffer.
- //
- // If replacement is bigger than what it is replacing,
- // then we are shifting the data toward the end of the buffer.
- //
- // memmove(void *dst, const void *src, size_t n);
- //
- // The memmove() function copies n bytes from src to dst.
- // The two areas may overlap; the copy is always done in a non-destructive manner.
-
- void *src = readBuffer + fullRange.location + fullRange.length;
- void *dst = readBuffer + fullRange.location + vLength;
-
- NSUInteger remaining = bufLen - (fullRange.location + fullRange.length);
-
- memmove(dst, src, remaining);
-
- // Now copy the replacement into its location.
- //
- // memcpy(void *restrict dst, const void *restrict src, size_t n)
- //
- // The memcpy() function copies n bytes from src to dst.
- // If the two areas overlap, behavior is undefined.
-
- memcpy(readBuffer + fullRange.location, [v bytes], vLength);
-
- // And don't forget to update our indices.
-
- bufLen += diff;
- offset += diff;
- stopOffset += diff;
- }
- }
-
- }
-
- found1 = found2 = NO;
- }
- }
- else
- {
- offset++;
- }
- }
-
- // We've gone through our buffer now, and performed all the replacements that we could.
- // It's now time to update the amount of available data we have.
-
- if (readOffset == fileLength)
- {
- // We've read in the entire file.
- // So there can be no more replacements.
-
- data = [[NSData alloc] initWithBytes:readBuffer length:bufLen];
- readBufferOffset = 0;
- }
- else
- {
- // There are a couple different situations that we need to take into account here.
- //
- // Imagine the following file:
- // My name is %%USER_NAME%%
- //
- // Situation 1:
- // The first chunk of data we read was "My name is %%".
- // So we found the first separator, but not the second.
- // In this case we can only return the data that precedes the first separator.
- //
- // Situation 2:
- // The first chunk of data we read was "My name is %".
- // So we didn't find any separators, but part of a separator may be included in our buffer.
-
- NSUInteger available;
- if (found1)
- {
- // Situation 1
- available = s1;
- }
- else
- {
- // Situation 2
- available = stopOffset;
- }
-
- // Copy available data
-
- data = [[NSData alloc] initWithBytes:readBuffer length:available];
-
- // Remove the copied data from the buffer.
- // We do this by shifting the remaining data toward the beginning of the buffer.
-
- NSUInteger remaining = bufLen - available;
-
- memmove(readBuffer, readBuffer + available, remaining);
- readBufferOffset = remaining;
- }
-
- [connection responseHasAvailableData:self];
- }
- - (void)dealloc
- {
- HTTPLogTrace();
-
-
- }
- @end
|