#include <CoreFoundation/CFNumber.h>
#include <CFNetwork/CFHTTPMessage.h>
#include <CFNetwork/CFHTTPStream.h>
#include <CFNetwork/CFHTTPStreamPriv.h>
#include <CFNetwork/CFSocketStream.h>
#include <CoreFoundation/CFStreamPriv.h>
#include <CFNetwork/CFSocketStreamPriv.h>
#include "CFHTTPInternal.h"
#include "CFNetworkInternal.h"
#include <stdlib.h>
#include <string.h>
extern CFDataRef _CFHTTPMessageCopySerializedHeaders(CFHTTPMessageRef msg, Boolean forProxy);
const SInt32 kCFStreamErrorDomainHTTP = 4;
const SInt32 _kCFStreamErrorHTTPStreamAtMark = -2095; const SInt32 _kCFStreamErrorHTTPSProxyFailure = -2096;
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFHTTPFilterCommaSeparator CFSTR(",")
#define _kCFHTTPFilterContentLengthHeader CFSTR("Content-Length")
#define _kCFHTTPFilterTransferEncodingHeader CFSTR("Transfer-Encoding")
#define _kCFHTTPFilterTransferEncodingChunked CFSTR("chunked")
#define _kCFHTTPFilterTransferEncodingChunked2 CFSTR("Chunked")
#define _kCFHTTPFilterTransferEncodingChunkedSeparator CFSTR(", Chunked")
#define _kCFHTTPFilterTransferEncodingIdentity CFSTR("Identity")
#define _kCFHTTPFilterTrailingHeadersFormat CFSTR("%@, %@")
#define _kCFHTTPFilterProxyAuthorizationHeader CFSTR("Proxy-Authorization")
#define _kCFHTTPFilterHTTPSScheme CFSTR("https")
#define _kCFHTTPStreamConnectionHeader CFSTR("Connection")
#define _kCFHTTPStreamProxyConnectionHeader CFSTR("Proxy-Connection")
#define _kCFHTTPStreamConnectionKeepAlive CFSTR("keep-alive")
#define _kCFHTTPStreamConnectionClose CFSTR("close")
#else
static CONST_STRING_DECL(_kCFHTTPFilterCommaSeparator, ",")
static CONST_STRING_DECL(_kCFHTTPFilterContentLengthHeader, "Content-Length")
static CONST_STRING_DECL(_kCFHTTPFilterTransferEncodingHeader, "Transfer-Encoding")
static CONST_STRING_DECL(_kCFHTTPFilterTransferEncodingChunked, "chunked")
static CONST_STRING_DECL(_kCFHTTPFilterTransferEncodingChunked2, "Chunked")
static CONST_STRING_DECL(_kCFHTTPFilterTransferEncodingChunkedSeparator, ", Chunked")
static CONST_STRING_DECL(_kCFHTTPFilterTransferEncodingIdentity, "Identity")
static CONST_STRING_DECL(_kCFHTTPFilterTrailingHeadersFormat, "%@, %@")
static CONST_STRING_DECL(_kCFHTTPFilterProxyAuthorizationHeader, "Proxy-Authorization")
static CONST_STRING_DECL(_kCFHTTPFilterHTTPSScheme, "https")
static CONST_STRING_DECL(_kCFHTTPStreamConnectionHeader, "Connection")
static CONST_STRING_DECL(_kCFHTTPStreamProxyConnectionHeader, "Proxy-Connection")
static CONST_STRING_DECL(_kCFHTTPStreamConnectionKeepAlive, "keep-alive")
static CONST_STRING_DECL(_kCFHTTPStreamConnectionClose, "close")
#endif
CONST_STRING_DECL(_kCFStreamPropertyHTTPSProxyHoldYourFire, "_kCFStreamPropertyHTTPSProxyHoldYourFire")
static CFIndex httpRdFilterRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
static Boolean httpRdFilterCanRead(CFReadStreamRef stream, void *info);
static CFTypeRef httpRdFilterCopyProperty(CFReadStreamRef stream, CFStringRef propertyName, void *info);
static Boolean httpWrFilterCanWrite(CFWriteStreamRef stream, void *info);
typedef struct {
CFHTTPMessageRef header;
UInt32 flags;
CFSpinLock_t lock;
long long expectedBytes; long long processedBytes; CFMutableDataRef _data;
union {
CFReadStreamRef r;
CFWriteStreamRef w;
} socketStream;
union {
CFReadStreamRef r;
CFWriteStreamRef w;
} filteredStream;
CFDataRef customSSLContext;
#if defined(DEBUG_FILTER)
CFMutableDataRef _allData;
#endif
} _CFHTTPFilter;
static Boolean httpRdFilterCanReadNoSignal(CFReadStreamRef stream, _CFHTTPFilter *httpFilter, CFStreamError *err);
#define IS_CHUNKED (0)
#define DATA_IS_MUTABLE (1)
#define MARK_ENABLED (2)
#define AT_MARK (3)
#define FIRST_CHUNK (4)
#define MARK_SIGNALLED (5)
#define PARSE_FAILED (8)
#define CONNECTION_LOST (9)
#define LAST_CHUNK (10)
#define ZERO_LENGTH_RESPONSE_EXPECTED (11)
#define LAX_PARSING (12)
#define HEADER_TRANSMITTED (16)
#define FIRST_HEADER_SEEN (17)
#define IS_PROXY (18)
#define IS_HTTPS_PROXY (19)
#define HTTPS_PROXY_FAILURE (20)
#define STRIP_PROXY_AUTH (21)
#define MID_CHUNK_HEADER_PARSE (-3)
#define HEADERS_NOT_YET_CHECKED (-2)
#define WAIT_FOR_END_OF_STREAM (-1)
CF_INLINE void setConnectionLost(_CFHTTPFilter *filter, CFStreamError *error) {
error->error = kCFStreamErrorHTTPConnectionLost;
error->domain = kCFStreamErrorDomainHTTP;
__CFBitSet(filter->flags, CONNECTION_LOST);
}
CF_INLINE void setParseFailure(_CFHTTPFilter *filter, CFStreamError *error) {
error->error = kCFStreamErrorHTTPParseFailure;
error->domain = kCFStreamErrorDomainHTTP;
__CFBitSet(filter->flags, PARSE_FAILED);
}
CF_INLINE Boolean httpRdFilterAtMark(_CFHTTPFilter *httpFilter) {
if (__CFBitIsSet(httpFilter->flags, MARK_ENABLED) && __CFBitIsSet(httpFilter->flags, AT_MARK)) {
return TRUE;
} else {
return FALSE;
}
}
static void *httpRdFilterCreate(CFReadStreamRef stream, void *info) {
_CFHTTPFilter *oldFilter = (_CFHTTPFilter *)info;
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFHTTPFilter), 0);
filter->header = oldFilter->header;
CFRetain(filter->header);
filter->lock = 0;
filter->flags = oldFilter->flags;
filter->expectedBytes = HEADERS_NOT_YET_CHECKED;
filter->processedBytes = 0;
filter->_data = NULL;
#if defined(DEBUG_FILTER)
filter->_allData = CFDataCreateMutable(NULL, 0);
#endif
filter->socketStream.r = oldFilter->socketStream.r;
CFRetain(filter->socketStream.r);
filter->filteredStream.r = stream; filter->customSSLContext = NULL;
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: Creating read filter 0x%x\n", (unsigned)filter);
#endif
return filter;
}
static void httpRdFilterDealloc(CFReadStreamRef stream, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: Destroying read filter 0x%x\n", (unsigned)filter);
#endif
__CFSpinLock(&filter->lock);
if (filter->header) CFRelease(filter->header);
if (filter->_data) CFRelease(filter->_data);
#if defined(DEBUG_FILTER)
filter->_allData = CFDataCreateMutable(NULL, 0);
#endif
CFReadStreamClose(filter->socketStream.r);
CFReadStreamSetClient(filter->socketStream.r, kCFStreamEventNone, NULL, NULL);
CFRelease(filter->socketStream.r);
CFAllocatorDeallocate(CFGetAllocator(stream), filter);
}
static void httpRdFilterStreamCallBack(CFReadStreamRef stream, CFStreamEventType event, void *clientCallBackInfo) {
CFReadStreamRef filterStream = (CFReadStreamRef)clientCallBackInfo;
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFReadStreamGetInfoPointer(filterStream);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterStreamCallBack(stream = 0x%x, event = %d, filter = 0x%x)\n", (unsigned)stream, event, (unsigned)filter);
#endif
switch (event) {
case kCFStreamEventHasBytesAvailable:
{
CFStreamEventType event = kCFStreamEventNone;
if (httpRdFilterCanRead(filterStream, filter)) {
event = kCFStreamEventHasBytesAvailable;
} else {
CFStreamStatus status = CFReadStreamGetStatus(stream);
if ((status != kCFStreamStatusAtEnd) &&
(status != kCFStreamStatusClosed) &&
(status != kCFStreamStatusError))
{
__CFSpinLock(&filter->lock);
if (httpRdFilterAtMark(filter) && !__CFBitIsSet(filter->flags, MARK_SIGNALLED)) {
__CFBitSet(filter->flags, MARK_SIGNALLED);
event = kCFStreamEventMarkEncountered;
}
__CFSpinUnlock(&filter->lock);
}
}
if (event != kCFStreamEventNone)
CFReadStreamSignalEvent(filterStream, event, NULL);
}
break;
case kCFStreamEventErrorOccurred: {
CFStreamError error = CFReadStreamGetError(stream);
if (error.domain == kCFStreamErrorDomainPOSIX && (error.error == ECONNRESET || error.error == EPIPE) && _CFHTTPMessageIsEmpty(filter->header)) {
error.domain = kCFStreamErrorDomainHTTP;
error.error = kCFStreamErrorHTTPConnectionLost;
}
CFReadStreamSignalEvent(filterStream, kCFStreamEventErrorOccurred, &error);
break;
}
default:
CFReadStreamSignalEvent(filterStream, event, NULL);
}
}
static Boolean httpRdFilterOpen(CFReadStreamRef stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
CFStreamClientContext clientContext = {0, stream, NULL, NULL, (CFStringRef(*)(void *))CFCopyDescription}; Boolean result;
CFStreamStatus status;
__CFSpinLock(&filter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterOpen(stream = 0x%x, filter = 0x%x)\n", (unsigned)stream, (unsigned)filter);
#endif
CFReadStreamSetClient(filter->socketStream.r, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, (CFReadStreamClientCallBack)httpRdFilterStreamCallBack, &clientContext);
status = CFReadStreamGetStatus(filter->socketStream.r);
if (status == kCFStreamStatusNotOpen) {
result = CFReadStreamOpen(filter->socketStream.r);
} else {
result = TRUE;
}
if (result) {
errorCode->error = 0;
*openComplete = TRUE;
} else {
*openComplete = TRUE;
*errorCode = CFReadStreamGetError(filter->socketStream.r);
}
__CFSpinUnlock(&filter->lock);
return result;
}
static void httpRdFilterClose(CFReadStreamRef stream, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterClose(stream = 0x%x, filter = 0x%x)\n", (unsigned)stream, (unsigned)filter);
#endif
CFReadStreamClose(filter->socketStream.r);
__CFSpinUnlock(&filter->lock);
}
static void httpRdFilterSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterSchedule(stream = 0x%x, runLoop = 0x%x, mode = 0x%x, filter = 0x%x)\n", (unsigned)stream, (unsigned)runLoop, (unsigned)runLoopMode, (unsigned)filter);
#endif
CFReadStreamScheduleWithRunLoop(filter->socketStream.r, runLoop, runLoopMode);
__CFSpinUnlock(&filter->lock);
}
static void httpRdFilterUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterUnschedule(stream = 0x%x, runLoop = 0x%x, mode = 0x%x, filter = 0x%x)\n", (unsigned)stream, (unsigned)runLoop, (unsigned)runLoopMode, (unsigned)filter);
#endif
CFReadStreamUnscheduleFromRunLoop(filter->socketStream.r, runLoop, runLoopMode);
__CFSpinUnlock(&filter->lock);
}
static long long expectedSizeFromHeaders(CFHTTPMessageRef responseHeader) {
CFStringRef contentLength;
Boolean gotGoodContentLength = FALSE;
if (CFHTTPMessageIsRequest(responseHeader) && _CFHTTPMessageIsGetMethod(responseHeader)) {
return 0; }
contentLength = CFHTTPMessageCopyHeaderFieldValue(responseHeader, _kCFHTTPFilterContentLengthHeader);
if (contentLength) {
CFIndex charIndex = 0, length = CFStringGetLength(contentLength);
UniChar ch;
long long expectedBytes = 0;
while (charIndex < length) {
ch = CFStringGetCharacterAtIndex(contentLength, charIndex);
if (ch != ' ' && ch != '\t') break;
charIndex ++;
}
if (charIndex >= length) gotGoodContentLength = TRUE;
while (charIndex < length) {
ch = CFStringGetCharacterAtIndex(contentLength, charIndex);
charIndex ++;
if (ch >= '0' && ch <= '9') {
long long newVal = expectedBytes * 10 + ch - '0';
if (charIndex < length && newVal > (LLONG_MAX/10LL - 10LL)) {
gotGoodContentLength = FALSE;
break;
} else {
gotGoodContentLength = TRUE;
expectedBytes = newVal;
}
} else {
break;
}
}
CFRelease(contentLength);
if (gotGoodContentLength) {
return expectedBytes;
}
}
if (!CFHTTPMessageIsRequest(responseHeader)) {
int status = CFHTTPMessageGetResponseStatusCode(responseHeader);
if (status == 204 || status == 304) {
return 0;
}
}
return WAIT_FOR_END_OF_STREAM;
}
static Boolean readHeaderBytes(_CFHTTPFilter *httpFilter, Boolean toCompletion, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error) {
Boolean parseSucceeded = TRUE;
Boolean connectionLost = FALSE;
CFStringRef headerString;
CFReadStreamRef stream = httpFilter->socketStream.r;
int status;
if (httpFilter->_data) {
CFIndex length = CFDataGetLength(httpFilter->_data);
const UInt8 *bytes = CFDataGetBytePtr(httpFilter->_data);
parseSucceeded = CFHTTPMessageAppendBytes(httpFilter->header, bytes, length);
CFRelease(httpFilter->_data);
httpFilter->_data = NULL;
}
else {
CFHTTPMessageRef proxyResponse = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyPreviousCONNECTResponse);
if (!proxyResponse)
proxyResponse = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyCONNECTResponse);
if (proxyResponse) {
int status = CFHTTPMessageGetResponseStatusCode(proxyResponse);
if (status == 200)
CFRelease(proxyResponse);
else {
CFStringRef connectionHeader = CFHTTPMessageCopyHeaderFieldValue(proxyResponse, _kCFHTTPStreamProxyConnectionHeader);
if (!connectionHeader || (CFStringCompare(connectionHeader, _kCFHTTPStreamConnectionKeepAlive, kCFCompareCaseInsensitive) != kCFCompareEqualTo))
CFHTTPMessageSetHeaderFieldValue(proxyResponse, _kCFHTTPStreamConnectionHeader, _kCFHTTPStreamConnectionClose);
if (connectionHeader) CFRelease(connectionHeader);
CFRelease(httpFilter->header);
httpFilter->header = proxyResponse;
}
}
}
while (parseSucceeded && !CFHTTPMessageIsHeaderComplete(httpFilter->header) && (toCompletion || CFReadStreamHasBytesAvailable(stream))) {
CFIndex bytesRead = CFReadStreamRead(stream, buffer, bufferLength);
if (bytesRead == 0) {
if (_CFHTTPMessageIsEmpty(httpFilter->header)) {
parseSucceeded = FALSE;
connectionLost = TRUE;
} else if (!_CFHTTPMessageConvertToDataOnlyResponse(httpFilter->header)) {
parseSucceeded = _CFHTTPMessageCanStandAlone(httpFilter->header) ? TRUE : FALSE;
} else {
break;
}
} else if (bytesRead < 0) {
break;
} else {
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, buffer, bytesRead);
#endif
parseSucceeded = CFHTTPMessageAppendBytes(httpFilter->header, buffer, bytesRead);
}
}
if (!parseSucceeded) {
if (connectionLost) {
setConnectionLost(httpFilter, error);
} else {
setParseFailure(httpFilter, error);
}
return FALSE;
} else if (CFReadStreamGetStatus(stream) == kCFStreamStatusError) {
*error = CFReadStreamGetError(stream);
if (error->domain == kCFStreamErrorDomainPOSIX && (error->error == ECONNRESET || error->error == EPIPE) && _CFHTTPMessageIsEmpty(httpFilter->header)) {
setConnectionLost(httpFilter, error);
}
return FALSE;
} else if (!CFHTTPMessageIsHeaderComplete(httpFilter->header)) {
return TRUE;
}
if (httpFilter->_data)
CFRelease(httpFilter->_data);
httpFilter->_data = (CFMutableDataRef)_CFHTTPMessageGetBody(httpFilter->header);
if (httpFilter->_data) {
CFRetain(httpFilter->_data);
CFHTTPMessageSetBody(httpFilter->header, NULL);
}
__CFBitClear(httpFilter->flags, DATA_IS_MUTABLE);
status = CFHTTPMessageGetResponseStatusCode(httpFilter->header);
if (status >= 100 && status < 200) {
CFHTTPMessageRef newHeader = CFHTTPMessageCreateEmpty(CFGetAllocator(httpFilter->header), FALSE);
if (__CFBitIsSet(httpFilter->flags, LAX_PARSING)) {
_CFHTTPMessageSetLaxParsing(newHeader, TRUE);
}
CFRelease(httpFilter->header);
httpFilter->header = newHeader;
return readHeaderBytes(httpFilter, toCompletion, buffer, bufferLength, error);
}
if (__CFBitIsSet(httpFilter->flags, ZERO_LENGTH_RESPONSE_EXPECTED)) {
__CFBitClear(httpFilter->flags, ZERO_LENGTH_RESPONSE_EXPECTED);
httpFilter->expectedBytes = 0;
return TRUE;
}
headerString = CFHTTPMessageCopyHeaderFieldValue(httpFilter->header, _kCFHTTPFilterTransferEncodingHeader);
if (headerString) {
CFArrayRef encodingsArray = CFStringCreateArrayBySeparatingStrings(CFGetAllocator(headerString), headerString, _kCFHTTPFilterCommaSeparator);
CFIndex i, c = CFArrayGetCount(encodingsArray);
for (i = 0; i < c; i ++) {
CFStringRef encoding = CFArrayGetValueAtIndex(encodingsArray, i);
if (CFStringFindWithOptions(encoding, _kCFHTTPFilterTransferEncodingChunked, CFRangeMake(0, CFStringGetLength(encoding)), kCFCompareCaseInsensitive, NULL)) {
break;
}
}
if (i < c) {
CFStringRef newString;
if (c == 1) {
newString = CFRetain(_kCFHTTPFilterTransferEncodingIdentity);
} else {
CFMutableArrayRef mArray = CFArrayCreateMutableCopy(CFGetAllocator(headerString), c, encodingsArray);
CFArrayRemoveValueAtIndex(mArray, i);
newString = CFStringCreateByCombiningStrings(CFGetAllocator(headerString), encodingsArray, _kCFHTTPFilterCommaSeparator);
CFRelease(mArray);
}
CFHTTPMessageSetHeaderFieldValue(httpFilter->header, _kCFHTTPFilterTransferEncodingHeader, newString);
CFRelease(newString);
__CFBitSet(httpFilter->flags, IS_CHUNKED);
}
CFRelease(encodingsArray);
CFRelease(headerString);
}
if (__CFBitIsSet(httpFilter->flags, IS_CHUNKED)) {
__CFBitSet(httpFilter->flags, FIRST_CHUNK);
httpFilter->expectedBytes = 0;
} else {
httpFilter->expectedBytes = expectedSizeFromHeaders(httpFilter->header);
if ((httpFilter->expectedBytes == WAIT_FOR_END_OF_STREAM) && (CFReadStreamGetStatus(stream) == kCFStreamStatusAtEnd))
httpFilter->expectedBytes = 0;
}
return TRUE;
}
static inline Boolean isToken(UInt8 ch) {
if (ch < 32 || ch == 127) return FALSE;
if (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@') return FALSE;
if (ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '\"') return FALSE;
if (ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=') return FALSE;
if (ch == '{' || ch == '}' || ch == ' ' || ch == '\t') return FALSE;
return TRUE;
}
static inline const UInt8 *skipLWS(const UInt8 *current, const UInt8 *end) {
while (current < end) {
if (*current != ' ' && *current != '\t') break;
current ++;
}
return current;
}
static inline const UInt8 *skipToken(const UInt8 *curr, const UInt8 *end) {
while (curr < end && isToken(*curr)) {
curr ++;
}
return curr;
}
static inline const UInt8 *skipQuotedString(const UInt8 *curr, const UInt8 *end) {
curr ++; while (curr < end) {
if (*curr == '\"') {
curr ++;
return curr;
} else if (*curr == '\\') {
curr ++;
}
curr ++;
}
return curr;
}
static CFIndex parseChunkedHeader(const UInt8 *buffer, CFIndex bufferLength, Boolean isFirstChunk, CFIndex *firstDataByte) {
const UInt8 *curr = buffer, *end = buffer + bufferLength;
CFIndex size = 0;
if (!isFirstChunk) {
while (curr != end) {
UInt8 c = *curr;
if ((c == '\r') || (c == '\n') || (c == ' ') || (c == '\t'))
curr++;
else
break;
}
if (curr == end) return MID_CHUNK_HEADER_PARSE;
}
for (; curr < end; curr ++) {
int digit;
if (*curr >= '0' && *curr <= '9') {
digit = *curr - '0';
} else if (*curr >= 'A' && *curr <= 'F') {
digit = *curr - 'A' + 10;
} else if (*curr >= 'a' && *curr <= 'f') {
digit = *curr - 'a' + 10;
} else {
break;
}
size = (size << 4) + digit;
}
curr = skipLWS(curr, end);
if (curr == end) return MID_CHUNK_HEADER_PARSE;
while (*curr == ';') {
curr++; curr = skipLWS(curr, end);
if (curr == end) return MID_CHUNK_HEADER_PARSE;
UInt8 c = *curr;
if (c == '\r' || c == '\n') {
break;
}
else {
curr = skipToken(curr, end);
curr = skipLWS(curr, end);
if (curr == end) return MID_CHUNK_HEADER_PARSE;
if (*curr == '=') {
curr = skipLWS(curr, end);
if (curr == end) return MID_CHUNK_HEADER_PARSE;
if (*curr == '\"') {
curr = skipQuotedString(curr, end);
} else {
curr = skipToken(curr, end);
}
curr = skipLWS(curr, end);
if (curr == end) return MID_CHUNK_HEADER_PARSE;
}
}
}
if (*curr == '\r') {
if (curr + 1 == end) {
return MID_CHUNK_HEADER_PARSE;
} else if (*(curr + 1) == '\n') {
*firstDataByte = curr + 2 - buffer;
return size;
} else {
return -1;
}
}
return -1;
}
static Boolean setFilterForChunkedHeaderBytes(_CFHTTPFilter *httpFilter, const UInt8 *headerBytes, CFIndex headerByteLength, CFIndex *firstDataByte, CFStreamError *error) {
CFIndex chunkSize;
if (__CFBitIsSet(httpFilter->flags, FIRST_CHUNK)) {
chunkSize = parseChunkedHeader(headerBytes, headerByteLength, TRUE, firstDataByte);
if (chunkSize != MID_CHUNK_HEADER_PARSE) {
__CFBitClear(httpFilter->flags, FIRST_CHUNK);
}
} else {
chunkSize = parseChunkedHeader(headerBytes, headerByteLength, FALSE, firstDataByte);
}
if (chunkSize == -1) {
*firstDataByte = -1;
setParseFailure(httpFilter, error);
return TRUE;
} else if (chunkSize == MID_CHUNK_HEADER_PARSE) {
return FALSE;
} else {
error->error = 0;
httpFilter->expectedBytes = chunkSize;
httpFilter->processedBytes = 0;
if (chunkSize == 0) {
__CFBitSet(httpFilter->flags, LAST_CHUNK);
}
return TRUE;
}
}
static inline void setDataForRange(_CFHTTPFilter *filter, CFRange newRange) {
if (newRange.length <= 0) {
CFRelease(filter->_data);
filter->_data = NULL;
} else if (__CFBitIsSet(filter->flags, DATA_IS_MUTABLE)) {
if (newRange.location > 0) {
CFDataDeleteBytes(filter->_data, CFRangeMake(0, newRange.location));
}
CFDataSetLength(filter->_data, newRange.length);
} else {
CFMutableDataRef newData = CFDataCreateMutable(CFGetAllocator(filter->_data), 0);
CFDataAppendBytes(newData, CFDataGetBytePtr(filter->_data)+newRange.location, newRange.length);
CFRelease(filter->_data);
filter->_data = newData;
__CFBitSet(filter->flags, DATA_IS_MUTABLE);
}
}
static inline void setDataForBytes(_CFHTTPFilter *filter, const UInt8 *bytes, CFIndex length) {
if (filter->_data) CFRelease(filter->_data);
if (length == 0) {
filter->_data = NULL;
} else {
filter->_data = CFDataCreateMutable(CFGetAllocator(filter->header), 0);
CFDataAppendBytes(filter->_data, bytes, length);
__CFBitSet(filter->flags, DATA_IS_MUTABLE);
}
}
static Boolean readChunkedHeader(_CFHTTPFilter *httpFilter, Boolean toCompletion, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error) {
CFIndex dataLength = httpFilter->_data ? CFDataGetLength(httpFilter->_data) : 0;
const UInt8 *dataBytes = dataLength != 0 ? CFDataGetBytePtr(httpFilter->_data) : NULL;
Boolean result = TRUE, done = FALSE;
CFReadStreamRef stream = httpFilter->socketStream.r;
if (dataBytes) {
CFIndex endOfHeader;
if (setFilterForChunkedHeaderBytes(httpFilter, dataBytes, dataLength, &endOfHeader, error)) {
done = TRUE;
result = (endOfHeader != -1);
if (result && endOfHeader != 0) {
setDataForRange(httpFilter, CFRangeMake(endOfHeader, dataLength-endOfHeader));
}
}
}
if (!done && !toCompletion && !CFReadStreamHasBytesAvailable(stream) && CFReadStreamGetStatus(stream) != kCFStreamStatusError) {
httpFilter->expectedBytes = MID_CHUNK_HEADER_PARSE;
done = TRUE;
result = TRUE;
}
if (!done && dataLength < bufferLength) {
UInt8 *readBuffer = buffer;
CFIndex readBufferLength = bufferLength;
CFIndex bytesRead;
if (dataBytes) {
memmove(buffer, dataBytes, dataLength);
readBuffer += dataLength;
readBufferLength -= dataLength;
}
bytesRead = CFReadStreamRead(stream, readBuffer, readBufferLength);
if (bytesRead < 0) {
*error = CFReadStreamGetError(stream);
done = TRUE;
result = FALSE;
} else if (bytesRead == 0) {
if (readBuffer == buffer + 2 && buffer[0] == '\r' && buffer[1] == '\n') {
httpFilter->expectedBytes = 0;
httpFilter->processedBytes = 0;
__CFBitSet(httpFilter->flags, LAST_CHUNK);
result = TRUE;
} else {
setParseFailure(httpFilter, error);
result = FALSE;
}
done = TRUE;
} else {
CFIndex endOfHeader;
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, readBuffer, bytesRead);
#endif
if (setFilterForChunkedHeaderBytes(httpFilter, buffer, dataLength + bytesRead, &endOfHeader, error)) {
done = TRUE;
result = (endOfHeader != -1);
if (result) {
setDataForBytes(httpFilter, buffer + endOfHeader, dataLength + bytesRead - endOfHeader);
}
} else {
if (dataLength && __CFBitIsSet(httpFilter->flags, DATA_IS_MUTABLE)) {
CFDataAppendBytes(httpFilter->_data, buffer + dataLength, bytesRead);
} else {
setDataForBytes(httpFilter, buffer, dataLength + bytesRead);
}
}
}
}
if (httpFilter->_data == NULL || !__CFBitIsSet(httpFilter->flags, DATA_IS_MUTABLE)) {
if (httpFilter->_data) {
CFMutableDataRef newData = CFDataCreateMutableCopy(CFGetAllocator(httpFilter->_data), 0, httpFilter->_data);
CFRelease(httpFilter->_data);
httpFilter->_data = newData;
} else {
httpFilter->_data = CFDataCreateMutable(CFGetAllocator(httpFilter->header), 0);
}
__CFBitSet(httpFilter->flags, DATA_IS_MUTABLE);
}
while (!done && (toCompletion || CFReadStreamHasBytesAvailable(stream) || CFReadStreamGetStatus(stream) == kCFStreamStatusError)) {
CFIndex bytesRead, oldLength = CFDataGetLength(httpFilter->_data);
UInt8 *bytes;
CFDataSetLength(httpFilter->_data, oldLength + bufferLength);
bytes = CFDataGetMutableBytePtr(httpFilter->_data);
bytesRead = CFReadStreamRead(stream, bytes + oldLength, bufferLength);
if (bytesRead < 0) {
done = TRUE;
result = FALSE;
*error = CFReadStreamGetError(stream);
} else if (bytesRead == 0) {
done = TRUE;
result = FALSE;
setParseFailure(httpFilter, error);
} else {
CFIndex firstDataByte;
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, bytes + oldLength, bytesRead);
#endif
if (setFilterForChunkedHeaderBytes(httpFilter, bytes, oldLength + bytesRead, &firstDataByte, error)) {
done = TRUE;
result = (firstDataByte != -1);
if (result && firstDataByte != 0) {
CFDataDeleteBytes(httpFilter->_data, CFRangeMake(0, firstDataByte));
CFDataSetLength(httpFilter->_data, oldLength + bytesRead - firstDataByte);
}
} else {
CFDataSetLength(httpFilter->_data, oldLength + bytesRead);
}
}
}
if (!done) {
result = TRUE;
httpFilter->expectedBytes = MID_CHUNK_HEADER_PARSE;
}
return result;
}
static void addTrailingHeader(CFStringRef key, CFStringRef value, CFHTTPMessageRef response) {
CFStringRef original = CFHTTPMessageCopyHeaderFieldValue(response, key);
if (original) {
value = CFStringCreateWithFormat(CFGetAllocator(response), NULL, _kCFHTTPFilterTrailingHeadersFormat, original, value);
CFRelease(original);
}
CFHTTPMessageSetHeaderFieldValue(response, key, value);
}
static Boolean readChunkedTrailers(_CFHTTPFilter *httpFilter, CFStreamError *error) {
static const char bogus[] = "HTTP/1.0 200 OK\r\n";
CFReadStreamRef stream = httpFilter->socketStream.r;
Boolean result = TRUE;
CFHTTPMessageRef hdrs = CFHTTPMessageCreateEmpty(CFGetAllocator(stream), FALSE);
CFHTTPMessageAppendBytes(hdrs, (const UInt8*)bogus, sizeof(bogus) - 1);
if (httpFilter->_data) {
CFIndex length = CFDataGetLength(httpFilter->_data);
const UInt8 *bytes = CFDataGetBytePtr(httpFilter->_data);
result = CFHTTPMessageAppendBytes(hdrs, bytes, length);
CFRelease(httpFilter->_data);
httpFilter->_data = NULL;
}
while (result && !CFHTTPMessageIsHeaderComplete(hdrs)) {
UInt8 buffer[2048];
CFIndex bytesRead = CFReadStreamRead(stream, buffer, sizeof(buffer));
if (bytesRead > 0) {
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, buffer, bytesRead);
#endif
result = CFHTTPMessageAppendBytes(hdrs, buffer, bytesRead);
}
else if (bytesRead == 0) {
setParseFailure(httpFilter, error);
result = FALSE;
}
else {
*error = CFReadStreamGetError(stream);
result = FALSE;
}
}
if (result) {
CFDictionaryRef fields;
CFDataRef leftovers = CFHTTPMessageCopyBody(hdrs);
if (leftovers) {
httpFilter->_data = (CFMutableDataRef)leftovers;
__CFBitClear(httpFilter->flags, DATA_IS_MUTABLE);
}
fields = CFHTTPMessageCopyAllHeaderFields(hdrs);
if (fields) {
CFDictionaryApplyFunction(fields, (CFDictionaryApplierFunction)addTrailingHeader, httpFilter->header);
CFRelease(fields);
}
}
CFRelease(hdrs);
return result;
}
static CFIndex doChunkedRead(_CFHTTPFilter *httpFilter, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF) {
CFIndex lengthFilled = 0; Boolean errorOccurred = FALSE;
CFReadStreamRef stream = httpFilter->socketStream.r;
*atEOF = FALSE;
error->error = 0;
while (bufferLength > 0) {
CFIndex bytesRemainingInChunk;
if (__CFBitIsSet(httpFilter->flags, LAST_CHUNK)) {
*atEOF = TRUE;
errorOccurred = (readChunkedTrailers(httpFilter, error)) ? FALSE : TRUE;
break;
}
if (httpFilter->expectedBytes == MID_CHUNK_HEADER_PARSE || httpFilter->expectedBytes == httpFilter->processedBytes) {
if (!readChunkedHeader(httpFilter, lengthFilled > 0 ? FALSE : TRUE, buffer, bufferLength, error)) {
errorOccurred = TRUE;
break;
} else if (httpFilter->expectedBytes == MID_CHUNK_HEADER_PARSE) {
errorOccurred = FALSE;
break;
} else if (httpFilter->expectedBytes == 0) {
*atEOF = TRUE;
errorOccurred = (readChunkedTrailers(httpFilter, error)) ? FALSE : TRUE;
break;
}
}
bytesRemainingInChunk = httpFilter->expectedBytes - httpFilter->processedBytes;
if (httpFilter->_data) {
const UInt8 *bytes = CFDataGetBytePtr(httpFilter->_data);
CFIndex length = CFDataGetLength(httpFilter->_data);
CFIndex numBytesToMove;
if (length >= bytesRemainingInChunk) {
if (bytesRemainingInChunk >= bufferLength) {
numBytesToMove = bufferLength;
} else {
numBytesToMove = bytesRemainingInChunk;
}
} else {
if (length >= bufferLength) {
numBytesToMove = bufferLength;
} else {
numBytesToMove = length;
}
}
memmove(buffer, bytes, numBytesToMove);
buffer += numBytesToMove;
bufferLength -= numBytesToMove;
lengthFilled += numBytesToMove;
httpFilter->processedBytes += numBytesToMove;
setDataForRange(httpFilter, CFRangeMake(numBytesToMove, length - numBytesToMove));
bytesRemainingInChunk -= numBytesToMove;
}
if (bufferLength == 0) break;
if (bytesRemainingInChunk == 0) continue;
if (lengthFilled > 0 && !CFReadStreamHasBytesAvailable(stream) && CFReadStreamGetStatus(stream) != kCFStreamStatusError) {
break;
} else {
CFIndex bytesToRead = bytesRemainingInChunk > bufferLength ? bufferLength : bytesRemainingInChunk;
CFIndex numRead = CFReadStreamRead(stream, buffer, bytesToRead);
if (numRead < 0) {
*error = CFReadStreamGetError(stream);
errorOccurred = TRUE;
break;
} else if (numRead == 0) {
setParseFailure(httpFilter, error);
errorOccurred = TRUE;
break;
} else {
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, buffer, numRead);
#endif
buffer += numRead;
bufferLength -= numRead;
lengthFilled += numRead;
httpFilter->processedBytes += numRead;
}
}
}
if (errorOccurred) {
return -1;
} else {
return lengthFilled;
}
}
static CFIndex doPlainRead(_CFHTTPFilter *httpFilter, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF) {
CFIndex result = 0;
CFReadStreamRef stream = httpFilter->socketStream.r;
*atEOF = FALSE;
error->error = 0;
if (httpFilter->expectedBytes != WAIT_FOR_END_OF_STREAM && bufferLength > (httpFilter->expectedBytes - httpFilter->processedBytes)) {
bufferLength = httpFilter->expectedBytes - httpFilter->processedBytes;
}
if (bufferLength == 0) {
*atEOF = TRUE;
return 0;
}
if (httpFilter->_data) {
const UInt8 *bytes = CFDataGetBytePtr(httpFilter->_data);
CFIndex length = CFDataGetLength(httpFilter->_data);
if (length > bufferLength) {
memcpy(buffer, bytes, bufferLength);
setDataForRange(httpFilter, CFRangeMake(bufferLength, length-bufferLength));
result = bufferLength;
} else {
memcpy(buffer, bytes, length);
CFRelease(httpFilter->_data);
httpFilter->_data = NULL;
result = length;
}
}
if (result == 0 || (result < bufferLength && CFReadStreamHasBytesAvailable(stream))) {
CFIndex bytesRead;
buffer += result;
bufferLength -= result;
bytesRead = CFReadStreamRead(stream, buffer, bufferLength);
if (bytesRead < 0) {
result = -1;
*error = CFReadStreamGetError(stream);
*atEOF = TRUE;
} else if (bytesRead == 0) {
*atEOF = TRUE;
} else {
#if defined(DEBUG_FILTER)
CFDataAppendBytes(httpFilter->_allData, buffer, bytesRead);
#endif
result += bytesRead;
if (CFReadStreamGetStatus(stream) == kCFStreamStatusAtEnd) {
*atEOF = TRUE;
}
}
}
if (result > 0) {
httpFilter->processedBytes += result;
if (httpFilter->expectedBytes != WAIT_FOR_END_OF_STREAM && httpFilter->processedBytes >= httpFilter->expectedBytes) {
*atEOF = TRUE;
}
}
return result;
}
static CFIndex httpRdFilterRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) {
_CFHTTPFilter *httpFilter = (_CFHTTPFilter *)info;
Boolean parseSucceeded = TRUE;
int result;
__CFSpinLock(&httpFilter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterRead(stream = 0x%x, filter = 0x%x) - ", (unsigned)stream, (unsigned)httpFilter);
#endif
if (__CFBitIsSet(httpFilter->flags, PARSE_FAILED)) {
error->error = kCFStreamErrorHTTPParseFailure;
error->domain = kCFStreamErrorDomainHTTP;
*atEOF = TRUE;
#if defined(LOG_FILTER)
fprintf(stderr, "returned -1 (error = {%d, %d})\n", (int)error->domain, (int)error->error);
#endif
__CFSpinUnlock(&httpFilter->lock);
return -1;
} else if (__CFBitIsSet(httpFilter->flags, CONNECTION_LOST)) {
error->error = kCFStreamErrorHTTPConnectionLost;
error->domain = kCFStreamErrorDomainHTTP;
*atEOF = TRUE;
#if defined(LOG_FILTER)
fprintf(stderr, "returned -1 (error = {%d, %d})\n", (int)error->domain, (int)error->error);
#endif
__CFSpinUnlock(&httpFilter->lock);
return -1;
} else if (__CFBitIsSet(httpFilter->flags, AT_MARK)) {
error->error = _kCFStreamErrorHTTPStreamAtMark;
error->domain = kCFStreamErrorDomainHTTP;
*atEOF = TRUE;
#if defined(LOG_FILTER)
fprintf(stderr, "returned -1 (error = {%d, %d})\n", (int)error->domain, (int)error->error);
#endif
__CFSpinUnlock(&httpFilter->lock);
return -1;
}
if (httpFilter->expectedBytes == HEADERS_NOT_YET_CHECKED) {
parseSucceeded = readHeaderBytes(httpFilter, TRUE, buffer, bufferLength, error);
}
if (!parseSucceeded) {
*atEOF = TRUE;
#if defined(LOG_FILTER)
fprintf(stderr, "returned -1 (error = {%d, %d})\n", (int)error->domain, (int)error->error);
#endif
__CFSpinUnlock(&httpFilter->lock);
return -1; }
if (__CFBitIsSet(httpFilter->flags, IS_CHUNKED)) {
result = doChunkedRead(httpFilter, buffer, bufferLength, error, atEOF);
} else {
result = doPlainRead(httpFilter, buffer, bufferLength, error, atEOF);
}
if (*atEOF && !error->error && __CFBitIsSet(httpFilter->flags, MARK_ENABLED)) {
*atEOF = FALSE;
__CFBitSet(httpFilter->flags, AT_MARK);
__CFBitSet(httpFilter->flags, MARK_SIGNALLED);
CFReadStreamSignalEvent(stream, kCFStreamEventMarkEncountered, NULL);
} else {
CFStreamError err = {0, 0};
if (result > 0 && !*atEOF && httpRdFilterCanReadNoSignal(stream, httpFilter, &err)) {
CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
}
if (err.error) {
CFReadStreamSignalEvent(stream, kCFStreamEventErrorOccurred, &err);
}
}
#if defined(LOG_FILTER)
fprintf(stderr, "returned %d (error = {%d, %d})\n", result, (int)error->domain, (int)error->error);
#endif
__CFSpinUnlock(&httpFilter->lock);
return result;
}
#define BUFFER_LENGTH (4 * 1024)
static Boolean httpRdFilterCanReadNoSignal(CFReadStreamRef stream, _CFHTTPFilter *httpFilter, CFStreamError *err) {
UInt8 buffer[BUFFER_LENGTH];
err->error = 0;
if (__CFBitIsSet(httpFilter->flags, AT_MARK)) {
return FALSE;
}
if (httpFilter->expectedBytes == HEADERS_NOT_YET_CHECKED) {
if (!readHeaderBytes(httpFilter, FALSE, buffer, BUFFER_LENGTH, err)) {
return FALSE;
}
if (!CFHTTPMessageIsHeaderComplete(httpFilter->header)) {
return FALSE;
}
}
if (__CFBitIsSet(httpFilter->flags, IS_CHUNKED) && (httpFilter->expectedBytes == httpFilter->processedBytes || httpFilter->expectedBytes == MID_CHUNK_HEADER_PARSE)) {
if (__CFBitIsSet(httpFilter->flags, LAST_CHUNK)) {
return TRUE;
} else if (!readChunkedHeader(httpFilter, FALSE, buffer, BUFFER_LENGTH, err)) {
return FALSE;
} else if (httpFilter->expectedBytes == MID_CHUNK_HEADER_PARSE) {
return FALSE;
}
}
if (httpFilter->_data && CFDataGetLength(httpFilter->_data) != 0) {
return TRUE;
} else if (httpFilter->expectedBytes == httpFilter->processedBytes) {
if (__CFBitIsSet(httpFilter->flags, MARK_ENABLED)) {
__CFBitSet(httpFilter->flags, AT_MARK);
return FALSE;
} else {
return TRUE;
}
} else if (CFReadStreamHasBytesAvailable(httpFilter->socketStream.r)) {
return TRUE;
}
return FALSE;
}
static Boolean httpRdFilterCanRead(CFReadStreamRef stream, void *info) {
_CFHTTPFilter *httpFilter = (_CFHTTPFilter *)info;
CFStreamError error;
Boolean result;
__CFSpinLock(&httpFilter->lock);
result = httpRdFilterCanReadNoSignal(stream, httpFilter, &error);
__CFSpinUnlock(&httpFilter->lock);
if (error.error) {
CFReadStreamSignalEvent(stream, kCFStreamEventErrorOccurred, &error);
}
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: httpRdFilterCanRead(stream = 0x%x, filter = 0x%x) - returning %s\n", (unsigned)stream, (unsigned)httpFilter, result ? "TRUE" : "FALSE");
#endif
return result;
}
extern
Boolean _CFHTTPReadStreamIsAtMark(CFReadStreamRef filteredStream) {
Boolean result;
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFReadStreamGetInfoPointer(filteredStream);
__CFSpinLock(&filter->lock);
result = httpRdFilterAtMark(filter);
__CFSpinUnlock(&filter->lock);
return result;
}
extern
void _CFHTTPReadStreamReadMark(CFReadStreamRef fStream) {
_CFHTTPFilter *httpFilter = (_CFHTTPFilter *)CFReadStreamGetInfoPointer(fStream);
struct _CFStream *filteredStream = (struct _CFStream *)fStream;
CFStreamError error = {0, 0};
__CFSpinLock(&httpFilter->lock);
#if defined(LOG_FILTER)
fprintf(stderr, "HTTPFilter: _CFHTTPReadStreamReadMark(stream = 0x%x, filter = 0x%x)\n ", (unsigned)fStream, (unsigned)httpFilter);
#endif
if (__CFBitIsSet(httpFilter->flags, MARK_ENABLED) && __CFBitIsSet(httpFilter->flags, AT_MARK)) {
CFHTTPMessageRef newHeader;
CFAllocatorRef alloc = CFGetAllocator(httpFilter->header);
newHeader = CFHTTPMessageCreateEmpty(alloc, CFHTTPMessageIsRequest(httpFilter->header));
if (__CFBitIsSet(httpFilter->flags, LAX_PARSING)) {
_CFHTTPMessageSetLaxParsing(newHeader, TRUE);
}
CFRelease(httpFilter->header);
httpFilter->header = newHeader;
httpFilter->expectedBytes = HEADERS_NOT_YET_CHECKED;
httpFilter->processedBytes = 0;
__CFBitClear(httpFilter->flags, AT_MARK);
__CFBitClear(httpFilter->flags, MARK_SIGNALLED);
__CFBitClear(httpFilter->flags, IS_CHUNKED);
__CFBitClear(httpFilter->flags, FIRST_CHUNK);
__CFBitClear(httpFilter->flags, LAST_CHUNK);
__CFBitClear(httpFilter->flags, PARSE_FAILED);
__CFBitClear(httpFilter->flags, CONNECTION_LOST);
__CFBitClear(httpFilter->flags, ZERO_LENGTH_RESPONSE_EXPECTED);
#if defined(DEBUG_FILTER)
CFRelease(httpFilter->_allData);
httpFilter->_allData = CFDataCreateMutable(NULL, 0);
if (httpFilter->_data) {
CFDataAppendBytes(httpFilter->_allData, CFDataGetBytePtr(httpFilter->_data), CFDataGetLength(httpFilter->_data));
}
#endif
}
if (httpRdFilterCanReadNoSignal(fStream, httpFilter, &error)) {
_CFReadStreamSignalEventDelayed(fStream, kCFStreamEventHasBytesAvailable, NULL);
} else if (error.error) {
if (error.domain == _kCFStreamErrorDomainNativeSockets && (error.error == ECONNRESET || error.error == EPIPE)) {
error.domain= kCFStreamErrorDomainHTTP;
error.error = kCFStreamErrorHTTPConnectionLost;
}
_CFReadStreamSignalEventDelayed(fStream, kCFStreamEventErrorOccurred, &error);
} else if (CFReadStreamGetStatus(httpFilter->socketStream.r) == kCFStreamStatusAtEnd) {
error.domain= kCFStreamErrorDomainHTTP;
error.error = kCFStreamErrorHTTPConnectionLost;
_CFReadStreamSignalEventDelayed(fStream, kCFStreamEventErrorOccurred, &error);
}
if (filteredStream->client) {
filteredStream->client->whatToSignal &= ~kCFStreamEventMarkEncountered;
}
__CFSpinUnlock(&httpFilter->lock);
}
static CFTypeRef httpRdFilterCopyProperty(CFReadStreamRef stream, CFStringRef propertyName, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
CFTypeRef result = NULL;
__CFSpinLock(&filter->lock);
if (CFEqual(propertyName, _kCFStreamPropertyHTTPPersistent)) {
result = (__CFBitIsSet(filter->flags, MARK_ENABLED)) ? kCFBooleanTrue : kCFBooleanFalse;
} else if ((!CFHTTPMessageIsRequest(filter->header) && CFEqual(propertyName, kCFStreamPropertyHTTPResponseHeader)) || (CFHTTPMessageIsRequest(filter->header) && CFEqual(propertyName, kCFStreamPropertyHTTPRequest))) {
CFHTTPMessageRef response = filter->header;
if (CFHTTPMessageIsHeaderComplete(response)) {
CFRetain(response);
} else {
response = NULL;
}
result = response;
} else {
result = CFReadStreamCopyProperty(filter->socketStream.r, propertyName);
}
__CFSpinUnlock(&filter->lock);
return result;
}
static Boolean httpRdFilterSetProperty(CFReadStreamRef stream, CFStringRef propName, CFTypeRef propValue, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
if (CFEqual(propName, _kCFStreamPropertyHTTPPersistent)) {
if (propValue == kCFBooleanTrue) {
__CFBitSet(filter->flags, MARK_ENABLED);
} else {
__CFBitClear(filter->flags, MARK_ENABLED);
}
__CFSpinUnlock(&filter->lock);
return TRUE;
} else if (propName == _kCFStreamPropertyHTTPZeroLengthResponseExpected) {
if (propValue == kCFBooleanTrue) {
__CFBitSet(filter->flags, ZERO_LENGTH_RESPONSE_EXPECTED);
}
__CFSpinUnlock(&filter->lock);
return TRUE;
} else if (CFEqual(propName, _kCFStreamPropertyHTTPLaxParsing)) {
if (propValue == kCFBooleanTrue) {
__CFBitSet(filter->flags, LAX_PARSING);
if (filter->header) _CFHTTPMessageSetLaxParsing(filter->header, TRUE);
} else {
__CFBitClear(filter->flags, LAX_PARSING);
if (filter->header) _CFHTTPMessageSetLaxParsing(filter->header, FALSE);
}
__CFSpinUnlock(&filter->lock);
return TRUE;
#if defined(__MACH__)
} else if (CFEqual(propName, kCFStreamPropertySocketSSLContext)) {
__CFSpinUnlock(&filter->lock);
return FALSE;
#endif
} else {
Boolean result = CFReadStreamSetProperty(filter->socketStream.r, propName, propValue);
__CFSpinUnlock(&filter->lock);
return result;
}
}
static const CFReadStreamCallBacks HTTPReadFilterCallBacks = {1,
httpRdFilterCreate
, httpRdFilterDealloc
, NULL , httpRdFilterOpen
, NULL , httpRdFilterRead
, NULL , httpRdFilterCanRead
, httpRdFilterClose
, httpRdFilterCopyProperty
, httpRdFilterSetProperty
, NULL , httpRdFilterSchedule
, httpRdFilterUnschedule};
CF_EXPORT
CFReadStreamRef CFReadStreamCreateHTTPStream(CFAllocatorRef alloc, CFReadStreamRef readStream, Boolean forResponse) {
_CFHTTPFilter filter;
CFReadStreamRef stream;
memset(&filter, 0, sizeof(_CFHTTPFilter));
filter.socketStream.r = readStream;
if (forResponse) {
filter.header = CFHTTPMessageCreateEmpty(alloc, FALSE);
} else {
filter.header = CFHTTPMessageCreateEmpty(alloc, TRUE);
}
stream = CFReadStreamCreate(alloc, &HTTPReadFilterCallBacks, &filter);
CFRelease(filter.header);
return stream;
}
static void *httpWrFilterCreate(CFWriteStreamRef stream, void *info) {
_CFHTTPFilter *oldFilter = (_CFHTTPFilter *)info;
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFHTTPFilter), 0);
filter->lock = 0;
filter->header = NULL;
filter->flags = 0;
if (__CFBitIsSet(oldFilter->flags, IS_PROXY)) {
__CFBitSet(filter->flags, IS_PROXY);
}
__CFBitSet(filter->flags, FIRST_CHUNK);
filter->expectedBytes = 0;
filter->processedBytes = 0;
filter->_data = NULL;
filter->socketStream.w = oldFilter->socketStream.w;
CFRetain(filter->socketStream.w);
filter->filteredStream.w = stream; filter->customSSLContext = NULL;
return filter;
}
static void httpWrFilterDealloc(CFWriteStreamRef stream, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
if (filter->header) CFRelease(filter->header);
if (filter->_data) CFRelease(filter->_data);
CFWriteStreamClose(filter->socketStream.w);
CFWriteStreamSetClient(filter->socketStream.w, kCFStreamEventNone, NULL, NULL);
CFRelease(filter->socketStream.w);
if (filter->customSSLContext) CFRelease(filter->customSSLContext);
CFAllocatorDeallocate(CFGetAllocator(stream), filter);
}
static void httpWrFilterStreamCallBack(CFWriteStreamRef stream, CFStreamEventType event, void *clientCallBackInfo) {
CFWriteStreamRef filterStream = (CFWriteStreamRef)clientCallBackInfo;
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFWriteStreamGetInfoPointer(filterStream);
switch (event) {
case kCFStreamEventErrorOccurred: {
CFStreamError error = CFWriteStreamGetError(stream);
__CFSpinLock(&filter->lock);
if (!__CFBitIsSet(filter->flags, HEADER_TRANSMITTED) && __CFBitIsSet(filter->flags, IS_HTTPS_PROXY)) {
CFHTTPMessageRef proxyResponse = (CFHTTPMessageRef)CFWriteStreamCopyProperty(filter->socketStream.w, kCFStreamPropertyCONNECTResponse);
int status = CFHTTPMessageGetResponseStatusCode(proxyResponse);
CFRelease(proxyResponse);
if (status != 200) {
if (!filter->processedBytes) {
if (filter->_data) CFRelease(filter->_data);
filter->_data = NULL;
__CFBitSet(filter->flags, HEADER_TRANSMITTED);
__CFBitSet(filter->flags, HTTPS_PROXY_FAILURE);
}
else {
error.domain = kCFStreamErrorDomainHTTP;
error.error = _kCFStreamErrorHTTPSProxyFailure;
}
}
}
__CFSpinUnlock(&filter->lock);
CFWriteStreamSignalEvent(filterStream, kCFStreamEventErrorOccurred, &error);
break;
}
case kCFStreamEventCanAcceptBytes:
if (httpWrFilterCanWrite(stream, filter)) {
CFWriteStreamSignalEvent(filterStream, kCFStreamEventCanAcceptBytes, NULL);
}
break;
default:
CFWriteStreamSignalEvent(filterStream, event, NULL);
}
}
static Boolean httpWrFilterOpen(CFWriteStreamRef stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
CFStreamClientContext clientContext = {0, stream, NULL, NULL, (CFStringRef(*)(void *))CFCopyDescription}; Boolean result;
CFStreamStatus status;
__CFSpinLock(&filter->lock);
CFWriteStreamSetClient(filter->socketStream.w, kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, (CFWriteStreamClientCallBack)httpWrFilterStreamCallBack, &clientContext);
status = CFWriteStreamGetStatus(filter->socketStream.w);
if (status == kCFStreamStatusNotOpen) {
result = CFWriteStreamOpen(filter->socketStream.w);
} else {
result = TRUE;
}
if (result) {
errorCode->error = 0;
*openComplete = TRUE;
} else {
*openComplete = TRUE;
*errorCode = CFWriteStreamGetError(filter->socketStream.w);
}
__CFSpinUnlock(&filter->lock);
return result;
}
static void httpWrFilterClose(CFWriteStreamRef stream, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
CFWriteStreamClose(filter->socketStream.w);
__CFSpinUnlock(&filter->lock);
}
static CFStreamError transmitHeader(_CFHTTPFilter *filter, Boolean blockUntilDone) {
CFStreamError err = {0, 0};
long long length;
const UInt8 *bytes;
Boolean firstTime = TRUE;
Boolean isFirstWriteOfHeader = FALSE;
CFWriteStreamRef stream = filter->socketStream.w;
if (__CFBitIsSet(filter->flags, HEADER_TRANSMITTED)) {
return err;
}
if (!filter->header) {
err.domain = kCFStreamErrorDomainHTTP;
err.error = kCFStreamErrorHTTPParseFailure;
return err;
}
if (__CFBitIsSet(filter->flags, IS_HTTPS_PROXY)) {
CFHTTPMessageRef proxyResponse = (CFHTTPMessageRef)CFWriteStreamCopyProperty(filter->socketStream.w, kCFStreamPropertyCONNECTResponse);
int status = CFHTTPMessageGetResponseStatusCode(proxyResponse);
CFRelease(proxyResponse);
if (status != 200) {
filter->_data = NULL;
filter->processedBytes = 0;
__CFBitSet(filter->flags, HEADER_TRANSMITTED);
__CFBitSet(filter->flags, HTTPS_PROXY_FAILURE);
return err;
} else {
__CFBitClear(filter->flags, IS_HTTPS_PROXY); #if defined(__MACH__)
if (filter->customSSLContext) {
if (CFGetTypeID(filter->customSSLContext) == CFDataGetTypeID())
CFWriteStreamSetProperty(filter->socketStream.w, kCFStreamPropertySocketSSLContext, filter->customSSLContext);
else
CFWriteStreamSetProperty(filter->socketStream.w, kCFStreamPropertySSLSettings, filter->customSSLContext);
CFRelease(filter->customSSLContext);
filter->customSSLContext = NULL;
} else
#endif
{
CFURLRef url = CFHTTPMessageCopyRequestURL(filter->header);
CFStringRef host = CFURLCopyHostName(url);
if (host) {
CFWriteStreamSetProperty(stream, _kCFStreamPropertySocketPeerName, host);
CFRelease(host);
}
CFRelease(url);
CFWriteStreamSetProperty(stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
}
}
}
if (!filter->_data) {
length = expectedSizeFromHeaders(filter->header);
if (length != WAIT_FOR_END_OF_STREAM) {
filter->expectedBytes = length;
__CFBitClear(filter->flags, IS_CHUNKED);
} else {
CFStringRef encodingString = CFHTTPMessageCopyHeaderFieldValue(filter->header, _kCFHTTPFilterTransferEncodingHeader);
if (!encodingString) {
CFHTTPMessageSetHeaderFieldValue(filter->header, _kCFHTTPFilterTransferEncodingHeader, _kCFHTTPFilterTransferEncodingChunked2);
} else {
if (!CFStringFindWithOptions(encodingString, _kCFHTTPFilterTransferEncodingChunked, CFRangeMake(0, CFStringGetLength(encodingString)), kCFCompareCaseInsensitive, NULL)) {
CFMutableStringRef newString = CFStringCreateMutableCopy(NULL, 0, encodingString);
CFStringAppend(newString, _kCFHTTPFilterTransferEncodingChunkedSeparator);
CFHTTPMessageSetHeaderFieldValue(filter->header, _kCFHTTPFilterTransferEncodingHeader, newString);
CFRelease(newString);
}
CFRelease(encodingString);
}
__CFBitSet(filter->flags, IS_CHUNKED);
filter->expectedBytes = 0;
}
filter->_data = (CFMutableDataRef)_CFHTTPMessageCopySerializedHeaders(filter->header, __CFBitIsSet(filter->flags, IS_PROXY));
isFirstWriteOfHeader = TRUE;
filter->processedBytes = 0;
}
if (!filter->_data) {
err.domain = kCFStreamErrorDomainHTTP;
err.error = kCFStreamErrorHTTPParseFailure;
return err;
}
length = CFDataGetLength(filter->_data);
bytes = CFDataGetBytePtr(filter->_data);
while (filter->processedBytes < length && (blockUntilDone || CFWriteStreamCanAcceptBytes(stream))) {
CFIndex bytesWritten = CFWriteStreamWrite(stream, bytes + filter->processedBytes, length - filter->processedBytes);
if (bytesWritten < 0) {
err = CFWriteStreamGetError(stream);
if (isFirstWriteOfHeader && err.domain == _kCFStreamErrorDomainNativeSockets && (err.error == EPIPE || err.error == ECONNRESET)) {
err.domain = kCFStreamErrorDomainHTTP;
err.error = kCFStreamErrorHTTPConnectionLost;
}
break;
} else if (bytesWritten == 0) {
if (isFirstWriteOfHeader) {
err.domain = kCFStreamErrorDomainHTTP;
err.error = kCFStreamErrorHTTPConnectionLost;
} else {
setParseFailure(filter, &err);
}
break;
}
filter->processedBytes += bytesWritten;
firstTime = FALSE;
isFirstWriteOfHeader = FALSE;
}
if (err.error != 0 || filter->processedBytes >= length) {
CFRelease(filter->_data);
filter->_data = NULL;
filter->processedBytes = 0;
__CFBitSet(filter->flags, HEADER_TRANSMITTED);
}
return err;
}
#define MAX_CHUNK_HEADER_SIZE (20)
static void sendChunkHeader(CFWriteStreamRef stream, CFIndex chunkLength, Boolean firstChunk, CFStreamError *error) {
UInt8 writeBuffer[MAX_CHUNK_HEADER_SIZE];
UInt8 *writeBase;
CFIndex bytesWritten;
error->error = 0;
writeBuffer[MAX_CHUNK_HEADER_SIZE - 1] = '\n';
writeBuffer[MAX_CHUNK_HEADER_SIZE - 2] = '\r';
writeBase = &(writeBuffer[MAX_CHUNK_HEADER_SIZE-3]);
while (chunkLength > 0) {
int nextDigit = chunkLength & 0xF;
*writeBase = nextDigit < 10 ? '0' + nextDigit : 'A' + nextDigit - 10;
writeBase --;
chunkLength = chunkLength >> 4;
}
if (firstChunk) {
writeBase ++;
} else {
*writeBase = '\n';
writeBase --;
*writeBase = '\r';
}
while (writeBase < writeBuffer + MAX_CHUNK_HEADER_SIZE) {
bytesWritten = CFWriteStreamWrite(stream, writeBase, writeBuffer + MAX_CHUNK_HEADER_SIZE - writeBase);
if (bytesWritten < 0) {
*error = CFWriteStreamGetError(stream);
break;
} else if (bytesWritten == 0) {
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPParseFailure;
break;
} else {
writeBase += bytesWritten;
}
}
}
static CFIndex doChunkedWrite(const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, _CFHTTPFilter *filter) {
CFIndex totalBytesWritten = 0;
CFWriteStreamRef stream = filter->socketStream.w;
if (filter->expectedBytes <= filter->processedBytes) {
sendChunkHeader(stream, bufferLength, __CFBitIsSet(filter->flags, FIRST_CHUNK), error);
__CFBitClear(filter->flags, FIRST_CHUNK);
if (error->error != 0) {
return -1;
}
filter->expectedBytes = bufferLength;
filter->processedBytes = 0;
}
while (filter->expectedBytes > filter->processedBytes && (totalBytesWritten == 0 || CFWriteStreamCanAcceptBytes(stream))) {
int bytesToWrite = filter->expectedBytes - filter->processedBytes > bufferLength ? bufferLength : filter->expectedBytes - filter->processedBytes;
int bytesWritten = CFWriteStreamWrite(stream, buffer, bytesToWrite);
if (bytesWritten < 0) {
*error = CFWriteStreamGetError(stream);
return -1;
} else if (bytesWritten == 0) {
break;
}
filter->processedBytes += bytesWritten;
totalBytesWritten += bytesWritten;
buffer += bytesWritten;
bufferLength -= bytesWritten;
}
if (bufferLength == 0 || !CFWriteStreamCanAcceptBytes(stream)) {
return totalBytesWritten;
} else {
CFIndex newBytesWritten = doChunkedWrite(buffer, bufferLength, error, filter);
if (newBytesWritten < 0) {
return -1;
} else {
return newBytesWritten + totalBytesWritten;
}
}
}
static CFIndex httpWrFilterWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
error->error = 0;
__CFSpinLock(&filter->lock);
if (__CFBitIsSet(filter->flags, HTTPS_PROXY_FAILURE)) {
error->domain = kCFStreamErrorDomainHTTP;
error->error = _kCFStreamErrorHTTPSProxyFailure;
__CFSpinUnlock(&filter->lock);
return -1;
}
if (__CFBitIsSet(filter->flags, MARK_ENABLED) && __CFBitIsSet(filter->flags, AT_MARK)) {
error->error = _kCFStreamErrorHTTPStreamAtMark;
error->domain = kCFStreamErrorDomainHTTP;
__CFSpinUnlock(&filter->lock);
return -1;
}
if (!__CFBitIsSet(filter->flags, HEADER_TRANSMITTED)) {
*error = transmitHeader(filter, TRUE);
}
if (error->error != 0) {
__CFSpinUnlock(&filter->lock);
return -1;
}
if (__CFBitIsSet(filter->flags, IS_CHUNKED)) {
CFIndex result = doChunkedWrite(buffer, bufferLength, error, filter);
__CFSpinUnlock(&filter->lock);
return result;
} else if (filter->processedBytes >= filter->expectedBytes) {
__CFSpinUnlock(&filter->lock);
return 0;
} else {
CFIndex bytesWritten;
if (bufferLength > filter->expectedBytes - filter->processedBytes) {
bufferLength = filter->expectedBytes - filter->processedBytes;
}
bytesWritten = CFWriteStreamWrite(filter->socketStream.w, buffer, bufferLength);
if (bytesWritten < 0) {
*error = CFWriteStreamGetError(filter->socketStream.w);
__CFSpinUnlock(&filter->lock);
return -1;
} else {
filter->processedBytes += bytesWritten;
__CFSpinUnlock(&filter->lock);
return bytesWritten;
}
}
}
static Boolean httpWrFilterCanWrite(CFWriteStreamRef stream, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
if (CFWriteStreamGetStatus(filter->socketStream.w) == kCFStreamStatusError) {
CFStreamError err = CFWriteStreamGetError(filter->socketStream.w);
__CFSpinUnlock(&filter->lock);
CFWriteStreamSignalEvent(stream, kCFStreamEventErrorOccurred, &err);
return FALSE;
}
if (!CFWriteStreamCanAcceptBytes(filter->socketStream.w)) {
__CFSpinUnlock(&filter->lock);
return FALSE;
}
if (__CFBitIsSet(filter->flags, MARK_ENABLED) && __CFBitIsSet(filter->flags, AT_MARK)) {
__CFSpinUnlock(&filter->lock);
return FALSE; }
if (!filter->header) {
__CFSpinUnlock(&filter->lock);
return FALSE; }
if (!__CFBitIsSet(filter->flags, HEADER_TRANSMITTED)) {
CFStreamError error = transmitHeader(filter, FALSE);
if (error.error != 0) {
__CFSpinUnlock(&filter->lock);
CFWriteStreamSignalEvent(stream, kCFStreamEventErrorOccurred, &error);
return FALSE;
}
}
if (!__CFBitIsSet(filter->flags, HEADER_TRANSMITTED) || !CFWriteStreamCanAcceptBytes(filter->socketStream.w)) {
__CFSpinUnlock(&filter->lock);
return FALSE;
}
__CFSpinUnlock(&filter->lock);
return TRUE;
}
static void writeChunkTrailer(CFWriteStreamRef stream, _CFHTTPFilter *filter) {
CFWriteStreamWrite(stream, (const UInt8*)"\r\n0\r\n\r\n", 7);
}
extern
void _CFHTTPWriteStreamWriteMark(CFWriteStreamRef filteredStream) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)CFWriteStreamGetInfoPointer(filteredStream);
if (__CFBitIsSet(filter->flags, MARK_ENABLED)) {
__CFBitSet(filter->flags, AT_MARK);
if (filter->header && __CFBitIsSet(filter->flags, IS_CHUNKED)) {
writeChunkTrailer(filter->socketStream.w, filter);
}
}
__CFBitSet(filter->flags, FIRST_CHUNK);
if (httpWrFilterCanWrite(filteredStream, (void *)filter)) {
_CFWriteStreamSignalEventDelayed(filteredStream, kCFStreamEventCanAcceptBytes, NULL);
}
}
static CFTypeRef httpWrFilterCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
CFTypeRef result = NULL;
__CFSpinLock(&filter->lock);
if (propertyName == _kCFStreamPropertyHTTPSProxyHoldYourFire) {
if (__CFBitIsSet(filter->flags, HTTPS_PROXY_FAILURE)) result = kCFBooleanTrue;
} else if (CFEqual(propertyName, _kCFStreamPropertyHTTPPersistent)) {
result = __CFBitIsSet(filter->flags, MARK_ENABLED) ? kCFBooleanTrue : kCFBooleanFalse;
} else if (filter->header && ((CFHTTPMessageIsRequest(filter->header) && CFEqual(propertyName, kCFStreamPropertyHTTPRequest)) || (!CFHTTPMessageIsRequest(filter->header) && CFEqual(propertyName, kCFStreamPropertyHTTPResponseHeader)))) {
CFRetain(filter->header);
result = filter->header;
#if defined(__MACH__)
} else if (CFEqual(propertyName, kCFStreamPropertySocketSSLContext)) {
if (__CFBitIsSet(filter->flags, FIRST_HEADER_SEEN) && !__CFBitIsSet(filter->flags, IS_HTTPS_PROXY)) {
result = CFWriteStreamCopyProperty(filter->socketStream.w, propertyName);
} else {
if (filter->customSSLContext) CFRetain(filter->customSSLContext);
result = filter->customSSLContext;
}
#endif
} else {
result = CFWriteStreamCopyProperty(filter->socketStream.w, propertyName);
}
__CFSpinUnlock(&filter->lock);
return result;
}
static void prepareHTTPSProxy(_CFHTTPFilter *filter) {
CFStringRef header = CFHTTPMessageCopyHeaderFieldValue(filter->header, _kCFHTTPFilterProxyAuthorizationHeader);
if (header) {
CFHTTPMessageSetHeaderFieldValue(filter->header, _kCFHTTPFilterProxyAuthorizationHeader, NULL);
CFRelease(header);
}
if (!__CFBitIsSet(filter->flags, STRIP_PROXY_AUTH)) {
__CFBitClear(filter->flags, IS_PROXY);
__CFBitSet(filter->flags, IS_HTTPS_PROXY);
__CFBitSet(filter->flags, STRIP_PROXY_AUTH);
}
}
static Boolean httpWrFilterSetProperty(CFWriteStreamRef stream, CFStringRef propName, CFTypeRef propValue, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
if (CFEqual(propName, _kCFStreamPropertyHTTPPersistent)) {
if (propValue == kCFBooleanTrue) {
__CFBitSet(filter->flags, MARK_ENABLED);
} else {
__CFBitClear(filter->flags, MARK_ENABLED);
}
__CFSpinUnlock(&filter->lock);
return TRUE;
} else if ((filter->header == NULL || (__CFBitIsSet(filter->flags, MARK_ENABLED) && __CFBitIsSet(filter->flags, AT_MARK))) && CFEqual(propName, _kCFStreamPropertyHTTPNewHeader) && CFGetTypeID(propValue) == CFHTTPMessageGetTypeID()) {
CFHTTPMessageRef msg = (CFHTTPMessageRef)propValue;
CFRetain(msg);
if (filter->header) CFRelease(filter->header);
filter->header = msg;
filter->expectedBytes = 0;
filter->processedBytes = 0;
if (filter->_data) {
CFRelease(filter->_data);
filter->_data = NULL;
}
__CFBitClear(filter->flags, IS_CHUNKED);
__CFBitClear(filter->flags, DATA_IS_MUTABLE);
__CFBitClear(filter->flags, AT_MARK);
__CFBitClear(filter->flags, HEADER_TRANSMITTED);
__CFBitClear(filter->flags, HTTPS_PROXY_FAILURE);
if ((__CFBitIsSet(filter->flags, STRIP_PROXY_AUTH) || __CFBitIsSet(filter->flags, IS_PROXY)) && CFHTTPMessageIsRequest(msg)) {
CFURLRef url = CFHTTPMessageCopyRequestURL(msg);
CFStringRef scheme = CFURLCopyScheme(url);
CFRelease(url);
if (CFEqual(scheme, _kCFHTTPFilterHTTPSScheme)) {
prepareHTTPSProxy(filter);
}
CFRelease(scheme);
}
if (!__CFBitIsSet(filter->flags, FIRST_HEADER_SEEN)) {
__CFBitSet(filter->flags, FIRST_HEADER_SEEN);
#if defined(__MACH__)
if (filter->customSSLContext && !__CFBitIsSet(filter->flags, IS_HTTPS_PROXY)) {
if (CFGetTypeID(filter->customSSLContext) == CFDataGetTypeID())
CFWriteStreamSetProperty(filter->socketStream.w, kCFStreamPropertySocketSSLContext, filter->customSSLContext);
else
CFWriteStreamSetProperty(filter->socketStream.w, kCFStreamPropertySSLSettings, filter->customSSLContext);
CFRelease(filter->customSSLContext);
filter->customSSLContext = NULL;
}
#endif
}
if (CFWriteStreamCanAcceptBytes(filter->socketStream.w)) {
transmitHeader(filter, FALSE);
}
__CFSpinUnlock(&filter->lock);
return TRUE;
#if defined(__MACH__)
} else if ((CFEqual(propName, kCFStreamPropertySocketSSLContext) || CFEqual(propName, kCFStreamPropertySSLSettings)) && (!__CFBitIsSet(filter->flags, FIRST_HEADER_SEEN) || __CFBitIsSet(filter->flags, IS_HTTPS_PROXY))) {
if (propValue) CFRetain(propValue);
if (filter->customSSLContext) {
CFRelease(filter->customSSLContext);
}
filter->customSSLContext = propValue;
__CFSpinUnlock(&filter->lock);
return TRUE;
#endif
} else {
Boolean result = CFWriteStreamSetProperty(filter->socketStream.w, propName, propValue);
__CFSpinUnlock(&filter->lock);
return result;
}
}
static void httpWrFilterSchedule(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
CFWriteStreamScheduleWithRunLoop(filter->socketStream.w, runLoop, runLoopMode);
__CFSpinUnlock(&filter->lock);
}
static void httpWrFilterUnschedule(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPFilter *filter = (_CFHTTPFilter *)info;
__CFSpinLock(&filter->lock);
CFWriteStreamUnscheduleFromRunLoop(filter->socketStream.w, runLoop, runLoopMode);
__CFSpinUnlock(&filter->lock);
}
static const CFWriteStreamCallBacks httpFilteredStreamCBs = {1,
httpWrFilterCreate,
httpWrFilterDealloc, NULL ,
httpWrFilterOpen, NULL ,
httpWrFilterWrite,
httpWrFilterCanWrite,
httpWrFilterClose,
httpWrFilterCopyProperty,
httpWrFilterSetProperty, NULL,
httpWrFilterSchedule,
httpWrFilterUnschedule};
CF_EXPORT
CFWriteStreamRef CFWriteStreamCreateHTTPStream(CFAllocatorRef alloc, CFHTTPMessageRef header, Boolean isProxy, CFWriteStreamRef socketStream) {
_CFHTTPFilter filter;
CFWriteStreamRef stream;
memset(&filter, 0, sizeof(_CFHTTPFilter));
if (isProxy) {
__CFBitSet(filter.flags, IS_PROXY);
}
filter.socketStream.w = socketStream;
stream = CFWriteStreamCreate(alloc, &httpFilteredStreamCBs, &filter);
if (header) {
CFWriteStreamSetProperty(stream, _kCFStreamPropertyHTTPNewHeader, header);
}
return stream;
}